Fix bug #7067 - Linux asynchronous IO (aio) can cause smbd to fail to respond to...
authorJeremy Allison <jra@samba.org>
Wed, 27 Jan 2010 00:51:57 +0000 (16:51 -0800)
committerJeremy Allison <jra@samba.org>
Wed, 27 Jan 2010 00:51:57 +0000 (16:51 -0800)
Only works on Linux kernels 2.6.26 and above. Grants CAP_KILL capability
to allow Linux threads under different euids to send signals to each other.

Jeremy.

source3/include/smb.h
source3/lib/system.c
source3/smbd/server.c

index bc7a90d5492911bcf418e894dbd81a24b35c69de..041c96badad0c96a1ff70d8f795b7ed4072f1d52 100644 (file)
@@ -1723,7 +1723,8 @@ minimum length == 24.
 enum smbd_capability {
     KERNEL_OPLOCK_CAPABILITY,
     DMAPI_ACCESS_CAPABILITY,
-    LEASE_CAPABILITY
+    LEASE_CAPABILITY,
+    KILL_CAPABILITY
 };
 
 /*
index a58d9037a79dc54964c4c72b1688ae95152d501c..9c1da3a78bdcdf301883cc74fcdc6bfcb00a5834 100644 (file)
@@ -883,6 +883,11 @@ char *sys_getwd(char *s)
 
 #if defined(HAVE_POSIX_CAPABILITIES)
 
+/* This define hasn't made it into the glibc capabilities header yet. */
+#ifndef SECURE_NO_SETUID_FIXUP
+#define SECURE_NO_SETUID_FIXUP          2
+#endif
+
 /**************************************************************************
  Try and abstract process capabilities (for systems that have them).
 ****************************************************************************/
@@ -913,6 +918,32 @@ static bool set_process_capability(enum smbd_capability capability,
        }
 #endif
 
+#if defined(HAVE_PRCTL) && defined(PR_SET_SECUREBITS) && defined(SECURE_NO_SETUID_FIXUP)
+        /* New way of setting capabilities as "sticky". */
+
+       /*
+        * Use PR_SET_SECUREBITS to prevent setresuid()
+        * atomically dropping effective capabilities on
+        * uid change. Only available in Linux kernels
+        * 2.6.26 and above.
+        *
+        * See here:
+        * http://www.kernel.org/doc/man-pages/online/pages/man7/capabilities.7.html
+        * for details.
+        *
+        * Specifically the CAP_KILL capability we need
+        * to allow Linux threads under different euids
+        * to send signals to each other.
+        */
+
+       if (prctl(PR_SET_SECUREBITS, 1 << SECURE_NO_SETUID_FIXUP)) {
+               DEBUG(0,("set_process_capability: "
+                       "prctl PR_SET_SECUREBITS failed with error %s\n",
+                       strerror(errno) ));
+               return false;
+       }
+#endif
+
        cap = cap_get_proc();
        if (cap == NULL) {
                DEBUG(0,("set_process_capability: cap_get_proc failed: %s\n",
@@ -939,6 +970,11 @@ static bool set_process_capability(enum smbd_capability capability,
                case LEASE_CAPABILITY:
 #ifdef CAP_LEASE
                        cap_vals[num_cap_vals++] = CAP_LEASE;
+#endif
+                       break;
+               case KILL_CAPABILITY:
+#ifdef CAP_KILL
+                       cap_vals[num_cap_vals++] = CAP_KILL;
 #endif
                        break;
        }
@@ -950,16 +986,37 @@ static bool set_process_capability(enum smbd_capability capability,
                return True;
        }
 
-       cap_set_flag(cap, CAP_EFFECTIVE, num_cap_vals, cap_vals,
-               enable ? CAP_SET : CAP_CLEAR);
+       /*
+        * Ensure the capability is effective. We assume that as a root
+        * process it's always permitted.
+        */
+
+       if (cap_set_flag(cap, CAP_EFFECTIVE, num_cap_vals, cap_vals,
+                       enable ? CAP_SET : CAP_CLEAR) == -1) {
+               DEBUG(0, ("set_process_capability: cap_set_flag effective "
+                       "failed (%d): %s\n",
+                       (int)capability,
+                       strerror(errno)));
+               cap_free(cap);
+               return false;
+       }
 
        /* We never want to pass capabilities down to our children, so make
         * sure they are not inherited.
         */
-       cap_set_flag(cap, CAP_INHERITABLE, num_cap_vals, cap_vals, CAP_CLEAR);
+       if (cap_set_flag(cap, CAP_INHERITABLE, num_cap_vals,
+                       cap_vals, CAP_CLEAR) == -1) {
+               DEBUG(0, ("set_process_capability: cap_set_flag inheritable "
+                       "failed (%d): %s\n",
+                       (int)capability,
+                       strerror(errno)));
+               cap_free(cap);
+               return false;
+       }
 
        if (cap_set_proc(cap) == -1) {
-               DEBUG(0, ("set_process_capability: cap_set_proc failed: %s\n",
+               DEBUG(0, ("set_process_capability: cap_set_flag (%d) failed: %s\n",
+                       (int)capability,
                        strerror(errno)));
                cap_free(cap);
                return False;
index 09ad8d8ea55af057d8fea68b266929d1c5b634d2..fb0efd2ae5202ed5315e3cd4b972e8783c89e6fb 100644 (file)
@@ -1047,6 +1047,14 @@ extern void build_options(bool screen);
        gain_root_privilege();
        gain_root_group_privilege();
 
+       /*
+        * Ensure we have CAP_KILL capability set on Linux,
+        * where we need this to communicate with threads.
+        * This is inherited by new threads, but not by new
+        * processes across exec().
+        */
+       set_effective_capability(KILL_CAPABILITY);
+
        fault_setup((void (*)(void *))exit_server_fault);
        dump_core_setup("smbd");