summaryrefslogtreecommitdiff
path: root/support/nfsidmap
diff options
context:
space:
mode:
authorJustin Mitchell <jumitche@redhat.com>2017-09-28 14:22:17 -0400
committerSteve Dickson <steved@redhat.com>2017-10-26 08:50:08 -0400
commit1ea6d9231f839b968adb44eaf98b363f436cb1d5 (patch)
treeb6bda545f3066b4b3f86ab10deef3fc9821f4c80 /support/nfsidmap
parent745287cca2fad6dfe63290bb8053ffe86637c18b (diff)
downloadnfs-utils-1ea6d9231f839b968adb44eaf98b363f436cb1d5.tar.gz
nfs-utils: Import libnfsidmap codebase
Merge the libnfsidmap code tree into nfs-utils. These are the original unmodified files, cherry picked only the necessary files that dont duplicate code, so will not build correctly at this stage. Signed-off-by: Justin Mitchell <jumitche@redhat.com> Signed-off-by: Steve Dickson <steved@redhat.com>
Diffstat (limited to 'support/nfsidmap')
-rw-r--r--support/nfsidmap/AUTHORS1
-rw-r--r--support/nfsidmap/COPYING30
-rw-r--r--support/nfsidmap/Makefile.am62
-rw-r--r--support/nfsidmap/README126
-rw-r--r--support/nfsidmap/gums.c788
-rw-r--r--support/nfsidmap/idmapd.conf137
-rw-r--r--support/nfsidmap/idmapd.conf.5308
-rw-r--r--support/nfsidmap/libnfsidmap.c709
-rw-r--r--support/nfsidmap/libnfsidmap.pc.in11
-rw-r--r--support/nfsidmap/libtest.c160
-rw-r--r--support/nfsidmap/nfs4_uid_to_name.3174
-rw-r--r--support/nfsidmap/nfsidmap.h67
-rw-r--r--support/nfsidmap/nfsidmap_internal.h72
-rw-r--r--support/nfsidmap/nss.c468
-rw-r--r--support/nfsidmap/static.c412
-rw-r--r--support/nfsidmap/umich_ldap.c1302
16 files changed, 4827 insertions, 0 deletions
diff --git a/support/nfsidmap/AUTHORS b/support/nfsidmap/AUTHORS
new file mode 100644
index 0000000..1101630
--- /dev/null
+++ b/support/nfsidmap/AUTHORS
@@ -0,0 +1 @@
+J. Bruce Fields <bfields@citi.umich.edu>
diff --git a/support/nfsidmap/COPYING b/support/nfsidmap/COPYING
new file mode 100644
index 0000000..7571bb7
--- /dev/null
+++ b/support/nfsidmap/COPYING
@@ -0,0 +1,30 @@
+Copyright (c) 2004 The Regents of the University of Michigan.
+All rights reserved.
+
+Marius Aamodt Eriksen <marius@umich.edu>
+J. Bruce Fields <bfields@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.
diff --git a/support/nfsidmap/Makefile.am b/support/nfsidmap/Makefile.am
new file mode 100644
index 0000000..85f19c8
--- /dev/null
+++ b/support/nfsidmap/Makefile.am
@@ -0,0 +1,62 @@
+ACLOCAL_AMFLAGS = -I m4
+
+if ENABLE_LDAP
+UMICH_LDAP_LIB = umich_ldap.la
+else
+UMICH_LDAP_LIB =
+endif
+if ENABLE_GUMS
+GUMS_MAPPING_LIB = gums.la
+else
+GUMS_MAPPING_LIB =
+endif
+lib_LTLIBRARIES = libnfsidmap.la
+pkglib_LTLIBRARIES = nsswitch.la static.la $(UMICH_LDAP_LIB) $(GUMS_MAPPING_LIB)
+
+# Library versioning notes from:
+# http://sources.redhat.com/autobook/autobook/autobook_91.html
+#
+# -version-info <current>:<revision>:<age>
+# <current> The number of the current interface exported by library.
+# <revision> The implementation number of the most recent interface
+# exported by the library. (i.e. revision should be updated
+# with each new release of the library, and reset to zero
+# when <current> is updated.)
+# <age> The number of previous additional interfaces supported
+# by this library.
+
+libnfsidmap_la_SOURCES = libnfsidmap.c cfg.c strlcpy.c cfg.h nfsidmap_internal.h queue.h
+libnfsidmap_la_LDFLAGS = -version-info 3:0:3
+libnfsidmap_la_LIBADD = -ldl
+
+nsswitch_la_SOURCES = nss.c
+nsswitch_la_LDFLAGS = -module -avoid-version
+
+static_la_SOURCES = static.c
+static_la_LDFLAGS = -module -avoid-version
+
+umich_ldap_la_SOURCES = umich_ldap.c
+umich_ldap_la_LDFLAGS = -module -avoid-version
+umich_ldap_la_LIBADD = -lldap
+
+gums_la_SOURCES = gums.c
+gums_la_LDFLAGS = -module -avoid-version
+
+man3_MANS = nfs4_uid_to_name.3
+man5_MANS = idmapd.conf.5
+include_HEADERS = nfsidmap.h
+
+EXTRA_DIST = $(man3_MANS) \
+ $(man5_MANS) \
+ libtest.c \
+ idmapd.conf
+
+# XXX: also exclude debian/files and debian/files.new ? do a clean??
+dist-hook:
+ mkdir $(distdir)/debian/
+ find $(srcdir)/debian -maxdepth 1 -not -type d |xargs -i cp {} $(distdir)/debian/
+
+pkgconfigdir=$(libdir)/pkgconfig
+pkgconfig_DATA = libnfsidmap.pc
+
+$(pkgconfig_DATA): $(top_builddir)/config.status
diff --git a/support/nfsidmap/README b/support/nfsidmap/README
new file mode 100644
index 0000000..5a448ef
--- /dev/null
+++ b/support/nfsidmap/README
@@ -0,0 +1,126 @@
+Library to help mapping id's, mainly for NFSv4.
+
+When NFSv4 is using AUTH_GSS (which currently only supports Kerberos v5), the
+NFSv4 server mapping functions MUST use secure communications.
+
+We provide several mapping functions, configured using /etc/idmapd.conf
+
+As of the 0.21 version of this library, mapping methods are separate
+dynamically-loaded libaries. This allows the separation of any
+LDAP requirements from the main libnfsidmap library. The main library
+now basically loads and calls the functions in the method-specific
+libaries. The method libraries are expected to be named
+"libnfsidmap_<method>.so", for example, "libnfsidmap_nsswitch.so".
+
+Several methods may be specified in the /etc/idmapd.conf configuration
+file. Each method is called until a mapping is found.
+
+The following translation methods are delivered in the default distribution:
+
+nsswitch
+--------
+
+The default method is called nsswitch. This method uses the get password
+file entry functions getpwname(), getpwid(), and the get group file entry
+functions getgrnam(), getgrgid(). The nsswitch method can therefore be
+configured by the /etc/nss_switch.conf passwd data base stanza. If secure
+communications are required (AUTH_GSS), the passwd data base stanza can contain
+the 'file' entry because the rpc.idmapd and rpc.svcgssd run as root, and/or the
+'ldap' entry if the ldap service is configured to use SASL in /etc/ldap.conf.
+The 'nis' entry is NOT recommended, it does not have a secure communications
+mode.
+
+
+static
+------
+
+This method works only for translating GSS authenticated names to local
+names. It uses a static mapping setup defined in the [Static] section
+of the idmapd.conf file. The form of the entries are:
+ <GSS-Authenticated name> = <localuser>
+
+For example:
+ nfs/host.domain.org@DOMAIN.ORG = root
+
+It is recommended that this module be used in combination with another
+module (e.g. the nsswitch module).
+
+umich_ldap
+----------
+An experimental method, umich_ldap uses an LDAP schema and ldap functions
+to perform translations. This method is designed to service remote users,
+allowing remote users to set and get ACLs as well as map GSS principals
+to id's. The functions are LDAP based, and the ldap search filters look
+for attribute names set by idmapd.conf [UMICH_SCHEMA]
+NFSv4_name_attr, NFSv4_group_attr, and GSS_principal_attr.
+
+It is assumed that the LDAP server will index these attributes, and that these
+attributes will be associated with the nss.schema posixAccount uidNumber and
+gidNumber. We expect that the uidNumber and gidNumber attribute will be
+configurable via the idmapd.conf file soon.
+
+NFSv4_name_attr holds an NFSv4 name of the form user@domain, where the domain
+portion of the name is a valid NFSv4 domain name. There is a one-to-one
+mapping between the NFSv4_name_attr name and a UID.
+
+NFSv4_group_attr holds an NFSv4 name of the form group@domain, where the domain
+portion of the name is a valid NFSv4 domain name. There is a one-to-one
+mapping between the NFSv4_group_attr name and a GID.
+
+GSS_principal_attr holds a GSS security mechanism specific context principal
+name. For Kerberos v5, it is a Kerberos principal <service/>principal@REALM.
+For SPKM3, it is a PKI DN such as (line is split):`
+"/C=US/ST=Michigan/O=University of Michigan/OU=UMICH Kerberos
+ Certification Authority/CN=andros/USERID=andros/Email=andros@UMICH.EDU".
+There is a many-to-one relationship between the GSS_principal_attr
+name and a UID plus GID.
+
+We have defined LDAP object classes for our experimental NFSv4 id mapping.
+We made the attribute names configurable so that other sites could still use
+the TR_UMICH_LDAP translation functions with different LDAP attribute names.
+
+We use the same attribute name, NFSv4Name for the NFSv4_name_attr and the
+NFSv4_group_attr. For local users and remote users that we wish to give
+a local machine account, we add the NFSv4Name attribute and the GSSAuthName
+attribute to the existing inetorgPerson and posixAccount schema.
+For remote users that we do not wish to give a local machine account,
+we use the NFSv4RemotePerson object to contain the NFSv4Name, uidNumber,
+gidNumber, and GSSAuthName.
+
+nfsv4.schema
+------------
+attributetype ( 1.3.6.1.4.1.250.1.61
+ NAME ( 'NFSv4Name')
+ DESC 'NFS version 4 Name'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE)
+
+attributetype ( 1.3.6.1.4.1.250.1.62
+ NAME ( 'GSSAuthName')
+ DESC 'RPCSEC GSS authenticated user name'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26)
+
+#
+# minimal information for NFSv4 access. used when local filesystem
+# access is not permitted (nsswitch ldap calls fail), or when
+# inetorgPerson is too much info.
+#
+objectclass ( 1.3.6.1.4.1.250.1.60 NAME 'NFSv4RemotePerson'
+ DESC 'NFS version4 person from remote NFSv4 Domain'
+ SUP top STRUCTURAL
+ MUST ( uidNumber $ gidNumber $ NFSv4Name )
+ MAY ( cn $ GSSAuthName $ description) )
+
+#
+# minimal information for NFSv4 access. used when local filesystem
+# access is not permitted (nsswitch ldap calls fail), or when
+# inetorgPerson is too much info.
+#
+objectclass ( 1.3.6.1.4.1.250.1.63 NAME 'NFSv4RemoteGroup'
+ DESC 'NFS version4 group from remote NFSv4 Domain'
+ SUP top STRUCTURAL
+ MUST ( gidNumber $ NFSv4Name )
+ MAY ( cn $ memberUid $ description) )
+
diff --git a/support/nfsidmap/gums.c b/support/nfsidmap/gums.c
new file mode 100644
index 0000000..2b12d95
--- /dev/null
+++ b/support/nfsidmap/gums.c
@@ -0,0 +1,788 @@
+/*
+ * gums.c
+ *
+ * Copyright (c) 2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Olga Kornievskaia <aglo@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 <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <err.h>
+#include <syslog.h>
+#include "nfsidmap.h"
+#include "nfsidmap_internal.h"
+#include "cfg.h"
+
+#include <voms_apic.h>
+
+#include <prima_logger.h>
+#include <prima_soap_client.h>
+#include <prima_saml_support.h>
+
+#define DEFAULT_PRIMA_CONF_LOCATION "/etc/grid-security/prima-authz.conf"
+#define DEFAULT_VOMSDIR "/etc/grid-security/vomsdir"
+#define DEFAULT_CADIR "/etc/grid-security/certificates"
+#define X509_DN_SIZE 1024
+
+//#define DEBUG_PRINT_VOMS
+
+#define USING_TEST_PROGRAM
+#ifdef USING_TEST_PROGRAM
+nfs4_idmap_log_function_t idmap_log_func = printf;
+int idmap_verbosity = 10;
+#endif
+
+/*
+ * GUMS Translation Methods
+ *
+ */
+
+/* global variables. voms/gums configuration attributes*/
+static char prima_conf[] = DEFAULT_PRIMA_CONF_LOCATION;
+typedef struct _plugin_config_params {
+ char *saml_schema_dir;
+ int saml_log_level;
+ char *server_cert;
+ char *server_key;
+ char *ca_dir;
+ char *gums_server_location;
+ char *voms_dir;
+} plugin_config_params;
+plugin_config_params conf;
+
+#ifdef VOMS_BUG
+static void my_VOMS_Delete(struct voms *v)
+{
+ int i;
+
+ if (!v) return;
+ if (v->user)
+ free(v->user);
+ if (v->server)
+ free(v->server);
+ if (v->fqan) {
+ for (i = 0; v->fqan[i] != NULL; i++)
+ free(v->fqan[i]);
+ free(v->fqan);
+ }
+ free(v);
+}
+
+static struct voms *my_VOMS_Copy(struct voms *v, int *err)
+{
+ struct voms *cv;
+ int i;
+
+ cv = calloc(1, sizeof(struct voms));
+ if (cv == NULL)
+ goto out;
+ cv->user = strdup(v->user);
+ if (cv->user == NULL)
+ goto out;
+ cv->server = strdup(v->server);
+ if (cv->server == NULL)
+ goto out;
+ for (i = 0; v->fqan[i] != NULL; i++) {
+ if (v->fqan[i] == NULL)
+ break;
+ }
+ cv->fqan = calloc(i+1, sizeof(char *));
+ if (cv->fqan == NULL)
+ goto out;
+ cv->fqan[i] = NULL;
+ for (i = 0; v->fqan[i] != NULL; i++) {
+ cv->fqan[i] = strdup(v->fqan[i]);
+ if (cv->fqan[i] == NULL)
+ goto out;
+ }
+ return cv;
+out:
+ if (cv)
+ my_VOMS_Delete(cv);
+
+ return NULL;
+}
+#endif
+
+
+#ifdef DEBUG_PRINT_VOMS
+void printvoms(struct voms *v)
+{
+ int j;
+
+ printf("SIGLEN: %d\nUSER: %s\n", v->siglen, v->user);
+ printf("UCA: %s\nSERVER: %s\n", v->userca, v->server);
+ printf("SCA: %s\nVO: %s\n", v->serverca, v->voname);
+ printf("URI: %s\nDATE1: %s\n", v->uri, v->date1);
+ printf("DATE2: %s\n", v->date2);
+
+ switch (v->type) {
+ case TYPE_NODATA:
+ printf("NO DATA\n");
+ break;
+ case TYPE_CUSTOM:
+ printf("%*s\n", v->datalen - 10, v->custom);
+ break;
+ case TYPE_STD:
+ j = 0;
+ while (v->std[j]) {
+ printf("GROUP: %s\nROLE: %s\nCAP: %s\n",v->std[j]->group,
+ v->std[j]->role,v->std[j]->cap);
+ j++;
+ }
+ }
+}
+
+void print(struct vomsdata *d)
+{
+ struct voms **vo = d->data;
+ struct voms *v;
+ int k = 0;
+
+ while(vo[k]) {
+ v = vo[k++];
+ printf("%d *******************************************\n",k);
+ printvoms(v);
+ }
+
+ if (d->workvo)
+ printf("WORKVO: %s\n", d->workvo);
+
+ if (d->extra_data)
+ printf("EXTRA: %s\n", d->extra_data);
+}
+#endif
+
+static void free_plugin_config_params()
+{
+ if (conf.saml_schema_dir)
+ free(conf.saml_schema_dir);
+ conf.saml_schema_dir = NULL;
+ if (conf.server_cert)
+ free(conf.server_cert);
+ conf.server_cert = NULL;
+ if (conf.server_key)
+ free(conf.server_key);
+ conf.server_key = NULL;
+ if (conf.ca_dir)
+ free(conf.ca_dir);
+ conf.ca_dir = NULL;
+ if (conf.voms_dir)
+ free(conf.voms_dir);
+ conf.voms_dir = NULL;
+}
+
+static int validate_plugin_config_params()
+{
+ if (conf.saml_schema_dir == NULL ||
+ conf.server_cert == NULL ||
+ conf.server_key == NULL ||
+ conf.gums_server_location == NULL)
+ return -1;
+
+ if (conf.ca_dir == NULL) {
+ conf.ca_dir = strdup(DEFAULT_CADIR);
+ if (conf.ca_dir == NULL)
+ return -1;
+ }
+ if (conf.voms_dir == NULL) {
+ conf.voms_dir = strdup(DEFAULT_VOMSDIR);
+ if (conf.voms_dir == NULL)
+ return -1;
+ }
+ return 0;
+}
+
+static int gums_init(void)
+{
+ FILE *f = NULL;
+ int ret = -1, i = 0;
+ char buf[512], type[128], value[256];
+ char *alt_conf = NULL;
+
+ alt_conf = conf_get_str("GUMS", "Conf_File");
+ if (alt_conf == NULL)
+ f = fopen(prima_conf, "r");
+ else
+ f = fopen(alt_conf, "r");
+ if (f == NULL)
+ goto out;
+
+ while (fgets(buf, 512, f)) {
+ i = 0;
+ while(buf[i] == ' ' || buf[i] == '\t')
+ i++;
+ if (buf[i] == '#' || buf[i] == '\0' || buf[i] == '\n')
+ continue;
+ if (sscanf(&buf[i], "%127s%255s",type,value) < 2) {
+ IDMAP_LOG(0, ("ERROR: malformed line: %s\n", &buf[i]));
+ goto out;
+ }
+ IDMAP_LOG(1, ("PRIMA conf: type=%s value=%s\n", type, value));
+ if (strncmp(type, "imsContact", 10) == 0) {
+ conf.gums_server_location = strdup(value);
+ } else if (strncmp(type, "serviceCert", 11) == 0) {
+ conf.server_cert = strdup(value);
+ } else if (strncmp(type, "serviceKey", 10) == 0) {
+ conf.server_key = strdup(value);
+ } else if (strncmp(type, "caCertDir", 9) == 0) {
+ conf.ca_dir = strdup(value);
+ } else if (strncmp(type, "samlSchemaDir", 13) == 0) {
+ conf.saml_schema_dir = strdup(value);
+ } else if (strncmp(type, "logLevel", 8) == 0) {
+ if (strncmp(value, "debug", 5) == 0)
+ conf.saml_log_level = PRIMA_LOG_DEBUG;
+ else if (strncmp(value, "error", 5) == 0)
+ conf.saml_log_level = PRIMA_LOG_ERROR;
+ else if (strncmp(value, "none", 4) == 0)
+ conf.saml_log_level = PRIMA_LOG_NONE;
+ else
+ conf.saml_log_level = PRIMA_LOG_INFO;
+ }
+ }
+
+ if (validate_plugin_config_params() != 0)
+ goto out;
+
+ ret = 0;
+out:
+ if (f)
+ fclose(f);
+ if (ret)
+ free_plugin_config_params();
+
+ return ret;
+}
+
+static int retrieve_attributes(X509 *cert, STACK_OF(X509) *cas,
+ struct voms **attrs)
+{
+ int ret = -1, err = 0;
+ struct vomsdata *vd = NULL;
+
+ vd = VOMS_Init(conf.voms_dir, conf.ca_dir);
+ if (vd == NULL) {
+ IDMAP_LOG(0, ("VOMS_Init failed\n"));
+ return -1;
+ }
+ ret = VOMS_Retrieve(cert, cas, RECURSE_CHAIN, vd, &err);
+ if (err) {
+ char *err_msg;
+ err_msg = VOMS_ErrorMessage(vd, err, NULL, 0);
+ if (err == VERR_NOEXT)
+ ret = 0;
+ else
+ IDMAP_LOG(0, ("VOMS error %s\n", err_msg));
+ goto out;
+ } else if (ret) {
+ struct voms *v, *v2;
+#ifdef DEBUG_PRINT_VOMS
+ print(vd);
+#endif
+ v = VOMS_DefaultData(vd, &err);
+ if (err == VERR_NONE) {
+#ifdef DEBUG_PRINT_VOMS
+ printvoms(v);
+ while (v->fqan[i] != NULL)
+ IDMAP_LOG(1, ("user's fqan: %s\n", v->fqan[i++]));
+#endif
+#ifdef VOMS_BUG
+ v2 = my_VOMS_Copy(v, &err);
+#else
+ v2 = VOMS_Copy(v, &err);
+#endif
+ if (v2 == NULL) {
+ IDMAP_LOG(0, ("VOMS_Copy failed err=%d\n", err));
+ goto out;
+ }
+ *attrs = v2;
+ }
+ }
+ ret = 0;
+out:
+ if (vd)
+ VOMS_Destroy(vd);
+ return ret;
+}
+
+static int get_server_dn(unsigned char **server_dn)
+{
+ BIO *tmp = NULL;
+ X509 *cert = NULL;
+ int ret = -1;
+ char dn[X509_DN_SIZE];
+
+ tmp = BIO_new(BIO_s_file());
+ if (tmp == NULL)
+ goto out;
+
+ ret = BIO_read_filename(tmp, conf.server_cert);
+ if (ret == 0) {
+ ret = errno;
+ goto out;
+ }
+
+ cert = (X509 *) PEM_read_bio_X509(tmp, NULL, NULL, NULL);
+ if (cert == NULL)
+ goto out;
+
+ X509_NAME_oneline(X509_get_subject_name(cert), dn, sizeof(dn));
+
+ *server_dn = strdup(dn);
+ if (*server_dn == NULL)
+ goto out;
+
+ ret = 0;
+out:
+ if (tmp)
+ BIO_free(tmp);
+ if (cert)
+ X509_free(cert);
+
+ return ret;
+}
+
+static int create_saml_request(char *dn, struct voms *attrs, char **saml_req)
+{
+ int ret = -1, i;
+ char *req = NULL;
+ unsigned char *server_dn = NULL;
+ prima_saml_fqans fqans;
+
+ IDMAP_LOG(2, ("create_saml_request start\n"));
+ ret = initPrimaSAMLFQANs(&fqans);
+ if (ret) {
+ IDMAP_LOG(0, ("initPrimaSAMLFQANs failed with %d\n", ret));
+ goto out;
+ }
+
+ if (attrs) {
+ for (i = 0; attrs->fqan[i] != NULL; i++) {
+ ret = addPrimaSAMLFQAN(&fqans, attrs->server, attrs->fqan[i]);
+ IDMAP_LOG(1, ("addPrimaSAMLFQAN returned %d\n", ret));
+ }
+ dn = attrs->user;
+ } else
+ IDMAP_LOG(1, ("No VOMS attributes present in the cert\n"));
+
+ if (get_server_dn(&server_dn) != 0)
+ goto out;
+ req = createSAMLQueryAndRequest(server_dn, dn, &fqans);
+ if (req == NULL) {
+ IDMAP_LOG(0, ("createSAMLQueryAndRequest failed to create "
+ "SAML request\n"));
+ goto out;
+ }
+ IDMAP_LOG(1, ("SAML Request %s\n", req));
+
+ ret = 0;
+ *saml_req = req;
+out:
+ cleanupPrimaSAMLFQANs(&fqans);
+
+ if (server_dn)
+ free(server_dn);
+
+ IDMAP_LOG(2, ("create_saml_request returning %d\n", ret));
+ return ret;
+}
+
+static int process_parameters(extra_mapping_params **ex, X509 **user_cert,
+ STACK_OF(X509) **user_chain)
+{
+
+ int ret = -1, i;
+ X509 *cert = NULL, *x;
+ STACK_OF(X509) *chain = NULL;
+ unsigned char *p;
+
+ if (ex[0]->content_type != X509_CERT)
+ return -1;
+
+ /* get user's x509 certificate */
+ p = ex[0]->content;
+ cert = d2i_X509(NULL, &p, ex[0]->content_len);
+ if (cert == NULL)
+ goto out;
+
+ /* get user's other certificates */
+ chain = sk_X509_new_null();
+ if (chain == NULL)
+ goto out;
+ for (i = 1; ex[i] != NULL; i++) {
+ if (ex[i]->content_type != X509_CERT)
+ continue;
+ p = ex[i]->content;
+ x = d2i_X509(NULL, &p, ex[i]->content_len);
+ if (x == NULL)
+ goto out;
+ sk_X509_push(chain, x);
+ }
+ ret = 0;
+
+ *user_cert = cert;
+ *user_chain = chain;
+out:
+ if (ret) {
+ int num;
+ if (cert)
+ X509_free(cert);
+ if (chain)
+ sk_X509_pop_free(chain, X509_free);
+ }
+
+ return ret;
+}
+
+struct pwbuf {
+ struct passwd pwbuf;
+ char buf[1];
+};
+
+static int translate_to_uid(char *local_uid, uid_t *uid, uid_t *gid)
+{
+ int ret = -1;
+ struct passwd *pw = NULL;
+ struct pwbuf *buf = NULL;
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+
+ buf = malloc(sizeof(*buf) + buflen);
+ if (buf == NULL)
+ goto out;
+
+ ret = getpwnam_r(local_uid, &buf->pwbuf, buf->buf, buflen, &pw);
+ if (pw == NULL) {
+ IDMAP_LOG(0, ("getpwnam: name %s not found\n", local_uid));
+ goto out;
+ }
+ *uid = pw->pw_uid;
+ *gid = pw->pw_gid;
+
+ ret = 0;
+out:
+ if (buf)
+ free(buf);
+ return ret;
+}
+
+static int translate_to_gid(char *local_gid, uid_t *gid)
+{
+ struct group *gr = NULL;
+ struct group grbuf;
+ char *buf = NULL;
+ size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ int ret = -1;
+
+ do {
+ buf = malloc(buflen);
+ if (buf == NULL)
+ goto out;
+
+ ret = -getgrnam_r(local_gid, &grbuf, buf, buflen, &gr);
+ if (gr == NULL && !ret)
+ ret = -ENOENT;
+ if (ret == -ERANGE) {
+ buflen *= 2;
+ free(buf);
+ }
+ } while (ret == -ERANGE);
+
+ if (ret)
+ goto out;
+
+ *gid = gr->gr_gid;
+
+ ret = 0;
+out:
+ if (buf)
+ free(buf);
+ return ret;
+}
+
+static int gums_gss_princ_to_ids(char *secname, char *princ,
+ uid_t *uid, uid_t *gid,
+ extra_mapping_params **ex)
+{
+ int ret = -1, size, i;
+ X509 *cert = NULL;
+ STACK_OF(X509) *cas = NULL;
+ char dn[X509_DN_SIZE];
+ struct voms *attrs = NULL;
+ char *saml_req = NULL, *saml_resp = NULL;
+ int saml_result;
+ char *local_uid = NULL, *local_gid = NULL, *p;
+
+ /* accept only spkm3 translations */
+ if (strcmp(secname, "spkm3"))
+ return -EINVAL;
+
+ /* must supply either a DN and/or at least 1 binary blob */
+ if (princ == NULL && (ex == NULL || (ex && ex[0] == NULL)))
+ return -EINVAL;
+
+ /* process extra parameters */
+ if (process_parameters(ex, &cert, &cas) != 0)
+ goto out;
+
+ IDMAP_LOG(1, ("Processing name translation of client\n"));
+ X509_NAME_oneline(X509_get_subject_name(cert), dn, sizeof(dn));
+ IDMAP_LOG(1, ("DN=%s\n", dn));
+ size = sk_X509_num(cas);
+ IDMAP_LOG(1, ("Including following CAs (%d)\n", size));
+ for (i=0; i < size; i++) {
+ X509_NAME_oneline(X509_get_subject_name(sk_X509_value(cas, i)),
+ dn, sizeof(dn));
+ IDMAP_LOG(1, ("DN=%s\n", dn));
+ }
+
+ /* retrieve VOMS attributes */
+ if (retrieve_attributes(cert, cas, &attrs) != 0)
+ goto out;
+ if (attrs == NULL)
+ X509_NAME_oneline(X509_get_subject_name(cert), dn, sizeof(dn));
+
+ /* initialize SAML library */
+ if (initPrimaSAMLSupport(conf.saml_schema_dir,
+ conf.saml_log_level) != 0) {
+ IDMAP_LOG(0, ("initPrimaSAMLSupport failed\n"));
+ goto out;
+ }
+
+ /* create SAML request */
+ if (create_saml_request(dn, attrs, &saml_req) != 0)
+ goto out;
+
+ /* contact GUMS server */
+ saml_resp = queryIdentityMappingService(conf.gums_server_location,
+ saml_req, conf.server_cert, conf.server_key,
+ conf.ca_dir);
+ if (saml_resp != NULL) {
+ saml_result = processResponse(saml_resp, saml_req, &local_uid,
+ &local_gid);
+ IDMAP_LOG(1, ("processResponse returned %d\n", saml_result));
+ if (saml_result || local_uid == NULL) {
+ IDMAP_LOG(0, ("processResponse failed to return "
+ "local id\n"));
+ ret = -ENOENT;
+ goto out;
+ }
+ IDMAP_LOG(1, ("GUMS returned uid=%s gid=%s\n", local_uid,
+ local_gid));
+ }
+
+ /* translate account name to uid */
+ if (translate_to_uid(local_uid, uid, gid))
+ goto out;
+ if (local_gid)
+ if (translate_to_gid(local_gid, gid))
+ goto out;
+
+ ret = 0;
+out:
+ if (cert)
+ X509_free(cert);
+
+ if (cas)
+ sk_X509_pop_free(cas, X509_free);
+
+ if (attrs)
+#ifdef VOMS_BUG
+ my_VOMS_Delete(attrs);
+#else
+ VOMS_Delete(attrs);
+#endif
+
+ if (saml_req)
+ free(saml_req);
+
+ if (saml_resp)
+ free(saml_resp);
+
+ cleanupPrimaSAMLSupport();
+
+ return ret;
+}
+
+struct trans_func gums_trans = {
+ .name = "gums",
+ .init = gums_init,
+ .princ_to_ids = gums_gss_princ_to_ids,
+ .name_to_uid = NULL,
+ .name_to_gid = NULL,
+ .uid_to_name = NULL,
+ .gid_to_name = NULL,
+ .gss_princ_to_grouplist = NULL,
+};
+
+struct trans_func *libnfsidmap_plugin_init()
+{
+ return (&gums_trans);
+}
+
+#ifdef USING_TEST_PROGRAM
+static STACK_OF(X509) *load_chain(char *certfile)
+{
+ STACK_OF(X509_INFO) *sk=NULL;
+ STACK_OF(X509) *stack=NULL, *ret=NULL;
+ BIO *in=NULL;
+ X509_INFO *xi;
+ int first = 1;
+
+ if (!(stack = sk_X509_new_null())) {
+ printf("memory allocation failure\n");
+ goto end;
+ }
+
+ if (!(in=BIO_new_file(certfile, "r"))) {
+ printf("error opening the file, %s\n",certfile);
+ goto end;
+ }
+
+ /* This loads from a file, a stack of x509/crl/pkey sets */
+ if (!(sk=(STACK_OF(X509_INFO) *)PEM_X509_INFO_read_bio(in,NULL,NULL,NULL))) {
+ /* if (!(sk=PEM_X509_read_bio(in,NULL,NULL,NULL))) { */
+ printf("error reading the file, %s\n",certfile);
+ goto end;
+ }
+
+ /* scan over it and pull out the certs */
+ while (sk_X509_INFO_num(sk)) {
+ /* skip first cert */
+ if (first) {
+ xi=sk_X509_INFO_shift(sk);
+ X509_INFO_free(xi);
+ first = 0;
+ continue;
+ }
+ xi=sk_X509_INFO_shift(sk);
+ if (xi->x509 != NULL) {
+ sk_X509_push(stack,xi->x509);
+ xi->x509=NULL;
+ }
+ X509_INFO_free(xi);
+ }
+ if (!sk_X509_num(stack)) {
+ printf("no certificates in file, %s\n",certfile);
+ sk_X509_free(stack);
+ goto end;
+ }
+ ret=stack;
+end:
+ BIO_free(in);
+ sk_X509_INFO_free(sk);
+ return(ret);
+}
+
+void create_params(X509 *cert, STACK_OF(X509) *cas,
+ extra_mapping_params ***ret_params)
+{
+ int len = 0, i, size = 0;
+ unsigned char *p, *buf = NULL;
+ extra_mapping_params **params = NULL;
+ X509 *x;
+
+ if (cas)
+ size = sk_X509_num(cas);
+ params = malloc((size+2)*sizeof(extra_mapping_params *));
+ params[size+1] = NULL;
+
+ /* 1st element is user's certificate */
+ len = i2d_X509(cert, NULL);
+ p = buf = malloc(len);
+ i2d_X509(cert, &p);
+ params[0] = malloc(sizeof(extra_mapping_params));
+ params[0]->content_type = X509_CERT;
+ params[0]->content = buf;
+ params[0]->content_len = len;
+
+ /* add other certificates to the array */
+ for (i = 0; i < size; i++) {
+ x = sk_X509_value(cas, i);
+ params[i+1] = malloc(sizeof(extra_mapping_params));
+ len = i2d_X509(x, NULL);
+ p = buf = malloc(len);
+ i2d_X509(x, &p);
+ params[i+1]->content_type = X509_CERT;
+ params[i+1]->content = buf;
+ params[i+1]->content_len = len;
+ }
+ *ret_params = params;
+}
+
+int main(void)
+{
+ int uid, gid, ret, i;
+ extra_mapping_params **params = NULL;
+ BIO *tmp = NULL;
+ X509 *cert = NULL, *x;
+ STACK_OF(X509) *cas = NULL;
+ unsigned char *proxy_file;
+
+ if (gums_init())
+ return -1;
+ proxy_file = getenv("X509_USER_PROXY");
+ if (proxy_file == NULL) {
+ fprintf(stderr, "X509_USER_PROXY is not set\n");
+ return -1;
+ }
+ tmp = BIO_new(BIO_s_file());
+ BIO_read_filename(tmp, proxy_file);
+ cert = (X509 *) PEM_read_bio_X509(tmp, NULL, NULL, NULL);
+ cas = load_chain(proxy_file);
+ create_params(cert, cas, &params);
+ ret = gums_gss_princ_to_ids("spkm3", NULL, &uid, &gid, params);
+ fprintf(stderr, "gums_gss_princ_to_ids returns %d uid=%d gid=%d\n",
+ ret, uid, gid);
+
+ if (tmp)
+ BIO_free(tmp);
+ if (cert)
+ X509_free(cert);
+ if (cas)
+ sk_X509_pop_free(cas, X509_free);
+
+ free_plugin_config_params();
+
+ if (params) {
+ for (i=0; params[i] != NULL; i++) {
+ free(params[i]->content);
+ free(params[i]);
+ }
+ free(params);
+ }
+
+ return 0;
+}
+#endif
diff --git a/support/nfsidmap/idmapd.conf b/support/nfsidmap/idmapd.conf
new file mode 100644
index 0000000..f07286c
--- /dev/null
+++ b/support/nfsidmap/idmapd.conf
@@ -0,0 +1,137 @@
+[General]
+#Verbosity = 0
+# The following should be set to the local NFSv4 domain name
+# The default is the host's DNS domain name.
+#Domain = local.domain.edu
+
+# In multi-domain environments, some NFS servers will append the identity
+# management domain to the owner and owner_group in lieu of a true NFSv4
+# domain. This option can facilitate lookups in such environments. If
+# set to a value other than "none", the nsswitch plugin will first pass
+# the name to the password/group lookup function without stripping the
+# domain off. If that mapping fails then the plugin will try again using
+# the old method (comparing the domain in the string to the Domain value,
+# stripping it if it matches, and passing the resulting short name to the
+# lookup function). Valid values are "user", "group", "both", and
+# "none". The default is "none".
+#No-Strip = none
+
+# Winbind has a quirk whereby doing a group lookup in UPN format
+# (e.g. staff@americas.example.com) will cause the group to be
+# displayed prefixed with the full domain in uppercase
+# (e.g. AMERICAS.EXAMPLE.COM\staff) instead of in the familiar netbios
+# name format (e.g. AMERICAS\staff). Setting this option to true
+# causes the name to be reformatted before passing it to the group
+# lookup function in order to work around this. This setting is
+# ignored unless No-Strip is set to either "both" or "group".
+# The default is "false".
+#Reformat-Group = false
+
+# The following is a comma-separated list of Kerberos realm
+# names that should be considered to be equivalent to the
+# local realm, such that <user>@REALM.A can be assumed to
+# be the same user as <user>@REALM.B
+# If not specified, the default local realm is the domain name,
+# which defaults to the host's DNS domain name,
+# translated to upper-case.
+# Note that if this value is specified, the local realm name
+# must be included in the list!
+#Local-Realms =
+
+[Mapping]
+
+#Nobody-User = nobody
+#Nobody-Group = nobody
+
+[Translation]
+
+# Translation Method is an comma-separated, ordered list of
+# translation methods that can be used. Distributed methods
+# include "nsswitch", "umich_ldap", and "static". Each method
+# is a dynamically loadable plugin library.
+# New methods may be defined and inserted in the list.
+# The default is "nsswitch".
+#Method = nsswitch
+
+# Optional. This is a comma-separated, ordered list of
+# translation methods to be used for translating GSS
+# authenticated names to ids.
+# If this option is omitted, the same methods as those
+# specified in "Method" are used.
+#GSS-Methods = <alternate method list for translating GSS names>
+
+#-------------------------------------------------------------------#
+# The following are used only for the "static" Translation Method.
+#-------------------------------------------------------------------#
+[Static]
+
+# A "static" list of GSS-Authenticated names to
+# local user name mappings
+
+#someuser@REALM = localuser
+
+
+#-------------------------------------------------------------------#
+# The following are used only for the "umich_ldap" Translation Method.
+#-------------------------------------------------------------------#
+
+[UMICH_SCHEMA]
+
+# server information (REQUIRED)
+LDAP_server = ldap-server.local.domain.edu
+
+# the default search base (REQUIRED)
+LDAP_base = dc=local,dc=domain,dc=edu
+
+#-----------------------------------------------------------#
+# The remaining options have defaults (as shown)
+# and are therefore not required.
+#-----------------------------------------------------------#
+
+# whether or not to perform canonicalization on the
+# name given as LDAP_server
+#LDAP_canonicalize_name = true
+
+# absolute search base for (people) accounts
+#LDAP_people_base = <LDAP_base>
+
+# absolute search base for groups
+#LDAP_group_base = <LDAP_base>
+
+# Set to true to enable SSL - anything else is not enabled
+#LDAP_use_ssl = false
+
+# You must specify a CA certificate location if you enable SSL
+#LDAP_ca_cert = /etc/ldapca.cert
+
+# Objectclass mapping information
+
+# Mapping for the person (account) object class
+#NFSv4_person_objectclass = NFSv4RemotePerson
+
+# Mapping for the nfsv4name attribute the person object
+#NFSv4_name_attr = NFSv4Name
+
+# Mapping for the UID number
+#NFSv4_uid_attr = UIDNumber
+
+# Mapping for the GSSAPI Principal name
+#GSS_principal_attr = GSSAuthName
+
+# Mapping for the account name attribute (usually uid)
+# The value for this attribute must match the value of
+# the group member attribute - NFSv4_member_attr
+#NFSv4_acctname_attr = uid
+
+# Mapping for the group object class
+#NFSv4_group_objectclass = NFSv4RemoteGroup
+
+# Mapping for the GID attribute
+#NFSv4_gid_attr = GIDNumber
+
+# Mapping for the Group NFSv4 name
+#NFSv4_group_attr = NFSv4Name
+
+# Mapping for the Group member attribute (usually memberUID)
+# The value of this attribute must match the value of NFSv4_acctname_attr
+#NFSv4_member_attr = memberUID
diff --git a/support/nfsidmap/idmapd.conf.5 b/support/nfsidmap/idmapd.conf.5
new file mode 100644
index 0000000..9a6457e
--- /dev/null
+++ b/support/nfsidmap/idmapd.conf.5
@@ -0,0 +1,308 @@
+.\"
+.\" idmapd.conf(5)
+.\"
+.\" COPYRIGHT (c) 2008
+.\" The Regents of the University of Michigan
+.\" ALL RIGHTS RESERVED
+.\"
+.\" Permission is granted to use, copy, create derivative works
+.\" and redistribute this software and such derivative works
+.\" for any purpose, so long as the name of The University of
+.\" Michigan is not used in any advertising or publicity
+.\" pertaining to the use of distribution of this software
+.\" without specific, written prior authorization. If the
+.\" above copyright notice or any other identification of the
+.\" University of Michigan is included in any copy of any
+.\" portion of this software, then the disclaimer below must
+.\" also be included.
+.\"
+.\" THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+.\" FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+.\" PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+.\" MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+.\" WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+.\" REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+.\" FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+.\" CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+.\" OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+.\" IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGES.
+.\"
+.TH idmapd.conf 5 "19 Nov 2008"
+.SH NAME
+idmapd.conf \- configuration file for libnfsidmap
+.SH SYNOPSIS
+Configuration file for libnfsidmap. Used by idmapd and svcgssd to map NFSv4 name to and from ids.
+.SH DESCRIPTION
+The
+.B idmapd.conf
+configuration file consists of several sections, initiated by strings of the
+form [General] and [Mapping]. Each section may contain lines of the form
+.nf
+ variable = value
+.fi
+The recognized sections and their recognized variables are as follows:
+.\"
+.\" -------------------------------------------------------------------
+.\" The [General] section
+.\" -------------------------------------------------------------------
+.\"
+.SS "[General] section variables"
+.nf
+
+
+.fi
+.TP
+.B Verbosity
+Verbosity level of debugging
+(Default: 0)
+.TP
+.B Domain
+The local NFSv4 domain name. An NFSv4 domain is a namespace with
+a unique username<->UID and groupname<->GID mapping.
+(Default: Host's fully-qualified DNS domain name)
+.TP
+.B No-Strip
+In multi-domain environments, some NFS servers will append the identity
+management domain to the owner and owner_group in lieu of a true NFSv4
+domain. This option can facilitate lookups in such environments. If
+set to a value other than "none", the nsswitch plugin will first pass
+the name to the password/group lookup function without stripping the
+domain off. If that mapping fails then the plugin will try again using
+the old method (comparing the domain in the string to the Domain value,
+stripping it if it matches, and passing the resulting short name to the
+lookup function). Valid values are "user", "group", "both", and
+"none".
+(Default: "none")
+.TP
+.B Reformat-Group
+Winbind has a quirk whereby doing a group lookup in UPN format
+(e.g. staff@americas.example.com) will cause the group to be
+displayed prefixed with the full domain in uppercase
+(e.g. AMERICAS.EXAMPLE.COM\\staff) instead of in the familiar netbios
+name format (e.g. AMERICAS\\staff). Setting this option to true
+causes the name to be reformatted before passing it to the group
+lookup function in order to work around this. This setting is
+ignored unless No-Strip is set to either "both" or "group".
+(Default: "false")
+.TP
+.B Local-Realms
+A comma-separated list of Kerberos realm names that may be considered equivalent to the
+local realm name. For example, users juser@ORDER.EDU and juser@MAIL.ORDER.EDU
+may be considered to be the same user in the specified
+.B Domain.
+(Default: the host's default realm name)
+.br
+.B Note:
+If a value is specified here, the default local realm must be included as well.
+.\"
+.\" -------------------------------------------------------------------
+.\" The [Mapping] section
+.\" -------------------------------------------------------------------
+.\"
+.SS "[Mapping] section variables"
+.nf
+
+.fi
+.TP
+.B Nobody-User
+Local user name to be used when a mapping cannot be completed.
+.TP
+.B Nobody-Group
+Local group name to be used when a mapping cannot be completed.
+.\"
+.\" -------------------------------------------------------------------
+.\" The [Translation] section
+.\" -------------------------------------------------------------------
+.\"
+.SS "[Translation] section variables"
+.nf
+
+.fi
+.TP
+.B Method
+A comma-separated, ordered list of mapping methods (plug-ins)
+to use when mapping between NFSv4 names and local IDs. Each
+specified method is tried in order until a mapping is found,
+or there are no more methods to try. The methods included in
+the default distribution include "nsswitch", "umich_ldap", and
+"static".
+(Default: nsswitch)
+.TP
+.B GSS-Methods
+An optional comma-separated, ordered list of mapping methods (plug-ins)
+to use when mapping between GSS Authenticated names and local IDs.
+(Default: the same list as specified for
+.B Method)
+.\"
+.\" -------------------------------------------------------------------
+.\" The [Static] section
+.\" -------------------------------------------------------------------
+.\"
+.SS "[Static] section variables"
+.nf
+
+.fi
+The "static" translation method uses a static list of GSS-Authenticated
+names to local user names. Entries in the list are of the form:
+.nf
+ principal@REALM = localusername
+.fi
+.\"
+.\" -------------------------------------------------------------------
+.\" The [UMICH_SCHEMA] section
+.\" -------------------------------------------------------------------
+.\"
+.SS "[UMICH_SCHEMA] section variables"
+.nf
+
+.fi
+If the "umich_ldap" translation method is specified, the following
+variables within the [UMICH_SCHEMA] section are used.
+.TP
+.B LDAP_server
+LDAP server name or address
+(Required if using UMICH_LDAP)
+.TP
+.B LDAP_base
+Absolute LDAP search base.
+(Required if using UMICH_LDAP)
+.TP
+.B LDAP_people_base
+Absolute LDAP search base for people accounts.
+(Default: The
+.B LDAP_base
+value)
+.TP
+.B LDAP_group_base
+Absolute LDAP search base for group accounts.
+(Default: The
+.B LDAP_base
+value)
+.TP
+.B LDAP_canonicalize_name
+Whether or not to perform name canonicalization on the
+name given as
+.B LDAP_server
+(Default: "true")
+.TP
+.B LDAP_use_ssl
+Set to "true" to enable SSL communication with the LDAP server.
+(Default: "false")
+.TP
+.B LDAP_ca_cert
+Location of a trusted CA certificate used when SSL is enabled
+(Required if
+.B LDAP_use_ssl
+is true)
+.TP
+.B NFSv4_person_objectclass
+The object class name for people accounts in your local LDAP schema
+(Default: NFSv4RemotePerson)
+.TP
+.B NFSv4_name_attr
+Your local schema's attribute name to be used for NFSv4 user names
+(Default: NFSv4Name)
+.TP
+.B NFSv4_uid_attr
+Your local schema's attribute name to be used for uidNumber
+(Default: uidNumber)
+.TP
+.B GSS_principal_attr
+Your local schema's attribute name for GSSAPI Principal names
+(Default: GSSAuthName)
+.TP
+.B NFSv4_acctname_attr
+Your local schema's attribute name to be used for account names
+(Default: uid)
+.TP
+.B NFSv4_group_objectclass
+The object class name for group accounts in your local LDAP schema
+(Default: NFSv4RemoteGroup)
+.TP
+.B NFSv4_gid_attr
+Your local schema's attribute name to be used for gidNumber
+(Default: gidNumber)
+.TP
+.B NFSv4_group_attr
+Your local schema's attribute name to be used for NFSv4 group names
+(Default: NFSv4Name)
+.TP
+.B LDAP_use_memberof_for_groups
+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
+.B 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.
+(Default: false)
+.TP
+.B NFSv4_member_attr
+If
+.B LDAP_use_memberof_for_groups
+is true, this is the attribute to be searched for.
+(Default: memberUid)
+.TP
+.B NFSv4_grouplist_filter
+An optional search filter for determining group membership.
+(No Default)
+.TP
+.B LDAP_timeout_seconds
+Number of seconds before timing out an LDAP request
+(Default: 4)
+.\"
+.\" -------------------------------------------------------------------
+.\" An Example
+.\" -------------------------------------------------------------------
+.\"
+.SH EXAMPLES
+An example
+.I /etc/idmapd.conf
+file:
+.nf
+
+
+[General]
+
+Verbosity = 0
+Domain = domain.org
+Local-Realms = DOMAIN.ORG,MY.DOMAIN.ORG,YOUR.DOMAIN.ORG
+
+[Mapping]
+
+Nobody-User = nfsnobody
+Nobody-Group = nfsnobody
+
+[Translation]
+
+Method = umich_ldap,nsswitch
+GSS-Methods = umich_ldap,static
+
+[Static]
+
+johndoe@OTHER.DOMAIN.ORG = johnny
+
+[UMICH_SCHEMA]
+
+LDAP_server = ldap.domain.org
+LDAP_base = dc=org,dc=domain
+
+.fi
+.\"
+.\" -------------------------------------------------------------------
+.\" Additional sections
+.\" -------------------------------------------------------------------
+.\"
+.SH SEE ALSO
+.BR idmapd (8)
+.BR svcgssd (8)
+.\".SH COMPATIBILITY
+.\".SH STANDARDS
+.\".SH ACKNOWLEDGEMENTS
+.\".SH AUTHORS
+.\".SH HISTORY
+.SH BUGS
+Report bugs to <nfsv4@linux-nfs.org>
+.\".SH CAVEATS
diff --git a/support/nfsidmap/libnfsidmap.c b/support/nfsidmap/libnfsidmap.c
new file mode 100644
index 0000000..d484101
--- /dev/null
+++ b/support/nfsidmap/libnfsidmap.c
@@ -0,0 +1,709 @@
+/*
+ * libnfsidmap.c
+ *
+ * nfs idmapping library, primarily for nfs4 client/server kernel idmapping
+ * and for userland nfs4 idmapping by acl libraries.
+ *
+ * Copyright (c) 2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Marius Aamodt Eriksen <marius@umich.edu>
+ * J. Bruce Fields <bfields@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 <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+#include <netdb.h>
+#include <err.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <dlfcn.h>
+#include <ctype.h>
+#include <resolv.h>
+#include <arpa/nameser.h>
+#include <arpa/nameser_compat.h>
+
+#include "nfsidmap.h"
+#include "nfsidmap_internal.h"
+#include "cfg.h"
+
+static char *default_domain;
+static struct conf_list *local_realms;
+int idmap_verbosity = 0;
+int no_strip = 0;
+int reformat_group = 0;
+static struct mapping_plugin **nfs4_plugins = NULL;
+static struct mapping_plugin **gss_plugins = NULL;
+uid_t nobody_uid = (uid_t)-1;
+gid_t nobody_gid = (gid_t)-1;
+
+#ifndef PATH_PLUGINS
+#define PATH_PLUGINS "/usr/lib/libnfsidmap"
+#endif
+#define PLUGIN_INIT_FUNC "libnfsidmap_plugin_init"
+
+
+#ifndef PATH_IDMAPDCONF
+#define PATH_IDMAPDCONF "/etc/idmapd.conf"
+#endif
+
+#ifndef IDMAPD_DEFAULT_DOMAIN
+#define IDMAPD_DEFAULT_DOMAIN "localdomain"
+#endif
+
+#ifndef NFS4DNSTXTREC
+#define NFS4DNSTXTREC "_nfsv4idmapdomain"
+#endif
+
+
+/* Default logging fuction */
+static void default_logger(const char *fmt, ...)
+{
+ va_list vp;
+
+ va_start(vp, fmt);
+ vsyslog(LOG_WARNING, fmt, vp);
+ va_end(vp);
+}
+nfs4_idmap_log_function_t idmap_log_func = default_logger;
+
+static char * toupper_str(char *s)
+{
+ int i;
+ for (i=0; i < strlen(s); i++)
+ s[i] = toupper(s[i]);
+ return s;
+}
+
+static int id_as_chars(char *name, uid_t *id)
+{
+ long int value;
+
+ if (name == NULL)
+ return 0;
+ value = strtol(name, NULL, 10);
+ if (value == 0) {
+ /* zero value ids are valid */
+ if (strcmp(name, "0") != 0)
+ return 0;
+ }
+ *id = (int)value;
+ return 1;
+}
+
+static int dns_txt_query(char *domain, char **nfs4domain)
+{
+ char *txtname = NFS4DNSTXTREC;
+ char *msg, *answ, *eom, *mptr;
+ int len, status = -1;
+ HEADER *hdr;
+
+ msg = calloc(1, NS_MAXMSG);
+ if (msg == NULL)
+ return -1;
+
+ answ = calloc(1, NS_MAXMSG);
+ if (answ == NULL) {
+ free(msg);
+ return -1;
+ }
+
+ if (res_init() < 0) {
+ IDMAP_LOG(2, ("libnfsidmap: res_init() failed for %s.%s: %s\n",
+ txtname, domain, hstrerror(h_errno)));
+ goto freemem;
+ }
+ len = res_querydomain(txtname, domain, C_IN, T_TXT, msg, NS_MAXMSG);
+ if (len < 0) {
+ IDMAP_LOG(2, ("libnfsidmap: res_querydomain() failed for %s.%s: %s\n",
+ txtname, domain, hstrerror(h_errno)));
+ goto freemem;
+ }
+ hdr = (HEADER *)msg;
+
+ /* See if there is an answer */
+ if (ntohs(hdr->ancount) < 1) {
+ IDMAP_LOG(2, ("libnfsidmap: No TXT record for %s.%s\n",
+ txtname, domain));
+ goto freemem;
+ }
+ /* find the EndOfMessage */
+ eom = msg + len;
+
+ /* skip header */
+ mptr = &msg[HFIXEDSZ];
+
+ /* skip name field in question section */
+ mptr += dn_skipname(mptr, eom) + QFIXEDSZ;
+
+ /* read in the question */
+ len = dn_expand(msg, eom, mptr, answ, NS_MAXDNAME);
+ if (len < 0) { /* does this really matter?? */
+ IDMAP_LOG(2, ("libnfsidmap: No question section for %s.%s: %s\n",
+ txtname, domain, hstrerror(h_errno)));
+ goto freemem;
+ }
+
+ /*
+ * Now, dissect the answer section, Note: if there
+ * are more than one answer only the first
+ * one will be used.
+ */
+
+ /* skip passed the name field */
+ mptr += dn_skipname(mptr, eom);
+ /* skip pass the type class and ttl fields */
+ mptr += 2 + 2 + 4;
+
+ /* make sure there is some data */
+ GETSHORT(len, mptr);
+ if (len < 0) {
+ IDMAP_LOG(2, ("libnfsidmap: No data in answer for %s.%s\n",
+ txtname, domain));
+ goto freemem;
+ }
+ /* get the lenght field */
+ len = (int)*mptr++;
+ /* copy the data */
+ memcpy(answ, mptr, len);
+ answ[len] = '\0';
+
+ *nfs4domain = strdup(answ);
+ status = 0;
+
+freemem:
+ free(msg);
+ free(answ);
+
+ return (status);
+}
+
+static int domain_from_dns(char **domain)
+{
+ struct hostent *he;
+ char hname[64], *c;
+
+ if (gethostname(hname, sizeof(hname)) == -1)
+ return -1;
+ if ((he = gethostbyname(hname)) == NULL)
+ return -1;
+ if ((c = strchr(he->h_name, '.')) == NULL || *++c == '\0')
+ return -1;
+ /*
+ * Query DNS to see if the _nfsv4idmapdomain TXT record exists
+ * If so use it...
+ */
+ if (dns_txt_query(c, domain) < 0)
+ *domain = strdup(c);
+
+ return 0;
+}
+
+static int load_translation_plugin(char *method, struct mapping_plugin *plgn)
+{
+ void *dl = NULL;
+ struct trans_func *trans = NULL;
+ libnfsidmap_plugin_init_t init_func;
+ char plgname[128];
+ int ret = 0;
+
+ snprintf(plgname, sizeof(plgname), "%s/%s.so", PATH_PLUGINS, method);
+
+ dl = dlopen(plgname, RTLD_NOW | RTLD_LOCAL);
+ if (dl == NULL) {
+ IDMAP_LOG(1, ("libnfsidmap: Unable to load plugin: %s",
+ dlerror()));
+ return -1;
+ }
+ init_func = (libnfsidmap_plugin_init_t) dlsym(dl, PLUGIN_INIT_FUNC);
+ if (init_func == NULL) {
+ IDMAP_LOG(1, ("libnfsidmap: Unable to get init function: %s",
+ dlerror()));
+ dlclose(dl);
+ return -1;
+ }
+ trans = init_func();
+ if (trans == NULL) {
+ IDMAP_LOG(1, ("libnfsidmap: Failed to initialize plugin %s",
+ PLUGIN_INIT_FUNC, plgname));
+ dlclose(dl);
+ return -1;
+ }
+ if (trans->init) {
+ ret = trans->init();
+ if (ret) {
+ IDMAP_LOG(1, ("libnfsidmap: Failed in %s's init(), "
+ "returned %d", plgname, ret));
+ dlclose(dl);
+ return -1;
+ }
+ }
+ plgn->dl_handle = dl;
+ plgn->trans = trans;
+ IDMAP_LOG(1, ("libnfsidmap: loaded plugin %s for method %s",
+ plgname, method));
+
+ return 0;
+}
+
+static void unload_plugins(struct mapping_plugin **plgns)
+{
+ int i;
+ for (i = 0; plgns[i] != NULL; i++) {
+ if (plgns[i]->dl_handle && dlclose(plgns[i]->dl_handle))
+ IDMAP_LOG(1, ("libnfsidmap: failed to "
+ "unload plugin for method = %s",
+ plgns[i]->trans->name));
+ free(plgns[i]);
+ }
+ free(plgns);
+}
+
+static int load_plugins(struct conf_list *methods,
+ struct mapping_plugin ***plugins)
+{
+ int ret = -1, i = 0;
+ struct mapping_plugin **plgns;
+ struct conf_list_node *m;
+
+ plgns = calloc(methods->cnt + 1, sizeof(struct mapping_plugin *));
+ if (plgns == NULL)
+ return -1;
+ plgns[methods->cnt] = NULL;
+ for (m = TAILQ_FIRST(&methods->fields), i = 0; m;
+ m = TAILQ_NEXT(m, link), i++) {
+ plgns[i] = calloc(1, sizeof(struct mapping_plugin));
+ if (plgns[i] == NULL)
+ goto out;
+ if (load_translation_plugin(m->field, plgns[i]) == -1) {
+ IDMAP_LOG(0, ("libnfsidmap: requested translation "
+ "method, '%s', is not available",
+ m->field));
+ goto out;
+ }
+ }
+ ret = 0;
+ *plugins = plgns;
+out:
+ if (ret)
+ unload_plugins(plgns);
+ return ret;
+}
+void nfs4_cleanup_name_mapping()
+{
+ if (nfs4_plugins)
+ unload_plugins(nfs4_plugins);
+ if (gss_plugins)
+ unload_plugins(gss_plugins);
+ nfs4_plugins = gss_plugins = NULL;
+}
+
+int nfs4_init_name_mapping(char *conffile)
+{
+ int ret = -ENOENT;
+ int dflt = 0;
+ struct conf_list *nfs4_methods, *gss_methods;
+ char *nobody_user, *nobody_group;
+ char *nostrip;
+ char *reformatgroup;
+
+ /* XXX: need to be able to reload configurations... */
+ if (nfs4_plugins) /* already succesfully initialized */
+ return 0;
+ if (conffile)
+ conf_path = conffile;
+ else
+ conf_path = PATH_IDMAPDCONF;
+ conf_init();
+ default_domain = conf_get_str("General", "Domain");
+ if (default_domain == NULL) {
+ dflt = 1;
+ ret = domain_from_dns(&default_domain);
+ if (ret) {
+ IDMAP_LOG(1, ("libnfsidmap: Unable to determine "
+ "the NFSv4 domain; Using '%s' as the NFSv4 domain "
+ "which means UIDs will be mapped to the 'Nobody-User' "
+ "user defined in %s",
+ IDMAPD_DEFAULT_DOMAIN, PATH_IDMAPDCONF));
+ default_domain = IDMAPD_DEFAULT_DOMAIN;
+ }
+ }
+ IDMAP_LOG(1, ("libnfsidmap: using%s domain: %s",
+ (dflt ? " (default)" : ""), default_domain));
+
+ /* Get list of "local equivalent" realms. Meaning the list of realms
+ * where john@REALM.A is considered the same user as john@REALM.B
+ * If not specified, default to upper-case of local domain name */
+ local_realms = conf_get_list("General", "Local-Realms");
+ if (local_realms == NULL) {
+ struct conf_list_node *node;
+
+ local_realms = malloc(sizeof *local_realms);
+ if (local_realms == NULL)
+ return -ENOMEM;
+ local_realms->cnt = 0;
+ TAILQ_INIT(&local_realms->fields);
+
+ node = calloc(1, sizeof *node);
+ if (node == NULL)
+ return -ENOMEM;
+ node->field = strdup(get_default_domain());
+ if (node->field == NULL)
+ return -ENOMEM;
+ toupper_str(node->field);
+
+ TAILQ_INSERT_TAIL(&local_realms->fields, node, link);
+ local_realms->cnt++;
+ }
+
+ if (idmap_verbosity >= 1) {
+ struct conf_list_node *r;
+ char *buf = NULL;
+ int siz=0;
+
+ if (local_realms) {
+ TAILQ_FOREACH(r, &local_realms->fields, link) {
+ siz += (strlen(r->field)+4);
+ }
+ buf = malloc(siz);
+ if (buf) {
+ *buf = 0;
+ TAILQ_FOREACH(r, &local_realms->fields, link) {
+ sprintf(buf+strlen(buf), "'%s' ", r->field);
+ }
+ IDMAP_LOG(1, ("libnfsidmap: Realms list: %s", buf));
+ free(buf);
+ }
+ } else
+ IDMAP_LOG(1, ("libnfsidmap: Realms list: <NULL> "));
+ }
+
+ nostrip = conf_get_str_with_def("General", "No-Strip", "none");
+ if (strcasecmp(nostrip, "both") == 0)
+ no_strip = IDTYPE_USER|IDTYPE_GROUP;
+ else if (strcasecmp(nostrip, "group") == 0)
+ no_strip = IDTYPE_GROUP;
+ else if (strcasecmp(nostrip, "user") == 0)
+ no_strip = IDTYPE_USER;
+ else
+ no_strip = 0;
+
+ if (no_strip & IDTYPE_GROUP) {
+ reformatgroup = conf_get_str_with_def("General", "Reformat-Group", "false");
+ if ((strcasecmp(reformatgroup, "true") == 0) ||
+ (strcasecmp(reformatgroup, "on") == 0) ||
+ (strcasecmp(reformatgroup, "yes") == 0))
+ reformat_group = 1;
+ else
+ reformat_group = 0;
+ }
+
+ nfs4_methods = conf_get_list("Translation", "Method");
+ if (nfs4_methods) {
+ IDMAP_LOG(1, ("libnfsidmap: processing 'Method' list"));
+ if (load_plugins(nfs4_methods, &nfs4_plugins) == -1)
+ return -ENOENT;
+ } else {
+ struct conf_list list;
+ struct conf_list_node node;
+
+ TAILQ_INIT(&list.fields);
+ list.cnt = 1;
+ node.field = "nsswitch";
+ TAILQ_INSERT_TAIL (&list.fields, &node, link);
+
+ if (load_plugins(&list, &nfs4_plugins) == -1)
+ return -ENOENT;
+ }
+
+ gss_methods = conf_get_list("Translation", "GSS-Methods");
+ if (gss_methods) {
+ IDMAP_LOG(1, ("libnfsidmap: processing 'GSS-Methods' list"));
+ if (load_plugins(gss_methods, &gss_plugins) == -1)
+ goto out;
+ }
+
+ nobody_user = conf_get_str("Mapping", "Nobody-User");
+ if (nobody_user) {
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ struct passwd *buf;
+ struct passwd *pw = NULL;
+ int err;
+
+ buf = malloc(sizeof(*buf) + buflen);
+ if (buf) {
+ err = getpwnam_r(nobody_user, buf, ((char *)buf) + sizeof(*buf), buflen, &pw);
+ if (err == 0 && pw != NULL)
+ nobody_uid = pw->pw_uid;
+ else
+ IDMAP_LOG(1, ("libnfsidmap: Nobody-User (%s) not found: %s",
+ nobody_user, strerror(errno)));
+ free(buf);
+ } else
+ IDMAP_LOG(0,("libnfsidmap: Nobody-User: no memory : %s",
+ nobody_user, strerror(errno)));
+ }
+
+ nobody_group = conf_get_str("Mapping", "Nobody-Group");
+ if (nobody_group) {
+ size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ struct group *buf;
+ struct group *gr = NULL;
+ int err;
+
+ buf = malloc(sizeof(*buf) + buflen);
+ if (buf) {
+ err = getgrnam_r(nobody_group, buf, ((char *)buf) + sizeof(*buf), buflen, &gr);
+ if (err == 0 && gr != NULL)
+ nobody_gid = gr->gr_gid;
+ else
+ IDMAP_LOG(1, ("libnfsidmap: Nobody-Group (%s) not found: %s",
+ nobody_group, strerror(errno)));
+ free(buf);
+ } else
+ IDMAP_LOG(0,("libnfsidmap: Nobody-Group: no memory : %s",
+ nobody_group, strerror(errno)));
+ }
+
+ ret = 0;
+out:
+ if (ret) {
+ if (nfs4_plugins)
+ unload_plugins(nfs4_plugins);
+ if (gss_plugins)
+ unload_plugins(gss_plugins);
+ nfs4_plugins = gss_plugins = NULL;
+ }
+
+ return ret ? -ENOENT: 0;
+}
+
+char * get_default_domain(void)
+{
+ int ret;
+
+ if (default_domain)
+ return default_domain;
+ ret = domain_from_dns(&default_domain);
+ if (ret) {
+ IDMAP_LOG(0, ("Unable to determine a default nfsv4 domain; "
+ " consider specifying one in idmapd.conf"));
+ default_domain = "";
+ }
+ return default_domain;
+}
+
+struct conf_list *get_local_realms(void)
+{
+ return local_realms;
+}
+
+int
+nfs4_get_default_domain(char *server, char *domain, size_t len)
+{
+ char *d = get_default_domain();
+
+ if (strlen(d) + 1 > len)
+ return -ERANGE;
+ strcpy(domain, d);
+ return 0;
+}
+
+/*
+ * Run through each configured translation method for
+ * function "funcname".
+ * If "prefer_gss" is true, then use the gss_plugins list,
+ * if present. Otherwise, use the default nfs4_plugins list.
+ *
+ * If the plugin function returns -ENOENT, then continue
+ * to the next plugin.
+ */
+#define RUN_TRANSLATIONS(funcname, prefer_gss, args...) \
+ do { \
+ int ret, i; \
+ struct mapping_plugin **plgns; \
+ \
+ ret = nfs4_init_name_mapping(NULL); \
+ if (ret) \
+ return ret; \
+ \
+ if ((prefer_gss) && gss_plugins) \
+ plgns = gss_plugins; \
+ else \
+ plgns = nfs4_plugins; \
+ \
+ for (i = 0; plgns[i] != NULL; i++) { \
+ if (plgns[i]->trans->funcname == NULL) \
+ continue; \
+ \
+ IDMAP_LOG(4, ("%s: calling %s->%s", __func__, \
+ plgns[i]->trans->name, #funcname)); \
+ \
+ ret = plgns[i]->trans->funcname(args); \
+ \
+ IDMAP_LOG(4, ("%s: %s->%s returned %d", \
+ __func__, plgns[i]->trans->name, \
+ #funcname, ret)); \
+ \
+ if (ret == -ENOENT) \
+ continue; \
+ \
+ break; \
+ } \
+ IDMAP_LOG(4, ("%s: final return value is %d", \
+ __func__, ret)); \
+ return ret; \
+ } while (0)
+
+int nfs4_uid_to_name(uid_t uid, char *domain, char *name, size_t len)
+{
+ RUN_TRANSLATIONS(uid_to_name, 0, uid, domain, name, len);
+}
+
+int nfs4_gid_to_name(gid_t gid, char *domain, char *name, size_t len)
+{
+ RUN_TRANSLATIONS(gid_to_name, 0, gid, domain, name, len);
+}
+
+int nfs4_uid_to_owner(uid_t uid, char *domain, char *name, size_t len)
+{
+ if (nfs4_uid_to_name(uid, domain, name, len))
+ sprintf(name, "%u", uid);
+ return 0;
+}
+
+int nfs4_gid_to_group_owner(gid_t gid, char *domain, char *name, size_t len)
+{
+ if (nfs4_gid_to_name(gid, domain, name, len))
+ sprintf(name, "%u", gid);
+ return 0;
+}
+
+int nfs4_name_to_uid(char *name, uid_t *uid)
+{
+ RUN_TRANSLATIONS(name_to_uid, 0, name, uid);
+}
+
+int nfs4_name_to_gid(char *name, gid_t *gid)
+{
+ RUN_TRANSLATIONS(name_to_gid, 0, name, gid);
+}
+
+static int set_id_to_nobody(uid_t *id, uid_t is_uid)
+{
+ int rc = 0;
+ const char name[] = "nobody@";
+ char nobody[strlen(name) + strlen(get_default_domain()) + 1];
+
+ /* First try to see whether a Nobody-User/Nobody-Group was
+ * configured, before we try to do a full lookup for the
+ * NFS nobody user. */
+ if (is_uid && nobody_uid != (uid_t)-1) {
+ *id = (uid_t)nobody_uid;
+ return 0;
+ } else if (!is_uid && nobody_gid != (gid_t)-1) {
+ *id = (uid_t)nobody_gid;
+ return 0;
+ }
+
+ strcpy(nobody, name);
+ strcat(nobody, get_default_domain());
+
+ if (is_uid)
+ rc = nfs4_name_to_uid(nobody, id);
+ else
+ rc = nfs4_name_to_gid(nobody, id);
+
+ if (rc) {
+ *id = -2;
+ rc = 0;
+ }
+ return rc;
+}
+
+int nfs4_owner_to_uid(char *name, uid_t *uid)
+{
+ int rc = nfs4_name_to_uid(name, uid);
+ if (rc && id_as_chars(name, uid))
+ rc = 0;
+ else if (rc)
+ rc = set_id_to_nobody(uid, 1);
+ return rc;
+}
+
+int nfs4_group_owner_to_gid(char *name, gid_t *gid)
+{
+ int rc = nfs4_name_to_gid(name, gid);
+ if (rc && id_as_chars(name, gid))
+ rc = 0;
+ else if (rc)
+ rc = set_id_to_nobody((uid_t *)gid, 0);
+ return rc;
+}
+
+int nfs4_gss_princ_to_ids(char *secname, char *princ, uid_t *uid, gid_t *gid)
+{
+ RUN_TRANSLATIONS(princ_to_ids, 1, secname, princ, uid, gid, NULL);
+}
+
+int nfs4_gss_princ_to_grouplist(char *secname, char *princ,
+ gid_t *groups, int *ngroups)
+{
+ RUN_TRANSLATIONS(gss_princ_to_grouplist, 1, secname, princ,
+ groups, ngroups, NULL);
+}
+
+int nfs4_gss_princ_to_ids_ex(char *secname, char *princ, uid_t *uid,
+ gid_t *gid, extra_mapping_params **ex)
+{
+ RUN_TRANSLATIONS(princ_to_ids, 1, secname, princ, uid, gid, ex);
+}
+
+int nfs4_gss_princ_to_grouplist_ex(char *secname, char *princ, gid_t *groups,
+ int *ngroups, extra_mapping_params **ex)
+{
+ RUN_TRANSLATIONS(gss_princ_to_grouplist, 1, secname, princ,
+ groups, ngroups, ex);
+}
+
+void nfs4_set_debug(int dbg_level, void (*logger)(const char *, ...))
+{
+ if (logger)
+ idmap_log_func = logger;
+ idmap_verbosity = dbg_level;
+}
+
diff --git a/support/nfsidmap/libnfsidmap.pc.in b/support/nfsidmap/libnfsidmap.pc.in
new file mode 100644
index 0000000..a11dbec
--- /dev/null
+++ b/support/nfsidmap/libnfsidmap.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libnfsidmap
+Description: Library that handles mapping between names and ids for NFSv4.
+Requires:
+Version: @PACKAGE_VERSION@
+Libs: -L@libdir@ -lnfsidmap
+Cflags: -I@includedir@
diff --git a/support/nfsidmap/libtest.c b/support/nfsidmap/libtest.c
new file mode 100644
index 0000000..1c717b8
--- /dev/null
+++ b/support/nfsidmap/libtest.c
@@ -0,0 +1,160 @@
+/*
+ * libtest.c
+ *
+ * nfs idmapping library, primarily for nfs4 client/server kernel idmapping
+ * and for userland nfs4 idmapping by acl libraries.
+ *
+ * Copyright (c) 2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ *
+ *
+ * libtest: Test the translation table functions
+ * Reads /etc/idmapd.conf
+ *
+ * To compile:
+ * gcc -g libtest.c -lnfsidmap -o libtest
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <nfsidmap.h>
+
+#define QUIT_ON_ERROR 1
+#define PATH_IDMAPDCONF "/etc/idmapd.conf"
+char *conf_path = PATH_IDMAPDCONF;
+
+main(int ac, char **av)
+{
+ char *name, *princ;
+ int err, uid = 0, gid = 0;
+ char name_buf[32];
+ int gids[1000];
+ int i, ngids;
+
+ if (ac != 3) {
+ printf("Usage: %s <user@nfsv4domain> <k5princ@REALM>\n",av[0]);
+ return 1;
+ }
+
+ nfs4_set_debug(3, NULL);
+
+ name = av[1];
+ princ = av[2];
+ err = nfs4_init_name_mapping(NULL);
+ if (err) {
+ printf("nfs4_init_name_mapping: error %d\n", err);
+ return 1;
+ }
+
+ err = nfs4_gss_princ_to_ids("krb5", princ, &uid, &gid);
+ if (err)
+ printf("nfs4_gss_princ_to_ids: error %d\n", err);
+ else
+ printf("nfs4_gss_princ_to_ids: princ %s has uid %d gid %d\n",
+ princ, uid, gid);
+#if QUIT_ON_ERROR
+ if (err) {
+ printf("calling it quits!\n");
+ return err;
+ }
+#endif
+
+ err = nfs4_name_to_uid(name, &uid);
+ if (err)
+ printf("nfs4_name_to_uid: error %d\n", err);
+ else
+ printf("nfs4_name_to_uid: name %s has uid %d\n",
+ name, uid);
+
+#if QUIT_ON_ERROR
+ if (err) {
+ printf("calling it quits!\n");
+ return err;
+ }
+#endif
+ err = nfs4_name_to_gid(name, &gid);
+ if (err)
+ printf("nfs4_name_to_gid: error %d\n", err);
+ else
+ printf("nfs4_name_to_gid: name %s has gid %d\n",
+ name, gid);
+
+ ngids = 1000;
+ err = nfs4_gss_princ_to_grouplist("krb5", princ, gids, &ngids);
+ if (err){
+ printf(" nfs4_gss_princ_to_grouplist: error %d\n", err);
+ } else {
+ printf(" nfs4_gss_princ_to_grouplist: princ %s has gids ",
+ princ);
+ for (i = 0; i < ngids; i++) printf("%d ", gids[i]);
+ printf("\n");
+ }
+
+#if QUIT_ON_ERROR
+ if (err) {
+ printf("calling it quits!\n");
+ return err;
+ }
+#endif
+ /* uid is set by nfs4_name_to_uid() */
+ memset(name_buf, 0, 32);
+ err = nfs4_uid_to_name(uid, NULL, name_buf, 32);
+ if (err)
+ printf("nfs4_uid_to_name: error %d\n", err);
+ else
+ printf("nfs4_uid_to_name: uid %d has name %s\n",
+ uid, name_buf);
+
+#if QUIT_ON_ERROR
+ if (err) {
+ printf("calling it quits!\n");
+ return err;
+ }
+#endif
+ /* gid is set by nfs4_name_to_gid() */
+ memset(name_buf, 0, 32);
+ err = nfs4_gid_to_name(gid, NULL, name_buf, 32);
+ if (err)
+ printf("nfs4_gid_to_name: error %d\n", err);
+ else
+ printf("nfs4_gid_to_name: gid %d has name %s\n",
+ gid, name_buf);
+
+#if QUIT_ON_ERROR
+ if (err) {
+ printf("calling it quits!\n");
+ return err;
+ }
+#endif
+ return 0;
+}
diff --git a/support/nfsidmap/nfs4_uid_to_name.3 b/support/nfsidmap/nfs4_uid_to_name.3
new file mode 100644
index 0000000..8a62d8a
--- /dev/null
+++ b/support/nfsidmap/nfs4_uid_to_name.3
@@ -0,0 +1,174 @@
+.TH nfs4_uid_to_name 3 2004-08-05
+.SH NAME
+nfs4_uid_to_name, nfs4_gid_to_name, nfs4_name_to_uid, nfs4_name_to_gid,
+nfs4_init_name_mapping, nfs4_get_default_domain,
+nfs4_gss_princ_to_ids, nfs4_gss_princ_to_grouplist,
+nfs4_gss_princ_to_ids_ex,
+nfs4_gss_princ_to_grouplist_ex,
+nfs4_set_debug \- ID mapping routines used for NFSv4
+.SH SYNOPSIS
+.B #include <nfs4_idmap.h>
+.sp
+.BI "int nfs4_init_name_mapping(char *conffile);"
+.sp
+.BI "int nfs4_get_default_domain(char *server, char *domain, size_t len);"
+.sp
+.BI "int nfs4_uid_to_name(uid_t uid, char *domain, char *name, size_t len);"
+.sp
+.BI "int nfs4_uid_to_owner(uid_t uid, char *domain, char *name, size_t len);"
+.sp
+.BI "int nfs4_gid_to_name(gid_t gid, char *domain, char *name, size_t len);"
+.sp
+.BI "int nfs4_gid_to_owner(gid_t gid, char *domain, char *name, size_t len);"
+.sp
+.BI "int nfs4_name_to_uid(char *name, uid_t *uid);"
+.sp
+.BI "int nfs4_name_to_gid(char *name, gid_t *gid);"
+.sp
+.BI "int nfs4_owner_to_uid(char *name, uid_t *uid);"
+.sp
+.BI "int nfs4_owner_to_gid(char *name, gid_t *gid);"
+.sp
+.BI "int nfs4_gss_princ_to_ids(char *secname, char *princ, uid_t *uid, gid_t *gid);"
+.sp
+.BI "int nfs4_gss_princ_to_grouplist(char *secname, char *princ, gid_t *groups, int *ngroups);"
+.sp
+.BI "int nfs4_gss_princ_to_ids_ex(char *secname, char *princ, uid_t *uid, gid_t *gid, extra_mapping_params **ex);"
+.sp
+.BI "int nfs4_gss_princ_to_grouplist_ex(char *secname, char *princ, gid_t *groups, int *ngroups, extra_mapping_params **ex);"
+.sp
+.BI "void nfs4_set_debug(int dbg_level, void (*logger)(const char *, ...));"
+.sp
+.fi
+.SH DESCRIPTION
+NFSv4 uses names of the form
+.IR user@domain .
+To write code that helps the kernel map uid's (as
+rpc.idmapd
+does) or that processes NFSv4 ACLs, you need to be able to convert between
+NFSv4 names and local uids and gids.
+.PP
+The
+.B nfs4_uid_to_name()
+and
+.B nfs4_gid_to_name()
+functions, given
+.I uid
+or
+.I gid
+and
+.I domain
+(as a null-terminated string),
+write the corresponding nfsv4 name into the buffer provided in
+.IR name ,
+which must be of length at least
+.IR len .
+.PP
+The
+.B nfs4_uid_to_owner()
+and
+.B nfs4_gid_to_group_owner()
+functions, given
+.I uid
+or
+.I gid
+and
+.I domain
+(as a null-terminated string),
+write the corresponding nfsv4 name into the buffer provided in
+.IR name ,
+which must be of length at least
+.IR len .
+If there is no valid mapping from
+.I uid
+or
+.I gid
+to
+.IR name ,
+then the numerical string representing uid or gid is returned instead.
+.PP
+The
+.B nfs4_name_to_uid()
+and
+.B nfs4_name_to_gid()
+functions, given
+.I name
+(as a null-terminated string), return the corresponding uid or gid in
+the second parameter.
+.PP
+The
+.B nfs4_owner_to_uid()
+and
+.B nfs4_group_owner_to_gid()
+functions, given
+.I name
+(as a null-terminated string), return the corresponding uid or gid in
+the second parameter.
+If there is no valid mapping from
+.I name
+to
+.I uid
+or
+.I gid
+the value for the user or group "nobody" will be returned instead.
+. PP
+The
+.B nfs4_init_name_mapping()
+function must be called before using any of these functions. It reads
+defaults from the configuration file at the provided path, usually
+"etc/idmapd.conf".
+.PP
+The
+.I domain
+argument to the id-to-name functions is there to provide a hint to the name
+mapper in the case where an id might be mapped to names in multiple domains.
+In most cases, this argument should just be the name returned in the
+.I domain
+argument to
+.B nfs4_get_default_domain()
+which should be called with
+.I server
+set to NULL. The
+.I domain
+should be a buffer of length
+.IR len .
+The constant NFS4_MAX_DOMAIN_LEN may be used to determine a reasonable
+value for that length.
+.PP
+The function
+.BR nfs4_get_grouplist() ,
+given a
+.IR name ,
+fills the provided array
+.I groups
+with up to
+.I *ngroups
+group IDs corresponding to which the user
+.I name
+belongs to, setting
+.I *ngroups
+to the actual number of such groups. If the user belongs to more than
+.I *ngroups
+groups, then an error is returned and the actual number of groups is stored in
+*ngroups.
+.PP
+Functions
+.BR nfs4_gss_princ_to_ids() ,
+.BR nfs4_gss_princ_to_grouplist() ,
+.BR nfs4_gss_princ_to_ids_ex() ,
+and
+.B nfs4_gss_princ_to_grouplist_ex()
+are used to convert from a gss principal name (as returned by
+.BR gss_display_name() )
+to a uid and gid, or list of gids.
+.PP
+Finally,
+.B nfs4_set_debug()
+allows the application to set a debugging level to produce extra
+debugging information from within the library. The optional
+.I logger
+function specifies an alternative logging function to call for
+the debug messages rather than the default internal function
+within the library.
+.SH RETURN VALUE
+All functions return 0 or, in the case of error, -ERRNO.
diff --git a/support/nfsidmap/nfsidmap.h b/support/nfsidmap/nfsidmap.h
new file mode 100644
index 0000000..1063065
--- /dev/null
+++ b/support/nfsidmap/nfsidmap.h
@@ -0,0 +1,67 @@
+/*
+ * nfsidmap.h
+ *
+ * nfs idmapping library, primarily for nfs4 client/server kernel idmapping
+ * and for userland nfs4 idmapping by acl libraries.
+ *
+ * Copyright (c) 2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * J. Bruce Fields <bfields@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.
+ */
+
+/* XXX arbitrary */
+#define NFS4_MAX_DOMAIN_LEN 512
+typedef enum {
+ X509_CERT = 1
+} extra_mapping_types;
+
+typedef struct _extra_mapping_params {
+ void *content;
+ int content_type;
+ int content_len;
+} extra_mapping_params;
+
+typedef void (*nfs4_idmap_log_function_t)(const char *, ...);
+
+int nfs4_init_name_mapping(char *conffile);
+int nfs4_get_default_domain(char *server, char *domain, size_t len);
+int nfs4_uid_to_name(uid_t uid, char *domain, char *name, size_t len);
+int nfs4_gid_to_name(gid_t gid, char *domain, char *name, size_t len);
+int nfs4_uid_to_owner(uid_t uid, char *domain, char *name, size_t len);
+int nfs4_gid_to_group_owner(gid_t gid, char *domain, char *name, size_t len);
+int nfs4_name_to_uid(char *name, uid_t *uid);
+int nfs4_name_to_gid(char *name, gid_t *gid);
+int nfs4_owner_to_uid(char *name, uid_t *uid);
+int nfs4_owner_to_gid(char *name, gid_t *gid);
+int nfs4_group_owner_to_gid(char *name, gid_t *gid);
+int nfs4_gss_princ_to_ids(char *secname, char *princ, uid_t *uid, gid_t *gid);
+int nfs4_gss_princ_to_grouplist(char *secname, char *princ, gid_t *groups, int *ngroups);
+int nfs4_gss_princ_to_ids_ex(char *secname, char *princ, uid_t *uid, gid_t *gid, extra_mapping_params **ex);
+int nfs4_gss_princ_to_grouplist_ex(char *secname, char *princ, gid_t *groups, int *ngroups, extra_mapping_params **ex);
+void nfs4_set_debug(int dbg_level, nfs4_idmap_log_function_t dbg_logfunc);
diff --git a/support/nfsidmap/nfsidmap_internal.h b/support/nfsidmap/nfsidmap_internal.h
new file mode 100644
index 0000000..6696f50
--- /dev/null
+++ b/support/nfsidmap/nfsidmap_internal.h
@@ -0,0 +1,72 @@
+/*
+ * nfsidmap_internal.h
+ *
+ * nfs idmapping library, primarily for nfs4 client/server kernel idmapping
+ * and for userland nfs4 idmapping by acl libraries.
+ *
+ * Copyright (c) 2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+char *get_default_domain(void);
+struct conf_list *get_local_realms(void);
+
+typedef struct trans_func * (*libnfsidmap_plugin_init_t)(void);
+
+struct trans_func {
+ char *name;
+ int (*init)(void);
+ int (*princ_to_ids)(char *secname, char *princ, uid_t *uid, gid_t *gid,
+ extra_mapping_params **ex);
+ int (*name_to_uid)(char *name, uid_t *uid);
+ int (*name_to_gid)(char *name, gid_t *gid);
+ int (*uid_to_name)(uid_t uid, char *domain, char *name, size_t len);
+ int (*gid_to_name)(gid_t gid, char *domain, char *name, size_t len);
+ int (*gss_princ_to_grouplist)(char *secname, char *princ, gid_t *groups,
+ int *ngroups, extra_mapping_params **ex);
+};
+
+struct mapping_plugin {
+ void *dl_handle;
+ struct trans_func *trans;
+};
+
+typedef enum {
+ IDTYPE_USER = 1,
+ IDTYPE_GROUP = 2
+} idtypes;
+
+extern int no_strip;
+extern int reformat_group;
+extern int idmap_verbosity;
+extern nfs4_idmap_log_function_t idmap_log_func;
+/* Level zero always prints, others print depending on verbosity level */
+#define IDMAP_LOG(LVL, MSG) \
+ do { if (LVL <= idmap_verbosity) (*idmap_log_func)MSG; } while (0)
diff --git a/support/nfsidmap/nss.c b/support/nfsidmap/nss.c
new file mode 100644
index 0000000..82799ce
--- /dev/null
+++ b/support/nfsidmap/nss.c
@@ -0,0 +1,468 @@
+/*
+ * nss.c
+ *
+ * nsswitch idmapping functions.
+ *
+ * Copyright (c) 2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * J. Bruce Fields <bfields@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 <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <netdb.h>
+#include <err.h>
+#include <grp.h>
+#include <limits.h>
+#include <ctype.h>
+#include "nfsidmap.h"
+#include "nfsidmap_internal.h"
+#include "cfg.h"
+#include <syslog.h>
+
+/*
+ * NSS Translation Methods
+ *
+ * These are all just wrappers around getpwnam and friends;
+ * we tack on the given domain to the results of getpwnam when looking up a uid,
+ * and ignore the domain entirely when looking up a name.
+ */
+
+static int write_name(char *dest, char *localname, char *domain, size_t len,
+ int doappend)
+{
+ if (doappend || !strchr(localname,'@')) {
+ if (strlen(localname) + 1 + strlen(domain) + 1 > len)
+ return -ENOMEM; /* XXX: Is there an -ETOOLONG? */
+ strcpy(dest, localname);
+ strcat(dest, "@");
+ strcat(dest, domain);
+ } else {
+ if (strlen(localname) + 1 > len)
+ return -ENOMEM;
+ strcpy(dest, localname);
+ }
+ return 0;
+}
+
+static int nss_uid_to_name(uid_t uid, char *domain, char *name, size_t len)
+{
+ struct passwd *pw = NULL;
+ struct passwd pwbuf;
+ char *buf;
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ int err = -ENOMEM;
+
+ buf = malloc(buflen);
+ if (!buf)
+ goto out;
+ if (domain == NULL)
+ domain = get_default_domain();
+ err = -getpwuid_r(uid, &pwbuf, buf, buflen, &pw);
+ if (pw == NULL)
+ err = -ENOENT;
+ if (err)
+ goto out_buf;
+ if (no_strip & IDTYPE_USER)
+ err = write_name(name, pw->pw_name, domain, len, 0);
+ else
+ err = write_name(name, pw->pw_name, domain, len, 1);
+out_buf:
+ free(buf);
+out:
+ return err;
+}
+
+static int nss_gid_to_name(gid_t gid, char *domain, char *name, size_t len)
+{
+ struct group *gr = NULL;
+ struct group grbuf;
+ char *buf;
+ size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ int err;
+
+ if (domain == NULL)
+ domain = get_default_domain();
+
+ do {
+ err = -ENOMEM;
+ buf = malloc(buflen);
+ if (!buf)
+ goto out;
+ err = -getgrgid_r(gid, &grbuf, buf, buflen, &gr);
+ if (gr == NULL && !err)
+ err = -ENOENT;
+ if (err == -ERANGE) {
+ buflen *= 2;
+ free(buf);
+ }
+ } while (err == -ERANGE);
+
+ if (err)
+ goto out_buf;
+ if (no_strip & IDTYPE_GROUP)
+ err = write_name(name, gr->gr_name, domain, len, 0);
+ else
+ err = write_name(name, gr->gr_name, domain, len, 1);
+out_buf:
+ free(buf);
+out:
+ return err;
+}
+
+/* XXX: actually should return error, so can distinguish between
+ * memory allocation failure and failure to match domain */
+static char *strip_domain(const char *name, const char *domain)
+{
+ const char *c;
+ char *l = NULL;
+ int len;
+
+ if (name == NULL)
+ goto out;
+
+ c = strrchr(name, '@');
+ if (c == NULL && domain != NULL)
+ goto out;
+ if (c == NULL && domain == NULL) {
+ len = strlen(name) + 1;
+ } else {
+ if (domain && strcasecmp(c + 1, domain) != 0)
+ goto out;
+ len = c - name;
+ }
+
+ l = malloc(len + 1);
+ if (l == NULL)
+ goto out;
+ memcpy(l, name, len);
+ l[len] = '\0';
+out:
+ return l;
+}
+
+struct pwbuf {
+ struct passwd pwbuf;
+ char buf[1];
+};
+
+static struct passwd *nss_getpwnam(const char *name, const char *domain,
+ int *err_p, int dostrip)
+{
+ struct passwd *pw;
+ struct pwbuf *buf;
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ char *localname;
+ int err = ENOMEM;
+
+ if (buflen > UINT_MAX)
+ goto err;
+
+ buf = malloc(sizeof(*buf) + buflen);
+ if (buf == NULL)
+ goto err;
+
+ err = EINVAL;
+ if (dostrip) {
+ localname = strip_domain(name, domain);
+ IDMAP_LOG(4, ("nss_getpwnam: name '%s' domain '%s': "
+ "resulting localname '%s'", name, domain, localname));
+ if (localname == NULL) {
+ IDMAP_LOG(0, ("nss_getpwnam: name '%s' does not map "
+ "into domain '%s'", name,
+ domain ? domain : "<not-provided>"));
+ goto err_free_buf;
+ }
+
+ err = getpwnam_r(localname, &buf->pwbuf, buf->buf, buflen, &pw);
+ if (pw == NULL && domain != NULL)
+ IDMAP_LOG(1,
+ ("nss_getpwnam: name '%s' not found in domain '%s'",
+ localname, domain));
+ free(localname);
+ } else {
+ err = getpwnam_r(name, &buf->pwbuf, buf->buf, buflen, &pw);
+ if (pw == NULL)
+ IDMAP_LOG(1,
+ ("nss_getpwnam: name '%s' not found (domain not stripped)", name));
+ }
+ if (err == 0 && pw != NULL) {
+ *err_p = 0;
+ return pw;
+ } else if (err == 0 && pw == NULL) {
+ err = ENOENT;
+ }
+
+err_free_buf:
+ free(buf);
+err:
+ *err_p = -err;
+ return NULL;
+}
+
+static int nss_name_to_uid(char *name, uid_t *uid)
+{
+ struct passwd *pw = NULL;
+ char *domain;
+ int err = -ENOENT;
+
+ domain = get_default_domain();
+ if (no_strip & IDTYPE_USER) {
+ pw = nss_getpwnam(name, domain, &err, 0);
+ if (pw != NULL)
+ goto out_uid;
+ }
+ pw = nss_getpwnam(name, domain, &err, 1);
+ if (pw == NULL)
+ goto out;
+out_uid:
+ *uid = pw->pw_uid;
+ IDMAP_LOG(4, ("nss_name_to_uid: name '%s' uid %u", name, *uid));
+ free(pw);
+ err = 0;
+out:
+ return err;
+}
+
+static char *reformat_name(const char *name)
+{
+ const char *domain;
+ const char *c;
+ const char *d;
+ char *l = NULL;
+ int len;
+ int dlen = 0;
+ int i;
+
+ c = strchr(name, '@');
+ if (c == NULL)
+ goto out;
+ len = c - name;
+ domain = ++c;
+ d = strchr(domain, '.');
+ if (d == NULL)
+ goto out;
+ dlen = d - domain;
+ l = malloc(dlen + 1 + len + 1);
+ if (l == NULL)
+ goto out;
+ for (i = 0; i < dlen; i++)
+ l[i] = toupper(domain[i]);
+ l[dlen] = '\\';
+ memcpy(l + dlen + 1, name, len);
+ l[dlen + 1 + len] = '\0';
+out:
+ return l;
+}
+
+static int _nss_name_to_gid(char *name, gid_t *gid, int dostrip)
+{
+ struct group *gr = NULL;
+ struct group grbuf;
+ char *buf, *domain;
+ size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ int err = -EINVAL;
+ char *localname = NULL;
+ char *ref_name = NULL;
+
+ domain = get_default_domain();
+ if (dostrip) {
+ localname = strip_domain(name, domain);
+ IDMAP_LOG(4, ("nss_name_to_gid: name '%s' domain '%s': "
+ "resulting localname '%s'", name, domain, localname));
+ if (!localname) {
+ IDMAP_LOG(0, ("nss_name_to_gid: name '%s' does not map "
+ "into domain '%s'", name, domain));
+ goto out;
+ }
+ } else if (reformat_group) {
+ ref_name = reformat_name(name);
+ if (ref_name == NULL) {
+ IDMAP_LOG(1, ("nss_name_to_gid: failed to reformat name '%s'",
+ name));
+ err = -ENOENT;
+ goto out;
+ }
+ }
+
+ err = -ENOMEM;
+ if (buflen > UINT_MAX)
+ goto out_name;
+
+ do {
+ buf = malloc(buflen);
+ if (!buf)
+ goto out_name;
+ if (dostrip)
+ err = -getgrnam_r(localname, &grbuf, buf, buflen, &gr);
+ else if (reformat_group)
+ err = -getgrnam_r(ref_name, &grbuf, buf, buflen, &gr);
+ else
+ err = -getgrnam_r(name, &grbuf, buf, buflen, &gr);
+ if (gr == NULL && !err) {
+ if (dostrip)
+ IDMAP_LOG(1, ("nss_name_to_gid: name '%s' not found "
+ "in domain '%s'", localname, domain));
+ else if (reformat_group)
+ IDMAP_LOG(1, ("nss_name_to_gid: name '%s' not found "
+ "(reformatted)", ref_name));
+ else
+ IDMAP_LOG(1, ("nss_name_to_gid: name '%s' not found "
+ "(domain not stripped)", name));
+ err = -ENOENT;
+ }
+ if (err == -ERANGE) {
+ buflen *= 2;
+ free(buf);
+ }
+ } while (err == -ERANGE);
+
+ if (err)
+ goto out_buf;
+ *gid = gr->gr_gid;
+ IDMAP_LOG(4, ("nss_name_to_gid: name '%s' gid %u", name, *gid));
+out_buf:
+ free(buf);
+out_name:
+ if (dostrip)
+ free(localname);
+ if (reformat_group)
+ free(ref_name);
+out:
+ return err;
+}
+
+static int nss_name_to_gid(char *name, gid_t *gid)
+{
+ int err = 0;
+
+ if (no_strip & IDTYPE_GROUP) {
+ err = _nss_name_to_gid(name, gid, 0);
+ if (!err)
+ goto out;
+ }
+ err = _nss_name_to_gid(name, gid, 1);
+out:
+ return err;
+}
+
+static int nss_gss_princ_to_ids(char *secname, char *princ,
+ uid_t *uid, uid_t *gid,
+ extra_mapping_params **ex)
+{
+ struct passwd *pw;
+ int err = 0;
+ char *princ_realm;
+ struct conf_list *realms;
+ struct conf_list_node *r;
+ int found = 0;
+
+ if (strcmp(secname, "spkm3") == 0)
+ return -ENOENT;
+
+ if (strcmp(secname, "krb5") != 0)
+ return -EINVAL;
+
+ /* get princ's realm */
+ princ_realm = strstr(princ, "@");
+ if (princ_realm == NULL)
+ return -EINVAL;
+ princ_realm++;
+
+ /* get list of "local-equivalent" realms and
+ * check against the principal's realm */
+ realms = get_local_realms();
+ TAILQ_FOREACH(r, &realms->fields, link) {
+ if (strcmp(r->field, princ_realm) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ IDMAP_LOG(1, ("nss_gss_princ_to_ids: Local-Realm '%s': NOT FOUND",
+ princ_realm));
+ return -ENOENT;
+ }
+ /* XXX: this should call something like getgssauthnam instead? */
+ pw = nss_getpwnam(princ, NULL, &err, 1);
+ if (pw == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+ *uid = pw->pw_uid;
+ *gid = pw->pw_gid;
+ free(pw);
+out:
+ return err;
+}
+
+int nss_gss_princ_to_grouplist(char *secname, char *princ,
+ gid_t *groups, int *ngroups,
+ extra_mapping_params **ex)
+{
+ struct passwd *pw;
+ int ret = -EINVAL;
+
+ if (strcmp(secname, "krb5") != 0)
+ goto out;
+ /* XXX: not quite right? Need to know default realm? */
+ /* XXX: this should call something like getgssauthnam instead? */
+ pw = nss_getpwnam(princ, NULL, &ret, 1);
+ if (pw == NULL) {
+ ret = -ENOENT;
+ goto out;
+ }
+ if (getgrouplist(pw->pw_name, pw->pw_gid, groups, ngroups) < 0)
+ ret = -ERANGE;
+ free(pw);
+out:
+ return ret;
+}
+
+
+struct trans_func nss_trans = {
+ .name = "nsswitch",
+ .init = NULL,
+ .princ_to_ids = nss_gss_princ_to_ids,
+ .name_to_uid = nss_name_to_uid,
+ .name_to_gid = nss_name_to_gid,
+ .uid_to_name = nss_uid_to_name,
+ .gid_to_name = nss_gid_to_name,
+ .gss_princ_to_grouplist = nss_gss_princ_to_grouplist,
+};
+
+struct trans_func *libnfsidmap_plugin_init()
+{
+ return (&nss_trans);
+}
diff --git a/support/nfsidmap/static.c b/support/nfsidmap/static.c
new file mode 100644
index 0000000..9f587af
--- /dev/null
+++ b/support/nfsidmap/static.c
@@ -0,0 +1,412 @@
+/*
+ * static.c
+ *
+ * static idmapping functions for gss principals.
+ *
+ * Copyright (c) 2008 David Härdeman <david@hardeman.nu>.
+ * All rights reserved.
+ *
+ * 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 <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <err.h>
+
+#include "queue.h"
+#include "cfg.h"
+#include "nfsidmap.h"
+#include "nfsidmap_internal.h"
+
+/*
+ * Static Translation Methods
+ *
+ * These functions use getpwnam to find uid/gid(s) for gss principals
+ * which are first mapped to local user names using static mappings
+ * in idmapd.conf.
+ */
+
+struct pwbuf {
+ struct passwd pwbuf;
+ char buf[1];
+};
+
+struct grbuf {
+ struct group grbuf;
+ char buf[1];
+};
+
+struct uid_mapping {
+ LIST_ENTRY (uid_mapping) link;
+ uid_t uid;
+ char * principal;
+ char * localname;
+};
+
+struct gid_mapping {
+ LIST_ENTRY (gid_mapping) link;
+ gid_t gid;
+ char * principal;
+ char * localgroup;
+};
+
+static __inline__ u_int8_t uid_hash (uid_t uid)
+{
+ return uid % 256;
+}
+
+static __inline__ u_int8_t gid_hash (gid_t gid)
+{
+ return gid % 256;
+}
+
+//Hash tables of uid and guids to principals mappings.
+//We reuse some queue/hash functions from cfg.c.
+LIST_HEAD (uid_mappings, uid_mapping) uid_mappings[256];
+LIST_HEAD (gid_mappings, gid_mapping) gid_mappings[256];
+
+static struct passwd *static_getpwnam(const char *name, const char *domain,
+ int *err_p)
+{
+ struct passwd *pw;
+ struct pwbuf *buf;
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ char *localname;
+ int err;
+
+ buf = malloc(sizeof(*buf) + buflen);
+ if (!buf) {
+ err = ENOMEM;
+ goto err;
+ }
+
+ localname = conf_get_str("Static", (char *)name);
+ if (!localname) {
+ err = ENOENT;
+ goto err_free_buf;
+ }
+
+again:
+ err = getpwnam_r(localname, &buf->pwbuf, buf->buf, buflen, &pw);
+
+ if (err == EINTR)
+ goto again;
+
+ if (!pw) {
+ if (err == 0)
+ err = ENOENT;
+
+ IDMAP_LOG(0, ("static_getpwnam: localname '%s' for '%s' not found",
+ localname, name));
+
+ goto err_free_buf;
+ }
+
+ IDMAP_LOG(4, ("static_getpwnam: name '%s' mapped to '%s'",
+ name, localname));
+
+ *err_p = 0;
+ return pw;
+
+err_free_buf:
+ free(buf);
+err:
+ *err_p = err;
+ return NULL;
+}
+
+static struct group *static_getgrnam(const char *name, const char *domain,
+ int *err_p)
+{
+ struct group *gr;
+ struct grbuf *buf;
+ size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ char *localgroup;
+ int err;
+
+ buf = malloc(sizeof(*buf) + buflen);
+ if (!buf) {
+ err = ENOMEM;
+ goto err;
+ }
+
+ localgroup = conf_get_str("Static", (char *)name);
+ if (!localgroup) {
+ err = ENOENT;
+ goto err_free_buf;
+ }
+
+again:
+ err = getgrnam_r(localgroup, &buf->grbuf, buf->buf, buflen, &gr);
+
+ if (err == EINTR)
+ goto again;
+
+ if (!gr) {
+ if (err == 0)
+ err = ENOENT;
+
+ IDMAP_LOG(0, ("static_getgrnam: local group '%s' for '%s' not found",
+ localgroup, name));
+
+ goto err_free_buf;
+ }
+
+ IDMAP_LOG(4, ("static_getgrnam: group '%s' mapped to '%s'",
+ name, localgroup));
+
+ *err_p = 0;
+ return gr;
+
+err_free_buf:
+ free(buf);
+err:
+ *err_p = err;
+ return NULL;
+}
+
+static int static_gss_princ_to_ids(char *secname, char *princ,
+ uid_t *uid, uid_t *gid,
+ extra_mapping_params **ex)
+{
+ struct passwd *pw;
+ int err;
+
+ /* XXX: Is this necessary? */
+ if (strcmp(secname, "krb5") != 0 && strcmp(secname, "spkm3") != 0)
+ return -EINVAL;
+
+ pw = static_getpwnam(princ, NULL, &err);
+
+ if (pw) {
+ *uid = pw->pw_uid;
+ *gid = pw->pw_gid;
+ free(pw);
+ }
+
+ return -err;
+}
+
+static int static_gss_princ_to_grouplist(char *secname, char *princ,
+ gid_t *groups, int *ngroups,
+ extra_mapping_params **ex)
+{
+ struct passwd *pw;
+ int err;
+
+ /* XXX: Is this necessary? */
+ if (strcmp(secname, "krb5") != 0 && strcmp(secname, "spkm3") != 0)
+ return -EINVAL;
+
+ pw = static_getpwnam(princ, NULL, &err);
+
+ if (pw) {
+ if (getgrouplist(pw->pw_name, pw->pw_gid, groups, ngroups) < 0)
+ err = -ERANGE;
+ free(pw);
+ }
+
+ return -err;
+}
+
+static int static_name_to_uid(char *name, uid_t *uid)
+{
+ struct passwd *pw;
+ int err;
+
+ pw = static_getpwnam(name, NULL, &err);
+
+ if (pw) {
+ *uid = pw->pw_uid;
+ free(pw);
+ }
+
+ return -err;
+}
+
+static int static_name_to_gid(char *name, gid_t *gid)
+{
+ struct group *gr;
+ int err;
+
+ gr = static_getgrnam(name, NULL, &err);
+
+ if (gr) {
+ *gid = gr->gr_gid;
+ free(gr);
+ }
+
+ return -err;
+}
+
+static int static_uid_to_name(uid_t uid, char *domain, char *name, size_t len)
+{
+ struct uid_mapping * um;
+
+ for (um = LIST_FIRST (&uid_mappings[uid_hash (uid)]); um;
+ um = LIST_NEXT (um, link)) {
+ if (um->uid == uid) {
+ strcpy(name, um->principal);
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+static int static_gid_to_name(gid_t gid, char *domain, char *name, size_t len)
+{
+ struct gid_mapping * gm;
+
+ for (gm = LIST_FIRST (&gid_mappings[gid_hash (gid)]); gm;
+ gm = LIST_NEXT (gm, link)) {
+ if (gm->gid == gid) {
+ strcpy(name, gm->principal);
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+/*
+ * We buffer all UID's for which static mappings is defined in advance, so the
+ * uid_to_name functions will be fast enough.
+ */
+
+static int static_init() {
+ int err;
+ struct conf_list * princ_list = NULL;
+ struct conf_list_node * cln, *next;
+ struct uid_mapping * unode;
+ struct gid_mapping * gnode;
+ struct passwd * pw = NULL;
+ struct group * gr = NULL;
+ unsigned int i;
+
+ //init hash_table first
+ for (i = 0; i < sizeof uid_mappings / sizeof uid_mappings[0]; i++)
+ LIST_INIT (&uid_mappings[i]);
+
+ //get all principals for which we have mappings
+ princ_list = conf_get_tag_list("Static");
+
+ if (!princ_list) {
+ return -ENOENT;
+ }
+
+ /* As we can not distinguish between mappings for users and groups, we try to
+ * resolve all mappings for both cases.
+ */
+
+ //resolve uid of localname account for all such principals and cache it
+ for (cln = TAILQ_FIRST (&princ_list->fields); cln; cln = next)
+ {
+ next = TAILQ_NEXT (cln, link);
+
+ pw = static_getpwnam(cln->field, NULL, &err);
+ if (!pw) {
+ continue;
+ }
+
+ unode = calloc (1, sizeof *unode);
+ if (!unode)
+ {
+ warnx("static_init: calloc (1, %lu) failed",
+ (unsigned long)sizeof *unode);
+ free(pw);
+ return -ENOMEM;
+ }
+ unode->uid = pw->pw_uid;
+ unode->principal = strdup(cln->field);
+
+ unode->localname = conf_get_str("Static", cln->field);
+ if (!unode->localname) {
+ free(pw);
+ return -ENOENT;
+ }
+
+ free(pw);
+
+ LIST_INSERT_HEAD (&uid_mappings[uid_hash(unode->uid)], unode, link);
+ }
+
+ //resolve gid of localgroup accounts and cache it
+ for (cln = TAILQ_FIRST (&princ_list->fields); cln; cln = next)
+ {
+ next = TAILQ_NEXT (cln, link);
+
+ gr = static_getgrnam(cln->field, NULL, &err);
+ if (!gr) {
+ continue;
+ }
+
+ gnode = calloc (1, sizeof *gnode);
+ if (!gnode)
+ {
+ warnx("static_init: calloc (1, %lu) failed",
+ (unsigned long)sizeof *gnode);
+ free(gr);
+ return -ENOMEM;
+ }
+ gnode->gid = gr->gr_gid;
+ gnode->principal = strdup(cln->field);
+
+ gnode->localgroup = conf_get_str("Static", cln->field);
+ if (!gnode->localgroup) {
+ free(gr);
+ return -ENOENT;
+ }
+
+ free(gr);
+
+ LIST_INSERT_HEAD (&gid_mappings[gid_hash(gnode->gid)], gnode, link);
+ }
+ return 0;
+}
+
+
+struct trans_func static_trans = {
+ .name = "static",
+ .init = static_init,
+ .name_to_uid = static_name_to_uid,
+ .name_to_gid = static_name_to_gid,
+ .uid_to_name = static_uid_to_name,
+ .gid_to_name = static_gid_to_name,
+ .princ_to_ids = static_gss_princ_to_ids,
+ .gss_princ_to_grouplist = static_gss_princ_to_grouplist,
+};
+
+struct trans_func *libnfsidmap_plugin_init()
+{
+ return (&static_trans);
+}
+
diff --git a/support/nfsidmap/umich_ldap.c b/support/nfsidmap/umich_ldap.c
new file mode 100644
index 0000000..886fa0c
--- /dev/null
+++ b/support/nfsidmap/umich_ldap.c
@@ -0,0 +1,1302 @@
+/*
+ * 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 <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>
+/* We are using deprecated functions, get the prototypes... */
+#define LDAP_DEPRECATED 1
+#include <ldap.h>
+#include "nfsidmap.h"
+#include "nfsidmap_internal.h"
+#include "cfg.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 memberof_for_groups;/* Use 'memberof' attribute when
+ looking up user groups */
+ int ldap_timeout; /* Timeout in seconds for searches
+ by ldap_search_st */
+};
+
+/* 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,
+ .memberof_for_groups = 0,
+ .ldap_timeout = DEFAULT_UMICH_SEARCH_TIMEOUT,
+};
+
+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,
+};
+
+/* 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 && linfo->ca_cert) ? "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, &current_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);
+ }
+
+ /* Set option to to use SSL/TLS if requested */
+ if (linfo->use_ssl && linfo->ca_cert) {
+ 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;
+ }
+ 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;
+ }
+ }
+
+ /* If we have a DN (and password) attempt an authenticated bind */
+ if (linfo->user_dn) {
+retry_bind:
+ lerr = ldap_simple_bind_s(ld, linfo->user_dn, linfo->passwd);
+ if (lerr) {
+ char *errmsg;
+ if (lerr == LDAP_PROTOCOL_ERROR) {
+ ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION,
+ &current_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;
+ }
+ 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));
+ 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;
+ } 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;
+ } 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);
+ }
+
+ err = 0;
+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 **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 *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 *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 **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:
+ freeaddrinfo(ap);
+out_err:
+ return return_name;
+}
+
+static int
+umichldap_init(void)
+{
+ char *tssl, *canonicalize, *memberof;
+ char missing_msg[128] = "";
+ char *server_in, *canon_name;
+
+ 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");
+ /* 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);
+
+ /* Verify required information is supplied */
+ if (server_in == NULL || strlen(server_in) == 0)
+ strncat(missing_msg, "LDAP_server ", sizeof(missing_msg));
+ if (ldap_info.base == NULL || strlen(ldap_info.base) == 0)
+ strncat(missing_msg, "LDAP_base ", sizeof(missing_msg));
+ 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.ca_cert == NULL) {
+ IDMAP_LOG(0, ("umichldap_init: You must specify LDAP_ca_cert "
+ "with LDAP_use_ssl=yes"));
+ 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: use_memberof_for_groups : %s",
+ ldap_info.memberof_for_groups ? "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()
+{
+ return (&umichldap_trans);
+}