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; "
smime.p7s
Description: S/MIME Cryptographic Signature