Commit ca05a99a54db1db5bca72eccb5866d2a86f8517f
Committed by
Chris Wright
1 parent
cc94bc37d5
Exists in
master
and in
4 other branches
capabilities: remain source compatible with 32-bit raw legacy capability support.
Source code out there hard-codes a notion of what the _LINUX_CAPABILITY_VERSION #define means in terms of the semantics of the raw capability system calls capget() and capset(). Its unfortunate, but true. Since the confusing header file has been in a released kernel, there is software that is erroneously using 64-bit capabilities with the semantics of 32-bit compatibilities. These recently compiled programs may suffer corruption of their memory when sys_getcap() overwrites more memory than they are coded to expect, and the raising of added capabilities when using sys_capset(). As such, this patch does a number of things to clean up the situation for all. It 1. forces the _LINUX_CAPABILITY_VERSION define to always retain its legacy value. 2. adopts a new #define strategy for the kernel's internal implementation of the preferred magic. 3. deprecates v2 capability magic in favor of a new (v3) magic number. The functionality of v3 is entirely equivalent to v2, the only difference being that the v2 magic causes the kernel to log a "deprecated" warning so the admin can find applications that may be using v2 inappropriately. [User space code continues to be encouraged to use the libcap API which protects the application from details like this. libcap-2.10 is the first to support v3 capabilities.] Fixes issue reported in https://bugzilla.redhat.com/show_bug.cgi?id=447518. Thanks to Bojan Smojver for the report. [akpm@linux-foundation.org: s/depreciate/deprecate/g] [akpm@linux-foundation.org: be robust about put_user size] [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Andrew G. Morgan <morgan@kernel.org> Cc: Serge E. Hallyn <serue@us.ibm.com> Cc: Bojan Smojver <bojan@rexursive.com> Cc: stable@kernel.org Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Chris Wright <chrisw@sous-sol.org>
Showing 3 changed files with 95 additions and 47 deletions Side-by-side Diff
fs/proc/array.c
include/linux/capability.h
... | ... | @@ -31,11 +31,11 @@ |
31 | 31 | #define _LINUX_CAPABILITY_VERSION_1 0x19980330 |
32 | 32 | #define _LINUX_CAPABILITY_U32S_1 1 |
33 | 33 | |
34 | -#define _LINUX_CAPABILITY_VERSION_2 0x20071026 | |
34 | +#define _LINUX_CAPABILITY_VERSION_2 0x20071026 /* deprecated - use v3 */ | |
35 | 35 | #define _LINUX_CAPABILITY_U32S_2 2 |
36 | 36 | |
37 | -#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_2 | |
38 | -#define _LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_2 | |
37 | +#define _LINUX_CAPABILITY_VERSION_3 0x20080522 | |
38 | +#define _LINUX_CAPABILITY_U32S_3 2 | |
39 | 39 | |
40 | 40 | typedef struct __user_cap_header_struct { |
41 | 41 | __u32 version; |
42 | 42 | |
43 | 43 | |
... | ... | @@ -77,10 +77,23 @@ |
77 | 77 | } data[VFS_CAP_U32]; |
78 | 78 | }; |
79 | 79 | |
80 | -#ifdef __KERNEL__ | |
80 | +#ifndef __KERNEL__ | |
81 | 81 | |
82 | +/* | |
83 | + * Backwardly compatible definition for source code - trapped in a | |
84 | + * 32-bit world. If you find you need this, please consider using | |
85 | + * libcap to untrap yourself... | |
86 | + */ | |
87 | +#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_1 | |
88 | +#define _LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_1 | |
89 | + | |
90 | +#else | |
91 | + | |
92 | +#define _KERNEL_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_3 | |
93 | +#define _KERNEL_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_3 | |
94 | + | |
82 | 95 | typedef struct kernel_cap_struct { |
83 | - __u32 cap[_LINUX_CAPABILITY_U32S]; | |
96 | + __u32 cap[_KERNEL_CAPABILITY_U32S]; | |
84 | 97 | } kernel_cap_t; |
85 | 98 | |
86 | 99 | #define _USER_CAP_HEADER_SIZE (sizeof(struct __user_cap_header_struct)) |
... | ... | @@ -351,7 +364,7 @@ |
351 | 364 | */ |
352 | 365 | |
353 | 366 | #define CAP_FOR_EACH_U32(__capi) \ |
354 | - for (__capi = 0; __capi < _LINUX_CAPABILITY_U32S; ++__capi) | |
367 | + for (__capi = 0; __capi < _KERNEL_CAPABILITY_U32S; ++__capi) | |
355 | 368 | |
356 | 369 | # define CAP_FS_MASK_B0 (CAP_TO_MASK(CAP_CHOWN) \ |
357 | 370 | | CAP_TO_MASK(CAP_DAC_OVERRIDE) \ |
... | ... | @@ -361,7 +374,7 @@ |
361 | 374 | |
362 | 375 | # define CAP_FS_MASK_B1 (CAP_TO_MASK(CAP_MAC_OVERRIDE)) |
363 | 376 | |
364 | -#if _LINUX_CAPABILITY_U32S != 2 | |
377 | +#if _KERNEL_CAPABILITY_U32S != 2 | |
365 | 378 | # error Fix up hand-coded capability macro initializers |
366 | 379 | #else /* HAND-CODED capability initializers */ |
367 | 380 | |
... | ... | @@ -372,7 +385,7 @@ |
372 | 385 | # define CAP_NFSD_SET ((kernel_cap_t){{ CAP_FS_MASK_B0|CAP_TO_MASK(CAP_SYS_RESOURCE), \ |
373 | 386 | CAP_FS_MASK_B1 } }) |
374 | 387 | |
375 | -#endif /* _LINUX_CAPABILITY_U32S != 2 */ | |
388 | +#endif /* _KERNEL_CAPABILITY_U32S != 2 */ | |
376 | 389 | |
377 | 390 | #define CAP_INIT_INH_SET CAP_EMPTY_SET |
378 | 391 |
kernel/capability.c
... | ... | @@ -53,6 +53,69 @@ |
53 | 53 | } |
54 | 54 | |
55 | 55 | /* |
56 | + * Version 2 capabilities worked fine, but the linux/capability.h file | |
57 | + * that accompanied their introduction encouraged their use without | |
58 | + * the necessary user-space source code changes. As such, we have | |
59 | + * created a version 3 with equivalent functionality to version 2, but | |
60 | + * with a header change to protect legacy source code from using | |
61 | + * version 2 when it wanted to use version 1. If your system has code | |
62 | + * that trips the following warning, it is using version 2 specific | |
63 | + * capabilities and may be doing so insecurely. | |
64 | + * | |
65 | + * The remedy is to either upgrade your version of libcap (to 2.10+, | |
66 | + * if the application is linked against it), or recompile your | |
67 | + * application with modern kernel headers and this warning will go | |
68 | + * away. | |
69 | + */ | |
70 | + | |
71 | +static void warn_deprecated_v2(void) | |
72 | +{ | |
73 | + static int warned; | |
74 | + | |
75 | + if (!warned) { | |
76 | + char name[sizeof(current->comm)]; | |
77 | + | |
78 | + printk(KERN_INFO "warning: `%s' uses deprecated v2" | |
79 | + " capabilities in a way that may be insecure.\n", | |
80 | + get_task_comm(name, current)); | |
81 | + warned = 1; | |
82 | + } | |
83 | +} | |
84 | + | |
85 | +/* | |
86 | + * Version check. Return the number of u32s in each capability flag | |
87 | + * array, or a negative value on error. | |
88 | + */ | |
89 | +static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy) | |
90 | +{ | |
91 | + __u32 version; | |
92 | + | |
93 | + if (get_user(version, &header->version)) | |
94 | + return -EFAULT; | |
95 | + | |
96 | + switch (version) { | |
97 | + case _LINUX_CAPABILITY_VERSION_1: | |
98 | + warn_legacy_capability_use(); | |
99 | + *tocopy = _LINUX_CAPABILITY_U32S_1; | |
100 | + break; | |
101 | + case _LINUX_CAPABILITY_VERSION_2: | |
102 | + warn_deprecated_v2(); | |
103 | + /* | |
104 | + * fall through - v3 is otherwise equivalent to v2. | |
105 | + */ | |
106 | + case _LINUX_CAPABILITY_VERSION_3: | |
107 | + *tocopy = _LINUX_CAPABILITY_U32S_3; | |
108 | + break; | |
109 | + default: | |
110 | + if (put_user((u32)_KERNEL_CAPABILITY_VERSION, &header->version)) | |
111 | + return -EFAULT; | |
112 | + return -EINVAL; | |
113 | + } | |
114 | + | |
115 | + return 0; | |
116 | +} | |
117 | + | |
118 | +/* | |
56 | 119 | * For sys_getproccap() and sys_setproccap(), any of the three |
57 | 120 | * capability set pointers may be NULL -- indicating that that set is |
58 | 121 | * uninteresting and/or not to be changed. |
59 | 122 | |
60 | 123 | |
... | ... | @@ -71,28 +134,14 @@ |
71 | 134 | { |
72 | 135 | int ret = 0; |
73 | 136 | pid_t pid; |
74 | - __u32 version; | |
75 | 137 | struct task_struct *target; |
76 | 138 | unsigned tocopy; |
77 | 139 | kernel_cap_t pE, pI, pP; |
78 | 140 | |
79 | - if (get_user(version, &header->version)) | |
80 | - return -EFAULT; | |
141 | + ret = cap_validate_magic(header, &tocopy); | |
142 | + if (ret != 0) | |
143 | + return ret; | |
81 | 144 | |
82 | - switch (version) { | |
83 | - case _LINUX_CAPABILITY_VERSION_1: | |
84 | - warn_legacy_capability_use(); | |
85 | - tocopy = _LINUX_CAPABILITY_U32S_1; | |
86 | - break; | |
87 | - case _LINUX_CAPABILITY_VERSION_2: | |
88 | - tocopy = _LINUX_CAPABILITY_U32S_2; | |
89 | - break; | |
90 | - default: | |
91 | - if (put_user(_LINUX_CAPABILITY_VERSION, &header->version)) | |
92 | - return -EFAULT; | |
93 | - return -EINVAL; | |
94 | - } | |
95 | - | |
96 | 145 | if (get_user(pid, &header->pid)) |
97 | 146 | return -EFAULT; |
98 | 147 | |
... | ... | @@ -118,7 +167,7 @@ |
118 | 167 | spin_unlock(&task_capability_lock); |
119 | 168 | |
120 | 169 | if (!ret) { |
121 | - struct __user_cap_data_struct kdata[_LINUX_CAPABILITY_U32S]; | |
170 | + struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S]; | |
122 | 171 | unsigned i; |
123 | 172 | |
124 | 173 | for (i = 0; i < tocopy; i++) { |
... | ... | @@ -128,7 +177,7 @@ |
128 | 177 | } |
129 | 178 | |
130 | 179 | /* |
131 | - * Note, in the case, tocopy < _LINUX_CAPABILITY_U32S, | |
180 | + * Note, in the case, tocopy < _KERNEL_CAPABILITY_U32S, | |
132 | 181 | * we silently drop the upper capabilities here. This |
133 | 182 | * has the effect of making older libcap |
134 | 183 | * implementations implicitly drop upper capability |
135 | 184 | |
136 | 185 | |
137 | 186 | |
... | ... | @@ -240,31 +289,17 @@ |
240 | 289 | */ |
241 | 290 | asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) |
242 | 291 | { |
243 | - struct __user_cap_data_struct kdata[_LINUX_CAPABILITY_U32S]; | |
292 | + struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S]; | |
244 | 293 | unsigned i, tocopy; |
245 | 294 | kernel_cap_t inheritable, permitted, effective; |
246 | - __u32 version; | |
247 | 295 | struct task_struct *target; |
248 | 296 | int ret; |
249 | 297 | pid_t pid; |
250 | 298 | |
251 | - if (get_user(version, &header->version)) | |
252 | - return -EFAULT; | |
299 | + ret = cap_validate_magic(header, &tocopy); | |
300 | + if (ret != 0) | |
301 | + return ret; | |
253 | 302 | |
254 | - switch (version) { | |
255 | - case _LINUX_CAPABILITY_VERSION_1: | |
256 | - warn_legacy_capability_use(); | |
257 | - tocopy = _LINUX_CAPABILITY_U32S_1; | |
258 | - break; | |
259 | - case _LINUX_CAPABILITY_VERSION_2: | |
260 | - tocopy = _LINUX_CAPABILITY_U32S_2; | |
261 | - break; | |
262 | - default: | |
263 | - if (put_user(_LINUX_CAPABILITY_VERSION, &header->version)) | |
264 | - return -EFAULT; | |
265 | - return -EINVAL; | |
266 | - } | |
267 | - | |
268 | 303 | if (get_user(pid, &header->pid)) |
269 | 304 | return -EFAULT; |
270 | 305 | |
... | ... | @@ -281,7 +316,7 @@ |
281 | 316 | permitted.cap[i] = kdata[i].permitted; |
282 | 317 | inheritable.cap[i] = kdata[i].inheritable; |
283 | 318 | } |
284 | - while (i < _LINUX_CAPABILITY_U32S) { | |
319 | + while (i < _KERNEL_CAPABILITY_U32S) { | |
285 | 320 | effective.cap[i] = 0; |
286 | 321 | permitted.cap[i] = 0; |
287 | 322 | inheritable.cap[i] = 0; |