فهرست منبع

setgroups() is not as easy afterall, steals some hacks from glibc to handle group limits

Ask Solem 13 سال پیش
والد
کامیت
ff9ce794e3
1فایلهای تغییر یافته به همراه55 افزوده شده و 12 حذف شده
  1. 55 12
      celery/platforms.py

+ 55 - 12
celery/platforms.py

@@ -283,7 +283,9 @@ def detached(logfile=None, pidfile=None, uid=None, gid=None, umask=0,
     workdir = os.getcwd() if workdir is None else workdir
 
     signals.reset("SIGCLD")  # Make sure SIGCLD is using the default handler.
-    set_effective_user(uid=uid, gid=gid)
+    if not os.geteuid():
+        # no point trying to setuid unless we're root.
+        maybe_drop_privileges(uid=uid, gid=gid)
 
     # Since without stderr any errors will be silently suppressed,
     # we need to know that we have access to the logfile.
@@ -330,12 +332,49 @@ def parse_gid(gid):
         raise
 
 
-def setgroups(uid):
+def _setgroups_hack(groups):
+    """:fun:`setgroups` may have a platform-dependent limit,
+    and it is not always possible to know in advance what this limit
+    is, so we use this ugly hack stolen from glibc."""
+    groups = groups[:]
+
+    while 1:
+        try:
+            return os.setgroups(groups)
+        except ValueError:   # error from Python's check.
+            if len(groups) <= 1:
+                raise
+            groups[:] = groups[:-1]
+        except OSError, exc:  # error from the OS.
+            if exc.errno != errno.EINVAL or len(groups) <= 1:
+                raise
+            groups[:] = groups[:-1]
+
+
+def setgroups(groups):
+    max_groups = None
+    try:
+        max_groups = os.sysconf("SC_NGROUPS_MAX")
+    except:
+        pass
+    try:
+        return _setgroups_hack(groups[:max_groups])
+    except OSError, exc:
+        if exc.errno != errno.EPERM:
+            raise
+        if any(group not in groups for group in os.getgroups()):
+            # we shouldn't be allowed to change to this group.
+            raise
+
+
+def initgroups(uid, gid):
     if grp and pwd:
-        user_name = pwd.getpwuid(uid)[0]
-        user_groups = [gr.gr_gid for gr in grp.getgrall()
-                                                if user_name in gr.gr_mem]
-        os.setgroups(user_groups)
+        username = pwd.getpwuid(uid)[0]
+        if hasattr(os, "initgroups"):  # Python 2.7+
+            return os.initgroups(username, gid)
+        groups = [gr.gr_gid for gr in grp.getgrall()
+                                if username in gr.gr_mem]
+        setgroups(groups)
 
 
 def setegid(gid):
@@ -360,15 +399,15 @@ def setuid(uid):
     os.setuid(parse_uid(uid))
 
 
-def set_effective_user(uid=None, gid=None):
+def maybe_drop_privileges(uid=None, gid=None):
     """Change process privileges to new user/group.
 
-    If UID and GID is set the effective user/group is set.
+    If UID and GID is specified, the real user/group is changed.
 
-    If only UID is set, the effective user is set, and the group is
-    set to the users primary group.
+    If only UID is specified, the real user is changed, and the group is
+    changed to the users primary group.
 
-    If only GID is set, the effective group is set.
+    If only GID is specified, only the group is changed.
 
     """
     uid = uid and parse_uid(uid)
@@ -378,8 +417,12 @@ def set_effective_user(uid=None, gid=None):
         # If GID isn't defined, get the primary GID of the user.
         if not gid and pwd:
             gid = pwd.getpwuid(uid).pw_gid
+        # Must set the GID before initgroups(), as setgid()
+        # is known to zap the group list on some platforms.
         setgid(gid)
-        setgroups(uid)
+        initgroups(uid, gid)
+
+        # at last:
         setuid(uid)
     else:
         gid and setgid(gid)