support-darwin-initgroups-syscall   [plain text]


Index: samba/source/lib/system.c
===================================================================
--- samba/source/lib/system.c.orig
+++ samba/source/lib/system.c
@@ -27,6 +27,10 @@
 #include <sys/prctl.h>
 #endif
 
+#if defined(HAVE_DARWIN_INITGROUPS)
+#include <sys/syscall.h>
+#endif
+
 /*
    The idea is that this file will eventually have wrappers around all
    important system calls in samba. The aims are:
@@ -944,14 +948,40 @@ int sys_getgroups(int setlen, gid_t *gid
 /**************************************************************************
  Wrapper for setgroups. Deals with broken (int) case. Automatically used
  if we have broken getgroups.
+
+ The gmuid argument is the uid of the user who is associated with the group
+ list we are setting. It is only used for Darwin.
 ****************************************************************************/
 
-int sys_setgroups(int setlen, gid_t *gidset)
+int sys_setgroups(uid_t gmuid, int setlen, gid_t *gidset)
 {
-#if !defined(HAVE_SETGROUPS)
+
+#if defined(HAVE_DARWIN_INITGROUPS)
+	/* The Darwin groups implementation is a little unusual. The list of
+	 * groups in the kernel credential is not exhaustive, but rather is a
+	 * cache. The full group list is held in userspace and checked on
+	 * dynamically.
+	 * This is an optional mechanism, and setgroups(2) opts out
+	 * of it. That is, if you call setgroups, then the list of groups you
+	 * set are the only groups that are ever checked. This is not what we
+	 * want. We want to opt in to the dynamic resolution mechanism, so we
+	 * need to specify the uid of the user whose group list (cache) we are
+	 * setting.
+	 *
+	 * The Darwin rules are:
+	 *  1. Thou shalt setegid, initgroups and seteuid IN THAT ORDER
+	 *  2. Thou shalt not pass more that NGROUPS_MAX to initgroups
+	 *  3. Thou shalt leave the first entry in the groups list well alone
+	 */
+	int maxgroups = sysconf(_SC_NGROUPS_MAX);
+	if (setlen > maxgroups) {
+	    setlen = maxgroups;
+	}
+	return syscall(SYS_initgroups, setlen, gidset, gmuid);
+
+#elif !defined(HAVE_SETGROUPS)
 	errno = ENOSYS;
 	return -1;
-#endif /* HAVE_SETGROUPS */
 
 #if !defined(HAVE_BROKEN_GETGROUPS)
 	return setgroups(setlen, gidset);
@@ -991,6 +1021,7 @@ int sys_setgroups(int setlen, gid_t *gid
 	SAFE_FREE(group_list);
 	return 0 ;
 #endif /* HAVE_BROKEN_GETGROUPS */
+#endif /* HAVE_SETGROUPS */
 }
 
 /**************************************************************************
Index: samba/source/smbd/sec_ctx.c
===================================================================
--- samba/source/smbd/sec_ctx.c.orig
+++ samba/source/smbd/sec_ctx.c
@@ -245,9 +245,8 @@ void set_sec_ctx(uid_t uid, gid_t gid, i
 
 	gain_root();
 
-#ifdef HAVE_SETGROUPS
-	sys_setgroups(ngroups, groups);
-#endif
+	become_gid(gid);
+	sys_setgroups(uid, ngroups, groups);
 
 	ctx_p->ut.ngroups = ngroups;
 
@@ -277,7 +276,7 @@ void set_sec_ctx(uid_t uid, gid_t gid, i
 		ctx_p->token = NULL;
 	}
 
-	become_id(uid, gid);
+	become_uid(uid);
 
 	ctx_p->ut.uid = uid;
 	ctx_p->ut.gid = gid;
@@ -338,11 +337,11 @@ BOOL pop_sec_ctx(void)
 
 	prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
 
-#ifdef HAVE_SETGROUPS
-	sys_setgroups(prev_ctx_p->ut.ngroups, prev_ctx_p->ut.groups);
-#endif
+	become_gid(prev_ctx_p->ut.gid);
+	sys_setgroups(prev_ctx_p->ut.uid,
+		prev_ctx_p->ut.ngroups, prev_ctx_p->ut.groups);
 
-	become_id(prev_ctx_p->ut.uid, prev_ctx_p->ut.gid);
+	become_uid(prev_ctx_p->ut.uid);
 
 	/* Update current_user stuff */