diff options
Diffstat (limited to 'support/nfsidmap/umich_ldap.c')
-rw-r--r-- | support/nfsidmap/umich_ldap.c | 1615 |
1 files changed, 1615 insertions, 0 deletions
diff --git a/support/nfsidmap/umich_ldap.c b/support/nfsidmap/umich_ldap.c new file mode 100644 index 0000000..1aa2af4 --- /dev/null +++ b/support/nfsidmap/umich_ldap.c @@ -0,0 +1,1615 @@ +/* + * umich_ldap.c + * + * Copyright (c) 2000 The Regents of the University of Michigan. + * All rights reserved. + * + * Copyright (c) 2004 Andy Adamson <andros@UMICH.EDU> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <limits.h> +#include <pwd.h> +#include <err.h> +#ifdef HAVE_GSSAPI_GSSAPI_KRB5_H +#include <gssapi/gssapi_krb5.h> +#endif /* HAVE_GSSAPI_GSSAPI_KRB5_H */ +#ifdef HAVE_SASL_H +#include <sasl.h> +#endif /* HAVE_SASL_H */ +#ifdef HAVE_SASL_SASL_H +#include <sasl/sasl.h> +#endif /* HAVE_SASL_SASL_H */ +/* We are using deprecated functions, get the prototypes... */ +#define LDAP_DEPRECATED 1 +#include <ldap.h> +#include "nfslib.h" +#include "nfsidmap.h" +#include "nfsidmap_plugin.h" +#include "nfsidmap_private.h" +#include "conffile.h" + +/* attribute/objectclass default mappings */ +#define DEFAULT_UMICH_OBJCLASS_REMOTE_PERSON "NFSv4RemotePerson" +#define DEFAULT_UMICH_OBJCLASS_REMOTE_GROUP "NFSv4RemoteGroup" +#define DEFAULT_UMICH_ATTR_NFSNAME "NFSv4Name" +#define DEFAULT_UMICH_ATTR_ACCTNAME "uid" +#define DEFAULT_UMICH_ATTR_UIDNUMBER "uidNumber" +#define DEFAULT_UMICH_ATTR_GROUP_NFSNAME "NFSv4Name" +#define DEFAULT_UMICH_ATTR_GIDNUMBER "gidNumber" +#define DEFAULT_UMICH_ATTR_MEMBERUID "memberUid" +#define DEFAULT_UMICH_ATTR_GSSAUTHNAME "GSSAuthName" +#define DEFAULT_UMICH_ATTR_MEMBEROF "memberof" + +#define DEFAULT_UMICH_SEARCH_TIMEOUT 4 + +/* config section */ +#define LDAP_SECTION "UMICH_SCHEMA" + +#ifndef LDAP_FILT_MAXSIZ +#define LDAP_FILT_MAXSIZ 1024 +#endif + + +/* Local structure definitions */ + +struct ldap_map_names{ + char *NFSv4_person_objcls; + char *NFSv4_nfsname_attr; + char *NFSv4_acctname_attr; + char *NFSv4_uid_attr; + char *NFSv4_group_objcls; + char *NFSv4_group_nfsname_attr; + char *NFSv4_gid_attr; + char *NFSv4_member_attr; + char *NFSv4_member_of_attr; + char *GSS_principal_attr; + char *NFSv4_grouplist_filter; /* Filter for grouplist lookups */ +}; + +struct umich_ldap_info { + char *server; /* server name/address */ + int port; /* server port */ + char *base; /* base DN */ + char *people_tree; /* base DN to start searches for people */ + char *group_tree; /* base DN to start searches for groups */ + char *user_dn; /* optional DN for user account when binding */ + char *passwd; /* Password to use when binding to directory */ + int use_ssl; /* SSL flag */ + char *ca_cert; /* File location of the ca_cert */ + int tls_reqcert; /* req and validate server cert */ + int memberof_for_groups;/* Use 'memberof' attribute when + looking up user groups */ + int ldap_timeout; /* Timeout in seconds for searches + by ldap_search_st */ + int follow_referrals; /* whether to follow ldap referrals */ + char *sasl_mech; /* sasl mech to be used */ + char *sasl_realm; /* SASL realm for SASL authentication */ + char *sasl_authcid; /* authentication identity to be used */ + char *sasl_authzid; /* authorization identity to be used */ + char *sasl_secprops; /* Cyrus SASL security properties. */ + int sasl_canonicalize; /* canonicalize LDAP server host name */ + char *sasl_krb5_ccname; /* krb5 ticket cache */ +}; + +/* GLOBAL data */ + +static struct umich_ldap_info ldap_info = { + .server = NULL, + .port = 0, + .base = NULL, + .people_tree = NULL, + .group_tree = NULL, + .user_dn = NULL, + .passwd = NULL, + .use_ssl = 0, + .ca_cert = NULL, + .tls_reqcert = LDAP_OPT_X_TLS_HARD, + .memberof_for_groups = 0, + .ldap_timeout = DEFAULT_UMICH_SEARCH_TIMEOUT, + .follow_referrals = 1, + .sasl_mech = NULL, + .sasl_realm = NULL, + .sasl_authcid = NULL, + .sasl_authzid = NULL, + .sasl_secprops = NULL, + .sasl_canonicalize = -1, /* leave to the LDAP lib */ + .sasl_krb5_ccname = NULL, +}; + +static struct ldap_map_names ldap_map = { + .NFSv4_person_objcls = NULL, + .NFSv4_nfsname_attr = NULL, + .NFSv4_uid_attr = NULL, + .NFSv4_acctname_attr = NULL, + .NFSv4_group_objcls = NULL, + .NFSv4_group_nfsname_attr = NULL, + .NFSv4_gid_attr = NULL, + .NFSv4_member_attr = NULL, + .NFSv4_member_of_attr = NULL, + .GSS_principal_attr = NULL, + .NFSv4_grouplist_filter = NULL, +}; + +#ifdef ENABLE_LDAP_SASL + +/** + * Set the path of the krb5 ticket cache + * use gss_krb5_ccache_name if available else set the env var + */ +static int set_krb5_ccname(const char *krb5_ccache_name) +{ + int retval = 0; +#ifdef HAVE_GSS_KRB5_CCACHE_NAME + OM_uint32 status; + + if (gss_krb5_ccache_name(&status, krb5_ccache_name, NULL) != + GSS_S_COMPLETE) { + IDMAP_LOG(5, + ("Failed to set creds cache for kerberos, minor_status(%d)", + status)); + retval = status; + goto out; + } +#else /* HAVE_GSS_KRB5_CCACHE_NAME */ + char *env; + int buflen = 0; + + buflen = strlen("KRB5CCNAME=") + strlen(krb5_ccache_name) + 1; + env = malloc(buflen); + if (env == NULL) { + retval = ENOMEM; + goto out; + } + snprintf(env, buflen, "KRB5CCNAME=%s", krb5_ccache_name); + if (putenv(env) != 0) { + retval = errno; + IDMAP_LOG(5, ("Failed to set creds cache for kerberos, err(%d)", + retval)); + } +#endif /* else HAVE_GSS_KRB5_CCACHE_NAME */ +out: + return retval; +} + +/** + * SASL interact callback + */ +static int sasl_interact_cb(__attribute__((unused)) LDAP * ld, + __attribute__((unused)) unsigned int flags, void *defaults, + void *ctx) +{ + struct umich_ldap_info *linfo = defaults; + sasl_interact_t *interact = ctx; + + while (interact->id != SASL_CB_LIST_END) { + switch (interact->id) { + case SASL_CB_AUTHNAME: + if (linfo->sasl_authcid == NULL || + linfo->sasl_authcid[0] == '\0') { + IDMAP_LOG(2, ("SASL_CB_AUTHNAME asked in " + "callback but not found in conf")); + } else { + IDMAP_LOG(5, + ("Setting SASL_CB_AUTHNAME to %s", + linfo->sasl_authcid)); + interact->result = linfo->sasl_authcid; + interact->len = strlen(linfo->sasl_authcid); + } + break; + case SASL_CB_PASS: + if (linfo->passwd == NULL || linfo->passwd[0] == '\0') { + IDMAP_LOG(2, ("SASL_CB_PASS asked in callback " + "but not found in conf")); + } else { + IDMAP_LOG(5, + ("Setting SASL_CB_PASS to ***")); + interact->result = linfo->passwd; + interact->len = strlen(linfo->passwd); + } + break; + case SASL_CB_GETREALM: + if (linfo->sasl_realm == NULL || + linfo->sasl_realm[0] == '\0') { + IDMAP_LOG(2, ("SASL_CB_GETREALM asked in " + "callback but not found in conf")); + } else { + IDMAP_LOG(5, + ("Setting SASL_CB_GETREALM to %s", + linfo->sasl_realm)); + interact->result = linfo->sasl_realm; + interact->len = strlen(linfo->sasl_realm); + } + break; + case SASL_CB_USER: + if (linfo->sasl_authzid == NULL || + linfo->sasl_authzid[0] == '\0') { + IDMAP_LOG(2, ("SASL_CB_USER asked in callback " + "but not found in conf")); + } else { + IDMAP_LOG(5, ("Setting SASL_CB_USER to %s", + linfo->sasl_authzid)); + interact->result = linfo->sasl_authzid; + interact->len = strlen(linfo->sasl_authzid); + } + break; + default: + IDMAP_LOG(2, ("Undefined value requested %d", + interact->id)); + break; + } + interact++; + } + return LDAP_SUCCESS; +} +#endif /* ENABLE_LDAP_SASL */ + +/* Local routines */ + +static int +ldap_init_and_bind(LDAP **pld, + int *sizelimit, + struct umich_ldap_info *linfo) +{ + LDAP *ld; + int lerr; + int err = -1; + int current_version, new_version; + char server_url[1024]; + int debug_level = 65535; + int i; + LDAPAPIInfo apiinfo = {.ldapai_info_version = LDAP_API_INFO_VERSION}; + + snprintf(server_url, sizeof(server_url), "%s://%s:%d", + (linfo->use_ssl) ? "ldaps" : "ldap", + linfo->server, linfo->port); + + /* + * XXX We really, REALLY only want to initialize once, not for + * each request. Figure out how to do that! + */ + if ((lerr = ldap_initialize(&ld, server_url)) != LDAP_SUCCESS) { + IDMAP_LOG(0, ("ldap_init_and_bind: ldap_initialize() failed " + "to [%s]: %s (%d)", server_url, + ldap_err2string(lerr), lerr)); + goto out; + } + + if ((ldap_set_option(ld, LDAP_OPT_DEBUG_LEVEL, &debug_level) + != LDAP_SUCCESS)) { + IDMAP_LOG(0, ("ldap_init_and_bind: error setting ldap " + "library debugging level")); + goto out; + } + + /* + * Get LDAP API information and compare the protocol version there + * to the protocol version returned directly from get_option. + */ + ldap_get_option(ld, LDAP_OPT_API_INFO, &apiinfo); + if (apiinfo.ldapai_info_version != LDAP_API_INFO_VERSION) { + IDMAP_LOG(0, ("ldap_init_and_bind: APIInfo version mismatch: " + "library %d, header %d", + apiinfo.ldapai_info_version, LDAP_API_INFO_VERSION)); + goto out; + } + ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION, ¤t_version); + if (apiinfo.ldapai_protocol_version == LDAP_VERSION3 && + current_version != LDAP_VERSION3) { + new_version = LDAP_VERSION3; + IDMAP_LOG(4, ("ldap_init_and_bind: version mismatch between " + "API information and protocol version. Setting " + "protocol version to %d", new_version)); + ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &new_version); + } + + for (i = 0; apiinfo.ldapai_extensions[i]; i++) { + char *extension = apiinfo.ldapai_extensions[i]; + ldap_memfree (extension); + } + ldap_memfree (apiinfo.ldapai_extensions); + ldap_memfree(apiinfo.ldapai_vendor_name); + + /* Set sizelimit option if requested */ + if (sizelimit) { + ldap_set_option(ld, LDAP_OPT_SIZELIMIT, (void *)sizelimit); + } + + lerr = ldap_set_option(ld, LDAP_OPT_REFERRALS, + linfo->follow_referrals ? (void *)LDAP_OPT_ON : + (void *)LDAP_OPT_OFF); + if (lerr != LDAP_SUCCESS) { + IDMAP_LOG(2, ("ldap_init_and_bind: setting LDAP_OPT_REFERRALS " + "failed: %s (%d)", ldap_err2string(lerr), lerr)); + goto out; + } + + /* Set option to to use SSL/TLS if requested */ + if (linfo->use_ssl) { + int tls_type = LDAP_OPT_X_TLS_HARD; + lerr = ldap_set_option(ld, LDAP_OPT_X_TLS, &tls_type); + if (lerr != LDAP_SUCCESS) { + IDMAP_LOG(2, ("ldap_init_and_bind: setting SSL " + "failed : %s (%d)", + ldap_err2string(lerr), lerr)); + goto out; + } + + if (linfo->ca_cert != NULL) { + lerr = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, + linfo->ca_cert); + if (lerr != LDAP_SUCCESS) { + IDMAP_LOG(2, ("ldap_init_and_bind: setting CA " + "certificate file failed : %s (%d)", + ldap_err2string(lerr), lerr)); + goto out; + } + } + + lerr = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, + &linfo->tls_reqcert); + if (lerr != LDAP_SUCCESS) { + IDMAP_LOG(2, ("ldap_init_and_bind: setting " + "req CA cert failed : %s(%d)", + ldap_err2string(lerr), lerr)); + goto out; + } + } + + /* If we have a DN (and password) attempt an authenticated bind */ + if (linfo->user_dn) { +retry_bind: +#ifdef ENABLE_LDAP_SASL + if (linfo->sasl_mech != NULL && linfo->sasl_mech[0] != '\0') { + /* use sasl bind */ + if (linfo->sasl_canonicalize != -1) { + lerr = ldap_set_option(ld, + LDAP_OPT_X_SASL_NOCANON, + linfo->sasl_canonicalize ? + LDAP_OPT_OFF : LDAP_OPT_ON); + if (lerr != LDAP_SUCCESS) { + IDMAP_LOG(2, ("ldap_init_and_bind: " + "setting sasl_canonicalize" + " failed: %s (%d)", + ldap_err2string(lerr), + lerr)); + goto out; + } + } + if (linfo->sasl_secprops != NULL && + linfo->sasl_secprops[0] != '\0') { + lerr = ldap_set_option(ld, + LDAP_OPT_X_SASL_SECPROPS, + (void *) linfo->sasl_secprops); + if (lerr != LDAP_SUCCESS) { + IDMAP_LOG(2, ("ldap_init_and_bind: " + "setting sasl_secprops" + " failed: %s (%d)", + ldap_err2string(lerr), + lerr)); + goto out; + } + } + if (linfo->sasl_krb5_ccname != NULL && + linfo->sasl_krb5_ccname[0] != '\0') { + lerr = set_krb5_ccname(linfo->sasl_krb5_ccname); + if (lerr != 0) { + IDMAP_LOG(2, + ("ldap_init_and_bind: Failed " + "to set krb5 ticket cache, " + "err=%d", lerr)); + } + } + lerr = ldap_sasl_interactive_bind_s(ld, linfo->user_dn, + linfo->sasl_mech, NULL, NULL, LDAP_SASL_QUIET, + sasl_interact_cb, linfo); + } else { + lerr = ldap_simple_bind_s(ld, linfo->user_dn, + linfo->passwd); + } +#else /* ENABLE_LDAP_SASL */ + lerr = ldap_simple_bind_s(ld, linfo->user_dn, linfo->passwd); +#endif /* else ENABLE_LDAP_SASL */ + if (lerr) { + char *errmsg; + if (lerr == LDAP_PROTOCOL_ERROR) { + ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION, + ¤t_version); + new_version = current_version == LDAP_VERSION2 ? + LDAP_VERSION3 : LDAP_VERSION2; + ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, + &new_version); + IDMAP_LOG(2, ("ldap_init_and_bind: " + "got protocol error while attempting " + "bind with protocol version %d, " + "trying protocol version %d", + current_version, new_version)); + if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS) + && (errmsg != NULL) && (*errmsg != '\0')) { + IDMAP_LOG(2, ("ldap_init_and_bind: " + "Additional info: %s", errmsg)); + ldap_memfree(errmsg); + } + goto retry_bind; + } +#ifdef ENABLE_LDAP_SASL + IDMAP_LOG(2, ("ldap_init_and_bind: %s " + "to [%s] as user '%s': %s (%d)", + (linfo->sasl_mech != NULL && + linfo->sasl_mech[0] != '\0') ? + "ldap_sasl_interactive_bind_s" : + "ldap_simple_bind_s", + server_url, linfo->user_dn, + ldap_err2string(lerr), lerr)); +#else /* ENABLE_LDAP_SASL */ + IDMAP_LOG(2, ("ldap_init_and_bind: ldap_simple_bind_s" + "to [%s] as user '%s': %s (%d)", + server_url, linfo->user_dn, + ldap_err2string(lerr), lerr)); + +#endif /* else ENABLE_LDAP_SASL */ + if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS) + && (errmsg != NULL)&& (*errmsg != '\0')) { + IDMAP_LOG(2, ("ldap_init_and_bind: " + "Additional info: %s", errmsg)); + ldap_memfree(errmsg); + } + goto out; + } + } +#ifdef LDAP_ANONYMOUS_BIND_REQUIRED + else { + lerr = ldap_simple_bind_s(ld, NULL, NULL); + if (lerr) { + char *errmsg; + + IDMAP_LOG(2, ("ldap_init_and_bind: ldap_simple_bind_s " + "to [%s] as anonymous: %s (%d)", server_url, + ldap_err2string(lerr), lerr)); + if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS) + && (errmsg != NULL) && (*errmsg != '\0')) { + IDMAP_LOG(2, ("ldap_init_and_bind: " + "Additional info: %s", errmsg)); + ldap_memfree(errmsg); + } + goto out; + } + } +#endif + + *pld = ld; + err = 0; +out: + return err; +} + +static int +umich_name_to_ids(char *name, int idtype, uid_t *uid, gid_t *gid, + char *attrtype, struct umich_ldap_info *linfo) +{ + LDAP *ld = NULL; + struct timeval timeout = { + .tv_sec = linfo->ldap_timeout, + }; + LDAPMessage *result = NULL, *entry; + BerElement *ber = NULL; + char **idstr, filter[LDAP_FILT_MAXSIZ], *base; + char *attrs[3]; + char *attr_res; + int count = 0, err, lerr, f_len; + int sizelimit = 1; + + err = -EINVAL; + if (uid == NULL || gid == NULL || name == NULL || + attrtype == NULL || linfo == NULL || linfo->server == NULL || + linfo->people_tree == NULL || linfo->group_tree == NULL) + goto out; + + *uid = -1; + *gid = -1; + + if (idtype == IDTYPE_USER) { + if ((f_len = snprintf(filter, LDAP_FILT_MAXSIZ, + "(&(objectClass=%s)(%s=%s))", + ldap_map.NFSv4_person_objcls, + attrtype, name)) + == LDAP_FILT_MAXSIZ) { + IDMAP_LOG(0, ("ERROR: umich_name_to_ids: filter " + "too long!")); + goto out; + } + base = linfo->people_tree; + } + else if (idtype == IDTYPE_GROUP) { + if ((f_len = snprintf(filter, LDAP_FILT_MAXSIZ, + "(&(objectClass=%s)(%s=%s))", + ldap_map.NFSv4_group_objcls, + attrtype, name)) + == LDAP_FILT_MAXSIZ) { + IDMAP_LOG(0, ("ERROR: umich_name_to_ids: filter " + "too long!")); + goto out; + } + base = linfo->group_tree; + } + else { + IDMAP_LOG(0, ("ERROR: umich_name_to_ids: invalid idtype (%d)", + idtype)); + goto out; + } + + if (ldap_init_and_bind(&ld, &sizelimit, linfo)) + goto out; + + attrs[0] = ldap_map.NFSv4_uid_attr; + attrs[1] = ldap_map.NFSv4_gid_attr; + attrs[2] = NULL; + + err = ldap_search_st(ld, base, LDAP_SCOPE_SUBTREE, + filter, (char **)attrs, + 0, &timeout, &result); + if (err) { + char *errmsg; + + IDMAP_LOG(2, ("umich_name_to_ids: ldap_search_st for " + "base '%s', filter '%s': %s (%d)", + base, filter, ldap_err2string(err), err)); + if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS) + && (errmsg != NULL) && (*errmsg != '\0')) { + IDMAP_LOG(2, ("umich_name_to_ids: " + "Additional info: %s", errmsg)); + ldap_memfree(errmsg); + } + err = -ENOENT; + goto out_unbind; + } + + err = -ENOENT; + count = ldap_count_entries(ld, result); + if (count != 1) { + goto out_unbind; + } + + if (!(entry = ldap_first_entry(ld, result))) { + lerr = ldap_result2error(ld, result, 0); + IDMAP_LOG(2, ("umich_name_to_ids: ldap_first_entry: " + "%s (%d)", ldap_err2string(lerr), lerr)); + goto out_unbind; + } + + /* + * Attributes come back in no particular order, so we need + * to check each one to see what it is before assigning values. + * XXX There must be a better way than comparing the + * name of each attribute? + */ + for (attr_res = ldap_first_attribute(ld, result, &ber); + attr_res != NULL; + attr_res = ldap_next_attribute(ld, result, ber)) { + + unsigned long tmp_u, tmp_g; + uid_t tmp_uid; + gid_t tmp_gid; + + if ((idstr = ldap_get_values(ld, result, attr_res)) == NULL) { + lerr = ldap_result2error(ld, result, 0); + IDMAP_LOG(2, ("umich_name_to_ids: ldap_get_values: " + "%s (%d)", ldap_err2string(lerr), lerr)); + goto out_memfree; + } + if (strcasecmp(attr_res, ldap_map.NFSv4_uid_attr) == 0) { + tmp_u = strtoul(*idstr, (char **)NULL, 10); + tmp_uid = tmp_u; + if (tmp_uid != tmp_u || + (errno == ERANGE && tmp_u == ULONG_MAX)) { + IDMAP_LOG(0, ("ERROR: umich_name_to_ids: " + "uidNumber too long converting '%s'", + *idstr)); + ldap_memfree(attr_res); + ldap_value_free(idstr); + goto out_memfree; + } + *uid = tmp_uid; + err = 0; + } else if (strcasecmp(attr_res, ldap_map.NFSv4_gid_attr) == 0) { + tmp_g = strtoul(*idstr, (char **)NULL, 10); + tmp_gid = tmp_g; + if (tmp_gid != tmp_g || + (errno == ERANGE && tmp_g == ULONG_MAX)) { + IDMAP_LOG(0, ("ERROR: umich_name_to_ids: " + "gidNumber too long converting '%s'", + *idstr)); + ldap_memfree(attr_res); + ldap_value_free(idstr); + goto out_memfree; + } + *gid = tmp_gid; + err = 0; + } else { + IDMAP_LOG(0, ("umich_name_to_ids: received attr " + "'%s' ???", attr_res)); + ldap_memfree(attr_res); + ldap_value_free(idstr); + goto out_memfree; + } + ldap_memfree(attr_res); + ldap_value_free(idstr); + } + +out_memfree: + ber_free(ber, 0); +out_unbind: + if (result) + ldap_msgfree(result); + ldap_unbind(ld); +out: + return err; +} + +static int +umich_id_to_name(uid_t id, int idtype, char **name, size_t len, + struct umich_ldap_info *linfo) +{ + LDAP *ld = NULL; + struct timeval timeout = { + .tv_sec = linfo->ldap_timeout, + }; + LDAPMessage *result = NULL, *entry; + BerElement *ber; + char **names = NULL, filter[LDAP_FILT_MAXSIZ], *base; + char idstr[16]; + char *attrs[2]; + char *attr_res; + int count = 0, err, lerr, f_len; + int sizelimit = 1; + + err = -EINVAL; + if (name == NULL || linfo == NULL || linfo->server == NULL || + linfo->people_tree == NULL || linfo->group_tree == NULL) + goto out; + + snprintf(idstr, sizeof(idstr), "%d", id); + + + if (idtype == IDTYPE_USER) { + if ((f_len = snprintf(filter, LDAP_FILT_MAXSIZ, + "(&(objectClass=%s)(%s=%s))", + ldap_map.NFSv4_person_objcls, + ldap_map.NFSv4_uid_attr, idstr)) + == LDAP_FILT_MAXSIZ) { + IDMAP_LOG(0, ("ERROR: umich_id_to_name: " + "uid filter too long!")); + goto out; + } + base = linfo->people_tree; + } else if (idtype == IDTYPE_GROUP) { + if ((f_len = snprintf(filter, LDAP_FILT_MAXSIZ, + "(&(objectClass=%s)(%s=%s))", + ldap_map.NFSv4_group_objcls, + ldap_map.NFSv4_gid_attr,idstr)) + == LDAP_FILT_MAXSIZ) { + IDMAP_LOG(0, ("ERROR: umich_id_to_name: " + "gid filter too long!")); + goto out; + } + base = linfo->group_tree; + } else { + IDMAP_LOG(0, ("ERROR: umich_id_to_name: invalid idtype (%d)", + idtype)); + err = -EINVAL; + goto out; + } + + if (ldap_init_and_bind(&ld, &sizelimit, linfo)) + goto out; + + if (idtype == IDTYPE_USER) + attrs[0] = ldap_map.NFSv4_nfsname_attr; + else + attrs[0] = ldap_map.NFSv4_group_nfsname_attr; + attrs[1] = NULL; + + err = ldap_search_st(ld, base, LDAP_SCOPE_SUBTREE, + filter, (char **)attrs, + 0, &timeout, &result); + if (err) { + char * errmsg; + + IDMAP_LOG(2, ("umich_id_to_name: ldap_search_st for " + "base '%s, filter '%s': %s (%d)", base, filter, + ldap_err2string(err), err)); + if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS) + && (errmsg != NULL) && (*errmsg != '\0')) { + IDMAP_LOG(2, ("umich_id_to_name: " + "Additional info: %s", errmsg)); + ldap_memfree(errmsg); + } + + err = -ENOENT; + goto out_unbind; + } + + err = -ENOENT; + count = ldap_count_entries(ld, result); + if (count != 1) + goto out_unbind; + + if (!(entry = ldap_first_entry(ld, result))) { + lerr = ldap_result2error(ld, result, 0); + IDMAP_LOG(2, ("umich_id_to_name: ldap_first_entry: " + "%s (%d)", ldap_err2string(lerr), lerr)); + goto out_unbind; + } + + if (!(attr_res = ldap_first_attribute(ld, result, &ber))) { + lerr = ldap_result2error(ld, result, 0); + IDMAP_LOG(2, ("umich_id_to_name: ldap_first_attribute: " + "%s (%d)", ldap_err2string(lerr), lerr)); + goto out_unbind; + } + + if ((names = ldap_get_values(ld, result, attr_res)) == NULL) { + lerr = ldap_result2error(ld, result, 0); + IDMAP_LOG(2, ("umich_id_to_name: ldap_get_values: " + "%s (%d)", ldap_err2string(lerr), lerr)); + goto out_memfree; + } + + /* + * Verify there is enough room in the output buffer before + * copying returned string. (strlen doesn't count the null, + * we make sure there is room for the null also, therefore + * we use ">=" not just ">") + */ + if (strlen(names[0]) >= len) { + /* not enough space to return the name */ + IDMAP_LOG(1, ("umich_id_to_name: output buffer size (%d) " + "too small to return string, '%s', of length %d", + len, names[0], strlen(names[0]))); + goto out_memfree; + } + strcpy(*name, names[0]); + + err = 0; +out_memfree: + if (names) + ldap_value_free(names); + ldap_memfree(attr_res); + ber_free(ber, 0); +out_unbind: + if (result) + ldap_msgfree(result); + ldap_unbind(ld); +out: + return err; +} + +static int +umich_gss_princ_to_grouplist(char *principal, gid_t *groups, int *ngroups, + struct umich_ldap_info *linfo) +{ + LDAP *ld = NULL; + struct timeval timeout = { + .tv_sec = linfo->ldap_timeout, + }; + LDAPMessage *result, *entry; + char **names, filter[LDAP_FILT_MAXSIZ]; + char *attrs[2]; + int count = 0, err = -ENOMEM, lerr, f_len; + int i, num_gids; + gid_t *curr_group = groups; + + err = -EINVAL; + if (linfo == NULL || linfo->server == NULL || + linfo->people_tree == NULL || linfo->group_tree == NULL) + goto out; + + + if (ldap_init_and_bind(&ld, NULL, linfo)) + goto out; + + /* + * First we need to map the gss principal name to a uid (name) string + */ + err = -EINVAL; + if ((f_len = snprintf(filter, LDAP_FILT_MAXSIZ, + "(&(objectClass=%s)(%s=%s))", + ldap_map.NFSv4_person_objcls, + ldap_map.GSS_principal_attr, principal)) + == LDAP_FILT_MAXSIZ) { + IDMAP_LOG(0, ("ERROR: umich_gss_princ_to_grouplist: " + "filter too long!")); + goto out; + } + + attrs[0] = ldap_map.NFSv4_acctname_attr; + attrs[1] = NULL; + + err = ldap_search_st(ld, linfo->people_tree, LDAP_SCOPE_SUBTREE, + filter, attrs, 0, &timeout, &result); + if (err) { + char *errmsg; + + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_search_st " + "for tree '%s, filter '%s': %s (%d)", + linfo->people_tree, filter, + ldap_err2string(err), err)); + if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS) + && (errmsg != NULL) && (*errmsg != '\0')) { + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: " + "Additional info: %s", errmsg)); + ldap_memfree(errmsg); + } + err = -ENOENT; + goto out_unbind; + } + + err = -ENOENT; + count = ldap_count_entries(ld, result); + if (count != 1) { + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: " + "ldap account lookup of gssauthname %s returned %d accounts", + principal,count)); + goto out_unbind; + } + + if (!(entry = ldap_first_entry(ld, result))) { + lerr = ldap_result2error(ld, result, 0); + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_first_entry: " + "%s (%d)", ldap_err2string(lerr), lerr)); + goto out_unbind; + } + + if ((names = ldap_get_values(ld, result, attrs[0])) == NULL) { + lerr = ldap_result2error(ld, result, 0); + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_get_values: " + "%s (%d)", ldap_err2string(lerr), lerr)); + goto out_unbind; + } + + if (ldap_info.memberof_for_groups) { + + /* + * Collect the groups the user belongs to + */ + if ((f_len = snprintf(filter, LDAP_FILT_MAXSIZ, + "(&(objectClass=%s)(%s=%s))", + ldap_map.NFSv4_person_objcls, + ldap_map.NFSv4_acctname_attr, + names[0])) == LDAP_FILT_MAXSIZ ) { + IDMAP_LOG(2, ("ERROR: umich_gss_princ_to_grouplist: " + "filter too long!")); + ldap_value_free(names); + goto out_unbind; + } + + ldap_value_free(names); + + attrs[0] = ldap_map.NFSv4_member_of_attr; + attrs[1] = NULL; + + err = ldap_search_st(ld, linfo->people_tree, LDAP_SCOPE_SUBTREE, + filter, attrs, 0, &timeout, &result); + + if (err) { + char *errmsg; + + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_search_st " + "for tree '%s, filter '%s': %s (%d)", + linfo->people_tree, filter, + ldap_err2string(err), err)); + if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS) + && (errmsg != NULL) && (*errmsg != '\0')) { + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: " + "Additional info: %s", errmsg)); + ldap_memfree(errmsg); + } + err = -ENOENT; + goto out_unbind; + } + err = -ENOENT; + + /* pull the list of groups and place into names */ + count = ldap_count_entries(ld, result); + if (count != 1) { + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: " + "ldap group member lookup of gssauthname %s returned %d multiple entries", + principal,count)); + goto out_unbind; + } + + if (!(entry = ldap_first_entry(ld, result))) { + lerr = ldap_result2error(ld, result, 0); + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_first_entry: " + "%s (%d)", ldap_err2string(lerr), lerr)); + goto out_unbind; + } + + if ((names = ldap_get_values(ld, result, attrs[0])) == NULL) { + lerr = ldap_result2error(ld, result, 0); + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_get_values: " + "%s (%d)", ldap_err2string(lerr), lerr)); + goto out_unbind; + } + + /* Count the groups first before doing a lookup of the group. + If it exceeds the desired number of groups set the needed value + and abort. */ + for (i = 0; names[i] != NULL; i++); + if ( i > *ngroups ) { + ldap_value_free(names); + err = -EINVAL; + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: User %s, " + "number of groups %d, exceeds requested number %d", + principal, i, *ngroups)); + *ngroups = i; + goto out_unbind; + } + + /* Loop through the groupnames (names) and get the group gid */ + num_gids = 0; + for (i = 0; names[i] != NULL; i++){ + char **vals; + int valcount; + unsigned long tmp_g; + gid_t tmp_gid; + char *cnptr = NULL; + + cnptr = strchr(names[i],','); + if (cnptr) *cnptr = '\0'; + + err = -ENOENT; + if (ldap_map.NFSv4_grouplist_filter) + f_len = snprintf(filter, LDAP_FILT_MAXSIZ, + "(&(objectClass=%s)(%s)%s)", + ldap_map.NFSv4_group_objcls, + names[i], + ldap_map.NFSv4_grouplist_filter); + else + f_len = snprintf(filter, LDAP_FILT_MAXSIZ, + "(&(objectClass=%s)(%s))", + ldap_map.NFSv4_group_objcls, + names[i]); + + if ( f_len == LDAP_FILT_MAXSIZ ) { + IDMAP_LOG(2, ("ERROR: umich_gss_princ_to_grouplist: " + "filter too long!")); + ldap_value_free(names); + goto out_unbind; + } + attrs[0] = ldap_map.NFSv4_gid_attr; + attrs[1] = NULL; + + err = ldap_search_st(ld, linfo->group_tree, LDAP_SCOPE_SUBTREE, + filter, attrs, 0, &timeout, &result); + if (err) { + char *errmsg; + + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_search_st " + "for tree '%s, filter '%s': %s (%d)", + linfo->group_tree, filter, + ldap_err2string(err), err)); + if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg)==LDAP_SUCCESS) + && + (errmsg != NULL) && (*errmsg != '\0')) { + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: " + "Additional info: %s", errmsg)); + ldap_memfree(errmsg); + } + continue; + } + + count = ldap_count_entries(ld, result); + if (count == 0) + continue; + if (count != 1 ){ + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist:" + "Group %s has %d gids defined - aborting", names[i], count)); + ldap_value_free(names); + err = -ENOENT; + goto out_unbind; + } + + vals = ldap_get_values(ld, result, ldap_map.NFSv4_gid_attr); + + /* There should be only one gidNumber attribute per group */ + if ((valcount = ldap_count_values(vals)) != 1) { + IDMAP_LOG(2, ("DB problem getting gidNumber of " + "posixGroup! (count was %d)", valcount)); + ldap_value_free(vals); + continue; + } + + tmp_g = strtoul(vals[0], (char **)NULL, 10); + tmp_gid = tmp_g; + if (tmp_gid != tmp_g || + (errno == ERANGE && tmp_g == ULONG_MAX)) { + IDMAP_LOG(2, ("ERROR: umich_gss_princ_to_grouplist: " + "gidNumber too long converting '%s'", + vals[0])); + ldap_value_free(vals); + continue; + } + *curr_group++ = tmp_gid; + num_gids++; + ldap_value_free(vals); + } + ldap_value_free(names); + *ngroups = num_gids; + err = 0; + } else { + + /* + * Then determine the groups that uid (name) string is a member of + */ + err = -EINVAL; + if (ldap_map.NFSv4_grouplist_filter) + f_len = snprintf(filter, LDAP_FILT_MAXSIZ, + "(&(objectClass=%s)(%s=%s)%s)", + ldap_map.NFSv4_group_objcls, + ldap_map.NFSv4_member_attr, + names[0], + ldap_map.NFSv4_grouplist_filter); + + else + f_len = snprintf(filter, LDAP_FILT_MAXSIZ, + "(&(objectClass=%s)(%s=%s))", + ldap_map.NFSv4_group_objcls, + ldap_map.NFSv4_member_attr, + names[0]); + + if ( f_len == LDAP_FILT_MAXSIZ ) { + IDMAP_LOG(0, ("ERROR: umich_gss_princ_to_grouplist: " + "filter too long!")); + ldap_value_free(names); + goto out_unbind; + } + + ldap_value_free(names); + + attrs[0] = ldap_map.NFSv4_gid_attr; + attrs[1] = NULL; + + err = ldap_search_st(ld, linfo->group_tree, LDAP_SCOPE_SUBTREE, + filter, attrs, 0, &timeout, &result); + + if (err) { + char *errmsg; + + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_search_st " + "for tree '%s, filter '%s': %s (%d)", + linfo->group_tree, filter, + ldap_err2string(err), err)); + if ((ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &errmsg) == LDAP_SUCCESS) && + (errmsg != NULL) && (*errmsg != '\0')) { + IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: " + "Additional info: %s", errmsg)); + ldap_memfree(errmsg); + } + err = -ENOENT; + goto out_unbind; + } + + /* + * If we can't determine count, return that error + * If we have nothing to return, return success + * If we have more than they asked for, tell them the + * number required and return an error + */ + count = ldap_count_entries(ld, result); + + if (count < 0) { + err = count; + goto out_unbind; + } + if (count == 0) { + *ngroups = 0; + err = 0; + goto out_unbind; + } + if (count > *ngroups) { + *ngroups = count; + err = -EINVAL; + goto out_unbind; + } + *ngroups = count; + + curr_group = groups; + + err = -ENOENT; + for (entry = ldap_first_entry(ld, result); + entry != NULL; + entry = ldap_next_entry(ld, entry)) { + + char **vals; + int valcount; + unsigned long tmp_g; + gid_t tmp_gid; + + vals = ldap_get_values(ld, entry, ldap_map.NFSv4_gid_attr); + + /* There should be only one gidNumber attribute per group */ + if ((valcount = ldap_count_values(vals)) != 1) { + IDMAP_LOG(0, ("DB problem getting gidNumber of " + "posixGroup! (count was %d)", valcount)); + goto out_unbind; + } + tmp_g = strtoul(vals[0], (char **)NULL, 10); + tmp_gid = tmp_g; + if (tmp_gid != tmp_g || + (errno == ERANGE && tmp_g == ULONG_MAX)) { + IDMAP_LOG(0, ("ERROR: umich_gss_princ_to_grouplist: " + "gidNumber too long converting '%s'", + vals[0])); + ldap_value_free(vals); + goto out_unbind; + } + *curr_group++ = tmp_gid; + ldap_value_free(vals); + } + err = 0; + } + +out_unbind: + ldap_unbind(ld); +out: + return err; +} + + +/* + * principal: krb5 - princ@realm, use KrbName ldap attribute + * spkm3 - X.509 dn, use X509Name ldap attribute + */ +static int +umichldap_gss_princ_to_ids(char *secname, char *principal, + uid_t *uid, gid_t *gid, + extra_mapping_params **UNUSED(ex)) +{ + uid_t rtnd_uid = -1; + gid_t rtnd_gid = -1; + int err = -EINVAL; + + if ((strcmp(secname, "krb5") != 0) && (strcmp(secname, "spkm3") != 0)) { + IDMAP_LOG(0, ("ERROR: umichldap_gss_princ_to_ids: " + "invalid secname '%s'", secname)); + return err; + } + + err = umich_name_to_ids(principal, IDTYPE_USER, &rtnd_uid, &rtnd_gid, + ldap_map.GSS_principal_attr, &ldap_info); + if (err < 0) + goto out; + + *uid = rtnd_uid; + *gid = rtnd_gid; +out: + return err; +} + +static int +umichldap_name_to_uid(char *name, uid_t *uid) +{ + gid_t gid; + + return umich_name_to_ids(name, IDTYPE_USER, uid, + &gid, ldap_map.NFSv4_nfsname_attr, &ldap_info); +} + +static int +umichldap_name_to_gid(char *name, gid_t *gid) +{ + uid_t uid; + + return umich_name_to_ids(name, IDTYPE_GROUP, &uid, gid, + ldap_map.NFSv4_group_nfsname_attr, &ldap_info); +} + +static int +umichldap_uid_to_name(uid_t uid, char *UNUSED(domain), char *name, size_t len) +{ + return umich_id_to_name(uid, IDTYPE_USER, &name, len, &ldap_info); +} + +static int +umichldap_gid_to_name(gid_t gid, char *UNUSED(domain), char *name, size_t len) +{ + return umich_id_to_name(gid, IDTYPE_GROUP, &name, len, &ldap_info); +} + +static int +umichldap_gss_princ_to_grouplist(char *secname, char *principal, + gid_t *groups, int *ngroups, extra_mapping_params **UNUSED(ex)) +{ + int err = -EINVAL; + + if ((strcmp(secname, "krb5") != 0) && (strcmp(secname, "spkm3") != 0)) { + IDMAP_LOG(0, ("ERROR: umichldap_gss_princ_to_grouplist: " + "invalid secname '%s'", secname)); + return err; + } + + return umich_gss_princ_to_grouplist(principal, groups, ngroups, + &ldap_info); +} + +/* + * TLS connections require that the hostname we specify matches + * the hostname in the certificate that the server uses. + * Get a canonical name for the host specified in the config file. + */ +static char * +get_canonical_hostname(const char *inname) +{ + int aierr, error; + struct addrinfo *ap, aihints; + char *return_name = NULL; + char tmphost[NI_MAXHOST]; + + memset(&aihints, 0, sizeof(aihints)); + aihints.ai_socktype = SOCK_STREAM; + aihints.ai_flags = AI_CANONNAME; + aihints.ai_family = PF_INET; + aierr = getaddrinfo(inname, NULL, &aihints, &ap); + if (aierr) { + const char *msg; + /* We want to customize some messages. */ + switch (aierr) { + case EAI_NONAME: + msg = "host unknown"; + break; + default: + msg = gai_strerror(aierr); + break; + } + IDMAP_LOG(1, ("%s: '%s': %s", __FUNCTION__, inname, msg)); + goto out_err; + } + if (ap == 0) { + IDMAP_LOG(1, ("%s: no addresses for host '%s'?", + __FUNCTION__, inname)); + goto out_err; + } + + error = getnameinfo (ap->ai_addr, ap->ai_addrlen, tmphost, + sizeof(tmphost), NULL, 0, 0); + if (error) { + IDMAP_LOG(1, ("%s: getnameinfo for host '%s' failed (%d)", + __FUNCTION__, inname)); + goto out_free; + } + return_name = strdup (tmphost); + +out_free: + nfs_freeaddrinfo(ap); +out_err: + return return_name; +} + +static int +umichldap_init(void) +{ + char *tssl, *canonicalize, *memberof, *cert_req, *follow_referrals; + char missing_msg[128] = ""; + char *server_in, *canon_name; + + if (nfsidmap_conf_path) + conf_init_file(nfsidmap_conf_path); + + server_in = conf_get_str(LDAP_SECTION, "LDAP_server"); + ldap_info.base = conf_get_str(LDAP_SECTION, "LDAP_base"); + ldap_info.people_tree = conf_get_str(LDAP_SECTION, "LDAP_people_base"); + ldap_info.group_tree = conf_get_str(LDAP_SECTION, "LDAP_group_base"); + ldap_info.user_dn = conf_get_str(LDAP_SECTION, "LDAP_user_dn"); + ldap_info.passwd = conf_get_str(LDAP_SECTION, "LDAP_passwd"); + tssl = conf_get_str_with_def(LDAP_SECTION, "LDAP_use_ssl", "false"); + if ((strcasecmp(tssl, "true") == 0) || + (strcasecmp(tssl, "on") == 0) || + (strcasecmp(tssl, "yes") == 0)) + ldap_info.use_ssl = 1; + else + ldap_info.use_ssl = 0; + ldap_info.ca_cert = conf_get_str(LDAP_SECTION, "LDAP_CA_CERT"); + cert_req = conf_get_str(LDAP_SECTION, "LDAP_tls_reqcert"); + if (cert_req != NULL) { + if (strcasecmp(cert_req, "hard") == 0) + ldap_info.tls_reqcert = LDAP_OPT_X_TLS_HARD; + else if (strcasecmp(cert_req, "demand") == 0) + ldap_info.tls_reqcert = LDAP_OPT_X_TLS_DEMAND; + else if (strcasecmp(cert_req, "try") == 0) + ldap_info.tls_reqcert = LDAP_OPT_X_TLS_TRY; + else if (strcasecmp(cert_req, "allow") == 0) + ldap_info.tls_reqcert = LDAP_OPT_X_TLS_ALLOW; + else if (strcasecmp(cert_req, "never") == 0) + ldap_info.tls_reqcert = LDAP_OPT_X_TLS_NEVER; + else { + IDMAP_LOG(0, ("umichldap_init: Invalid value(%s) for " + "LDAP_tls_reqcert.")); + goto fail; + } + } + /* vary the default port depending on whether they use SSL or not */ + ldap_info.port = conf_get_num(LDAP_SECTION, "LDAP_port", + (ldap_info.use_ssl) ? + LDAPS_PORT : LDAP_PORT); + + ldap_info.sasl_mech = conf_get_str(LDAP_SECTION, "LDAP_sasl_mech"); + ldap_info.sasl_realm = conf_get_str(LDAP_SECTION, "LDAP_sasl_realm"); + ldap_info.sasl_authcid = conf_get_str(LDAP_SECTION, + "LDAP_sasl_authcid"); + ldap_info.sasl_authzid = conf_get_str(LDAP_SECTION, + "LDAP_sasl_authzid"); + ldap_info.sasl_secprops = conf_get_str(LDAP_SECTION, + "LDAP_sasl_secprops"); + + /* If it is not set let the ldap lib work with the lib default */ + canonicalize = conf_get_str_with_def(LDAP_SECTION, + "LDAP_sasl_canonicalize", "undef"); + if ((strcasecmp(canonicalize, "true") == 0) || + (strcasecmp(canonicalize, "on") == 0) || + (strcasecmp(canonicalize, "yes") == 0)) { + ldap_info.sasl_canonicalize = 1; + } else if ((strcasecmp(canonicalize, "false") == 0) || + (strcasecmp(canonicalize, "off") == 0) || + (strcasecmp(canonicalize, "no") == 0)) { + ldap_info.sasl_canonicalize = 0; + } + ldap_info.sasl_krb5_ccname = conf_get_str(LDAP_SECTION, + "LDAP_sasl_krb5_ccname"); + + follow_referrals = conf_get_str_with_def(LDAP_SECTION, + "LDAP_follow_referrals", + "true"); + if ((strcasecmp(follow_referrals, "true") == 0) || + (strcasecmp(follow_referrals, "on") == 0) || + (strcasecmp(follow_referrals, "yes") == 0)) + ldap_info.follow_referrals = 1; + else + ldap_info.follow_referrals = 0; + + /* Verify required information is supplied */ + if (server_in == NULL || strlen(server_in) == 0) + strncat(missing_msg, "LDAP_server ", sizeof(missing_msg)-1); + if (ldap_info.base == NULL || strlen(ldap_info.base) == 0) + strncat(missing_msg, "LDAP_base ", sizeof(missing_msg)-1); + if (strlen(missing_msg) != 0) { + IDMAP_LOG(0, ("umichldap_init: Missing required information: " + "%s", missing_msg)); + goto fail; + } + + ldap_info.server = server_in; + canonicalize = conf_get_str_with_def(LDAP_SECTION, + "LDAP_canonicalize_name", "yes"); + if ((strcasecmp(canonicalize, "true") == 0) || + (strcasecmp(canonicalize, "on") == 0) || + (strcasecmp(canonicalize, "yes") == 0)) { + canon_name = get_canonical_hostname(server_in); + if (canon_name == NULL) + IDMAP_LOG(0, ("umichldap_init: Warning! Unable to " + "canonicalize server name '%s' as requested.", + server_in)); + else + ldap_info.server = canon_name; + } + + /* get the ldap mapping attributes/objectclasses (all have defaults) */ + ldap_map.NFSv4_person_objcls = + conf_get_str_with_def(LDAP_SECTION, "NFSv4_person_objectclass", + DEFAULT_UMICH_OBJCLASS_REMOTE_PERSON); + + ldap_map.NFSv4_group_objcls = + conf_get_str_with_def(LDAP_SECTION, "NFSv4_group_objectclass", + DEFAULT_UMICH_OBJCLASS_REMOTE_GROUP); + + ldap_map.NFSv4_nfsname_attr = + conf_get_str_with_def(LDAP_SECTION, "NFSv4_name_attr", + DEFAULT_UMICH_ATTR_NFSNAME); + + ldap_map.NFSv4_uid_attr = + conf_get_str_with_def(LDAP_SECTION, "NFSv4_uid_attr", + DEFAULT_UMICH_ATTR_UIDNUMBER); + + ldap_map.NFSv4_acctname_attr = + conf_get_str_with_def(LDAP_SECTION, "NFSv4_acctname_attr", + DEFAULT_UMICH_ATTR_ACCTNAME); + + ldap_map.NFSv4_group_nfsname_attr = + conf_get_str_with_def(LDAP_SECTION, "NFSv4_group_attr", + DEFAULT_UMICH_ATTR_GROUP_NFSNAME); + + ldap_map.NFSv4_gid_attr = + conf_get_str_with_def(LDAP_SECTION, "NFSv4_gid_attr", + DEFAULT_UMICH_ATTR_GIDNUMBER); + + ldap_map.NFSv4_member_attr = + conf_get_str_with_def(LDAP_SECTION, "NFSv4_member_attr", + DEFAULT_UMICH_ATTR_MEMBERUID); + + ldap_map.GSS_principal_attr = + conf_get_str_with_def(LDAP_SECTION, "GSS_principal_attr", + DEFAULT_UMICH_ATTR_GSSAUTHNAME); + + ldap_map.NFSv4_grouplist_filter = + conf_get_str_with_def(LDAP_SECTION, "NFSv4_grouplist_filter", + NULL); + + ldap_map.NFSv4_member_of_attr = + conf_get_str_with_def(LDAP_SECTION, "NFSv4_member_of_attr", + DEFAULT_UMICH_ATTR_MEMBEROF); + + ldap_info.ldap_timeout = + conf_get_num(LDAP_SECTION, "LDAP_timeout_seconds", + DEFAULT_UMICH_SEARCH_TIMEOUT); + + + /* + * Some LDAP servers do a better job with indexing where searching + * through all the groups searching for the user in the memberuid + * list. Others like SunOne directory that search can takes minutes + * if there are thousands of groups. So setting + * LDAP_use_memberof_for_groups to true in the configuration file + * will use the memberof lists of the account and search through + * only those groups to obtain gids. + */ + memberof = conf_get_str_with_def(LDAP_SECTION, + "LDAP_use_memberof_for_groups", "false"); + if ((strcasecmp(memberof, "true") == 0) || + (strcasecmp(memberof, "on") == 0) || + (strcasecmp(memberof, "yes") == 0)) + ldap_info.memberof_for_groups = 1; + else + ldap_info.memberof_for_groups = 0; + + /* + * If they specified a search base for the + * people tree or group tree we use that. + * Otherwise we use the default search base. + * Note: We no longer append the default base to the tree -- + * that should already be specified. + * this functions much like the NSS_LDAP modules + */ + if (ldap_info.people_tree == NULL || strlen(ldap_info.people_tree) == 0) + ldap_info.people_tree = ldap_info.base; + if (ldap_info.group_tree == NULL || strlen(ldap_info.group_tree) == 0) + ldap_info.group_tree = ldap_info.base; + + if (ldap_info.use_ssl && + ldap_info.tls_reqcert != LDAP_OPT_X_TLS_NEVER && + ldap_info.ca_cert == NULL) { + IDMAP_LOG(0, ("umichldap_init: You must specify LDAP_ca_cert " + "with LDAP_use_ssl=yes and " + "LDAP_tls_reqcert not set to \"never\"")); + goto fail; + } + + + /* print out some good debugging info */ + IDMAP_LOG(1, ("umichldap_init: canonicalize_name: %s", + canonicalize)); + IDMAP_LOG(1, ("umichldap_init: server : %s (from config value '%s')", + ldap_info.server, server_in)); + IDMAP_LOG(1, ("umichldap_init: port : %d", ldap_info.port)); + IDMAP_LOG(1, ("umichldap_init: people : %s", ldap_info.people_tree)); + IDMAP_LOG(1, ("umichldap_init: groups : %s", ldap_info.group_tree)); + + IDMAP_LOG(1, ("umichldap_init: user_dn : %s", + (ldap_info.user_dn && strlen(ldap_info.user_dn) != 0) + ? ldap_info.user_dn : "<not-supplied>")); + /* Don't print actual password into the log. */ + IDMAP_LOG(1, ("umichldap_init: passwd : %s", + (ldap_info.passwd && strlen(ldap_info.passwd) != 0) ? + "<supplied>" : "<not-supplied>")); + IDMAP_LOG(1, ("umichldap_init: use_ssl : %s", + ldap_info.use_ssl ? "yes" : "no")); + IDMAP_LOG(1, ("umichldap_init: ca_cert : %s", + ldap_info.ca_cert ? ldap_info.ca_cert : "<not-supplied>")); + IDMAP_LOG(1, ("umichldap_init: tls_reqcert : %s(%d)", + cert_req ? cert_req : "<not-supplied>", + ldap_info.tls_reqcert)); + IDMAP_LOG(1, ("umichldap_init: use_memberof_for_groups : %s", + ldap_info.memberof_for_groups ? "yes" : "no")); + IDMAP_LOG(1, ("umichldap_init: sasl_mech: %s", + (ldap_info.sasl_mech && strlen(ldap_info.sasl_mech) != 0) ? + ldap_info.sasl_mech : "<not-supplied>")); + IDMAP_LOG(1, ("umichldap_init: sasl_realm: %s", + (ldap_info.sasl_realm && strlen(ldap_info.sasl_realm) != 0) ? + ldap_info.sasl_realm : "<not-supplied>")); + IDMAP_LOG(1, ("umichldap_init: sasl_authcid: %s", + (ldap_info.sasl_authcid && + strlen(ldap_info.sasl_authcid) != 0) ? + ldap_info.sasl_authcid : "<not-supplied>")); + IDMAP_LOG(1, ("umichldap_init: sasl_authzid: %s", + (ldap_info.sasl_authzid && + strlen(ldap_info.sasl_authzid) != 0) ? + ldap_info.sasl_authzid : "<not-supplied>")); + IDMAP_LOG(1, ("umichldap_init: sasl_secprops: %s", + (ldap_info.sasl_secprops && + strlen(ldap_info.sasl_secprops) != 0) ? + ldap_info.sasl_secprops : "<not-supplied>")); + IDMAP_LOG(1, ("umichldap_init: sasl_canonicalize: %d", + ldap_info.sasl_canonicalize)); + IDMAP_LOG(1, ("umichldap_init: sasl_krb5_ccname: %s", + ldap_info.sasl_krb5_ccname)); + IDMAP_LOG(1, ("umichldap_init: follow_referrals: %s", + ldap_info.follow_referrals ? "yes" : "no")); + + IDMAP_LOG(1, ("umichldap_init: NFSv4_person_objectclass : %s", + ldap_map.NFSv4_person_objcls)); + IDMAP_LOG(1, ("umichldap_init: NFSv4_nfsname_attr : %s", + ldap_map.NFSv4_nfsname_attr)); + IDMAP_LOG(1, ("umichldap_init: NFSv4_acctname_attr : %s", + ldap_map.NFSv4_acctname_attr)); + IDMAP_LOG(1, ("umichldap_init: NFSv4_uid_attr : %s", + ldap_map.NFSv4_uid_attr)); + IDMAP_LOG(1, ("umichldap_init: NFSv4_group_objectclass : %s", + ldap_map.NFSv4_group_objcls)); + IDMAP_LOG(1, ("umichldap_init: NFSv4_gid_attr : %s", + ldap_map.NFSv4_gid_attr)); + IDMAP_LOG(1, ("umichldap_init: NFSv4_group_nfsname_attr : %s", + ldap_map.NFSv4_group_nfsname_attr)); + IDMAP_LOG(1, ("umichldap_init: NFSv4_member_attr : %s", + ldap_map.NFSv4_member_attr)); + IDMAP_LOG(1, ("umichldap_init: NFSv4_member_of_attr : %s", + ldap_map.NFSv4_member_of_attr)); + IDMAP_LOG(1, ("umichldap_init: NFSv4_grouplist_filter : %s", + ldap_map.NFSv4_grouplist_filter ? + ldap_map.NFSv4_grouplist_filter : "<not-specified>")); + IDMAP_LOG(1, ("umichldap_init: GSS_principal_attr : %s", + ldap_map.GSS_principal_attr)); + return 0; +fail: + return -1; +} + + +/* The external interface */ + +struct trans_func umichldap_trans = { + .name = "umich_ldap", + .init = umichldap_init, + .princ_to_ids = umichldap_gss_princ_to_ids, + .name_to_uid = umichldap_name_to_uid, + .name_to_gid = umichldap_name_to_gid, + .uid_to_name = umichldap_uid_to_name, + .gid_to_name = umichldap_gid_to_name, + .gss_princ_to_grouplist = umichldap_gss_princ_to_grouplist, +}; + +struct trans_func *libnfsidmap_plugin_init(void) +{ + return (&umichldap_trans); +} |