Package: libapache2-mpm-itk
Version: 2.4.6-01-1
Severity: important

Dear Maintainer,

The current version of mpm-itk breaks code attempting to do legitimate setuid-type operations because of the seccomp BPF protections.

The reason stems from the fact that -1 is a permitted argument for syscalls that change multiple ID's at once, such as setresuid, to which (uid_t) -1 can be passed to indicate "don't change this value."

Since (uid_t) -1 == 4294967295 for everything except the old 16-bit i386 syscalls, these do-nothing values well exceed the max_uid = 65535, and so these calls are blocked. Thus code which *should* be allowed (such as setresuid(-1, currentid, -1) return EPERM.

In practice, this breaks anything that relies on uid changes that *should* be permitted: in my case, a cgi script that invokes ssh fails because ssh calls something with a -1 argument.

The attached patch updates the filters to extend the syscall argument limits by allowing a specific value outside that min...max range, and -1 is passed for this value where needed. Thus in effect the uid/gid syscall limits now allow anything in [min, max], and also -1.

With the patch, -1 is allowed for all the arguments of the blocked syscalls except for __NR_setuid/__NR_setgid (and the ...32 version on i386): -1 isn't a special value there. -1 isn't special for __NR_setfsuid (and ...gid), either, but since man setfsuid specifically suggests calling setfsuid a second time (with -1 as an argument) to detect failures, I allowed it for those syscalls, too.

(I also had to reorganize the various limit_syscall_range code because the -1 value ((__u16) -1 == 65535) for the i386, 16-bit __NR_set*uid calls doesn't coincide with the ((uid_t) -1 == 4294967295) value of non-i386's __NR_set*uid syscalls.)

I tested the code (on amd64) by extracting the capabilities and BPF filter code into a test program, to make sure it was working, and indeed it is: after the limits, all the restricted functions are indeed restricted, while the various seteuid and related failures are now fixed.


Jason Rhinelander
--- mpm-itk-2.4.6-01.orig/seccomp.c	2013-07-10 07:22:20.000000000 -0400
+++ mpm-itk-2.4.6-01/seccomp.c	2014-02-07 17:16:55.139802820 -0500
@@ -95,7 +95,7 @@
     ++*pos;
 }
 
-static int limit_syscall_range(int syscall_to_match, int nr_args, int min, int max)
+static int limit_syscall_range(int syscall_to_match, int nr_args, __u32 min, __u32 max, __u32 or_eq)
 {
     static struct sock_filter syscall_filter[BPF_MAXINSNS];
 
@@ -108,6 +108,8 @@
 
     for (int i = 0; i < nr_args; ++i) {
         add_bpf_stmt(syscall_filter, &pos, BPF_LD + BPF_W + BPF_ABS, syscall_arg(i));
+        if (or_eq < min || or_eq > max)
+            add_bpf_jump(syscall_filter, &pos, BPF_JMP + BPF_JEQ + BPF_K, or_eq, 4, 0);
         add_bpf_jump(syscall_filter, &pos, BPF_JMP + BPF_JGE + BPF_K, min, 1, 0);
         add_bpf_stmt(syscall_filter, &pos, BPF_RET + BPF_K, SECCOMP_RET_ERRNO | EPERM);
         add_bpf_jump(syscall_filter, &pos, BPF_JMP + BPF_JGT + BPF_K, max, 0, 1);
@@ -128,6 +130,11 @@
     gid_t min_gid16 = (min_gid > 65535) ? 65535 : min_gid;
     gid_t max_gid16 = (max_gid > 65535) ? 65535 : max_gid;
 
+    uid_t minus_one = (uid_t) -1;
+#ifdef __i386__
+    __u16 minus_one16 = (__u16) -1;
+#endif // defined(__i386__)
+
     /* Apply a seccomp BPF to ourselves that disallows all setuid- and
      * setgid-like calls if the first argument is 0.  The list of calls comes from
      * the descriptions of CAP_SETUID and CAP_SETGID in capabilities(7), although
@@ -146,29 +153,43 @@
     if (apply_seccomp_filter(arch_filter, sizeof(arch_filter) / sizeof(arch_filter[0])) != 0) {
         return;
     }
-#ifdef __i386__
-    limit_syscall_range(__NR_setfsuid32, 1, min_uid, max_uid);
-    limit_syscall_range(__NR_setuid32, 1, min_uid, max_uid);
-    limit_syscall_range(__NR_setreuid32, 2, min_uid, max_uid);
-    limit_syscall_range(__NR_setresuid32, 3, min_uid, max_uid);
-#endif  // defined(__i386__)
-
-    limit_syscall_range(__NR_setfsuid, 1, min_uid16, max_uid16);
-    limit_syscall_range(__NR_setuid, 1, min_uid16, max_uid16);
-    limit_syscall_range(__NR_setreuid, 2, min_uid16, max_uid16);
-    limit_syscall_range(__NR_setresuid, 3, min_uid16, max_uid16);
 
 #ifdef __i386__
-    limit_syscall_range(__NR_setfsgid32, 1, min_gid, max_gid);
-    limit_syscall_range(__NR_setgid32, 1, min_gid, max_gid);
-    limit_syscall_range(__NR_setregid32, 2, min_gid, max_gid);
-    limit_syscall_range(__NR_setresgid32, 3, min_gid, max_gid);
-#endif  // defined(__i386__)
-
-    limit_syscall_range(__NR_setfsgid, 1, min_gid16, max_gid16);
-    limit_syscall_range(__NR_setgid, 1, min_gid16, max_gid16);
-    limit_syscall_range(__NR_setregid, 2, min_gid16, max_gid16);
-    limit_syscall_range(__NR_setresgid, 3, min_gid16, max_gid16);
+    /* Newer, 32-bit uid_t/gid_t syscalls: */
+    limit_syscall_range(__NR_setfsuid32, 1, min_uid, max_uid, minus_one);
+    limit_syscall_range(__NR_setuid32, 1, min_uid, max_uid, min_uid);
+    limit_syscall_range(__NR_setreuid32, 2, min_uid, max_uid, minus_one);
+    limit_syscall_range(__NR_setresuid32, 3, min_uid, max_uid, minus_one);
+
+    limit_syscall_range(__NR_setfsgid32, 1, min_gid, max_gid, minus_one);
+    limit_syscall_range(__NR_setgid32, 1, min_gid, max_gid, min_gid);
+    limit_syscall_range(__NR_setregid32, 2, min_gid, max_gid, minus_one);
+    limit_syscall_range(__NR_setresgid32, 3, min_gid, max_gid, minus_one);
+
+    /* Older 16-bit old_uid_t/old_gid_t syscalls: */
+    limit_syscall_range(__NR_setfsuid, 1, min_uid16, max_uid16, minus_one16);
+    limit_syscall_range(__NR_setuid, 1, min_uid16, max_uid16, min_uid16);
+    limit_syscall_range(__NR_setreuid, 2, min_uid16, max_uid16, minus_one16);
+    limit_syscall_range(__NR_setresuid, 3, min_uid16, max_uid16, minus_one16);
+
+    limit_syscall_range(__NR_setfsgid, 1, min_gid16, max_gid16, minus_one16);
+    limit_syscall_range(__NR_setgid, 1, min_gid16, max_gid16, min_gid16);
+    limit_syscall_range(__NR_setregid, 2, min_gid16, max_gid16, minus_one16);
+    limit_syscall_range(__NR_setresgid, 3, min_gid16, max_gid16, minus_one16);
+
+#else // not defined(__i386__)
+    /* Just one set of 32-bit uid_t/gid_t syscalls to worry about: */
+    limit_syscall_range(__NR_setfsuid, 1, min_uid, max_uid, minus_one);
+    limit_syscall_range(__NR_setuid, 1, min_uid, max_uid, min_uid);
+    limit_syscall_range(__NR_setreuid, 2, min_uid, max_uid, minus_one);
+    limit_syscall_range(__NR_setresuid, 3, min_uid, max_uid, minus_one);
+
+    limit_syscall_range(__NR_setfsgid, 1, min_gid, max_gid, minus_one);
+    limit_syscall_range(__NR_setgid, 1, min_gid, max_gid, min_gid);
+    limit_syscall_range(__NR_setregid, 2, min_gid, max_gid, minus_one);
+    limit_syscall_range(__NR_setresgid, 3, min_gid, max_gid, minus_one);
+#endif
+
 #else
     ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
                  "Your platform or architecture does not support seccomp v2; "

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

Reply via email to