summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/config/SSSDConfig.py1
-rw-r--r--src/providers/ipa/ipa_common.c74
-rw-r--r--src/providers/ipa/ipa_common.h2
-rw-r--r--src/providers/ldap/ldap_child.c5
-rw-r--r--src/providers/ldap/ldap_common.c1
-rw-r--r--src/providers/ldap/sdap.h1
-rw-r--r--src/providers/ldap/sdap_async_connection.c9
-rw-r--r--src/providers/ldap/sdap_child_helpers.c9
-rw-r--r--src/util/sss_krb5.c174
-rw-r--r--src/util/sss_krb5.h8
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,