diff options
author | Jan Zeleny <jzeleny@redhat.com> | 2011-03-29 02:46:25 -0400 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2011-04-25 08:06:34 -0400 |
commit | e81a816cddab4a62f263d1a0274d5d3f101e8e0f (patch) | |
tree | de3d6baa2ac2d39c4d50d1ce5a911e435dc0e3a9 /src | |
parent | d03617ab9106c14b46ab3dc85d5c8ced393da533 (diff) | |
download | sssd-e81a816cddab4a62f263d1a0274d5d3f101e8e0f.tar.gz sssd-e81a816cddab4a62f263d1a0274d5d3f101e8e0f.tar.xz sssd-e81a816cddab4a62f263d1a0274d5d3f101e8e0f.zip |
Modify principal selection for keytab authentication
Currently we construct the principal as host/fqdn@REALM. The problem
with this is that this principal doesn't have to be in the keytab. In
that case the provider fails to start. It is better to scan the keytab
and find the most suitable principal to use. Only in case no suitable
principal is found the backend should fail to start.
The second issue solved by this patch is that the realm we are
authenticating the machine to can be in general different from the realm
our users are part of (in case of cross Kerberos trust).
The patch adds new configuration option SDAP_SASL_REALM.
https://fedorahosted.org/sssd/ticket/781
Diffstat (limited to 'src')
-rw-r--r-- | src/config/SSSDConfig.py | 1 | ||||
-rw-r--r-- | src/providers/ipa/ipa_common.c | 74 | ||||
-rw-r--r-- | src/providers/ipa/ipa_common.h | 2 | ||||
-rw-r--r-- | src/providers/ldap/ldap_child.c | 5 | ||||
-rw-r--r-- | src/providers/ldap/ldap_common.c | 1 | ||||
-rw-r--r-- | src/providers/ldap/sdap.h | 1 | ||||
-rw-r--r-- | src/providers/ldap/sdap_async_connection.c | 9 | ||||
-rw-r--r-- | src/providers/ldap/sdap_child_helpers.c | 9 | ||||
-rw-r--r-- | src/util/sss_krb5.c | 174 | ||||
-rw-r--r-- | src/util/sss_krb5.h | 8 |
10 files changed, 254 insertions, 30 deletions
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py index c3d9ed408..02f76af28 100644 --- a/src/config/SSSDConfig.py +++ b/src/config/SSSDConfig.py @@ -133,6 +133,7 @@ option_strings = { 'ldap_tls_reqcert' : _('Require TLS certificate verification'), 'ldap_sasl_mech' : _('Specify the sasl mechanism to use'), 'ldap_sasl_authid' : _('Specify the sasl authorization id to use'), + 'ldap_sasl_realm' : _('Specify the sasl authorization realm to use'), 'ldap_krb5_keytab' : _('Kerberos service keytab'), 'ldap_krb5_init_creds' : _('Use Kerberos auth for LDAP connection'), 'ldap_referrals' : _('Follow LDAP referrals'), diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index 61859a981..7ba4fd5a4 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -28,6 +28,7 @@ #include "providers/ipa/ipa_common.h" #include "providers/ldap/sdap_async_private.h" +#include "util/sss_krb5.h" struct dp_option ipa_basic_opts[] = { { "ipa_domain", DP_OPT_STRING, NULL_STRING, NULL_STRING }, @@ -69,6 +70,7 @@ struct dp_option ipa_def_ldap_opts[] = { { "ldap_id_use_start_tls", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, { "ldap_sasl_mech", DP_OPT_STRING, { "GSSAPI" } , NULL_STRING }, { "ldap_sasl_authid", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "ldap_sasl_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_krb5_keytab", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_krb5_init_creds", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, /* use the same parm name as the krb5 module so we set it only once */ @@ -263,10 +265,14 @@ int ipa_get_id_options(struct ipa_options *ipa_opts, struct sdap_options **_opts) { TALLOC_CTX *tmpctx; - char *hostname; + char *primary; char *basedn; char *realm; char *value; + char *desired_realm; + char *desired_primary; + bool primary_requested = true; + bool realm_requested = true; int ret; int i; @@ -323,26 +329,6 @@ int ipa_get_id_options(struct ipa_options *ipa_opts, dp_opt_get_string(ipa_opts->id->basic, SDAP_SEARCH_BASE))); } - /* set the ldap_sasl_authid if the ipa_hostname override was specified */ - if (NULL == dp_opt_get_string(ipa_opts->id->basic, SDAP_SASL_AUTHID)) { - hostname = dp_opt_get_string(ipa_opts->basic, IPA_HOSTNAME); - if (hostname) { - value = talloc_asprintf(tmpctx, "host/%s", hostname); - if (!value) { - ret = ENOMEM; - goto done; - } - ret = dp_opt_set_string(ipa_opts->id->basic, - SDAP_SASL_AUTHID, value); - if (ret != EOK) { - goto done; - } - } - DEBUG(6, ("Option %s set to %s\n", - ipa_opts->id->basic[SDAP_SASL_AUTHID].opt_name, - dp_opt_get_string(ipa_opts->id->basic, SDAP_SASL_AUTHID))); - } - /* set krb realm */ if (NULL == dp_opt_get_string(ipa_opts->id->basic, SDAP_KRB5_REALM)) { realm = dp_opt_get_string(ipa_opts->basic, IPA_KRB5_REALM); @@ -362,6 +348,52 @@ int ipa_get_id_options(struct ipa_options *ipa_opts, dp_opt_get_string(ipa_opts->id->basic, SDAP_KRB5_REALM))); } + /* Configuration of SASL auth ID and realm */ + desired_primary = dp_opt_get_string(ipa_opts->id->basic, SDAP_SASL_AUTHID); + if (!desired_primary) { + primary_requested = false; + desired_primary = dp_opt_get_string(ipa_opts->id->basic, IPA_HOSTNAME); + } + desired_realm = dp_opt_get_string(ipa_opts->id->basic, SDAP_SASL_REALM); + if (!desired_realm) { + realm_requested = false; + desired_realm = dp_opt_get_string(ipa_opts->id->basic, IPA_KRB5_REALM); + } + + ret = select_principal_from_keytab(tmpctx, + dp_opt_get_string(ipa_opts->auth, + KRB5_KEYTAB), + desired_primary, desired_realm, + NULL, &primary, &realm); + if (ret != EOK) { + goto done; + } + + if ((primary_requested && strcmp(desired_primary, primary) != 0) || + (realm_requested && strcmp(desired_realm, realm) != 0)) { + DEBUG(1, ("Configured SASL auth ID/realm not found in keytab.\n")); + ret = ENOENT; + goto done; + } + + ret = dp_opt_set_string(ipa_opts->id->basic, + SDAP_SASL_AUTHID, primary); + if (ret != EOK) { + goto done; + } + DEBUG(6, ("Option %s set to %s\n", + ipa_opts->id->basic[SDAP_SASL_AUTHID].opt_name, + dp_opt_get_string(ipa_opts->id->basic, SDAP_SASL_AUTHID))); + + ret = dp_opt_set_string(ipa_opts->id->basic, + SDAP_SASL_REALM, realm); + if (ret != EOK) { + goto done; + } + DEBUG(6, ("Option %s set to %s\n", + ipa_opts->id->basic[SDAP_SASL_REALM].opt_name, + dp_opt_get_string(ipa_opts->id->basic, SDAP_SASL_REALM))); + /* fix schema to IPAv1 for now */ ipa_opts->id->schema_type = SDAP_SCHEMA_IPA_V1; diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h index 5ff0ba4ff..12a492707 100644 --- a/src/providers/ipa/ipa_common.h +++ b/src/providers/ipa/ipa_common.h @@ -35,7 +35,7 @@ struct ipa_service { /* the following defines are used to keep track of the options in the ldap * module, so that if they change and ipa is not updated correspondingly * this will trigger a runtime abort error */ -#define IPA_OPTS_BASIC_TEST 48 +#define IPA_OPTS_BASIC_TEST 49 /* the following define is used to keep track of the options in the krb5 * module, so that if they change and ipa is not updated correspondingly diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c index f4be18571..fb8dd8063 100644 --- a/src/providers/ldap/ldap_child.c +++ b/src/providers/ldap/ldap_child.c @@ -196,8 +196,9 @@ static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx, } hostname[511] = '\0'; - full_princ = talloc_asprintf(memctx, "host/%s@%s", - hostname, realm_name); + ret = select_principal_from_keytab(memctx, hostname, realm_name, + keytab_name, &full_princ, NULL, NULL); + if (ret) goto done; } if (!full_princ) { krberr = KRB5KRB_ERR_GENERIC; diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index 39e9b71dc..11c4491f9 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -63,6 +63,7 @@ struct dp_option default_basic_opts[] = { { "ldap_id_use_start_tls", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, { "ldap_sasl_mech", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_sasl_authid", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "ldap_sasl_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_krb5_keytab", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_krb5_init_creds", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, /* use the same parm name as the krb5 module so we set it only once */ diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index fce95accc..c06b8a3b7 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -172,6 +172,7 @@ enum sdap_basic_opt { SDAP_ID_TLS, SDAP_SASL_MECH, SDAP_SASL_AUTHID, + SDAP_SASL_REALM, SDAP_KRB5_KEYTAB, SDAP_KRB5_KINIT, SDAP_KRB5_KDC, diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c index b295c56e9..500e5f886 100644 --- a/src/providers/ldap/sdap_async_connection.c +++ b/src/providers/ldap/sdap_async_connection.c @@ -1318,6 +1318,12 @@ static void sdap_cli_kinit_step(struct tevent_req *req) struct sdap_cli_connect_state *state = tevent_req_data(req, struct sdap_cli_connect_state); struct tevent_req *subreq; + const char *realm; + + realm = dp_opt_get_string(state->opts->basic, SDAP_SASL_REALM); + if (!realm) { + realm = dp_opt_get_string(state->opts->basic, SDAP_KRB5_REALM); + } subreq = sdap_kinit_send(state, state->ev, state->be, @@ -1329,8 +1335,7 @@ static void sdap_cli_kinit_step(struct tevent_req *req) SDAP_KRB5_KEYTAB), dp_opt_get_string(state->opts->basic, SDAP_SASL_AUTHID), - dp_opt_get_string(state->opts->basic, - SDAP_KRB5_REALM), + realm, dp_opt_get_int(state->opts->basic, SDAP_KRB5_TICKET_LIFETIME)); if (!subreq) { diff --git a/src/providers/ldap/sdap_child_helpers.c b/src/providers/ldap/sdap_child_helpers.c index 5a15e661e..d0f6caeb2 100644 --- a/src/providers/ldap/sdap_child_helpers.c +++ b/src/providers/ldap/sdap_child_helpers.c @@ -458,6 +458,12 @@ int setup_child(struct sdap_id_ctx *ctx) const char *mech; unsigned v; FILE *debug_filep; + const char *realm; + + realm = dp_opt_get_string(ctx->opts->basic, SDAP_SASL_REALM); + if (!realm) { + realm = dp_opt_get_string(ctx->opts->basic, SDAP_KRB5_REALM); + } mech = dp_opt_get_string(ctx->opts->basic, SDAP_SASL_MECH); @@ -468,8 +474,7 @@ int setup_child(struct sdap_id_ctx *ctx) if (mech && (strcasecmp(mech, "GSSAPI") == 0)) { ret = sss_krb5_verify_keytab(dp_opt_get_string(ctx->opts->basic, SDAP_SASL_AUTHID), - dp_opt_get_string(ctx->opts->basic, - SDAP_KRB5_REALM), + realm, dp_opt_get_string(ctx->opts->basic, SDAP_KRB5_KEYTAB)); diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c index 459f48b5e..064bc6b12 100644 --- a/src/util/sss_krb5.c +++ b/src/util/sss_krb5.c @@ -26,6 +26,175 @@ #include "util/util.h" #include "util/sss_krb5.h" +errno_t select_principal_from_keytab(TALLOC_CTX *mem_ctx, + const char *hostname, + const char *desired_realm, + const char *keytab_name, + char **_principal, + char **_primary, + char **_realm) +{ + krb5_error_code kerr = 0; + krb5_context krb_ctx = NULL; + krb5_keytab keytab; + krb5_principal client_princ = NULL; + TALLOC_CTX *tmp_ctx; + char *primary = NULL; + char *realm = NULL; + int i = 0; + errno_t ret; + char *principal_string; + + /** + * Priority of lookup: + * - foobar$@REALM (AD domain) + * - host/our.hostname@REALM + * - host/foobar@REALM + * - host/foo@BAR + * - pick the first principal in the keytab + */ + const char *primary_patterns[] = {"%s$", "*$", "host/%s", "host/*", "host/*", NULL}; + const char *realm_patterns[] = {"%s", "%s", "%s", "%s", NULL, NULL}; + + DEBUG(5, ("trying to select the most appropriate principal from keytab\n")); + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + DEBUG(1, ("talloc_new failed\n")); + return ENOMEM; + } + + kerr = krb5_init_context(&krb_ctx); + if (kerr) { + DEBUG(2, ("Failed to init kerberos context\n")); + ret = EFAULT; + goto done; + } + + if (keytab_name != NULL) { + kerr = krb5_kt_resolve(krb_ctx, keytab_name, &keytab); + } else { + kerr = krb5_kt_default(krb_ctx, &keytab); + } + if (kerr) { + DEBUG(0, ("Failed to read keytab file: %s\n", + sss_krb5_get_error_message(krb_ctx, kerr))); + ret = EFAULT; + goto done; + } + + if (!desired_realm) { + desired_realm = "*"; + } + if (!hostname) { + hostname = "*"; + } + + do { + if (primary_patterns[i]) { + primary = talloc_asprintf(tmp_ctx, primary_patterns[i], hostname); + if (primary == NULL) { + ret = ENOMEM; + goto done; + } + } else { + primary = NULL; + } + if (realm_patterns[i]) { + realm = talloc_asprintf(tmp_ctx, realm_patterns[i], desired_realm); + if (realm == NULL) { + ret = ENOMEM; + goto done; + } + } else { + realm = NULL; + } + + kerr = find_principal_in_keytab(krb_ctx, keytab, primary, realm, + &client_princ); + talloc_zfree(primary); + talloc_zfree(realm); + if (kerr == 0) { + break; + } + if (client_princ != NULL) { + krb5_free_principal(krb_ctx, client_princ); + client_princ = NULL; + } + i++; + } while(primary_patterns[i-1] != NULL || realm_patterns[i-1] != NULL); + + if (kerr == 0) { + if (_principal) { + kerr = krb5_unparse_name(krb_ctx, client_princ, &principal_string); + if (kerr) { + DEBUG(1, ("krb5_unparse_name failed")); + ret = EFAULT; + goto done; + } + + *_principal = talloc_strdup(mem_ctx, principal_string); + free(principal_string); + if (!*_principal) { + DEBUG(1, ("talloc_strdup failed")); + ret = ENOMEM; + goto done; + } + DEBUG(5, ("Selected principal: %s\n", *_principal)); + } + + if (_primary) { + kerr = krb5_unparse_name_flags(krb_ctx, client_princ, + KRB5_PRINCIPAL_UNPARSE_NO_REALM, + &principal_string); + if (kerr) { + DEBUG(1, ("krb5_unparse_name failed")); + ret = EFAULT; + goto done; + } + + *_primary = talloc_strdup(mem_ctx, principal_string); + free(principal_string); + if (!*_primary) { + DEBUG(1, ("talloc_strdup failed")); + if (_principal) talloc_zfree(*_principal); + ret = ENOMEM; + goto done; + } + DEBUG(5, ("Selected primary: %s\n", *_primary)); + } + + if (_realm) { + *_realm = talloc_asprintf(mem_ctx, "%.*s", + krb5_princ_realm(ctx, client_princ)->length, + krb5_princ_realm(ctx, client_princ)->data); + if (!*_realm) { + DEBUG(1, ("talloc_asprintf failed")); + if (_principal) talloc_zfree(*_principal); + if (_primary) talloc_zfree(*_primary); + ret = ENOMEM; + goto done; + } + DEBUG(5, ("Selected realm: %s\n", *_realm)); + } + + ret = EOK; + } else { + DEBUG(3, ("No suitable principal found in keytab\n")); + ret = ENOENT; + } + +done: + if (keytab) krb5_kt_close(krb_ctx, keytab); + if (krb_ctx) krb5_free_context(krb_ctx); + if (client_princ != NULL) { + krb5_free_principal(krb_ctx, client_princ); + client_princ = NULL; + } + talloc_free(tmp_ctx); + return ret; +} + + int sss_krb5_verify_keytab(const char *principal, const char *realm_str, const char *keytab_name) @@ -104,8 +273,9 @@ int sss_krb5_verify_keytab(const char *principal, } hostname[511] = '\0'; - full_princ = talloc_asprintf(tmp_ctx, "host/%s@%s", - hostname, realm_name); + ret = select_principal_from_keytab(tmp_ctx, hostname, realm_name, + keytab_name, &full_princ, NULL, NULL); + if (ret) goto done; } if (!full_princ) { ret = ENOMEM; diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h index f25f3285b..d17bfe969 100644 --- a/src/util/sss_krb5.h +++ b/src/util/sss_krb5.h @@ -64,6 +64,14 @@ krb5_error_code find_principal_in_keytab(krb5_context ctx, const char *pattern_realm, krb5_principal *princ); +errno_t select_principal_from_keytab(TALLOC_CTX *mem_ctx, + const char *hostname, + const char *desired_realm, + const char *keytab_name, + char **_principal, + char **_primary, + char **_realm); + #ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_EXPIRE_CALLBACK typedef void krb5_expire_callback_func(krb5_context context, void *data, krb5_timestamp password_expiration, |