diff options
Diffstat (limited to 'security/commoncap.c')
-rw-r--r-- | security/commoncap.c | 260 |
1 files changed, 224 insertions, 36 deletions
diff --git a/security/commoncap.c b/security/commoncap.c index 7520361663e..778cb0cfc5d 100644 --- a/security/commoncap.c +++ b/security/commoncap.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/ptrace.h> | 22 | #include <linux/ptrace.h> |
23 | #include <linux/xattr.h> | 23 | #include <linux/xattr.h> |
24 | #include <linux/hugetlb.h> | 24 | #include <linux/hugetlb.h> |
25 | #include <linux/mount.h> | ||
25 | 26 | ||
26 | int cap_netlink_send(struct sock *sk, struct sk_buff *skb) | 27 | int cap_netlink_send(struct sock *sk, struct sk_buff *skb) |
27 | { | 28 | { |
@@ -29,8 +30,6 @@ int cap_netlink_send(struct sock *sk, struct sk_buff *skb) | |||
29 | return 0; | 30 | return 0; |
30 | } | 31 | } |
31 | 32 | ||
32 | EXPORT_SYMBOL(cap_netlink_send); | ||
33 | |||
34 | int cap_netlink_recv(struct sk_buff *skb, int cap) | 33 | int cap_netlink_recv(struct sk_buff *skb, int cap) |
35 | { | 34 | { |
36 | if (!cap_raised(NETLINK_CB(skb).eff_cap, cap)) | 35 | if (!cap_raised(NETLINK_CB(skb).eff_cap, cap)) |
@@ -108,14 +107,130 @@ void cap_capset_set (struct task_struct *target, kernel_cap_t *effective, | |||
108 | target->cap_permitted = *permitted; | 107 | target->cap_permitted = *permitted; |
109 | } | 108 | } |
110 | 109 | ||
110 | static inline void bprm_clear_caps(struct linux_binprm *bprm) | ||
111 | { | ||
112 | cap_clear(bprm->cap_inheritable); | ||
113 | cap_clear(bprm->cap_permitted); | ||
114 | bprm->cap_effective = false; | ||
115 | } | ||
116 | |||
117 | #ifdef CONFIG_SECURITY_FILE_CAPABILITIES | ||
118 | |||
119 | int cap_inode_need_killpriv(struct dentry *dentry) | ||
120 | { | ||
121 | struct inode *inode = dentry->d_inode; | ||
122 | int error; | ||
123 | |||
124 | if (!inode->i_op || !inode->i_op->getxattr) | ||
125 | return 0; | ||
126 | |||
127 | error = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, NULL, 0); | ||
128 | if (error <= 0) | ||
129 | return 0; | ||
130 | return 1; | ||
131 | } | ||
132 | |||
133 | int cap_inode_killpriv(struct dentry *dentry) | ||
134 | { | ||
135 | struct inode *inode = dentry->d_inode; | ||
136 | |||
137 | if (!inode->i_op || !inode->i_op->removexattr) | ||
138 | return 0; | ||
139 | |||
140 | return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS); | ||
141 | } | ||
142 | |||
143 | static inline int cap_from_disk(__le32 *caps, struct linux_binprm *bprm, | ||
144 | int size) | ||
145 | { | ||
146 | __u32 magic_etc; | ||
147 | |||
148 | if (size != XATTR_CAPS_SZ) | ||
149 | return -EINVAL; | ||
150 | |||
151 | magic_etc = le32_to_cpu(caps[0]); | ||
152 | |||
153 | switch ((magic_etc & VFS_CAP_REVISION_MASK)) { | ||
154 | case VFS_CAP_REVISION: | ||
155 | if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE) | ||
156 | bprm->cap_effective = true; | ||
157 | else | ||
158 | bprm->cap_effective = false; | ||
159 | bprm->cap_permitted = to_cap_t( le32_to_cpu(caps[1]) ); | ||
160 | bprm->cap_inheritable = to_cap_t( le32_to_cpu(caps[2]) ); | ||
161 | return 0; | ||
162 | default: | ||
163 | return -EINVAL; | ||
164 | } | ||
165 | } | ||
166 | |||
167 | /* Locate any VFS capabilities: */ | ||
168 | static int get_file_caps(struct linux_binprm *bprm) | ||
169 | { | ||
170 | struct dentry *dentry; | ||
171 | int rc = 0; | ||
172 | __le32 v1caps[XATTR_CAPS_SZ]; | ||
173 | struct inode *inode; | ||
174 | |||
175 | if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) { | ||
176 | bprm_clear_caps(bprm); | ||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | dentry = dget(bprm->file->f_dentry); | ||
181 | inode = dentry->d_inode; | ||
182 | if (!inode->i_op || !inode->i_op->getxattr) | ||
183 | goto out; | ||
184 | |||
185 | rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, &v1caps, | ||
186 | XATTR_CAPS_SZ); | ||
187 | if (rc == -ENODATA || rc == -EOPNOTSUPP) { | ||
188 | /* no data, that's ok */ | ||
189 | rc = 0; | ||
190 | goto out; | ||
191 | } | ||
192 | if (rc < 0) | ||
193 | goto out; | ||
194 | |||
195 | rc = cap_from_disk(v1caps, bprm, rc); | ||
196 | if (rc) | ||
197 | printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", | ||
198 | __FUNCTION__, rc, bprm->filename); | ||
199 | |||
200 | out: | ||
201 | dput(dentry); | ||
202 | if (rc) | ||
203 | bprm_clear_caps(bprm); | ||
204 | |||
205 | return rc; | ||
206 | } | ||
207 | |||
208 | #else | ||
209 | int cap_inode_need_killpriv(struct dentry *dentry) | ||
210 | { | ||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | int cap_inode_killpriv(struct dentry *dentry) | ||
215 | { | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static inline int get_file_caps(struct linux_binprm *bprm) | ||
220 | { | ||
221 | bprm_clear_caps(bprm); | ||
222 | return 0; | ||
223 | } | ||
224 | #endif | ||
225 | |||
111 | int cap_bprm_set_security (struct linux_binprm *bprm) | 226 | int cap_bprm_set_security (struct linux_binprm *bprm) |
112 | { | 227 | { |
113 | /* Copied from fs/exec.c:prepare_binprm. */ | 228 | int ret; |
114 | 229 | ||
115 | /* We don't have VFS support for capabilities yet */ | 230 | ret = get_file_caps(bprm); |
116 | cap_clear (bprm->cap_inheritable); | 231 | if (ret) |
117 | cap_clear (bprm->cap_permitted); | 232 | printk(KERN_NOTICE "%s: get_file_caps returned %d for %s\n", |
118 | cap_clear (bprm->cap_effective); | 233 | __FUNCTION__, ret, bprm->filename); |
119 | 234 | ||
120 | /* To support inheritance of root-permissions and suid-root | 235 | /* To support inheritance of root-permissions and suid-root |
121 | * executables under compatibility mode, we raise all three | 236 | * executables under compatibility mode, we raise all three |
@@ -131,9 +246,10 @@ int cap_bprm_set_security (struct linux_binprm *bprm) | |||
131 | cap_set_full (bprm->cap_permitted); | 246 | cap_set_full (bprm->cap_permitted); |
132 | } | 247 | } |
133 | if (bprm->e_uid == 0) | 248 | if (bprm->e_uid == 0) |
134 | cap_set_full (bprm->cap_effective); | 249 | bprm->cap_effective = true; |
135 | } | 250 | } |
136 | return 0; | 251 | |
252 | return ret; | ||
137 | } | 253 | } |
138 | 254 | ||
139 | void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | 255 | void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) |
@@ -149,6 +265,7 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | |||
149 | if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || | 265 | if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || |
150 | !cap_issubset (new_permitted, current->cap_permitted)) { | 266 | !cap_issubset (new_permitted, current->cap_permitted)) { |
151 | set_dumpable(current->mm, suid_dumpable); | 267 | set_dumpable(current->mm, suid_dumpable); |
268 | current->pdeath_signal = 0; | ||
152 | 269 | ||
153 | if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) { | 270 | if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) { |
154 | if (!capable(CAP_SETUID)) { | 271 | if (!capable(CAP_SETUID)) { |
@@ -170,8 +287,8 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | |||
170 | * capability rules */ | 287 | * capability rules */ |
171 | if (!is_init(current)) { | 288 | if (!is_init(current)) { |
172 | current->cap_permitted = new_permitted; | 289 | current->cap_permitted = new_permitted; |
173 | current->cap_effective = | 290 | current->cap_effective = bprm->cap_effective ? |
174 | cap_intersect (new_permitted, bprm->cap_effective); | 291 | new_permitted : 0; |
175 | } | 292 | } |
176 | 293 | ||
177 | /* AUD: Audit candidate if current->cap_effective is set */ | 294 | /* AUD: Audit candidate if current->cap_effective is set */ |
@@ -181,11 +298,15 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | |||
181 | 298 | ||
182 | int cap_bprm_secureexec (struct linux_binprm *bprm) | 299 | int cap_bprm_secureexec (struct linux_binprm *bprm) |
183 | { | 300 | { |
184 | /* If/when this module is enhanced to incorporate capability | 301 | if (current->uid != 0) { |
185 | bits on files, the test below should be extended to also perform a | 302 | if (bprm->cap_effective) |
186 | test between the old and new capability sets. For now, | 303 | return 1; |
187 | it simply preserves the legacy decision algorithm used by | 304 | if (!cap_isclear(bprm->cap_permitted)) |
188 | the old userland. */ | 305 | return 1; |
306 | if (!cap_isclear(bprm->cap_inheritable)) | ||
307 | return 1; | ||
308 | } | ||
309 | |||
189 | return (current->euid != current->uid || | 310 | return (current->euid != current->uid || |
190 | current->egid != current->gid); | 311 | current->egid != current->gid); |
191 | } | 312 | } |
@@ -193,7 +314,11 @@ int cap_bprm_secureexec (struct linux_binprm *bprm) | |||
193 | int cap_inode_setxattr(struct dentry *dentry, char *name, void *value, | 314 | int cap_inode_setxattr(struct dentry *dentry, char *name, void *value, |
194 | size_t size, int flags) | 315 | size_t size, int flags) |
195 | { | 316 | { |
196 | if (!strncmp(name, XATTR_SECURITY_PREFIX, | 317 | if (!strcmp(name, XATTR_NAME_CAPS)) { |
318 | if (!capable(CAP_SETFCAP)) | ||
319 | return -EPERM; | ||
320 | return 0; | ||
321 | } else if (!strncmp(name, XATTR_SECURITY_PREFIX, | ||
197 | sizeof(XATTR_SECURITY_PREFIX) - 1) && | 322 | sizeof(XATTR_SECURITY_PREFIX) - 1) && |
198 | !capable(CAP_SYS_ADMIN)) | 323 | !capable(CAP_SYS_ADMIN)) |
199 | return -EPERM; | 324 | return -EPERM; |
@@ -202,7 +327,11 @@ int cap_inode_setxattr(struct dentry *dentry, char *name, void *value, | |||
202 | 327 | ||
203 | int cap_inode_removexattr(struct dentry *dentry, char *name) | 328 | int cap_inode_removexattr(struct dentry *dentry, char *name) |
204 | { | 329 | { |
205 | if (!strncmp(name, XATTR_SECURITY_PREFIX, | 330 | if (!strcmp(name, XATTR_NAME_CAPS)) { |
331 | if (!capable(CAP_SETFCAP)) | ||
332 | return -EPERM; | ||
333 | return 0; | ||
334 | } else if (!strncmp(name, XATTR_SECURITY_PREFIX, | ||
206 | sizeof(XATTR_SECURITY_PREFIX) - 1) && | 335 | sizeof(XATTR_SECURITY_PREFIX) - 1) && |
207 | !capable(CAP_SYS_ADMIN)) | 336 | !capable(CAP_SYS_ADMIN)) |
208 | return -EPERM; | 337 | return -EPERM; |
@@ -299,6 +428,83 @@ int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid, | |||
299 | return 0; | 428 | return 0; |
300 | } | 429 | } |
301 | 430 | ||
431 | #ifdef CONFIG_SECURITY_FILE_CAPABILITIES | ||
432 | /* | ||
433 | * Rationale: code calling task_setscheduler, task_setioprio, and | ||
434 | * task_setnice, assumes that | ||
435 | * . if capable(cap_sys_nice), then those actions should be allowed | ||
436 | * . if not capable(cap_sys_nice), but acting on your own processes, | ||
437 | * then those actions should be allowed | ||
438 | * This is insufficient now since you can call code without suid, but | ||
439 | * yet with increased caps. | ||
440 | * So we check for increased caps on the target process. | ||
441 | */ | ||
442 | static inline int cap_safe_nice(struct task_struct *p) | ||
443 | { | ||
444 | if (!cap_issubset(p->cap_permitted, current->cap_permitted) && | ||
445 | !__capable(current, CAP_SYS_NICE)) | ||
446 | return -EPERM; | ||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | int cap_task_setscheduler (struct task_struct *p, int policy, | ||
451 | struct sched_param *lp) | ||
452 | { | ||
453 | return cap_safe_nice(p); | ||
454 | } | ||
455 | |||
456 | int cap_task_setioprio (struct task_struct *p, int ioprio) | ||
457 | { | ||
458 | return cap_safe_nice(p); | ||
459 | } | ||
460 | |||
461 | int cap_task_setnice (struct task_struct *p, int nice) | ||
462 | { | ||
463 | return cap_safe_nice(p); | ||
464 | } | ||
465 | |||
466 | int cap_task_kill(struct task_struct *p, struct siginfo *info, | ||
467 | int sig, u32 secid) | ||
468 | { | ||
469 | if (info != SEND_SIG_NOINFO && (is_si_special(info) || SI_FROMKERNEL(info))) | ||
470 | return 0; | ||
471 | |||
472 | if (secid) | ||
473 | /* | ||
474 | * Signal sent as a particular user. | ||
475 | * Capabilities are ignored. May be wrong, but it's the | ||
476 | * only thing we can do at the moment. | ||
477 | * Used only by usb drivers? | ||
478 | */ | ||
479 | return 0; | ||
480 | if (cap_issubset(p->cap_permitted, current->cap_permitted)) | ||
481 | return 0; | ||
482 | if (capable(CAP_KILL)) | ||
483 | return 0; | ||
484 | |||
485 | return -EPERM; | ||
486 | } | ||
487 | #else | ||
488 | int cap_task_setscheduler (struct task_struct *p, int policy, | ||
489 | struct sched_param *lp) | ||
490 | { | ||
491 | return 0; | ||
492 | } | ||
493 | int cap_task_setioprio (struct task_struct *p, int ioprio) | ||
494 | { | ||
495 | return 0; | ||
496 | } | ||
497 | int cap_task_setnice (struct task_struct *p, int nice) | ||
498 | { | ||
499 | return 0; | ||
500 | } | ||
501 | int cap_task_kill(struct task_struct *p, struct siginfo *info, | ||
502 | int sig, u32 secid) | ||
503 | { | ||
504 | return 0; | ||
505 | } | ||
506 | #endif | ||
507 | |||
302 | void cap_task_reparent_to_init (struct task_struct *p) | 508 | void cap_task_reparent_to_init (struct task_struct *p) |
303 | { | 509 | { |
304 | p->cap_effective = CAP_INIT_EFF_SET; | 510 | p->cap_effective = CAP_INIT_EFF_SET; |
@@ -324,21 +530,3 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages) | |||
324 | return __vm_enough_memory(mm, pages, cap_sys_admin); | 530 | return __vm_enough_memory(mm, pages, cap_sys_admin); |
325 | } | 531 | } |
326 | 532 | ||
327 | EXPORT_SYMBOL(cap_capable); | ||
328 | EXPORT_SYMBOL(cap_settime); | ||
329 | EXPORT_SYMBOL(cap_ptrace); | ||
330 | EXPORT_SYMBOL(cap_capget); | ||
331 | EXPORT_SYMBOL(cap_capset_check); | ||
332 | EXPORT_SYMBOL(cap_capset_set); | ||
333 | EXPORT_SYMBOL(cap_bprm_set_security); | ||
334 | EXPORT_SYMBOL(cap_bprm_apply_creds); | ||
335 | EXPORT_SYMBOL(cap_bprm_secureexec); | ||
336 | EXPORT_SYMBOL(cap_inode_setxattr); | ||
337 | EXPORT_SYMBOL(cap_inode_removexattr); | ||
338 | EXPORT_SYMBOL(cap_task_post_setuid); | ||
339 | EXPORT_SYMBOL(cap_task_reparent_to_init); | ||
340 | EXPORT_SYMBOL(cap_syslog); | ||
341 | EXPORT_SYMBOL(cap_vm_enough_memory); | ||
342 | |||
343 | MODULE_DESCRIPTION("Standard Linux Common Capabilities Security Module"); | ||
344 | MODULE_LICENSE("GPL"); | ||