]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
util: add virGetGroupList
authorEric Blake <eblake@redhat.com>
Tue, 21 May 2013 23:47:48 +0000 (17:47 -0600)
committerEric Blake <eblake@redhat.com>
Mon, 22 Jul 2013 16:25:48 +0000 (10:25 -0600)
https://bugzilla.redhat.com/show_bug.cgi?id=964358

Since neither getpwuid_r() nor initgroups() are safe to call in
between fork and exec (they obtain a mutex, but if some other
thread in the parent also held the mutex at the time of the fork,
the child will deadlock), we have to split out the functionality
that is unsafe.  At least glibc's initgroups() uses getgrouplist
under the hood, so the ideal split is to expose getgrouplist for
use before a fork.  Gnulib already gives us a nice wrapper via
mgetgroups; we wrap it once more to look up by uid instead of name.

* bootstrap.conf (gnulib_modules): Add mgetgroups.
* src/util/virutil.h (virGetGroupList): New declaration.
* src/util/virutil.c (virGetGroupList): New function.
* src/libvirt_private.syms (virutil.h): Export it.

Signed-off-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit 75c125641ac73473ba4b0542524d67a184769c8e)

Conflicts:
bootstrap.conf - not updating gnulib submodule...
configure.ac - ...so checking for getgrouplist by hand...
src/util/virutil.c - ...and copying only the getgrouplist implementation rather than calling the gnulib function

configure.ac
src/libvirt_private.syms
src/util/virutil.c
src/util/virutil.h

index 2765f291d1ff18b6889cafbcae7f31292d306d7d..dac89f338dd8969dded8d3433229fcc55fc5b021 100644 (file)
@@ -192,7 +192,7 @@ AC_CHECK_SIZEOF([long])
 
 dnl Availability of various common functions (non-fatal if missing),
 dnl and various less common threadsafe functions
-AC_CHECK_FUNCS_ONCE([cfmakeraw geteuid getgid getgrnam_r getmntent_r \
+AC_CHECK_FUNCS_ONCE([cfmakeraw geteuid getgid getgrnam_r getgrouplist getmntent_r \
   getpwuid_r getuid initgroups kill mmap newlocale posix_fallocate \
   posix_memalign prlimit regexec sched_getaffinity setns setrlimit symlink])
 
index df5b16590e0918988aa33953833103e01b4a7b3d..bf3b426c78e266bd615bbeb239516a172ceb2142 100644 (file)
@@ -1889,6 +1889,7 @@ virGetDeviceID;
 virGetDeviceUnprivSGIO;
 virGetFCHostNameByWWN;
 virGetGroupID;
+virGetGroupList;
 virGetGroupName;
 virGetHostname;
 virGetUnprivSGIOSysfsPath;
index 906687f4d9e5797c1315e8d3330f6b1d20ea099a..735e4c92dc302ebd098f36d0bfc58accfce8a9de 100644 (file)
@@ -2675,6 +2675,68 @@ virGetGroupID(const char *group, gid_t *gid)
     return 0;
 }
 
+
+/* Compute the list of supplementary groups associated with @uid, and
+ * including @gid in the list (unless it is -1), storing a malloc'd
+ * result into @list.  Return the size of the list on success, or -1
+ * on failure with error reported and errno set. May not be called
+ * between fork and exec. */
+int
+virGetGroupList(uid_t uid, gid_t gid, gid_t **list)
+{
+    int ret = -1;
+    char *user = NULL;
+
+    *list = NULL;
+    if (uid == (uid_t)-1)
+        return 0;
+
+    if (virGetUserEnt(uid, &user,
+                      gid == (gid_t)-1 ? &gid : NULL, NULL) < 0)
+        return -1;
+
+# if HAVE_GETGROUPLIST
+    /* Borrowing from gnulib's LGPLv2+ mgetgroups.c as of July 2013. */
+    /* Avoid a bug in older glibc with size 0, by pre-allocating a
+     * list size and then enlarging if needed.  */
+    int max = 10;
+    if (VIR_ALLOC_N(*list, max) < 0)
+        goto no_memory;
+    while (1)
+    {
+        int ngroups;
+        int last = max;
+
+        ngroups = getgrouplist(user, gid, *list, &max);
+
+        /* Avoid a bug in Darwin where max is not increased.  */
+        if (ngroups < 0 && last == max)
+            max *= 2;
+        if (VIR_REALLOC_N(*list, max) < 0) {
+            VIR_FREE(*list);
+            goto no_memory;
+        }
+        if (0 <= ngroups) {
+            ret = ngroups;
+            break;
+        }
+    }
+# else
+    if (VIR_ALLOC_N(*list, 1) < 0)
+        goto no_memory;
+    (*list)[0] = gid;
+    ret = 1;
+# endif
+
+cleanup:
+    VIR_FREE(user);
+    return ret;
+no_memory:
+    virReportOOMError();
+    goto cleanup;
+}
+
+
 /* Set the real and effective uid and gid to the given values, and call
  * initgroups so that the process has all the assumed group membership of
  * that uid. return 0 on success, -1 on failure (the original system error
index 39033db780c9bea4cf786b6af7cda7d7400fca2e..d4a57848ec0388f0c6921298a4dca9b9828ccb7e 100644 (file)
@@ -268,6 +268,8 @@ char *virGetUserCacheDirectory(void);
 char *virGetUserRuntimeDirectory(void);
 char *virGetUserName(uid_t uid);
 char *virGetGroupName(gid_t gid);
+int virGetGroupList(uid_t uid, gid_t group, gid_t **groups)
+    ATTRIBUTE_NONNULL(3);
 int virGetUserID(const char *name,
                  uid_t *uid) ATTRIBUTE_RETURN_CHECK;
 int virGetGroupID(const char *name,