add alternate backend support for getgrouplist
authorJosiah Worcester <josiahw@gmail.com>
Mon, 16 Mar 2015 00:20:53 +0000 (19:20 -0500)
committerRich Felker <dalias@aerifal.cx>
Mon, 16 Mar 2015 02:32:22 +0000 (22:32 -0400)
This completes the alternate backend support that was previously added
to the getpw* and getgr* functions. Unlike those, though, it
unconditionally queries nscd. Any groups from nscd that aren't in the
/etc/groups file are added to the returned list, and any that are
present in the file are ignored. The purpose of this behavior is to
provide a view of the group database consistent with what is observed
by the getgr* functions. If group memberships reported by nscd were
honored when the corresponding group already has a definition in the
/etc/groups file, the user's getgrouplist-based membership in the
group would conflict with their non-membership in the reported
gr_mem[] for the group.

The changes made also make getgrouplist thread-safe and eliminate its
clobbering of the global getgrent state.

src/misc/getgrouplist.c [deleted file]
src/passwd/getgrouplist.c [new file with mode: 0644]
src/passwd/nscd.h

diff --git a/src/misc/getgrouplist.c b/src/misc/getgrouplist.c
deleted file mode 100644 (file)
index c9a8f09..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#define _GNU_SOURCE
-#include <grp.h>
-#include <string.h>
-#include <limits.h>
-
-int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
-{
-       size_t n, i;
-       struct group *gr;
-       if (*ngroups<1) return -1;
-       n = *ngroups;
-       *groups++ = gid;
-       *ngroups = 1;
-
-       setgrent();
-       while ((gr = getgrent()) && *ngroups < INT_MAX) {
-               for (i=0; gr->gr_mem[i] && strcmp(user, gr->gr_mem[i]); i++);
-               if (!gr->gr_mem[i]) continue;
-               if (++*ngroups <= n) *groups++ = gr->gr_gid;
-       }
-       endgrent();
-
-       return *ngroups > n ? -1 : *ngroups;
-}
diff --git a/src/passwd/getgrouplist.c b/src/passwd/getgrouplist.c
new file mode 100644 (file)
index 0000000..0fddc9a
--- /dev/null
@@ -0,0 +1,80 @@
+#define _GNU_SOURCE
+#include "pwf.h"
+#include <grp.h>
+#include <string.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <byteswap.h>
+#include <errno.h>
+#include "nscd.h"
+
+int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
+{
+       int rv, nlim, ret = -1;
+       ssize_t i, n = 1;
+       struct group gr;
+       struct group *res;
+       FILE *f;
+       int swap = 0;
+       int32_t resp[INITGR_LEN];
+       uint32_t *nscdbuf = 0;
+       char *buf = 0;
+       char **mem = 0;
+       size_t nmem = 0;
+       size_t size;
+       nlim = *ngroups;
+       if (nlim >= 1) *groups++ = gid;
+
+       f = __nscd_query(GETINITGR, user, resp, sizeof resp, &swap);
+       if (!f) goto cleanup;
+       if (f != (FILE*)-1 && resp[INITGRFOUND]) {
+               nscdbuf = calloc(resp[INITGRNGRPS], sizeof(uint32_t));
+               if (!nscdbuf) goto cleanup;
+               if (!fread(nscdbuf, sizeof(*nscdbuf)*resp[INITGRNGRPS], 1, f)) {
+                       if (!ferror(f)) errno = EIO;
+                       goto cleanup;
+               }
+               if (swap) {
+                       for (i = 0; i < resp[INITGRNGRPS]; i++)
+                               nscdbuf[i] = bswap_32(nscdbuf[i]);
+               }
+       }
+       if (f != (FILE*)-1) fclose(f);
+
+       f = fopen("/etc/group", "rbe");
+       if (!f && errno != ENOENT && errno != ENOTDIR)
+               goto cleanup;
+
+       if (f) {
+               while (!(rv = __getgrent_a(f, &gr, &buf, &size, &mem, &nmem, &res)) && res) {
+                       if (nscdbuf)
+                               for (i=0; i < resp[INITGRNGRPS]; i++) {
+                                       if (nscdbuf[i] == gr.gr_gid) nscdbuf[i] = gid;
+                               }
+                       for (i=0; gr.gr_mem[i] && strcmp(user, gr.gr_mem[i]); i++);
+                       if (!gr.gr_mem[i]) continue;
+                       if (++n <= nlim) *groups++ = gr.gr_gid;
+               }
+               if (rv) {
+                       errno = rv;
+                       goto cleanup;
+               }
+       }
+       if (nscdbuf) {
+               for(i=0; i < resp[INITGRNGRPS]; i++) {
+                       if (nscdbuf[i] != gid)
+                               if(++n <= nlim) *groups++ = nscdbuf[i];
+               }
+       }
+
+       ret = n > nlim ? -1 : n;
+       *ngroups = n;
+
+cleanup:
+       if (f) fclose(f);
+       free(nscdbuf);
+       free(buf);
+       free(mem);
+       return ret;
+}
index 102f0b4b284691a924e858bd633c3b8128371c06..9a53c3283338de530eda6af1a8a8e612178fa0d4 100644 (file)
@@ -8,6 +8,7 @@
 #define GETPWBYUID 1
 #define GETGRBYNAME 2
 #define GETGRBYGID 3
+#define GETINITGR 15
 
 #define REQVERSION 0
 #define REQTYPE 1
 #define GRMEMCNT 5
 #define GR_LEN 6
 
+#define INITGRVERSION 0
+#define INITGRFOUND 1
+#define INITGRNGRPS 2
+#define INITGR_LEN 3
+
 FILE *__nscd_query(int32_t req, const char *key, int32_t *buf, size_t len, int *swap);
 
 #endif