autofs-5.0.1 - add external bind method From: Ian Kent <raven@themaw.net> Add sasl external bind handler. --- include/lookup_ldap.h | 7 ++ modules/Makefile | 5 + modules/cyrus-sasl-extern.c | 117 ++++++++++++++++++++++++++++++++++++++++++ modules/cyrus-sasl.c | 20 +++++++ modules/lookup_ldap.c | 117 ++++++++++++++++++++++++++++++++++++------ samples/autofs_ldap_auth.conf | 28 +++++++++- 6 files changed, 277 insertions(+), 17 deletions(-) create mode 100644 modules/cyrus-sasl-extern.c --- autofs-5.0.1.orig/include/lookup_ldap.h +++ autofs-5.0.1/include/lookup_ldap.h @@ -9,6 +9,7 @@ #include <libxml/tree.h> #endif +#include "list.h" #include "dclist.h" struct ldap_schema { @@ -75,9 +76,13 @@ struct lookup_context { int kinit_done; int kinit_successful; #ifdef WITH_SASL + /* Kerberos */ krb5_context krb5ctxt; krb5_ccache krb5_ccache; sasl_conn_t *sasl_conn; + /* SASL external */ + char *extern_cert; + char *extern_key; #endif /* keytab file name needs to be added */ @@ -109,6 +114,8 @@ int autofs_sasl_bind(unsigned logopt, LD void autofs_sasl_unbind(struct lookup_context *ctxt); void autofs_sasl_dispose(struct lookup_context *ctxt); void autofs_sasl_done(void); +/* cyrus-sasl-extern */ +int do_sasl_extern(LDAP *ldap, struct lookup_context *ctxt); #endif #endif --- autofs-5.0.1.orig/modules/Makefile +++ autofs-5.0.1/modules/Makefile @@ -42,7 +42,7 @@ ifeq ($(LDAP), 1) SRCS += lookup_ldap.c MODS += lookup_ldap.so ifeq ($(SASL), 1) - SASL_OBJ = cyrus-sasl.o + SASL_OBJ = cyrus-sasl.o cyrus-sasl-extern.o LDAP_FLAGS += $(SASL_FLAGS) $(XML_FLAGS) -DLDAP_THREAD_SAFE LIBLDAP += $(LIBSASL) $(XML_LIBS) endif @@ -87,6 +87,9 @@ lookup_hesiod.so: lookup_hesiod.c cyrus-sasl.o: cyrus-sasl.c $(CC) $(CFLAGS) $(LDAP_FLAGS) -c $< +cyrus-sasl-extern.o: cyrus-sasl-extern.c + $(CC) $(CFLAGS) $(LDAP_FLAGS) -c $< + lookup_ldap.so: lookup_ldap.c dclist.o $(SASL_OBJ) $(CC) $(SOLDFLAGS) $(CFLAGS) $(LDAP_FLAGS) -o lookup_ldap.so \ lookup_ldap.c dclist.o $(SASL_OBJ) \ --- /dev/null +++ autofs-5.0.1/modules/cyrus-sasl-extern.c @@ -0,0 +1,117 @@ +/* + * cyrus-sasl-extern.c - Module for Cyrus sasl external authentication. + * + * Copyright 2010 Ian Kent <raven@themaw.net> + * Copyright 2010 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "config.h" + +#ifdef WITH_SASL +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sasl/sasl.h> +#include <ldap.h> +#include <ldap_cdefs.h> +#include <lber_types.h> + +#include "lookup_ldap.h" + +struct values { + char *mech; + char *realm; + char *authcid; + char *authzid; + char *password; + char **resps; + int nresps; +}; + +static int interaction(unsigned flags, sasl_interact_t *interact, void *values) +{ + const char *val = interact->defresult; + struct values *vals = values; + + switch(interact->id) { + case SASL_CB_GETREALM: + if (values) + val = vals->realm; + break; + + case SASL_CB_AUTHNAME: + if (values) + val = vals->authcid; + break; + + case SASL_CB_PASS: + if (values) + val = vals->password; + break; + + case SASL_CB_USER: + if (values) + val = vals->authzid; + break; + + case SASL_CB_NOECHOPROMPT: + case SASL_CB_ECHOPROMPT: + break; + } + + if (val && !*val) + val = NULL; + + if (val || interact->id == SASL_CB_USER) { + interact->result = (val && *val) ? val : ""; + interact->len = strlen(interact->result); + } + + return LDAP_SUCCESS; +} + +int sasl_extern_interact(LDAP *ldap, unsigned flags, void *values, void *list) +{ + sasl_interact_t *interact = list; + + if (!ldap) + return LDAP_PARAM_ERROR; + + while (interact->id != SASL_CB_LIST_END) { + int rc = interaction(flags, interact, values); + if (rc) + return rc; + interact++; + } + + return LDAP_SUCCESS; +} + +int do_sasl_extern(LDAP *ldap, struct lookup_context *ctxt) +{ + int flags = LDAP_SASL_QUIET; + char *mech = ctxt->sasl_mech; + struct values values; + int rc; + + memset(&values, 0, sizeof(struct values)); + values.mech = mech; + + rc = ldap_sasl_interactive_bind_s(ldap, NULL, mech, NULL, NULL, + flags, sasl_extern_interact, &values); + return rc; +} +#endif --- autofs-5.0.1.orig/modules/cyrus-sasl.c +++ autofs-5.0.1/modules/cyrus-sasl.c @@ -867,6 +867,26 @@ autofs_sasl_bind(unsigned logopt, LDAP * if (ctxt->sasl_conn) return 0; + if (ctxt->sasl_mech && !strncmp(ctxt->sasl_mech, "EXTERNAL", 8)) { + int result; + + debug(logopt, + "Attempting sasl bind with mechanism %s", + ctxt->sasl_mech); + + result = do_sasl_extern(ldap, ctxt); + if (result) + debug(logopt, + "Failed to authenticate with mech %s", + ctxt->sasl_mech); + else + debug(logopt, + "sasl bind with mechanism %s succeeded", + ctxt->sasl_mech); + + return result; + } + sasl_auth_id = ctxt->user; sasl_auth_secret = ctxt->secret; --- autofs-5.0.1.orig/modules/lookup_ldap.c +++ autofs-5.0.1/modules/lookup_ldap.c @@ -42,6 +42,9 @@ int lookup_version = AUTOFS_LOOKUP_VERSION; /* Required by protocol */ +#define ENV_LDAPTLS_CERT "LDAPTLS_CERT" +#define ENV_LDAPTLS_KEY "LDAPTLS_KEY" + static struct ldap_schema common_schema[] = { {"nisMap", "nisMapName", "nisObject", "cn", "nisMapEntry"}, {"automountMap", "ou", "automount", "cn", "automountInformation"}, @@ -51,6 +54,43 @@ static unsigned int common_schema_count static int decode_percent_hack(const char *, char **); +static char *get_set_env(unsigned logopt, const char *name, const char *val) +{ + char *save = NULL; + char *tmp; + int ret; + + tmp = getenv(name); + if (tmp) { + save = strdup(tmp); + if (!save) { + error(logopt, + "failed to alloc save string for %s", name); + return NULL; + } + } + + ret = setenv(name, val, 1); + if (ret == -1) { + error(logopt, "failed to set config value for %s", name); + free(save); + return NULL; + } + + return save; +} + +static void restore_env(unsigned logopt, const char *name, char *val) +{ + int ret; + + ret = setenv(name, val, 1); + if (ret == -1) + error(logopt, "failed to restore config value for %s", name); + free(val); + return; +} + static void uris_mutex_lock(struct lookup_context *ctxt) { int status = pthread_mutex_lock(&ctxt->uris_mutex); @@ -506,17 +546,29 @@ static int do_bind(unsigned logopt, LDAP static LDAP *do_connect(unsigned logopt, const char *uri, struct lookup_context *ctxt) { + char *env_tls_cert = NULL; + char *env_tls_key = NULL; LDAP *ldap; - ldap = init_ldap_connection(logopt, uri, ctxt); - if (!ldap) - return NULL; + if (ctxt->extern_cert && ctxt->extern_key) { + env_tls_cert = get_set_env(logopt, ENV_LDAPTLS_CERT, ctxt->extern_cert); + env_tls_key = get_set_env(logopt, ENV_LDAPTLS_KEY, ctxt->extern_key); + } - if (!do_bind(logopt, ldap, uri, ctxt)) { - unbind_ldap_connection(logopt, ldap, ctxt); - return NULL; + ldap = init_ldap_connection(logopt, uri, ctxt); + if (ldap) { + if (!do_bind(logopt, ldap, uri, ctxt)) { + unbind_ldap_connection(logopt, ldap, ctxt); + ldap = NULL; + } } + if (env_tls_cert) + restore_env(logopt, ENV_LDAPTLS_CERT, env_tls_cert); + + if (env_tls_key) + restore_env(logopt, ENV_LDAPTLS_KEY, env_tls_key); + return ldap; } @@ -750,6 +802,7 @@ int parse_ldap_config(unsigned logopt, s xmlNodePtr root = NULL; char *authrequired, *auth_conf, *authtype; char *user = NULL, *secret = NULL; + char *extern_cert = NULL, *extern_key = NULL; char *client_princ = NULL, *client_cc = NULL; char *usetls, *tlsrequired; @@ -931,6 +984,32 @@ int parse_ldap_config(unsigned logopt, s ret = -1; goto out; } + } else if (auth_required == LDAP_AUTH_REQUIRED && + (authtype && !strncmp(authtype, "EXTERNAL", 8))) { + ret = get_property(logopt, root, "external_cert", &extern_cert); + ret |= get_property(logopt, root, "external_key", &extern_key); + /* + * For EXTERNAL auth to function we need a client certificate + * and and certificate key. The ca certificate used to verify + * the server certificate must also be set correctly in the + * global configuration as the connection must be encrypted + * and the server and client certificates must have been + * verified for the EXTERNAL method to be offerred by the + * server. + */ + if (ret != 0 || !extern_cert || !extern_key) { + error(logopt, MODPREFIX + "failure getting values for authentication " + "type %s", authtype); + free(authtype); + if (extern_cert) + free(extern_cert); + if (extern_key) + free(extern_key); + + ret = -1; + goto out; + } } /* @@ -951,6 +1030,8 @@ int parse_ldap_config(unsigned logopt, s ctxt->secret = secret; ctxt->client_princ = client_princ; ctxt->client_cc = client_cc; + ctxt->extern_cert = extern_cert; + ctxt->extern_key = extern_key; debug(logopt, MODPREFIX "ldap authentication configured with the following options:"); @@ -960,14 +1041,18 @@ int parse_ldap_config(unsigned logopt, s "auth_required: %u, " "sasl_mech: %s", use_tls, tls_required, auth_required, authtype); - debug(logopt, MODPREFIX - "user: %s, " - "secret: %s, " - "client principal: %s " - "credential cache: %s", - user, secret ? "specified" : "unspecified", - client_princ, client_cc); - + if (authtype && !strncmp(authtype, "EXTERNAL", 8)) { + debug(logopt, MODPREFIX "external cert: %s", extern_cert); + debug(logopt, MODPREFIX "external key: %s ", extern_key); + } else { + debug(logopt, MODPREFIX + "user: %s, " + "secret: %s, " + "client principal: %s " + "credential cache: %s", + user, secret ? "specified" : "unspecified", + client_princ, client_cc); + } out: xmlFreeDoc(doc); @@ -1219,6 +1304,10 @@ static void free_context(struct lookup_c defaults_free_searchdns(ctxt->sdns); if (ctxt->dclist) free_dclist(ctxt->dclist); + if (ctxt->extern_cert) + free(ctxt->extern_cert); + if (ctxt->extern_key) + free(ctxt->extern_key); free(ctxt); return; --- autofs-5.0.1.orig/samples/autofs_ldap_auth.conf +++ autofs-5.0.1/samples/autofs_ldap_auth.conf @@ -34,13 +34,37 @@ authtype - This attribute can be used automounter will attempt to authenticate to the ldap server using the list of supportedSASLmechanisms obtained from the directory server. Explicitly setting the authtype will bypass - this selection and only try the mechanism specified. Legal - values for this attribute include: + this selection and only try the mechanism specified. The + EXTERNAL mechanism may be used to authenticate using a client + certificate and requires that authrequired set to "yes" if + using SSL or usetls, tlsrequired and authrequired all set to + "yes" if using TLS, in addition to authtype being set EXTERNAL. + + Legal values for this attribute include: "GSSAPI" "LOGIN" "PLAIN" "ANONYMOUS" "DIGEST-MD5" + "EXTERNAL" + + If using authtype EXTERNAL two additional configuration entries + are required: + + external_cert="<client certificate path>" + + This specifies the path of the file containing the client + certificate. + + external_key="<client certificate key path>" + + This specifies the path of the file containing the client + certificate key. + + These two configuration entries are mandatory when using the + EXTERNAL method as the HOME environment variable cannot be + assumed to be set or, if it is, to be set to the location we + expect. user - This attribute holds the authentication identity used by authentication mechanisms that require it. Legal values for