]> andersk Git - nss_nonlocal.git/commitdiff
Initial release. 1.0
authorAnders Kaseorg <andersk@mit.edu>
Tue, 17 Jul 2007 06:13:25 +0000 (02:13 -0400)
committerAnders Kaseorg <andersk@mit.edu>
Sun, 27 Jul 2008 13:20:52 +0000 (09:20 -0400)
Signed-off-by: Anders Kaseorg <andersk@mit.edu>
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
nonlocal-group.c [new file with mode: 0644]
nonlocal-passwd.c [new file with mode: 0644]
nonlocal-shadow.c [new file with mode: 0644]
nonlocal.h [new file with mode: 0644]
nsswitch-internal.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..58e185f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+exec_prefix = /
+libdir = $(exec_prefix)/lib
+
+INSTALL = install
+CC = gcc
+CFLAGS = -O2 -Wall
+LD = ld
+
+ALL_CFLAGS = $(CFLAGS) -fPIC
+ALL_LDFLAGS = $(LDFLAGS) -shared -Wl,-x
+
+all: libnss_nonlocal.so.2 linktest
+
+libnss_nonlocal.so.2: nonlocal-passwd.o nonlocal-group.o nonlocal-shadow.o
+       $(CC) -o $@ $(ALL_LDFLAGS) -Wl,-soname,$@ $^ $(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
+       $(LD) --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 test-nonlocal
+
+.PHONY: all linktest install clean
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..c0b0eee
--- /dev/null
+++ b/README
@@ -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
diff --git a/nonlocal-group.c b/nonlocal-group.c
new file mode 100644 (file)
index 0000000..9071fe1
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ * nonlocal-group.c
+ * group 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 <syslog.h>
+#include <errno.h>
+#include <grp.h>
+#include <nss.h>
+#include "nsswitch-internal.h"
+#include "nonlocal.h"
+
+
+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;
+}
+
+
+static __thread int local_only = 0;
+
+enum nss_status
+local_getgrgid_r(gid_t gid, struct group *grp,
+                char *buffer, size_t buflen, int *errnop)
+{
+    int old_local_only = local_only;
+    int old_errno = errno;
+    int ret;
+    errno = *errnop;
+    local_only = 1;
+
+    ret = getgrgid_r(gid, grp, buffer, buflen, &grp);
+
+    local_only = old_local_only;
+    *errnop = errno;
+    errno = old_errno;
+
+    if (grp != NULL)
+       return NSS_STATUS_SUCCESS;
+    else if (ret == 0)
+       return NSS_STATUS_NOTFOUND;
+    else
+       return NSS_STATUS_TRYAGAIN;
+}
+
+enum nss_status
+check_nonlocal_gid(const char *user, gid_t gid, int *errnop)
+{
+    struct group local_grp;
+    int local_errno = errno;
+    enum nss_status local_status, status = NSS_STATUS_SUCCESS;
+    int local_buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+    char *local_buffer = malloc(local_buflen);
+    if (local_buffer == NULL) {
+       *errnop = ENOMEM;
+       errno = local_errno;
+       return NSS_STATUS_TRYAGAIN;
+    }
+    local_errno = 0;
+    local_status = local_getgrgid_r(gid, &local_grp, local_buffer,
+                                   local_buflen, &local_errno);
+    if (local_status == NSS_STATUS_SUCCESS) {
+       syslog(LOG_WARNING, "nss_nonlocal: removing local group %u (%s) from non-local user %s\n", local_grp.gr_gid, local_grp.gr_name, user);
+       status = NSS_STATUS_NOTFOUND;
+    } else if (local_status != NSS_STATUS_NOTFOUND &&
+              local_status != NSS_STATUS_UNAVAIL) {
+       *errnop = local_errno;
+       status = local_status;
+    }
+    free(local_buffer);
+    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;
+    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;
+
+    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;
+
+    if (local_only == 1)
+       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;
+    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;
+
+       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;
+}
diff --git a/nonlocal-passwd.c b/nonlocal-passwd.c
new file mode 100644 (file)
index 0000000..cebc819
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * nonlocal-passwd.c
+ * passwd 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 <syslog.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <nss.h>
+#include "nsswitch-internal.h"
+#include "nonlocal.h"
+
+
+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;
+}
+
+
+static __thread int local_only = 0;
+
+enum nss_status
+local_getpwuid_r(uid_t uid, struct passwd *pwd,
+                char *buffer, size_t buflen, int *errnop)
+{
+    int old_local_only = local_only;
+    int old_errno = errno;
+    int ret;
+    errno = *errnop;
+    local_only = 1;
+
+    ret = getpwuid_r(uid, pwd, buffer, buflen, &pwd);
+
+    local_only = old_local_only;
+    *errnop = errno;
+    errno = old_errno;
+
+    if (pwd != NULL)
+       return NSS_STATUS_SUCCESS;
+    else if (ret == 0)
+       return NSS_STATUS_NOTFOUND;
+    else
+       return NSS_STATUS_TRYAGAIN;
+}
+
+enum nss_status
+check_nonlocal_uid(const char *user, uid_t uid, int *errnop)
+{
+    struct passwd local_pwd;
+    int local_errno = errno;
+    enum nss_status local_status, status = NSS_STATUS_SUCCESS;
+    int local_buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+    char *local_buffer = malloc(local_buflen);
+    if (local_buffer == NULL) {
+       *errnop = ENOMEM;
+       errno = local_errno;
+       return NSS_STATUS_TRYAGAIN;
+    }
+    local_errno = 0;
+    local_status = local_getpwuid_r(uid, &local_pwd, local_buffer,
+                                   local_buflen, &local_errno);
+    if (local_status == NSS_STATUS_SUCCESS) {
+       syslog(LOG_ERR, "nss_nonlocal: possible spoofing attack: non-local user %s has same UID as local user %s!\n", user, local_pwd.pw_name);
+       status = NSS_STATUS_NOTFOUND;
+    } else if (local_status != NSS_STATUS_NOTFOUND &&
+              local_status != NSS_STATUS_UNAVAIL) {
+       *errnop = local_errno;
+       status = local_status;
+    }
+    free(local_buffer);
+    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;
+    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;
+
+    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;
+
+    if (local_only == 1)
+       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;
+}
diff --git a/nonlocal-shadow.c b/nonlocal-shadow.c
new file mode 100644 (file)
index 0000000..6524ee8
--- /dev/null
@@ -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;
+}
diff --git a/nonlocal.h b/nonlocal.h
new file mode 100644 (file)
index 0000000..c583bf4
--- /dev/null
@@ -0,0 +1,5 @@
+#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);
+#endif /* NON_LOCAL_H */
diff --git a/nsswitch-internal.h b/nsswitch-internal.h
new file mode 100644 (file)
index 0000000..94ed968
--- /dev/null
@@ -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 */
This page took 0.112416 seconds and 5 git commands to generate.