Index: /server/common/oursrc/nss_nonlocal/Makefile
===================================================================
--- /server/common/oursrc/nss_nonlocal/Makefile	(revision 750)
+++ /server/common/oursrc/nss_nonlocal/Makefile	(revision 750)
@@ -0,0 +1,35 @@
+exec_prefix = /
+libdir = $(exec_prefix)/lib
+
+INSTALL = install
+CC = gcc
+CFLAGS = -O2 -Wall
+
+ALL_CFLAGS = $(CFLAGS) -fPIC
+ALL_LDFLAGS = $(LDFLAGS) -shared -Wl,-x
+
+all: libnss_nonlocal.so.2 linktest
+
+OBJS = nonlocal-passwd.o nonlocal-group.o nonlocal-shadow.o
+
+libnss_nonlocal.so.2: $(OBJS) libnss_nonlocal.map
+	$(CC) -o $@ $(ALL_LDFLAGS) -Wl,-soname,$@ -Wl,--version-script=libnss_nonlocal.map $(OBJS) $(LOADLIBES) $(LDLIBS)
+
+%.o: %.c
+	$(CC) -c $(ALL_CFLAGS) $(CPPFLAGS) $<
+
+nonlocal-passwd.o: nonlocal-passwd.c nsswitch-internal.h nonlocal.h
+nonlocal-group.o: nonlocal-group.c nsswitch-internal.h nonlocal.h
+nonlocal-shadow.o: nonlocal-shadow.c nsswitch-internal.h nonlocal.h
+
+linktest: libnss_nonlocal.so.2
+	$(CC) $(LDFLAGS) -nostdlib -Wl,--entry=0 -o /dev/null $^
+
+install: libnss_nonlocal.so.2
+	$(INSTALL) -d $(DESTDIR)$(libdir)
+	$(INSTALL) -m a+r,u+w $< $(DESTDIR)$(libdir)/
+
+clean:
+	rm -f *.so.* *.o
+
+.PHONY: all linktest install clean
Index: /server/common/oursrc/nss_nonlocal/README
===================================================================
--- /server/common/oursrc/nss_nonlocal/README	(revision 750)
+++ /server/common/oursrc/nss_nonlocal/README	(revision 750)
@@ -0,0 +1,10 @@
+This is nss_nonlocal, an nsswitch module that acts as a proxy for other 
+nsswitch modules like hesiod, but prevents non-local users from 
+potentially gaining local privileges by spoofing local UIDs and GIDs.
+
+To use it, configure /etc/nsswitch.conf as follows:
+
+passwd:         compat nonlocal
+passwd_nonlocal: hesiod
+group:          compat nonlocal
+group_nonlocal: hesiod
Index: /server/common/oursrc/nss_nonlocal/libnss_nonlocal.map
===================================================================
--- /server/common/oursrc/nss_nonlocal/libnss_nonlocal.map	(revision 750)
+++ /server/common/oursrc/nss_nonlocal/libnss_nonlocal.map	(revision 750)
@@ -0,0 +1,24 @@
+GLIBC_2.0 {
+  global:
+    _nss_nonlocal_setpwent;
+    _nss_nonlocal_endpwent;
+    _nss_nonlocal_getpwent_r;
+    _nss_nonlocal_getpwuid_r;
+    _nss_nonlocal_getpwnam_r;
+
+    _nss_nonlocal_setspent;
+    _nss_nonlocal_endspent;
+    _nss_nonlocal_getspent_r;
+    _nss_nonlocal_getspnam_r;
+
+    _nss_nonlocal_setgrent;
+    _nss_nonlocal_endgrent;
+    _nss_nonlocal_getgrent_r;
+    _nss_nonlocal_getgrgid_r;
+    _nss_nonlocal_getgrnam_r;
+
+    _nss_nonlocal_initgroups_dyn;
+
+  local:
+    *;
+};
Index: /server/common/oursrc/nss_nonlocal/nonlocal-group.c
===================================================================
--- /server/common/oursrc/nss_nonlocal/nonlocal-group.c	(revision 750)
+++ /server/common/oursrc/nss_nonlocal/nonlocal-group.c	(revision 750)
@@ -0,0 +1,492 @@
+/*
+ * nonlocal-group.c
+ * group database for nss_nonlocal proxy
+ *
+ * Copyright © 2007 Anders Kaseorg <andersk@mit.edu> and Tim Abbott
+ * <tabbott@mit.edu>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <errno.h>
+#include <grp.h>
+#include <nss.h>
+#include "nsswitch-internal.h"
+#include "nonlocal.h"
+
+#define MAGIC_LOCAL_GR_BUFLEN (sysconf(_SC_GETGR_R_SIZE_MAX) + 7)
+#define MAGIC_NONLOCAL_GROUPNAME "nss-nonlocal-users"
+#define MAGIC_LOCAL_GROUPNAME "nss-local-users"
+
+
+static service_user *
+nss_group_nonlocal_database(void)
+{
+    static service_user *nip = NULL;
+    if (nip == NULL)
+	__nss_database_lookup("group_nonlocal", NULL, "", &nip);
+
+    return nip;
+}
+
+
+enum nss_status
+check_nonlocal_gid(const char *user, gid_t gid, int *errnop)
+{
+    enum nss_status status = NSS_STATUS_SUCCESS;
+    struct group gbuf;
+    struct group *gbufp = &gbuf;
+    int ret;
+    int old_errno = errno;
+    int buflen = MAGIC_LOCAL_GR_BUFLEN;
+    char *buf = malloc(buflen);
+    if (buf == NULL) {
+	*errnop = ENOMEM;
+	errno = old_errno;
+	return NSS_STATUS_TRYAGAIN;
+    }
+    errno = 0;
+    ret = getgrgid_r(gid, gbufp, buf, buflen, &gbufp);
+    if (ret != 0) {
+	*errnop = old_errno;
+	status = NSS_STATUS_TRYAGAIN;
+    } else if (gbufp != NULL) {
+	syslog(LOG_WARNING, "nss_nonlocal: removing local group %u (%s) from non-local user %s\n", gbuf.gr_gid, gbuf.gr_name, user);
+	status = NSS_STATUS_NOTFOUND;
+    }
+    free(buf);
+    errno = old_errno;
+    return status;
+}
+
+enum nss_status
+get_local_group(const char *name, struct group *grp, char *buffer, size_t buflen, int *errnop)
+{
+    enum nss_status status = NSS_STATUS_NOTFOUND;
+    struct group gbuf;
+    struct group *gbufp = &gbuf;
+    int ret, n;
+    int old_errno = errno;
+    int len = MAGIC_LOCAL_GR_BUFLEN;
+    char *buf = malloc(len);
+    if (buf == NULL) {
+	*errnop = ENOMEM;
+	errno = old_errno;
+	return NSS_STATUS_TRYAGAIN;
+    }
+    errno = 0;
+    ret = getgrnam_r(name, gbufp, buf, len, &gbufp);
+    if (ret != 0) {
+	*errnop = old_errno;
+	status = NSS_STATUS_TRYAGAIN;
+    } else if (gbufp != NULL) {
+	status = NSS_STATUS_SUCCESS;
+
+	n = snprintf(buffer, buflen, "%s", gbufp->gr_name);
+	if (n < 0 || n >= buflen) {
+	    *errnop = ERANGE;
+	    status = NSS_STATUS_TRYAGAIN;
+	    goto get_local_group_done;
+	}
+	grp->gr_name = buffer;
+	buffer += n;
+	buflen -= n;
+
+	n = snprintf(buffer, buflen, "%s", gbufp->gr_passwd);
+	if (n < 0 || n >= buflen) {
+	    *errnop = ERANGE;
+	    status = NSS_STATUS_TRYAGAIN;
+	    goto get_local_group_done;
+	}
+	grp->gr_passwd = buffer;
+	buffer += n;
+	buflen -= n;
+
+	grp->gr_gid = gbufp->gr_gid;
+
+	if (buflen < sizeof(void *)) {
+	    *errnop = ERANGE;
+	    status = NSS_STATUS_TRYAGAIN;
+	    goto get_local_group_done;
+	}
+	*(void **)buffer = NULL;
+	buffer += sizeof(void *);
+	buflen -= sizeof(void *);
+    }
+ get_local_group_done:
+    free(buf);
+    errno = old_errno;
+    return status;
+}
+
+static service_user *grent_nip = NULL;
+static void *grent_fct_start;
+static union {
+    enum nss_status (*l)(struct group *grp, char *buffer, size_t buflen,
+			 int *errnop);
+    void *ptr;
+} grent_fct;
+static const char *grent_fct_name = "getgrent_r";
+
+enum nss_status
+_nss_nonlocal_setgrent(int stayopen)
+{
+    static const char *fct_name = "setgrent";
+    static void *fct_start = NULL;
+    enum nss_status status;
+    service_user *nip;
+    union {
+	enum nss_status (*l)(int stayopen);
+	void *ptr;
+    } fct;
+
+    nip = nss_group_nonlocal_database();
+    if (nip == NULL)
+	return NSS_STATUS_UNAVAIL;
+    if (fct_start == NULL)
+	fct_start = __nss_lookup_function(nip, fct_name);
+    fct.ptr = fct_start;
+    do {
+	if (fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else
+	    status = DL_CALL_FCT(fct.l, (stayopen));
+    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
+    if (status != NSS_STATUS_SUCCESS)
+	return status;
+
+    grent_nip = nip;
+    if (grent_fct_start == NULL)
+	grent_fct_start = __nss_lookup_function(nip, grent_fct_name);
+    grent_fct.ptr = grent_fct_start;
+    return NSS_STATUS_SUCCESS;
+}
+
+enum nss_status
+_nss_nonlocal_endgrent(void)
+{
+    static const char *fct_name = "endgrent";
+    static void *fct_start = NULL;
+    enum nss_status status;
+    service_user *nip;
+    union {
+	enum nss_status (*l)(void);
+	void *ptr;
+    } fct;
+
+    grent_nip = NULL;
+
+    nip = nss_group_nonlocal_database();
+    if (nip == NULL)
+	return NSS_STATUS_UNAVAIL;
+    if (fct_start == NULL)
+	fct_start = __nss_lookup_function(nip, fct_name);
+    fct.ptr = fct_start;
+    do {
+	if (fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else
+	    status = DL_CALL_FCT(fct.l, ());
+    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
+    return status;
+}
+
+enum nss_status
+_nss_nonlocal_getgrent_r(struct group *grp, char *buffer, size_t buflen,
+			 int *errnop)
+{
+    enum nss_status status;
+
+    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
+    if (buflen == MAGIC_LOCAL_GR_BUFLEN ||
+	(nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
+	return NSS_STATUS_UNAVAIL;
+
+    if (grent_nip == NULL) {
+	status = _nss_nonlocal_setgrent(0);
+	if (status != NSS_STATUS_SUCCESS)
+	    return status;
+    }
+    do {
+	if (grent_fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else {
+	    int nonlocal_errno;
+	    do
+		status = DL_CALL_FCT(grent_fct.l, (grp, buffer, buflen, errnop));
+	    while (status == NSS_STATUS_SUCCESS &&
+		   check_nonlocal_gid("(unknown)", grp->gr_gid, &nonlocal_errno) != NSS_STATUS_SUCCESS);
+	}
+	if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
+	    return status;
+
+	if (status == NSS_STATUS_SUCCESS)
+	    return NSS_STATUS_SUCCESS;
+    } while (__nss_next(&grent_nip, grent_fct_name, &grent_fct.ptr, status, 0) == 0);
+
+    grent_nip = NULL;
+    return NSS_STATUS_NOTFOUND;
+}
+
+
+enum nss_status
+_nss_nonlocal_getgrnam_r(const char *name, struct group *grp,
+			 char *buffer, size_t buflen, int *errnop)
+{
+    static const char *fct_name = "getgrnam_r";
+    static void *fct_start = NULL;
+    enum nss_status status;
+    service_user *nip;
+    union {
+	enum nss_status (*l)(const char *name, struct group *grp,
+			     char *buffer, size_t buflen, int *errnop);
+	void *ptr;
+    } fct;
+
+    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
+    if (buflen == MAGIC_LOCAL_GR_BUFLEN ||
+	(nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
+	return NSS_STATUS_UNAVAIL;
+
+    nip = nss_group_nonlocal_database();
+    if (nip == NULL)
+	return NSS_STATUS_UNAVAIL;
+    if (fct_start == NULL)
+	fct_start = __nss_lookup_function(nip, fct_name);
+    fct.ptr = fct_start;
+    do {
+	if (fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else
+	    status = DL_CALL_FCT(fct.l, (name, grp, buffer, buflen, errnop));
+	if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
+	    break;
+    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
+    if (status != NSS_STATUS_SUCCESS)
+	return status;
+
+    return check_nonlocal_gid(name, grp->gr_gid, errnop);
+}
+
+enum nss_status
+_nss_nonlocal_getgrgid_r(gid_t gid, struct group *grp,
+			 char *buffer, size_t buflen, int *errnop)
+{
+    static const char *fct_name = "getgrgid_r";
+    static void *fct_start = NULL;
+    enum nss_status status;
+    service_user *nip;
+    union {
+	enum nss_status (*l)(gid_t gid, struct group *grp,
+			     char *buffer, size_t buflen, int *errnop);
+	void *ptr;
+    } fct;
+
+    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
+    if (buflen == MAGIC_LOCAL_GR_BUFLEN ||
+	(nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
+	return NSS_STATUS_UNAVAIL;
+
+    nip = nss_group_nonlocal_database();
+    if (nip == NULL)
+	return NSS_STATUS_UNAVAIL;
+    if (fct_start == NULL)
+	fct_start = __nss_lookup_function(nip, fct_name);
+    fct.ptr = fct_start;
+    do {
+	if (fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else
+	    status = DL_CALL_FCT(fct.l, (gid, grp, buffer, buflen, errnop));
+	if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
+	    break;
+    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
+    if (status != NSS_STATUS_SUCCESS)
+	return status;
+
+    return check_nonlocal_gid(grp->gr_name, grp->gr_gid, errnop);
+}
+
+enum nss_status
+_nss_nonlocal_initgroups_dyn(const char *user, gid_t group, long int *start,
+			     long int *size, gid_t **groupsp, long int limit,
+			     int *errnop)
+{
+    static const char *fct_name = "initgroups_dyn";
+    static void *fct_start = NULL;
+    enum nss_status status;
+    service_user *nip;
+    union {
+	enum nss_status (*l)(const char *user, gid_t group, long int *start,
+			     long int *size, gid_t **groupsp, long int limit,
+			     int *errnop);
+	void *ptr;
+    } fct;
+
+    struct group local_users_group, nonlocal_users_group;
+    gid_t local_users_gid, gid;
+    int is_local = 0;
+    int buflen;
+    char *buffer;
+
+    /* Check that the user is a nonlocal user before adding any groups. */
+    status = check_nonlocal_user(user, errnop);
+    if (status == NSS_STATUS_NOTFOUND)
+	is_local = 1;
+    else if (status != NSS_STATUS_SUCCESS)
+	return status;
+
+    int old_errno = errno;
+
+    buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+    buffer = malloc(buflen);
+    if (buffer == NULL) {
+	*errnop = ENOMEM;
+	errno = old_errno;
+	return NSS_STATUS_TRYAGAIN;
+    }
+    status = get_local_group(MAGIC_LOCAL_GROUPNAME,
+			     &local_users_group, buffer, buflen, errnop);
+    if (status == NSS_STATUS_NOTFOUND) {
+	syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
+	       MAGIC_LOCAL_GROUPNAME);
+	local_users_gid = -1;
+    } else if (status != NSS_STATUS_SUCCESS) {
+	return status;
+    } else
+	local_users_gid = local_users_group.gr_gid;
+    free(buffer);
+
+    if (is_local) {
+	gid = local_users_gid;
+    } else {
+	buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+	buffer = malloc(buflen);
+	if (buffer == NULL) {
+	    *errnop = ENOMEM;
+	    errno = old_errno;
+	    return NSS_STATUS_TRYAGAIN;
+	}
+ 	status = get_local_group(MAGIC_NONLOCAL_GROUPNAME,
+				 &nonlocal_users_group, buffer, buflen, errnop);
+	if (status == NSS_STATUS_NOTFOUND) {
+	    syslog(LOG_WARNING, "nss_nonlocal: Group %s does not exist locally!",
+		   MAGIC_NONLOCAL_GROUPNAME);
+	    gid = -1;
+	} else if (status != NSS_STATUS_SUCCESS) {
+	    errno = old_errno;
+	    return status;
+	} else
+	    gid = nonlocal_users_group.gr_gid;
+	free(buffer);
+    }
+
+    if (gid != -1) {
+	int i;
+	for (i = 0; i < *start; ++i)
+	    if ((*groupsp)[i] == gid)
+		break;
+	if (i >= *start) {
+	    if (*start + 1 > *size) {
+		gid_t *newgroups;
+		long int newsize = 2 * *size;
+		if (limit > 0) {
+		    if (*size >= limit)
+			return NSS_STATUS_SUCCESS;
+		    if (newsize > limit)
+			newsize = limit;
+		}
+		newgroups = realloc(*groupsp, *size * sizeof((*groupsp)[0]));
+		if (newgroups == NULL) {
+		    *errnop = ENOMEM;
+		    errno = old_errno;
+		    return NSS_STATUS_TRYAGAIN;
+		}
+		*groupsp = newgroups;
+		*size = newsize;
+	    }
+	    (*groupsp)[(*start)++] = gid;
+	}
+    }
+
+    errno = old_errno;
+
+    if (is_local)
+	return NSS_STATUS_SUCCESS;
+
+    int in = *start, out = *start, i;
+
+    nip = nss_group_nonlocal_database();
+    if (nip == NULL)
+	return NSS_STATUS_UNAVAIL;
+    if (fct_start == NULL)
+	fct_start = __nss_lookup_function(nip, fct_name);
+    fct.ptr = fct_start;
+
+    do {
+	if (fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else
+	    status = DL_CALL_FCT(fct.l, (user, group, start, size, groupsp, limit, errnop));
+        if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
+            break;
+    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
+    if (status != NSS_STATUS_SUCCESS)
+        return status;
+
+    for (; in < *start; ++in) {
+	int nonlocal_errno = *errnop;
+
+	for (i = 0; i < out; ++i)
+	    if ((*groupsp)[i] == (*groupsp)[in])
+		break;
+	if (i < out)
+	    continue;
+
+	/* Don't let users get into MAGIC_LOCAL_GROUPNAME from nonlocal reasons. */
+	if (local_users_gid == (*groupsp)[in]) {
+	    syslog(LOG_WARNING, "nss_nonlocal: Nonlocal user %s removed from special local users group %s",
+		   user, MAGIC_LOCAL_GROUPNAME);
+	    continue;
+	}
+
+	status = check_nonlocal_gid(user, (*groupsp)[in], &nonlocal_errno);
+	if (status == NSS_STATUS_SUCCESS) {
+	    (*groupsp)[out++] = (*groupsp)[in];
+	} else if (status != NSS_STATUS_NOTFOUND) {
+	    *start = out;
+	    *errnop = nonlocal_errno;
+	    return status;
+	}
+    }
+
+    *start = out;
+    return NSS_STATUS_SUCCESS;
+}
Index: /server/common/oursrc/nss_nonlocal/nonlocal-passwd.c
===================================================================
--- /server/common/oursrc/nss_nonlocal/nonlocal-passwd.c	(revision 750)
+++ /server/common/oursrc/nss_nonlocal/nonlocal-passwd.c	(revision 750)
@@ -0,0 +1,320 @@
+/*
+ * nonlocal-passwd.c
+ * passwd database for nss_nonlocal proxy.
+ *
+ * Copyright © 2007 Anders Kaseorg <andersk@mit.edu> and Tim Abbott
+ * <tabbott@mit.edu>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <nss.h>
+#include "nsswitch-internal.h"
+#include "nonlocal.h"
+
+#define MAGIC_LOCAL_PW_BUFLEN (sysconf(_SC_GETPW_R_SIZE_MAX) + 7)
+
+
+static service_user *
+nss_passwd_nonlocal_database(void)
+{
+    static service_user *nip = NULL;
+    if (nip == NULL)
+	__nss_database_lookup("passwd_nonlocal", NULL, "", &nip);
+
+    return nip;
+}
+
+
+enum nss_status
+check_nonlocal_uid(const char *user, uid_t uid, int *errnop)
+{
+    enum nss_status status = NSS_STATUS_SUCCESS;
+    struct passwd pwbuf;
+    struct passwd *pwbufp = &pwbuf;
+    int ret;
+    int old_errno = errno;
+    int buflen = MAGIC_LOCAL_PW_BUFLEN;
+    char *buf = malloc(buflen);
+    if (buf == NULL) {
+	*errnop = ENOMEM;
+	errno = old_errno;
+	return NSS_STATUS_TRYAGAIN;
+    }
+    errno = 0;
+    ret = getpwuid_r(uid, pwbufp, buf, buflen, &pwbufp);
+    if (ret != 0) {
+	*errnop = errno;
+	status = NSS_STATUS_TRYAGAIN;
+    } else if (pwbufp != NULL) {
+	syslog(LOG_ERR, "nss_nonlocal: possible spoofing attack: non-local user %s has same UID as local user %s!\n", user, pwbuf.pw_name);
+	status = NSS_STATUS_NOTFOUND;
+    }
+    free(buf);
+    errno = old_errno;
+    return status;
+}
+
+enum nss_status
+check_nonlocal_user(const char *user, int *errnop)
+{
+    enum nss_status status = NSS_STATUS_SUCCESS;
+    struct passwd pwbuf;
+    struct passwd *pwbufp = &pwbuf;
+    int ret;
+    int old_errno = errno;
+    int buflen = MAGIC_LOCAL_PW_BUFLEN;
+    char *buf = malloc(buflen);
+    if (buf == NULL) {
+	*errnop = ENOMEM;
+	errno = old_errno;
+	return NSS_STATUS_TRYAGAIN;
+    }
+    errno = 0;
+    ret = getpwnam_r(user, pwbufp, buf, buflen, &pwbufp);
+    if (ret != 0) {
+	*errnop = errno;
+	status = NSS_STATUS_TRYAGAIN;
+    } else if (pwbufp != NULL) {
+	status = NSS_STATUS_NOTFOUND;
+    }
+    free(buf);
+    errno = old_errno;
+    return status;
+}
+
+
+static service_user *pwent_nip = NULL;
+static void *pwent_fct_start;
+static union {
+    enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
+			 int *errnop);
+    void *ptr;
+} pwent_fct;
+static const char *pwent_fct_name = "getpwent_r";
+
+enum nss_status
+_nss_nonlocal_setpwent(int stayopen)
+{
+    static const char *fct_name = "setpwent";
+    static void *fct_start = NULL;
+    enum nss_status status;
+    service_user *nip;
+    union {
+	enum nss_status (*l)(int stayopen);
+	void *ptr;
+    } fct;
+
+    nip = nss_passwd_nonlocal_database();
+    if (nip == NULL)
+	return NSS_STATUS_UNAVAIL;
+    if (fct_start == NULL)
+	fct_start = __nss_lookup_function(nip, fct_name);
+    fct.ptr = fct_start;
+    do {
+	if (fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else
+	    status = DL_CALL_FCT(fct.l, (stayopen));
+    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
+    if (status != NSS_STATUS_SUCCESS)
+	return status;
+
+    pwent_nip = nip;
+    if (pwent_fct_start == NULL)
+	pwent_fct_start = __nss_lookup_function(nip, pwent_fct_name);
+    pwent_fct.ptr = pwent_fct_start;
+    return NSS_STATUS_SUCCESS;
+}
+
+enum nss_status
+_nss_nonlocal_endpwent(void)
+{
+    static const char *fct_name = "endpwent";
+    static void *fct_start = NULL;
+    enum nss_status status;
+    service_user *nip;
+    union {
+	enum nss_status (*l)(void);
+	void *ptr;
+    } fct;
+
+    pwent_nip = NULL;
+
+    nip = nss_passwd_nonlocal_database();
+    if (nip == NULL)
+	return NSS_STATUS_UNAVAIL;
+    if (fct_start == NULL)
+	fct_start = __nss_lookup_function(nip, fct_name);
+    fct.ptr = fct_start;
+    do {
+	if (fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else
+	    status = DL_CALL_FCT(fct.l, ());
+    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
+    return status;
+}
+
+enum nss_status
+_nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
+			 int *errnop)
+{
+    enum nss_status status;
+
+    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
+    if (buflen == MAGIC_LOCAL_PW_BUFLEN ||
+	(nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
+	return NSS_STATUS_UNAVAIL;
+
+    if (pwent_nip == NULL) {
+	status = _nss_nonlocal_setpwent(0);
+	if (status != NSS_STATUS_SUCCESS)
+	    return status;
+    }
+    do {
+	if (pwent_fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else {
+	    int nonlocal_errno;
+	    do
+		status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop));
+	    while (status == NSS_STATUS_SUCCESS &&
+		   check_nonlocal_uid(pwd->pw_name, pwd->pw_uid, &nonlocal_errno) != NSS_STATUS_SUCCESS);
+	}
+	if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
+	    return status;
+
+	if (status == NSS_STATUS_SUCCESS)
+	    return NSS_STATUS_SUCCESS;
+    } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0);
+
+    pwent_nip = NULL;
+    return NSS_STATUS_NOTFOUND;
+}
+
+
+enum nss_status
+_nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
+			 char *buffer, size_t buflen, int *errnop)
+{
+    static const char *fct_name = "getpwnam_r";
+    static void *fct_start = NULL;
+    enum nss_status status;
+    service_user *nip;
+    union {
+	enum nss_status (*l)(const char *name, struct passwd *pwd,
+			     char *buffer, size_t buflen, int *errnop);
+	void *ptr;
+    } fct;
+    int group_errno;
+
+    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
+    if (buflen == MAGIC_LOCAL_PW_BUFLEN ||
+	(nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
+	return NSS_STATUS_UNAVAIL;
+
+    nip = nss_passwd_nonlocal_database();
+    if (nip == NULL)
+	return NSS_STATUS_UNAVAIL;
+    if (fct_start == NULL)
+	fct_start = __nss_lookup_function(nip, fct_name);
+    fct.ptr = fct_start;
+    do {
+	if (fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else
+	    status = DL_CALL_FCT(fct.l, (name, pwd, buffer, buflen, errnop));
+	if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
+	    break;
+    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
+    if (status != NSS_STATUS_SUCCESS)
+	return status;
+
+    status = check_nonlocal_uid(name, pwd->pw_uid, errnop);
+    if (status != NSS_STATUS_SUCCESS)
+	return status;
+
+    if (check_nonlocal_gid(name, pwd->pw_gid, &group_errno) !=
+	NSS_STATUS_SUCCESS)
+	pwd->pw_gid = 65534 /* nogroup */;
+    return NSS_STATUS_SUCCESS;
+}
+
+enum nss_status
+_nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
+			 char *buffer, size_t buflen, int *errnop)
+{
+    static const char *fct_name = "getpwuid_r";
+    static void *fct_start = NULL;
+    enum nss_status status;
+    service_user *nip;
+    union {
+	enum nss_status (*l)(uid_t uid, struct passwd *pwd,
+			     char *buffer, size_t buflen, int *errnop);
+	void *ptr;
+    } fct;
+    int group_errno;
+
+    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
+    if (buflen == MAGIC_LOCAL_PW_BUFLEN ||
+	(nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0'))
+	return NSS_STATUS_UNAVAIL;
+
+    nip = nss_passwd_nonlocal_database();
+    if (nip == NULL)
+	return NSS_STATUS_UNAVAIL;
+    if (fct_start == NULL)
+	fct_start = __nss_lookup_function(nip, fct_name);
+    fct.ptr = fct_start;
+    do {
+	if (fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else
+	    status = DL_CALL_FCT(fct.l, (uid, pwd, buffer, buflen, errnop));
+	if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
+	    break;
+    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
+    if (status != NSS_STATUS_SUCCESS)
+	return status;
+
+    status = check_nonlocal_uid(pwd->pw_name, pwd->pw_uid, errnop);
+    if (status != NSS_STATUS_SUCCESS)
+	return status;
+
+    if (check_nonlocal_gid(pwd->pw_name, pwd->pw_gid, &group_errno) !=
+	NSS_STATUS_SUCCESS)
+	pwd->pw_gid = 65534 /* nogroup */;
+    return NSS_STATUS_SUCCESS;
+}
Index: /server/common/oursrc/nss_nonlocal/nonlocal-shadow.c
===================================================================
--- /server/common/oursrc/nss_nonlocal/nonlocal-shadow.c	(revision 750)
+++ /server/common/oursrc/nss_nonlocal/nonlocal-shadow.c	(revision 750)
@@ -0,0 +1,183 @@
+/*
+ * nonlocal-shadow.c
+ * shadow database for nss_nonlocal proxy.
+ *
+ * Copyright © 2007 Anders Kaseorg <andersk@mit.edu>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <errno.h>
+#include <shadow.h>
+#include <nss.h>
+
+#include "nsswitch-internal.h"
+#include "nonlocal.h"
+
+
+static service_user *
+nss_shadow_nonlocal_database(void)
+{
+    static service_user *nip = NULL;
+    if (nip == NULL)
+        __nss_database_lookup("shadow_nonlocal", NULL, "", &nip);
+
+    return nip;
+}
+
+
+static service_user *spent_nip = NULL;
+static void *spent_fct_start;
+static union {
+    enum nss_status (*l)(struct spwd *pwd, char *buffer, size_t buflen,
+			 int *errnop);
+    void *ptr;
+} spent_fct;
+static const char *spent_fct_name = "getspent_r";
+
+enum nss_status
+_nss_nonlocal_setspent(int stayopen)
+{
+    static const char *fct_name = "setspent";
+    static void *fct_start = NULL;
+    enum nss_status status;
+    service_user *nip;
+    union {
+	enum nss_status (*l)(int stayopen);
+	void *ptr;
+    } fct;
+
+    nip = nss_shadow_nonlocal_database();
+    if (nip == NULL)
+	return NSS_STATUS_UNAVAIL;
+    if (fct_start == NULL)
+	fct_start = __nss_lookup_function(nip, fct_name);
+    fct.ptr = fct_start;
+    do {
+	if (fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else
+	    status = DL_CALL_FCT(fct.l, (stayopen));
+    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
+    if (status != NSS_STATUS_SUCCESS)
+	return status;
+
+    spent_nip = nip;
+    if (spent_fct_start == NULL)
+	spent_fct_start = __nss_lookup_function(nip, spent_fct_name);
+    spent_fct.ptr = spent_fct_start;
+    return NSS_STATUS_SUCCESS;
+}
+
+enum nss_status
+_nss_nonlocal_endspent(void)
+{
+    static const char *fct_name = "endspent";
+    static void *fct_start = NULL;
+    enum nss_status status;
+    service_user *nip;
+    union {
+	enum nss_status (*l)(void);
+	void *ptr;
+    } fct;
+
+    spent_nip = NULL;
+
+    nip = nss_shadow_nonlocal_database();
+    if (nip == NULL)
+	return NSS_STATUS_UNAVAIL;
+    if (fct_start == NULL)
+	fct_start = __nss_lookup_function(nip, fct_name);
+    fct.ptr = fct_start;
+    do {
+	if (fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else
+	    status = DL_CALL_FCT(fct.l, ());
+    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
+    return status;
+}
+
+enum nss_status
+_nss_nonlocal_getspent_r(struct spwd *pwd, char *buffer, size_t buflen,
+			 int *errnop)
+{
+    enum nss_status status;
+    if (spent_nip == NULL) {
+	status = _nss_nonlocal_setspent(0);
+	if (status != NSS_STATUS_SUCCESS)
+	    return status;
+    }
+    do {
+	if (spent_fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else
+	    status = DL_CALL_FCT(spent_fct.l, (pwd, buffer, buflen, errnop));	
+	if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
+	    return status;
+
+	if (status == NSS_STATUS_SUCCESS)
+	    return NSS_STATUS_SUCCESS;
+    } while (__nss_next(&spent_nip, spent_fct_name, &spent_fct.ptr, status, 0) == 0);
+
+    spent_nip = NULL;
+    return NSS_STATUS_NOTFOUND;
+}
+
+
+enum nss_status
+_nss_nonlocal_getspnam_r(const char *name, struct spwd *pwd,
+			 char *buffer, size_t buflen, int *errnop)
+{
+    static const char *fct_name = "getspnam_r";
+    static void *fct_start = NULL;
+    enum nss_status status;
+    service_user *nip;
+    union {
+	enum nss_status (*l)(const char *name, struct spwd *pwd,
+			     char *buffer, size_t buflen, int *errnop);
+	void *ptr;
+    } fct;
+
+    nip = nss_shadow_nonlocal_database();
+    if (nip == NULL)
+	return NSS_STATUS_UNAVAIL;
+    if (fct_start == NULL)
+	fct_start = __nss_lookup_function(nip, fct_name);
+    fct.ptr = fct_start;
+    do {
+	if (fct.ptr == NULL)
+	    status = NSS_STATUS_UNAVAIL;
+	else
+	    status = DL_CALL_FCT(fct.l, (name, pwd, buffer, buflen, errnop));
+	if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
+	    break;
+    } while (__nss_next(&nip, fct_name, &fct.ptr, status, 0) == 0);
+    return status;
+}
Index: /server/common/oursrc/nss_nonlocal/nonlocal.h
===================================================================
--- /server/common/oursrc/nss_nonlocal/nonlocal.h	(revision 750)
+++ /server/common/oursrc/nss_nonlocal/nonlocal.h	(revision 750)
@@ -0,0 +1,7 @@
+#ifndef NONLOCAL_H
+#define NONLOCAL_H
+enum nss_status check_nonlocal_uid(const char *user, uid_t uid, int *errnop);
+enum nss_status check_nonlocal_gid(const char *user, gid_t gid, int *errnop);
+enum nss_status check_nonlocal_user(const char *user, int *errnop);
+#define NONLOCAL_IGNORE_ENV "NSS_NONLOCAL_IGNORE"
+#endif /* NON_LOCAL_H */
Index: /server/common/oursrc/nss_nonlocal/nsswitch-internal.h
===================================================================
--- /server/common/oursrc/nss_nonlocal/nsswitch-internal.h	(revision 750)
+++ /server/common/oursrc/nss_nonlocal/nsswitch-internal.h	(revision 750)
@@ -0,0 +1,26 @@
+/*
+ * nsswitch_internal.h
+ * Prototypes for some internal glibc functions that we use.  Shhh.
+ */
+
+#ifndef NSSWITCH_INTERNAL_H
+#define NSSWITCH_INTERNAL_H
+
+typedef struct service_user service_user;
+
+extern int
+__nss_next (service_user **ni, const char *fct_name, void **fctp, int status,
+            int all_values);
+
+extern int
+__nss_database_lookup (const char *database,
+		       const char *alternative_name,
+		       const char *defconfig, service_user **ni);
+
+extern int
+__nss_configure_lookup (const char *dbname, const char *service_line);
+
+extern void
+*__nss_lookup_function (service_user *ni, const char *fct_name);
+
+#endif /* NSSWITCH_INTERNAL_H */
Index: /server/fedora/Makefile
===================================================================
--- /server/fedora/Makefile	(revision 749)
+++ /server/fedora/Makefile	(revision 750)
@@ -20,5 +20,5 @@
 
 upstream	= openafs krb5 httpd mit-zephyr openssh
-oursrc		= execsys tokensys accountadm httpdmods logview sql-signup
+oursrc		= execsys tokensys accountadm httpdmods logview sql-signup nss_nonlocal nss_nonlocal.i386
 allsrc		= $(upstream) $(oursrc)
 oursrcdir	= ${PWD}/../common/oursrc
@@ -114,7 +114,11 @@
 	make $(allsrc)
 
-$(allsrc): setup
+$(filter %.i386,$(allsrc)): %.i386: setup
 	PATH="/usr/kerberos/sbin:/usr/kerberos/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin" \
-	rpmbuild $(rpmbuild_args) -ba ${tmp_specs}/$@*.spec
+	setarch i386 rpmbuild $(rpmbuild_args) --target=i386 --define="_lib lib" -bb ${tmp_specs}/$**.spec
+
+$(filter-out %.i386,$(allsrc)): %: setup
+	PATH="/usr/kerberos/sbin:/usr/kerberos/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin" \
+	rpmbuild $(rpmbuild_args) -ba ${tmp_specs}/$**.spec
 
 openafs-kernel: setup
Index: /server/fedora/specs/nss_nonlocal.spec
===================================================================
--- /server/fedora/specs/nss_nonlocal.spec	(revision 750)
+++ /server/fedora/specs/nss_nonlocal.spec	(revision 750)
@@ -0,0 +1,49 @@
+Summary: nsswitch proxy module to prevent local account spoofing
+Group: System Environment/Libraries
+Name: nss_nonlocal
+Version: 1.6
+Release: 0
+URL: http://debathena.mit.edu/nss_nonlocal/
+License: GPL
+Source: %{name}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+
+%description
+This nsswitch module acts as a proxy for other nsswitch modules like hesiod,
+but prevents non-local users from potentially gaining local privileges by
+spoofing local UIDs and GIDs.
+
+%prep
+%setup -q -n %{name}
+
+%build
+make CFLAGS='%optflags' LDFLAGS='%optflags'
+
+%install
+[ $RPM_BUILD_ROOT != / ] && rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT libdir=/%{_lib}
+
+%clean
+[ $RPM_BUILD_ROOT != / ] && rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-, root, root)
+%doc README
+/%{_lib}/libnss_nonlocal.so.2
+
+%pre
+groupadd -r nss-local-users || :
+groupadd -r nss-nonlocal-users || :
+
+%post
+/sbin/ldconfig
+
+%postun
+/sbin/ldconfig
+test "$1" != 0 || groupdel nss-local-users || :
+test "$1" != 0 || groupdel nss-nonlocal-users || :
+
+%changelog
+
+* Thu May  8 2008 Anders Kaseorg <andersk@mit.edu> 1.6-0
+- Initial RPM release.
