diff options
author | Serge E. Hallyn <serue@us.ibm.com> | 2008-02-05 01:29:45 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-05 12:44:20 -0500 |
commit | 3b7391de67da515c91f48aa371de77cb6cc5c07e (patch) | |
tree | 22b9f5d9d1c36b374eb5765219aca3c7e1f23486 /include | |
parent | 46c383cc4530ccc438cb325e92e11eb21dd3d4fc (diff) |
capabilities: introduce per-process capability bounding set
The capability bounding set is a set beyond which capabilities cannot grow.
Currently cap_bset is per-system. It can be manipulated through sysctl,
but only init can add capabilities. Root can remove capabilities. By
default it includes all caps except CAP_SETPCAP.
This patch makes the bounding set per-process when file capabilities are
enabled. It is inherited at fork from parent. Noone can add elements,
CAP_SETPCAP is required to remove them.
One example use of this is to start a safer container. For instance, until
device namespaces or per-container device whitelists are introduced, it is
best to take CAP_MKNOD away from a container.
The bounding set will not affect pP and pE immediately. It will only
affect pP' and pE' after subsequent exec()s. It also does not affect pI,
and exec() does not constrain pI'. So to really start a shell with no way
of regain CAP_MKNOD, you would do
prctl(PR_CAPBSET_DROP, CAP_MKNOD);
cap_t cap = cap_get_proc();
cap_value_t caparray[1];
caparray[0] = CAP_MKNOD;
cap_set_flag(cap, CAP_INHERITABLE, 1, caparray, CAP_DROP);
cap_set_proc(cap);
cap_free(cap);
The following test program will get and set the bounding
set (but not pI). For instance
./bset get
(lists capabilities in bset)
./bset drop cap_net_raw
(starts shell with new bset)
(use capset, setuid binary, or binary with
file capabilities to try to increase caps)
************************************************************
cap_bound.c
************************************************************
#include <sys/prctl.h>
#include <linux/capability.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef PR_CAPBSET_READ
#define PR_CAPBSET_READ 23
#endif
#ifndef PR_CAPBSET_DROP
#define PR_CAPBSET_DROP 24
#endif
int usage(char *me)
{
printf("Usage: %s get\n", me);
printf(" %s drop <capability>\n", me);
return 1;
}
#define numcaps 32
char *captable[numcaps] = {
"cap_chown",
"cap_dac_override",
"cap_dac_read_search",
"cap_fowner",
"cap_fsetid",
"cap_kill",
"cap_setgid",
"cap_setuid",
"cap_setpcap",
"cap_linux_immutable",
"cap_net_bind_service",
"cap_net_broadcast",
"cap_net_admin",
"cap_net_raw",
"cap_ipc_lock",
"cap_ipc_owner",
"cap_sys_module",
"cap_sys_rawio",
"cap_sys_chroot",
"cap_sys_ptrace",
"cap_sys_pacct",
"cap_sys_admin",
"cap_sys_boot",
"cap_sys_nice",
"cap_sys_resource",
"cap_sys_time",
"cap_sys_tty_config",
"cap_mknod",
"cap_lease",
"cap_audit_write",
"cap_audit_control",
"cap_setfcap"
};
int getbcap(void)
{
int comma=0;
unsigned long i;
int ret;
printf("i know of %d capabilities\n", numcaps);
printf("capability bounding set:");
for (i=0; i<numcaps; i++) {
ret = prctl(PR_CAPBSET_READ, i);
if (ret < 0)
perror("prctl");
else if (ret==1)
printf("%s%s", (comma++) ? ", " : " ", captable[i]);
}
printf("\n");
return 0;
}
int capdrop(char *str)
{
unsigned long i;
int found=0;
for (i=0; i<numcaps; i++) {
if (strcmp(captable[i], str) == 0) {
found=1;
break;
}
}
if (!found)
return 1;
if (prctl(PR_CAPBSET_DROP, i)) {
perror("prctl");
return 1;
}
return 0;
}
int main(int argc, char *argv[])
{
if (argc<2)
return usage(argv[0]);
if (strcmp(argv[1], "get")==0)
return getbcap();
if (strcmp(argv[1], "drop")!=0 || argc<3)
return usage(argv[0]);
if (capdrop(argv[2])) {
printf("unknown capability\n");
return 1;
}
return execl("/bin/bash", "/bin/bash", NULL);
}
************************************************************
[serue@us.ibm.com: fix typo]
Signed-off-by: Serge E. Hallyn <serue@us.ibm.com>
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Cc: Stephen Smalley <sds@tycho.nsa.gov>
Cc: James Morris <jmorris@namei.org>
Cc: Chris Wright <chrisw@sous-sol.org>
Cc: Casey Schaufler <casey@schaufler-ca.com>a
Signed-off-by: "Serge E. Hallyn" <serue@us.ibm.com>
Tested-by: Jiri Slaby <jirislaby@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'include')
-rw-r--r-- | include/linux/capability.h | 11 | ||||
-rw-r--r-- | include/linux/init_task.h | 13 | ||||
-rw-r--r-- | include/linux/prctl.h | 4 | ||||
-rw-r--r-- | include/linux/sched.h | 2 | ||||
-rw-r--r-- | include/linux/security.h | 5 | ||||
-rw-r--r-- | include/linux/sysctl.h | 3 |
6 files changed, 27 insertions, 11 deletions
diff --git a/include/linux/capability.h b/include/linux/capability.h index a1d93da67fe9..ffe7bab8c3a0 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h | |||
@@ -152,7 +152,9 @@ typedef struct kernel_cap_struct { | |||
152 | * Transfer any capability in your permitted set to any pid, | 152 | * Transfer any capability in your permitted set to any pid, |
153 | * remove any capability in your permitted set from any pid | 153 | * remove any capability in your permitted set from any pid |
154 | * With VFS support for capabilities (neither of above, but) | 154 | * With VFS support for capabilities (neither of above, but) |
155 | * Add any capability to the current process' inheritable set | 155 | * Add any capability from current's capability bounding set |
156 | * to the current process' inheritable set | ||
157 | * Allow taking bits out of capability bounding set | ||
156 | */ | 158 | */ |
157 | 159 | ||
158 | #define CAP_SETPCAP 8 | 160 | #define CAP_SETPCAP 8 |
@@ -202,7 +204,6 @@ typedef struct kernel_cap_struct { | |||
202 | #define CAP_IPC_OWNER 15 | 204 | #define CAP_IPC_OWNER 15 |
203 | 205 | ||
204 | /* Insert and remove kernel modules - modify kernel without limit */ | 206 | /* Insert and remove kernel modules - modify kernel without limit */ |
205 | /* Modify cap_bset */ | ||
206 | #define CAP_SYS_MODULE 16 | 207 | #define CAP_SYS_MODULE 16 |
207 | 208 | ||
208 | /* Allow ioperm/iopl access */ | 209 | /* Allow ioperm/iopl access */ |
@@ -314,6 +315,10 @@ typedef struct kernel_cap_struct { | |||
314 | 315 | ||
315 | #define CAP_SETFCAP 31 | 316 | #define CAP_SETFCAP 31 |
316 | 317 | ||
318 | #define CAP_LAST_CAP CAP_SETFCAP | ||
319 | |||
320 | #define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP) | ||
321 | |||
317 | /* | 322 | /* |
318 | * Bit location of each capability (used by user-space library and kernel) | 323 | * Bit location of each capability (used by user-space library and kernel) |
319 | */ | 324 | */ |
@@ -465,6 +470,8 @@ extern const kernel_cap_t __cap_init_eff_set; | |||
465 | int capable(int cap); | 470 | int capable(int cap); |
466 | int __capable(struct task_struct *t, int cap); | 471 | int __capable(struct task_struct *t, int cap); |
467 | 472 | ||
473 | extern long cap_prctl_drop(unsigned long cap); | ||
474 | |||
468 | #endif /* __KERNEL__ */ | 475 | #endif /* __KERNEL__ */ |
469 | 476 | ||
470 | #endif /* !_LINUX_CAPABILITY_H */ | 477 | #endif /* !_LINUX_CAPABILITY_H */ |
diff --git a/include/linux/init_task.h b/include/linux/init_task.h index f42663eaf655..1f74e1d7415f 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h | |||
@@ -121,6 +121,18 @@ extern struct group_info init_groups; | |||
121 | #else | 121 | #else |
122 | #define INIT_IDS | 122 | #define INIT_IDS |
123 | #endif | 123 | #endif |
124 | |||
125 | #ifdef CONFIG_SECURITY_FILE_CAPABILITIES | ||
126 | /* | ||
127 | * Because of the reduced scope of CAP_SETPCAP when filesystem | ||
128 | * capabilities are in effect, it is safe to allow CAP_SETPCAP to | ||
129 | * be available in the default configuration. | ||
130 | */ | ||
131 | # define CAP_INIT_BSET CAP_FULL_SET | ||
132 | #else | ||
133 | # define CAP_INIT_BSET CAP_INIT_EFF_SET | ||
134 | #endif | ||
135 | |||
124 | /* | 136 | /* |
125 | * INIT_TASK is used to set up the first task table, touch at | 137 | * INIT_TASK is used to set up the first task table, touch at |
126 | * your own risk!. Base=0, limit=0x1fffff (=2MB) | 138 | * your own risk!. Base=0, limit=0x1fffff (=2MB) |
@@ -156,6 +168,7 @@ extern struct group_info init_groups; | |||
156 | .cap_effective = CAP_INIT_EFF_SET, \ | 168 | .cap_effective = CAP_INIT_EFF_SET, \ |
157 | .cap_inheritable = CAP_INIT_INH_SET, \ | 169 | .cap_inheritable = CAP_INIT_INH_SET, \ |
158 | .cap_permitted = CAP_FULL_SET, \ | 170 | .cap_permitted = CAP_FULL_SET, \ |
171 | .cap_bset = CAP_INIT_BSET, \ | ||
159 | .keep_capabilities = 0, \ | 172 | .keep_capabilities = 0, \ |
160 | .user = INIT_USER, \ | 173 | .user = INIT_USER, \ |
161 | .comm = "swapper", \ | 174 | .comm = "swapper", \ |
diff --git a/include/linux/prctl.h b/include/linux/prctl.h index e2eff9079fe9..3800639775ae 100644 --- a/include/linux/prctl.h +++ b/include/linux/prctl.h | |||
@@ -63,4 +63,8 @@ | |||
63 | #define PR_GET_SECCOMP 21 | 63 | #define PR_GET_SECCOMP 21 |
64 | #define PR_SET_SECCOMP 22 | 64 | #define PR_SET_SECCOMP 22 |
65 | 65 | ||
66 | /* Get/set the capability bounding set */ | ||
67 | #define PR_CAPBSET_READ 23 | ||
68 | #define PR_CAPBSET_DROP 24 | ||
69 | |||
66 | #endif /* _LINUX_PRCTL_H */ | 70 | #endif /* _LINUX_PRCTL_H */ |
diff --git a/include/linux/sched.h b/include/linux/sched.h index c30d174a02fa..9c13be3a21e8 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -1098,7 +1098,7 @@ struct task_struct { | |||
1098 | uid_t uid,euid,suid,fsuid; | 1098 | uid_t uid,euid,suid,fsuid; |
1099 | gid_t gid,egid,sgid,fsgid; | 1099 | gid_t gid,egid,sgid,fsgid; |
1100 | struct group_info *group_info; | 1100 | struct group_info *group_info; |
1101 | kernel_cap_t cap_effective, cap_inheritable, cap_permitted; | 1101 | kernel_cap_t cap_effective, cap_inheritable, cap_permitted, cap_bset; |
1102 | unsigned keep_capabilities:1; | 1102 | unsigned keep_capabilities:1; |
1103 | struct user_struct *user; | 1103 | struct user_struct *user; |
1104 | #ifdef CONFIG_KEYS | 1104 | #ifdef CONFIG_KEYS |
diff --git a/include/linux/security.h b/include/linux/security.h index 9d289e726fd8..fe52cdeab0a6 100644 --- a/include/linux/security.h +++ b/include/linux/security.h | |||
@@ -40,11 +40,6 @@ | |||
40 | #define ROOTCONTEXT_MNT 0x04 | 40 | #define ROOTCONTEXT_MNT 0x04 |
41 | #define DEFCONTEXT_MNT 0x08 | 41 | #define DEFCONTEXT_MNT 0x08 |
42 | 42 | ||
43 | /* | ||
44 | * Bounding set | ||
45 | */ | ||
46 | extern kernel_cap_t cap_bset; | ||
47 | |||
48 | extern unsigned securebits; | 43 | extern unsigned securebits; |
49 | 44 | ||
50 | struct ctl_table; | 45 | struct ctl_table; |
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index bf4ae4e138f7..571f01d20a86 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h | |||
@@ -102,7 +102,6 @@ enum | |||
102 | KERN_NODENAME=7, | 102 | KERN_NODENAME=7, |
103 | KERN_DOMAINNAME=8, | 103 | KERN_DOMAINNAME=8, |
104 | 104 | ||
105 | KERN_CAP_BSET=14, /* int: capability bounding set */ | ||
106 | KERN_PANIC=15, /* int: panic timeout */ | 105 | KERN_PANIC=15, /* int: panic timeout */ |
107 | KERN_REALROOTDEV=16, /* real root device to mount after initrd */ | 106 | KERN_REALROOTDEV=16, /* real root device to mount after initrd */ |
108 | 107 | ||
@@ -965,8 +964,6 @@ extern int proc_dostring(struct ctl_table *, int, struct file *, | |||
965 | void __user *, size_t *, loff_t *); | 964 | void __user *, size_t *, loff_t *); |
966 | extern int proc_dointvec(struct ctl_table *, int, struct file *, | 965 | extern int proc_dointvec(struct ctl_table *, int, struct file *, |
967 | void __user *, size_t *, loff_t *); | 966 | void __user *, size_t *, loff_t *); |
968 | extern int proc_dointvec_bset(struct ctl_table *, int, struct file *, | ||
969 | void __user *, size_t *, loff_t *); | ||
970 | extern int proc_dointvec_minmax(struct ctl_table *, int, struct file *, | 967 | extern int proc_dointvec_minmax(struct ctl_table *, int, struct file *, |
971 | void __user *, size_t *, loff_t *); | 968 | void __user *, size_t *, loff_t *); |
972 | extern int proc_dointvec_jiffies(struct ctl_table *, int, struct file *, | 969 | extern int proc_dointvec_jiffies(struct ctl_table *, int, struct file *, |