diff --git a/CHANGELOG b/CHANGELOG index 85af0ad..fcfbe62 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -50,6 +50,7 @@ - fix off-by-one error for lookup of map keys exactly 255 characters long. - improve handling of server not available. - fix LDAP_URI server selection. +- add authentication option for using an external credential cache. 18/06/2007 autofs-5.0.2 ----------------------- diff --git a/include/lookup_ldap.h b/include/lookup_ldap.h index 5b5c475..d6a754d 100644 --- a/include/lookup_ldap.h +++ b/include/lookup_ldap.h @@ -67,10 +67,10 @@ struct lookup_context { char *user; char *secret; char *client_princ; + char *client_cc; int kinit_done; int kinit_successful; #ifdef WITH_SASL - krb5_principal krb5_client_princ; krb5_context krb5ctxt; krb5_ccache krb5_ccache; sasl_conn_t *sasl_conn; diff --git a/modules/cyrus-sasl.c b/modules/cyrus-sasl.c index 303b7f2..f24f46b 100644 --- a/modules/cyrus-sasl.c +++ b/modules/cyrus-sasl.c @@ -72,6 +72,7 @@ */ static const char *krb5ccenv = "KRB5CCNAME"; static const char *krb5ccval = "MEMORY:_autofstkt"; +static const char *default_client = "autofsclient"; static pthread_mutex_t krb5cc_mutex = PTHREAD_MUTEX_INITIALIZER; static unsigned int krb5cc_in_use = 0; @@ -376,7 +377,7 @@ int sasl_do_kinit(unsigned logopt, struct lookup_context *ctxt) { krb5_error_code ret; - krb5_principal tgs_princ, krb5_client_princ = ctxt->krb5_client_princ; + krb5_principal tgs_princ, krb5_client_princ; krb5_creds my_creds; char *tgs_name; int status; @@ -386,8 +387,8 @@ sasl_do_kinit(unsigned logopt, struct lookup_context *ctxt) ctxt->kinit_done = 1; debug(logopt, - "initializing kerberos ticket: client principal %s ", - ctxt->client_princ ? ctxt->client_princ : "autofsclient"); + "initializing kerberos ticket: client principal %s", + ctxt->client_princ ? ctxt->client_princ : default_client); ret = krb5_init_context(&ctxt->krb5ctxt); if (ret) { @@ -424,13 +425,12 @@ sasl_do_kinit(unsigned logopt, struct lookup_context *ctxt) "calling krb5_sname_to_principal using defaults"); ret = krb5_sname_to_principal(ctxt->krb5ctxt, NULL, - "autofsclient", KRB5_NT_SRV_HST, + default_client, KRB5_NT_SRV_HST, &krb5_client_princ); if (ret) { error(logopt, "krb5_sname_to_principal failed for " - "%s with error %d", - ctxt->client_princ ? "" : "autofsclient", ret); + "%s with error %d", default_client, ret); goto out_cleanup_cc; } @@ -441,11 +441,11 @@ sasl_do_kinit(unsigned logopt, struct lookup_context *ctxt) debug(logopt, "krb5_unparse_name failed with error %d", ret); - goto out_cleanup_cc; + goto out_cleanup_client_princ; } debug(logopt, - "principal used for authentication: \"%s\"", tmp_name); + "principal used for authentication: %s", tmp_name); krb5_free_unparsed_name(ctxt->krb5ctxt, tmp_name); } @@ -461,14 +461,14 @@ sasl_do_kinit(unsigned logopt, struct lookup_context *ctxt) if (ret) { error(logopt, "krb5_build_principal failed with error %d", ret); - goto out_cleanup_cc; + goto out_cleanup_client_princ; } ret = krb5_unparse_name(ctxt->krb5ctxt, tgs_princ, &tgs_name); if (ret) { error(logopt, "krb5_unparse_name failed with error %d", ret); - goto out_cleanup_cc; + goto out_cleanup_client_princ; } debug(logopt, "Using tgs name %s", tgs_name); @@ -486,7 +486,6 @@ sasl_do_kinit(unsigned logopt, struct lookup_context *ctxt) goto out_cleanup_unparse; } - status = pthread_mutex_lock(&krb5cc_mutex); if (status) fatal(status); @@ -503,7 +502,7 @@ sasl_do_kinit(unsigned logopt, struct lookup_context *ctxt) if (ret) { error(logopt, "krb5_cc_initialize failed with error %d", ret); - goto out_cleanup_unparse; + goto out_cleanup_creds; } /* and store credentials for that principal */ @@ -511,26 +510,34 @@ sasl_do_kinit(unsigned logopt, struct lookup_context *ctxt) if (ret) { error(logopt, "krb5_cc_store_cred failed with error %d", ret); - goto out_cleanup_unparse; + goto out_cleanup_creds; } /* finally, set the environment variable to point to our * credentials cache */ if (setenv(krb5ccenv, krb5ccval, 1) != 0) { error(logopt, "setenv failed with %d", errno); - goto out_cleanup_unparse; + goto out_cleanup_creds; } ctxt->kinit_successful = 1; debug(logopt, "Kerberos authentication was successful!"); krb5_free_unparsed_name(ctxt->krb5ctxt, tgs_name); + krb5_free_cred_contents(ctxt->krb5ctxt, &my_creds); + krb5_free_principal(ctxt->krb5ctxt, tgs_princ); + krb5_free_principal(ctxt->krb5ctxt, krb5_client_princ); return 0; -out_cleanup_unparse: +out_cleanup_creds: krb5cc_in_use--; + krb5_free_cred_contents(ctxt->krb5ctxt, &my_creds); +out_cleanup_unparse: + krb5_free_principal(ctxt->krb5ctxt, tgs_princ); krb5_free_unparsed_name(ctxt->krb5ctxt, tgs_name); +out_cleanup_client_princ: + krb5_free_principal(ctxt->krb5ctxt, krb5_client_princ); out_cleanup_cc: status = pthread_mutex_lock(&krb5cc_mutex); if (status) @@ -554,6 +561,152 @@ out_cleanup_cc: } /* + * Check a client given external credential cache. + * + * Returns 0 upon success. ctxt->kinit_done and ctxt->kinit_successful + * are set for cleanup purposes. The krb5 context and ccache entries in + * the lookup_context are also filled in. + * + * Upon failure, -1 is returned. + */ +int +sasl_do_kinit_ext_cc(unsigned logopt, struct lookup_context *ctxt) +{ + krb5_principal def_princ; + krb5_principal krb5_client_princ; + krb5_error_code ret; + char *cc_princ, *client_princ; + + if (ctxt->kinit_done) + return 0; + ctxt->kinit_done = 1; + + debug(logopt, + "using external credential cache for auth: client principal %s", + ctxt->client_princ ? ctxt->client_princ : default_client); + + ret = krb5_init_context(&ctxt->krb5ctxt); + if (ret) { + error(logopt, "krb5_init_context failed with %d", ret); + return -1; + } + + ret = krb5_cc_resolve(ctxt->krb5ctxt, ctxt->client_cc, &ctxt->krb5_ccache); + if (ret) { + error(logopt, "krb5_cc_resolve failed with error %d", + ret); + krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache); + krb5_free_context(ctxt->krb5ctxt); + return -1; + } + + ret = krb5_cc_get_principal(ctxt->krb5ctxt, ctxt->krb5_ccache, &def_princ); + if (ret) { + error(logopt, "krb5_cc_get_principal failed with error %d", ret); + krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache); + krb5_free_context(ctxt->krb5ctxt); + return -1; + } + + ret = krb5_unparse_name(ctxt->krb5ctxt, def_princ, &cc_princ); + if (ret) { + error(logopt, "krb5_unparse_name failed with error %d", ret); + krb5_free_principal(ctxt->krb5ctxt, def_princ); + krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache); + krb5_free_context(ctxt->krb5ctxt); + return -1; + } + + debug(logopt, "external credential cache default principal %s", cc_princ); + + /* + * If the principal isn't set in the config construct the default + * so we can check against the default principal of the external + * cred cache. + */ + if (ctxt->client_princ) + client_princ = ctxt->client_princ; + else { + debug(logopt, + "calling krb5_sname_to_principal using defaults"); + + ret = krb5_sname_to_principal(ctxt->krb5ctxt, NULL, + default_client, KRB5_NT_SRV_HST, + &krb5_client_princ); + if (ret) { + error(logopt, + "krb5_sname_to_principal failed for " + "%s with error %d", default_client, ret); + krb5_free_principal(ctxt->krb5ctxt, def_princ); + krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache); + krb5_free_context(ctxt->krb5ctxt); + return -1; + } + + + ret = krb5_unparse_name(ctxt->krb5ctxt, + krb5_client_princ, &client_princ); + if (ret) { + debug(logopt, + "krb5_unparse_name failed with error %d", + ret); + krb5_free_principal(ctxt->krb5ctxt, krb5_client_princ); + krb5_free_principal(ctxt->krb5ctxt, def_princ); + krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache); + krb5_free_context(ctxt->krb5ctxt); + return -1; + } + + debug(logopt, + "principal used for authentication: %s", client_princ); + + krb5_free_principal(ctxt->krb5ctxt, krb5_client_princ); + } + + /* + * Check if the principal to be used matches the default principal in + * the external cred cache. + */ + if (strcmp(cc_princ, client_princ)) { + error(logopt, + "configured client principal %s ", + ctxt->client_princ); + error(logopt, + "external credential cache default principal %s", + cc_princ); + error(logopt, + "cannot use credential cache, external " + "default principal does not match configured " + "principal"); + if (!ctxt->client_princ) + krb5_free_unparsed_name(ctxt->krb5ctxt, client_princ); + krb5_free_unparsed_name(ctxt->krb5ctxt, cc_princ); + krb5_free_principal(ctxt->krb5ctxt, def_princ); + krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache); + krb5_free_context(ctxt->krb5ctxt); + return -1; + } + + if (!ctxt->client_princ) + krb5_free_unparsed_name(ctxt->krb5ctxt, client_princ); + krb5_free_unparsed_name(ctxt->krb5ctxt, cc_princ); + krb5_free_principal(ctxt->krb5ctxt, def_princ); + + /* Set the environment variable to point to the external cred cache */ + if (setenv(krb5ccenv, ctxt->client_cc, 1) != 0) { + error(logopt, "setenv failed with %d", errno); + krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache); + krb5_free_context(ctxt->krb5ctxt); + return -1; + } + ctxt->kinit_successful = 1; + + debug(logopt, "Kerberos authentication was successful!"); + + return 0; +} + +/* * Attempt to bind to the ldap server using a given authentication * mechanism. ldap should be a properly initialzed ldap pointer. * @@ -570,7 +723,11 @@ sasl_bind_mech(unsigned logopt, LDAP *ldap, struct lookup_context *ctxt, const c int result; if (!strncmp(mech, "GSSAPI", 6)) { - if (sasl_do_kinit(logopt, ctxt) != 0) + if (ctxt->client_cc) + result = sasl_do_kinit_ext_cc(logopt, ctxt); + else + result = sasl_do_kinit(logopt, ctxt); + if (result != 0) return NULL; } @@ -774,7 +931,7 @@ autofs_sasl_done(struct lookup_context *ctxt) if (status) fatal(status); - if (--krb5cc_in_use) + if (--krb5cc_in_use || ctxt->client_cc) ret = krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache); else ret = krb5_cc_destroy(ctxt->krb5ctxt, ctxt->krb5_ccache); diff --git a/modules/lookup_ldap.c b/modules/lookup_ldap.c index 93f0477..13fbff7 100644 --- a/modules/lookup_ldap.c +++ b/modules/lookup_ldap.c @@ -651,7 +651,7 @@ int parse_ldap_config(unsigned logopt, struct lookup_context *ctxt) xmlNodePtr root = NULL; char *authrequired, *auth_conf, *authtype; char *user = NULL, *secret = NULL; - char *client_princ = NULL; + char *client_princ = NULL, *client_cc = NULL; char *usetls, *tlsrequired; authtype = user = secret = NULL; @@ -840,6 +840,7 @@ int parse_ldap_config(unsigned logopt, struct lookup_context *ctxt) * client. The default is "autofsclient/hostname@REALM". */ (void)get_property(logopt, root, "clientprinc", &client_princ); + (void)get_property(logopt, root, "credentialcache", &client_cc); ctxt->auth_conf = auth_conf; ctxt->use_tls = use_tls; @@ -851,6 +852,7 @@ int parse_ldap_config(unsigned logopt, struct lookup_context *ctxt) ctxt->user = user; ctxt->secret = secret; ctxt->client_princ = client_princ; + ctxt->client_cc = client_cc; debug(logopt, MODPREFIX "ldap authentication configured with the following options:"); @@ -863,9 +865,10 @@ int parse_ldap_config(unsigned logopt, struct lookup_context *ctxt) debug(logopt, MODPREFIX "user: %s, " "secret: %s, " - "client principal: %s", + "client principal: %s " + "credential cache: %s", user, secret ? "specified" : "unspecified", - client_princ); + client_princ, client_cc); out: xmlFreeDoc(doc); @@ -1128,6 +1131,8 @@ static void free_context(struct lookup_context *ctxt) free(ctxt->secret); if (ctxt->client_princ) free(ctxt->client_princ); + if (ctxt->client_cc) + free(ctxt->client_cc); if (ctxt->mapname) free(ctxt->mapname); if (ctxt->qdn) diff --git a/samples/autofs_ldap_auth.conf b/samples/autofs_ldap_auth.conf index e10d1ea..a1f60c0 100644 --- a/samples/autofs_ldap_auth.conf +++ b/samples/autofs_ldap_auth.conf @@ -56,6 +56,11 @@ clientprinc - When using GSSAPI authentication, this attribute is consulted to determine the principal name to use when authenticating to the directory server. By default, this will be set to "autofsclient/@. + +credentialcache - When using GSSAPI authentication, this attribute + can be used to specify an externally configured credential + cache that is used during authentication. By default, autofs + will setup a memory based credential cache. -->