Mapping root user to Kerberos principal (and other static mappings) - version 2

David Härdeman david at hardeman.nu
Sun Mar 23 12:13:30 EDT 2008


In November last year I submitted a patch to allow static mappings for 
GSS principals (especially important for root-over-nfsv4 since the 
machine will normally use machine credentials).

In response to the feedback I got back then, I've written some new 
patches (attached) which does the following things:

1) Implements chained methods in libnfsidmap so that it is possible to 
    specify e.g. "umich_ldap nsswitch" in /etc/idmapd.conf

2) Adds the "static" method for mapping between GSS principals and local 
    user names by using static mappings from /etc/idmapd.conf

3) (minor and not related to the first two) some changes to libtest.c to 
    remove some compilation warnings and to allow the quit-on-error 
    behaviour to be enabled/disabled.

Please review.

-- 
David Härdeman
-------------- next part --------------
Index: libnfsidmap-0.20/libnfsidmap.c
===================================================================
--- libnfsidmap-0.20.orig/libnfsidmap.c	2008-03-23 15:36:54.000000000 +0100
+++ libnfsidmap-0.20/libnfsidmap.c	2008-03-23 15:37:11.000000000 +0100
@@ -52,7 +52,7 @@
 #include "cfg.h"
 
 /* forward declarations */
-int set_trans_method(char *);
+static int set_trans_method(char *);
 
 static char *default_domain;
 
@@ -88,16 +88,15 @@
 	return 0;
 }
 
-static struct trans_func *trans = NULL;
+static struct trans_func *first_trans = NULL;
 
 int nfs4_init_name_mapping(char *conffile)
 {
 	int ret;
-	char *method;
 	int dflt = 0;
 
 	/* XXX: need to be able to reload configurations... */
-	if (trans) /* already succesfully initialized */
+	if (first_trans) /* already succesfully initialized */
 		return 0;
 	if (conffile)
 		conf_path = conffile;
@@ -118,23 +117,7 @@
 	IDMAP_LOG(1, ("libnfsidmap: using%s domain: %s\n",
 		(dflt ? " (default)" : ""), default_domain));
 
-	method = conf_get_str_with_def("Translation", "Method", "nsswitch");
-	if (set_trans_method(method) == -1) {
-		IDMAP_LOG(0, ("libnfsidmap: requested tranlation method, "
-			 "'%s', is not available\n", method));
-		return -1;
-	}
-	IDMAP_LOG(1, ("libnfsidmap: using translation method: %s\n", method)); 
-
-	if (trans->init) {
-		ret = trans->init();
-		if (ret) {
-			trans = NULL;
-			return ret;
-		}
-	}
-
-	return 0;
+	return set_trans_method();
 }
 
 char * get_default_domain(void)
@@ -173,89 +156,132 @@
 #endif
 };
 #define TR_SIZE (sizeof(t_array)/sizeof(*t_array))
+#define TR_DELIM " \t\r\n\f\v"
 
-int
-set_trans_method(char *method)
+static int
+set_trans_method()
 {
+	char *methods;
+	char *methodcpy;
+	char *saveptr;
+	char *method;
 	int i;
+	int ret = 0;
+	struct trans_func *prevfunc = NULL;
 
-	trans = NULL;
-	for (i = 0; i < TR_SIZE; i++) {
-		if (strcmp(t_array[i]->name, method) == 0) {
-			trans = t_array[i];
-			return 0;
+	methods = conf_get_str_with_def("Translation", "Method", "nsswitch");
+	if (!methods || first_trans)
+		return -EINVAL;
+
+	methodcpy = strdup(methods);
+	if (!methodcpy)
+		return -ENOMEM;
+
+	method = strtok_r(methodcpy, TR_DELIM, &saveptr);
+	while (method) {
+		for (i = 0; i < TR_SIZE; i++) {
+			if (strcmp(t_array[i]->name, method))
+				continue;
+
+			if (t_array[i]->next || prevfunc == t_array[i]) {
+				IDMAP_LOG(0, ("libnfsidmap: invalid method "
+					  "specification\n"));
+				first_trans = NULL;
+				ret = -EINVAL;
+				goto out;
+			}
+
+			if (t_array[i]->init) {
+				ret = t_array[i]->init();
+				if (ret) {
+					IDMAP_LOG(0, ("libnfsidmap: failed to "
+						  "initialize method '%s'\n",
+						  method));
+					first_trans = NULL;
+					goto out;
+
+				}
+			}
+
+			IDMAP_LOG(1, ("libnfsidmap: using translation method "
+				  "'%s'\n", method));
+
+			if (prevfunc)
+				prevfunc->next = t_array[i];
+			prevfunc = t_array[i];
+
+			if (!first_trans)
+				first_trans = t_array[i];
+
+			break;
 		}
+
+		if (i >= TR_SIZE)
+			IDMAP_LOG(0, ("libnfsidmap: unknown method '%s'\n",
+				  method));
+
+		method = strtok_r(NULL, TR_DELIM, &saveptr);
 	}
-	return -1;
+
+out:
+	free(methodcpy);
+	return ret;
 }
 
+#define RUN_TRANS(method, args...)					\
+	do {								\
+		struct trans_func *func;				\
+		int rv;							\
+									\
+		rv = nfs4_init_name_mapping(NULL);			\
+		if (rv)							\
+			return rv;					\
+									\
+		rv = -ENOENT;						\
+		for (func = first_trans; func; func = func->next) {	\
+			if (!func->method)				\
+				continue;				\
+									\
+			rv = func->method(args);			\
+									\
+			if (rv == -ENOENT)				\
+				continue;				\
+									\
+			break;						\
+		}							\
+									\
+		return rv;						\
+	} while(0)
+
 int nfs4_uid_to_name(uid_t uid, char *domain, char *name, size_t len)
 {
-	int ret;
-
-	ret = nfs4_init_name_mapping(NULL);
-	if (ret)
-		return ret;
-	return trans->uid_to_name(uid, domain, name, len);
+	RUN_TRANS(uid_to_name, uid, domain, name, len);
 }
 
 int nfs4_gid_to_name(gid_t gid, char *domain, char *name, size_t len)
 {
-	int ret;
-
-	ret = nfs4_init_name_mapping(NULL);
-	if (ret)
-		return ret;
-	return trans->gid_to_name(gid, domain, name, len);
+	RUN_TRANS(gid_to_name, gid, domain, name, len);
 }
 
 int nfs4_name_to_uid(char *name, uid_t *uid)
 {
-	int ret;
-
-	ret = nfs4_init_name_mapping(NULL);
-	if (ret)
-		goto out;
-	ret = trans->name_to_uid(name, uid);
-  out:
-  	return ret;
+	RUN_TRANS(name_to_uid, name, uid);
 }
 
 int nfs4_name_to_gid(char *name, gid_t *gid)
 {
-	int ret;
-
-	ret = nfs4_init_name_mapping(NULL);
-	if (ret)
-		goto out;
-	ret = trans->name_to_gid(name, gid);
-  out:
-  	return ret;
+	RUN_TRANS(name_to_gid, name, gid);
 }
 
 int nfs4_gss_princ_to_ids(char *secname, char *princ, uid_t *uid, gid_t *gid)
 {
-	int ret;
-
-	ret = nfs4_init_name_mapping(NULL);
-	if (ret)
-		goto out;
-	ret = trans->princ_to_ids(secname, princ, uid, gid);
-  out:
-	return ret;
+	RUN_TRANS(princ_to_ids, secname, princ, uid, gid);
 }
 
 int nfs4_gss_princ_to_grouplist(char *secname, char *princ,
 		gid_t *groups, int *ngroups)
 {
-	int ret;
-
-	ret = nfs4_init_name_mapping(NULL);
-	if (ret)
-		goto out;
-	ret =  trans->gss_princ_to_grouplist(secname, princ, groups, ngroups);
-  out:
-  	return ret;
+	RUN_TRANS(gss_princ_to_grouplist, secname, princ, groups, ngroups);
 }
 
 void nfs4_set_debug(int dbg_level, void (*logger)(const char *, ...))
Index: libnfsidmap-0.20/nfsidmap_internal.h
===================================================================
--- libnfsidmap-0.20.orig/nfsidmap_internal.h	2008-03-23 15:36:54.000000000 +0100
+++ libnfsidmap-0.20/nfsidmap_internal.h	2008-03-23 15:37:11.000000000 +0100
@@ -46,6 +46,7 @@
 	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);
+	struct trans_func *next;
 };
 
 typedef enum {
Index: libnfsidmap-0.20/nss.c
===================================================================
--- libnfsidmap-0.20.orig/nss.c	2008-03-23 15:36:54.000000000 +0100
+++ libnfsidmap-0.20/nss.c	2008-03-23 15:37:11.000000000 +0100
@@ -312,4 +312,5 @@
 	.uid_to_name	= nss_uid_to_name,
 	.gid_to_name	= nss_gid_to_name,
 	.gss_princ_to_grouplist = nss_gss_princ_to_grouplist,
+	.next		= NULL,
 };
Index: libnfsidmap-0.20/umich_ldap.c
===================================================================
--- libnfsidmap-0.20.orig/umich_ldap.c	2008-03-23 15:36:54.000000000 +0100
+++ libnfsidmap-0.20/umich_ldap.c	2008-03-23 15:37:11.000000000 +0100
@@ -1294,6 +1294,7 @@
 	.uid_to_name    = umichldap_uid_to_name,
 	.gid_to_name    = umichldap_gid_to_name,
 	.gss_princ_to_grouplist = umichldap_gss_princ_to_grouplist,
+	.next		= NULL,
 };
 
 #endif
-------------- next part --------------
Index: libnfsidmap-0.20/static.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnfsidmap-0.20/static.c	2008-03-23 15:46:41.000000000 +0100
@@ -0,0 +1,162 @@
+/*
+ *  static.c
+ *
+ *  static idmapping functions for gss principals.
+ *
+ *  Copyright (c) 2008 David Härdeman <david at 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 "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];
+};
+
+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;
+	}
+
+	IDMAP_LOG(4, ("static_getpwnam: name '%s' mapped to '%s'\n",
+		  name, localname));
+
+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: name '%s' not found\n",
+			  localname));
+
+		goto err_free_buf;
+	}
+
+	*err_p = 0;
+	return pw;
+
+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)
+{
+	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)
+{
+	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;
+}
+
+
+struct trans_func static_trans = {
+	.name			= "static",
+	.init			= NULL,
+	.name_to_uid		= NULL,
+	.name_to_gid		= NULL,
+	.uid_to_name		= NULL,
+	.gid_to_name		= NULL,
+	.princ_to_ids		= static_gss_princ_to_ids,
+	.gss_princ_to_grouplist	= static_gss_princ_to_grouplist,
+	.next			= NULL,
+};
+
Index: libnfsidmap-0.20/Makefile.am
===================================================================
--- libnfsidmap-0.20.orig/Makefile.am	2008-03-23 15:45:52.000000000 +0100
+++ libnfsidmap-0.20/Makefile.am	2008-03-23 15:46:41.000000000 +0100
@@ -1,6 +1,6 @@
 man_MANS = nfs4_uid_to_name.3
 lib_LTLIBRARIES = libnfsidmap.la
-libnfsidmap_la_SOURCES = libnfsidmap.c nss.c umich_ldap.c cfg.c strlcpy.c cfg.h nfsidmap_internal.h queue.h
+libnfsidmap_la_SOURCES = libnfsidmap.c nss.c static.c umich_ldap.c cfg.c strlcpy.c cfg.h nfsidmap_internal.h queue.h
 libnfsidmap_la_LDFLAGS = -version-info 2:0:2
 include_HEADERS = nfsidmap.h
 EXTRA_DIST = $(man_MANS) libtest.c idmapd.conf
Index: libnfsidmap-0.20/Makefile.in
===================================================================
--- libnfsidmap-0.20.orig/Makefile.in	2008-03-23 15:45:52.000000000 +0100
+++ libnfsidmap-0.20/Makefile.in	2008-03-23 15:46:41.000000000 +0100
@@ -63,8 +63,8 @@
 libLTLIBRARIES_INSTALL = $(INSTALL)
 LTLIBRARIES = $(lib_LTLIBRARIES)
 libnfsidmap_la_LIBADD =
-am_libnfsidmap_la_OBJECTS = libnfsidmap.lo nss.lo umich_ldap.lo cfg.lo \
-	strlcpy.lo
+am_libnfsidmap_la_OBJECTS = libnfsidmap.lo nss.lo static.lo \
+	umich_ldap.lo cfg.lo strlcpy.lo
 libnfsidmap_la_OBJECTS = $(am_libnfsidmap_la_OBJECTS)
 DEFAULT_INCLUDES = -I. -I$(srcdir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
@@ -203,7 +203,7 @@
 target_alias = @target_alias@
 man_MANS = nfs4_uid_to_name.3
 lib_LTLIBRARIES = libnfsidmap.la
-libnfsidmap_la_SOURCES = libnfsidmap.c nss.c umich_ldap.c cfg.c strlcpy.c cfg.h nfsidmap_internal.h queue.h
+libnfsidmap_la_SOURCES = libnfsidmap.c nss.c static.c umich_ldap.c cfg.c strlcpy.c cfg.h nfsidmap_internal.h queue.h
 libnfsidmap_la_LDFLAGS = -version-info 2:0:2
 include_HEADERS = nfsidmap.h
 EXTRA_DIST = $(man_MANS) libtest.c idmapd.conf
@@ -287,6 +287,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/cfg.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/libnfsidmap.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/nss.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/static.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/strlcpy.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/umich_ldap.Plo at am__quote@
 
Index: libnfsidmap-0.20/libnfsidmap.c
===================================================================
--- libnfsidmap-0.20.orig/libnfsidmap.c	2008-03-23 15:45:52.000000000 +0100
+++ libnfsidmap-0.20/libnfsidmap.c	2008-03-23 15:46:41.000000000 +0100
@@ -147,10 +147,12 @@
 }
 
 extern struct trans_func nss_trans;
+extern struct trans_func static_trans;
 extern struct trans_func umichldap_trans;
 
 static struct trans_func * t_array[] = {
 	&nss_trans,
+	&static_trans,
 #ifdef ENABLE_LDAP
 	&umichldap_trans,
 #endif
Index: libnfsidmap-0.20/idmapd.conf
===================================================================
--- libnfsidmap-0.20.orig/idmapd.conf	2008-03-23 15:45:52.000000000 +0100
+++ libnfsidmap-0.20/idmapd.conf	2008-03-23 15:48:33.000000000 +0100
@@ -10,11 +10,21 @@
 
 [Translation]
 
-# Translation method is either "nsswitch" (DEFAULT) or "umich_ldap"
+# Translation method is a space separated combination of:
+# "nsswitch", "umich_ldap" and "static", e.g. "nsswitch static"
+# The default is "nsswitch"
 #Method = nsswitch
 
 #-------------------------------------------------------------------#
-# The following are only used if Translation Method is umich_ldap.
+# The following are only used for the static Translation Method.
+#-------------------------------------------------------------------#
+
+[Static]
+
+#someuser at REALM = localuser
+
+#-------------------------------------------------------------------#
+# The following are only used for the umich_ldap Translation Method.
 #-------------------------------------------------------------------#
 
 [UMICH_SCHEMA]
Index: libnfsidmap-0.20/README
===================================================================
--- libnfsidmap-0.20.orig/README	2008-03-23 15:49:30.000000000 +0100
+++ libnfsidmap-0.20/README	2008-03-23 15:52:15.000000000 +0100
@@ -3,14 +3,10 @@
 When NFSv4 is using AUTH_GSS (which currently only supports Kerberos v5), the
 NFSv4 server mapping functions MUST use secure communications.
 
-We provide two sets of mapping functions, configured using /etc/idmapd.conf
+We provide several sets of mapping functions, configured using /etc/idmapd.conf
 
 Excerpt from /etc/idmapd.conf:
 ---------------------
-#
-# One of
-#   nsswitch (getpwXXX routines, all names must be in default domain)
-#   umich_ldap (ldap schema, also capable of handling multiple domains)
 [Translation]
 
 Method = umich_ldap
@@ -37,9 +33,18 @@
 mode.
 
 
+static
+------
+
+Another set of functions which only works with GSS principals. It uses a static
+mapping setup in idmapd.conf under the [Static] section of the form
+principal = localuser (e.g. "nfs/somehost at REALM = root"). It is recommended that
+this module be used in combination with another module (e.g. the nsswitch module).
+
+
 umich_ldap
 ------------
-The second set of mapping functions is a new experimental set called umich_ldap
+The last set of mapping functions is a new experimental set called umich_ldap
 in the idmapd.conf example above.  This set of translation functions are
 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
-------------- next part --------------
Index: libnfsidmap-0.20/libtest.c
===================================================================
--- libnfsidmap-0.20.orig/libtest.c	2008-03-23 17:04:52.000000000 +0100
+++ libnfsidmap-0.20/libtest.c	2008-03-23 17:06:22.000000000 +0100
@@ -45,9 +45,11 @@
  */
 
 #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;
 
@@ -80,7 +82,7 @@
 	else
 		printf("nfs4_gss_princ_to_ids: princ %s has uid %d gid %d\n",
 			princ, uid, gid);
-#if 1
+#if QUIT_ON_ERROR
 	if (err) {
 		printf("calling it quits!\n");
 		return err;
@@ -94,7 +96,7 @@
 		printf("nfs4_name_to_uid: name %s has uid %d\n",
 			name, uid);
 
-#if 1
+#if QUIT_ON_ERROR
 	if (err) {
 		printf("calling it quits!\n");
 		return err;
@@ -118,7 +120,7 @@
 		printf("\n");
 	}
 
-#if 1
+#if QUIT_ON_ERROR
 	if (err) {
 		printf("calling it quits!\n");
 		return err;
@@ -133,7 +135,7 @@
 		printf("nfs4_uid_to_name: uid %d has name %s\n",
 			uid, name_buf);
 
-#if 1
+#if QUIT_ON_ERROR
 	if (err) {
 		printf("calling it quits!\n");
 		return err;
@@ -148,7 +150,7 @@
 		printf("nfs4_gid_to_name: gid %d has name %s\n",
 			gid, name_buf);
 
-#if 1
+#if QUIT_ON_ERROR
 	if (err) {
 		printf("calling it quits!\n");
 		return err;


More information about the NFSv4 mailing list