diff options
-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 c3d9ed40..02f76af2 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 61859a98..7ba4fd5a 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 5ff0ba4f..12a49270 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 f4be1857..fb8dd806 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 39e9b71d..11c4491f 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 fce95acc..c06b8a3b 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 b295c56e..500e5f88 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 5a15e661..d0f6caeb 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 459f48b5..064bc6b1 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 f25f3285..d17bfe96 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, |