diff options
| author | Ingo Molnar <mingo@elte.hu> | 2008-07-28 18:07:55 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2008-07-28 18:07:55 -0400 |
| commit | cb28a1bbdb4790378e7366d6c9ee1d2340b84f92 (patch) | |
| tree | 316436f77dac75335fd2c3ef5f109e71606c50d3 /security | |
| parent | b6d4f7e3ef25beb8c658c97867d98883e69dc544 (diff) | |
| parent | f934fb19ef34730263e6afc01e8ec27a8a71470f (diff) | |
Merge branch 'linus' into core/generic-dma-coherent
Conflicts:
arch/x86/Kconfig
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'security')
| -rw-r--r-- | security/Kconfig | 3 | ||||
| -rw-r--r-- | security/capability.c | 3 | ||||
| -rw-r--r-- | security/commoncap.c | 108 | ||||
| -rw-r--r-- | security/device_cgroup.c | 158 | ||||
| -rw-r--r-- | security/security.c | 5 | ||||
| -rw-r--r-- | security/selinux/hooks.c | 54 | ||||
| -rw-r--r-- | security/smack/smack_lsm.c | 3 |
7 files changed, 146 insertions, 188 deletions
diff --git a/security/Kconfig b/security/Kconfig index 62ed4717d334..559293922a47 100644 --- a/security/Kconfig +++ b/security/Kconfig | |||
| @@ -74,8 +74,7 @@ config SECURITY_NETWORK_XFRM | |||
| 74 | If you are unsure how to answer this question, answer N. | 74 | If you are unsure how to answer this question, answer N. |
| 75 | 75 | ||
| 76 | config SECURITY_FILE_CAPABILITIES | 76 | config SECURITY_FILE_CAPABILITIES |
| 77 | bool "File POSIX Capabilities (EXPERIMENTAL)" | 77 | bool "File POSIX Capabilities" |
| 78 | depends on EXPERIMENTAL | ||
| 79 | default n | 78 | default n |
| 80 | help | 79 | help |
| 81 | This enables filesystem capabilities, allowing you to give | 80 | This enables filesystem capabilities, allowing you to give |
diff --git a/security/capability.c b/security/capability.c index 5b01c0b02422..63d10da515a5 100644 --- a/security/capability.c +++ b/security/capability.c | |||
| @@ -211,8 +211,7 @@ static int cap_inode_follow_link(struct dentry *dentry, | |||
| 211 | return 0; | 211 | return 0; |
| 212 | } | 212 | } |
| 213 | 213 | ||
| 214 | static int cap_inode_permission(struct inode *inode, int mask, | 214 | static int cap_inode_permission(struct inode *inode, int mask) |
| 215 | struct nameidata *nd) | ||
| 216 | { | 215 | { |
| 217 | return 0; | 216 | return 0; |
| 218 | } | 217 | } |
diff --git a/security/commoncap.c b/security/commoncap.c index 0b6537a3672d..4afbece37a08 100644 --- a/security/commoncap.c +++ b/security/commoncap.c | |||
| @@ -162,8 +162,7 @@ void cap_capset_set (struct task_struct *target, kernel_cap_t *effective, | |||
| 162 | 162 | ||
| 163 | static inline void bprm_clear_caps(struct linux_binprm *bprm) | 163 | static inline void bprm_clear_caps(struct linux_binprm *bprm) |
| 164 | { | 164 | { |
| 165 | cap_clear(bprm->cap_inheritable); | 165 | cap_clear(bprm->cap_post_exec_permitted); |
| 166 | cap_clear(bprm->cap_permitted); | ||
| 167 | bprm->cap_effective = false; | 166 | bprm->cap_effective = false; |
| 168 | } | 167 | } |
| 169 | 168 | ||
| @@ -198,6 +197,7 @@ static inline int cap_from_disk(struct vfs_cap_data *caps, | |||
| 198 | { | 197 | { |
| 199 | __u32 magic_etc; | 198 | __u32 magic_etc; |
| 200 | unsigned tocopy, i; | 199 | unsigned tocopy, i; |
| 200 | int ret; | ||
| 201 | 201 | ||
| 202 | if (size < sizeof(magic_etc)) | 202 | if (size < sizeof(magic_etc)) |
| 203 | return -EINVAL; | 203 | return -EINVAL; |
| @@ -225,19 +225,40 @@ static inline int cap_from_disk(struct vfs_cap_data *caps, | |||
| 225 | bprm->cap_effective = false; | 225 | bprm->cap_effective = false; |
| 226 | } | 226 | } |
| 227 | 227 | ||
| 228 | for (i = 0; i < tocopy; ++i) { | 228 | ret = 0; |
| 229 | bprm->cap_permitted.cap[i] = | 229 | |
| 230 | le32_to_cpu(caps->data[i].permitted); | 230 | CAP_FOR_EACH_U32(i) { |
| 231 | bprm->cap_inheritable.cap[i] = | 231 | __u32 value_cpu; |
| 232 | le32_to_cpu(caps->data[i].inheritable); | 232 | |
| 233 | } | 233 | if (i >= tocopy) { |
| 234 | while (i < VFS_CAP_U32) { | 234 | /* |
| 235 | bprm->cap_permitted.cap[i] = 0; | 235 | * Legacy capability sets have no upper bits |
| 236 | bprm->cap_inheritable.cap[i] = 0; | 236 | */ |
| 237 | i++; | 237 | bprm->cap_post_exec_permitted.cap[i] = 0; |
| 238 | continue; | ||
| 239 | } | ||
| 240 | /* | ||
| 241 | * pP' = (X & fP) | (pI & fI) | ||
| 242 | */ | ||
| 243 | value_cpu = le32_to_cpu(caps->data[i].permitted); | ||
| 244 | bprm->cap_post_exec_permitted.cap[i] = | ||
| 245 | (current->cap_bset.cap[i] & value_cpu) | | ||
| 246 | (current->cap_inheritable.cap[i] & | ||
| 247 | le32_to_cpu(caps->data[i].inheritable)); | ||
| 248 | if (value_cpu & ~bprm->cap_post_exec_permitted.cap[i]) { | ||
| 249 | /* | ||
| 250 | * insufficient to execute correctly | ||
| 251 | */ | ||
| 252 | ret = -EPERM; | ||
| 253 | } | ||
| 238 | } | 254 | } |
| 239 | 255 | ||
| 240 | return 0; | 256 | /* |
| 257 | * For legacy apps, with no internal support for recognizing they | ||
| 258 | * do not have enough capabilities, we return an error if they are | ||
| 259 | * missing some "forced" (aka file-permitted) capabilities. | ||
| 260 | */ | ||
| 261 | return bprm->cap_effective ? ret : 0; | ||
| 241 | } | 262 | } |
| 242 | 263 | ||
| 243 | /* Locate any VFS capabilities: */ | 264 | /* Locate any VFS capabilities: */ |
| @@ -269,9 +290,9 @@ static int get_file_caps(struct linux_binprm *bprm) | |||
| 269 | goto out; | 290 | goto out; |
| 270 | 291 | ||
| 271 | rc = cap_from_disk(&vcaps, bprm, rc); | 292 | rc = cap_from_disk(&vcaps, bprm, rc); |
| 272 | if (rc) | 293 | if (rc == -EINVAL) |
| 273 | printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", | 294 | printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", |
| 274 | __func__, rc, bprm->filename); | 295 | __func__, rc, bprm->filename); |
| 275 | 296 | ||
| 276 | out: | 297 | out: |
| 277 | dput(dentry); | 298 | dput(dentry); |
| @@ -304,25 +325,24 @@ int cap_bprm_set_security (struct linux_binprm *bprm) | |||
| 304 | int ret; | 325 | int ret; |
| 305 | 326 | ||
| 306 | ret = get_file_caps(bprm); | 327 | ret = get_file_caps(bprm); |
| 307 | if (ret) | ||
| 308 | printk(KERN_NOTICE "%s: get_file_caps returned %d for %s\n", | ||
| 309 | __func__, ret, bprm->filename); | ||
| 310 | |||
| 311 | /* To support inheritance of root-permissions and suid-root | ||
| 312 | * executables under compatibility mode, we raise all three | ||
| 313 | * capability sets for the file. | ||
| 314 | * | ||
| 315 | * If only the real uid is 0, we only raise the inheritable | ||
| 316 | * and permitted sets of the executable file. | ||
| 317 | */ | ||
| 318 | 328 | ||
| 319 | if (!issecure (SECURE_NOROOT)) { | 329 | if (!issecure(SECURE_NOROOT)) { |
| 330 | /* | ||
| 331 | * To support inheritance of root-permissions and suid-root | ||
| 332 | * executables under compatibility mode, we override the | ||
| 333 | * capability sets for the file. | ||
| 334 | * | ||
| 335 | * If only the real uid is 0, we do not set the effective | ||
| 336 | * bit. | ||
| 337 | */ | ||
| 320 | if (bprm->e_uid == 0 || current->uid == 0) { | 338 | if (bprm->e_uid == 0 || current->uid == 0) { |
| 321 | cap_set_full (bprm->cap_inheritable); | 339 | /* pP' = (cap_bset & ~0) | (pI & ~0) */ |
| 322 | cap_set_full (bprm->cap_permitted); | 340 | bprm->cap_post_exec_permitted = cap_combine( |
| 341 | current->cap_bset, current->cap_inheritable | ||
| 342 | ); | ||
| 343 | bprm->cap_effective = (bprm->e_uid == 0); | ||
| 344 | ret = 0; | ||
| 323 | } | 345 | } |
| 324 | if (bprm->e_uid == 0) | ||
| 325 | bprm->cap_effective = true; | ||
| 326 | } | 346 | } |
| 327 | 347 | ||
| 328 | return ret; | 348 | return ret; |
| @@ -330,17 +350,9 @@ int cap_bprm_set_security (struct linux_binprm *bprm) | |||
| 330 | 350 | ||
| 331 | void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | 351 | void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) |
| 332 | { | 352 | { |
| 333 | /* Derived from fs/exec.c:compute_creds. */ | ||
| 334 | kernel_cap_t new_permitted, working; | ||
| 335 | |||
| 336 | new_permitted = cap_intersect(bprm->cap_permitted, | ||
| 337 | current->cap_bset); | ||
| 338 | working = cap_intersect(bprm->cap_inheritable, | ||
| 339 | current->cap_inheritable); | ||
| 340 | new_permitted = cap_combine(new_permitted, working); | ||
| 341 | |||
| 342 | if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || | 353 | if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || |
| 343 | !cap_issubset (new_permitted, current->cap_permitted)) { | 354 | !cap_issubset(bprm->cap_post_exec_permitted, |
| 355 | current->cap_permitted)) { | ||
| 344 | set_dumpable(current->mm, suid_dumpable); | 356 | set_dumpable(current->mm, suid_dumpable); |
| 345 | current->pdeath_signal = 0; | 357 | current->pdeath_signal = 0; |
| 346 | 358 | ||
| @@ -350,9 +362,9 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | |||
| 350 | bprm->e_gid = current->gid; | 362 | bprm->e_gid = current->gid; |
| 351 | } | 363 | } |
| 352 | if (cap_limit_ptraced_target()) { | 364 | if (cap_limit_ptraced_target()) { |
| 353 | new_permitted = | 365 | bprm->cap_post_exec_permitted = cap_intersect( |
| 354 | cap_intersect(new_permitted, | 366 | bprm->cap_post_exec_permitted, |
| 355 | current->cap_permitted); | 367 | current->cap_permitted); |
| 356 | } | 368 | } |
| 357 | } | 369 | } |
| 358 | } | 370 | } |
| @@ -364,9 +376,9 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | |||
| 364 | * in the init_task struct. Thus we skip the usual | 376 | * in the init_task struct. Thus we skip the usual |
| 365 | * capability rules */ | 377 | * capability rules */ |
| 366 | if (!is_global_init(current)) { | 378 | if (!is_global_init(current)) { |
| 367 | current->cap_permitted = new_permitted; | 379 | current->cap_permitted = bprm->cap_post_exec_permitted; |
| 368 | if (bprm->cap_effective) | 380 | if (bprm->cap_effective) |
| 369 | current->cap_effective = new_permitted; | 381 | current->cap_effective = bprm->cap_post_exec_permitted; |
| 370 | else | 382 | else |
| 371 | cap_clear(current->cap_effective); | 383 | cap_clear(current->cap_effective); |
| 372 | } | 384 | } |
| @@ -381,9 +393,7 @@ int cap_bprm_secureexec (struct linux_binprm *bprm) | |||
| 381 | if (current->uid != 0) { | 393 | if (current->uid != 0) { |
| 382 | if (bprm->cap_effective) | 394 | if (bprm->cap_effective) |
| 383 | return 1; | 395 | return 1; |
| 384 | if (!cap_isclear(bprm->cap_permitted)) | 396 | if (!cap_isclear(bprm->cap_post_exec_permitted)) |
| 385 | return 1; | ||
| 386 | if (!cap_isclear(bprm->cap_inheritable)) | ||
| 387 | return 1; | 397 | return 1; |
| 388 | } | 398 | } |
| 389 | 399 | ||
diff --git a/security/device_cgroup.c b/security/device_cgroup.c index ddd92cec78ed..7bd296cca041 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c | |||
| @@ -41,6 +41,7 @@ struct dev_whitelist_item { | |||
| 41 | short type; | 41 | short type; |
| 42 | short access; | 42 | short access; |
| 43 | struct list_head list; | 43 | struct list_head list; |
| 44 | struct rcu_head rcu; | ||
| 44 | }; | 45 | }; |
| 45 | 46 | ||
| 46 | struct dev_cgroup { | 47 | struct dev_cgroup { |
| @@ -59,6 +60,11 @@ static inline struct dev_cgroup *cgroup_to_devcgroup(struct cgroup *cgroup) | |||
| 59 | return css_to_devcgroup(cgroup_subsys_state(cgroup, devices_subsys_id)); | 60 | return css_to_devcgroup(cgroup_subsys_state(cgroup, devices_subsys_id)); |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 63 | static inline struct dev_cgroup *task_devcgroup(struct task_struct *task) | ||
| 64 | { | ||
| 65 | return css_to_devcgroup(task_subsys_state(task, devices_subsys_id)); | ||
| 66 | } | ||
| 67 | |||
| 62 | struct cgroup_subsys devices_subsys; | 68 | struct cgroup_subsys devices_subsys; |
| 63 | 69 | ||
| 64 | static int devcgroup_can_attach(struct cgroup_subsys *ss, | 70 | static int devcgroup_can_attach(struct cgroup_subsys *ss, |
| @@ -128,11 +134,19 @@ static int dev_whitelist_add(struct dev_cgroup *dev_cgroup, | |||
| 128 | } | 134 | } |
| 129 | 135 | ||
| 130 | if (whcopy != NULL) | 136 | if (whcopy != NULL) |
| 131 | list_add_tail(&whcopy->list, &dev_cgroup->whitelist); | 137 | list_add_tail_rcu(&whcopy->list, &dev_cgroup->whitelist); |
| 132 | spin_unlock(&dev_cgroup->lock); | 138 | spin_unlock(&dev_cgroup->lock); |
| 133 | return 0; | 139 | return 0; |
| 134 | } | 140 | } |
| 135 | 141 | ||
| 142 | static void whitelist_item_free(struct rcu_head *rcu) | ||
| 143 | { | ||
| 144 | struct dev_whitelist_item *item; | ||
| 145 | |||
| 146 | item = container_of(rcu, struct dev_whitelist_item, rcu); | ||
| 147 | kfree(item); | ||
| 148 | } | ||
| 149 | |||
| 136 | /* | 150 | /* |
| 137 | * called under cgroup_lock() | 151 | * called under cgroup_lock() |
| 138 | * since the list is visible to other tasks, we need the spinlock also | 152 | * since the list is visible to other tasks, we need the spinlock also |
| @@ -156,8 +170,8 @@ static void dev_whitelist_rm(struct dev_cgroup *dev_cgroup, | |||
| 156 | remove: | 170 | remove: |
| 157 | walk->access &= ~wh->access; | 171 | walk->access &= ~wh->access; |
| 158 | if (!walk->access) { | 172 | if (!walk->access) { |
| 159 | list_del(&walk->list); | 173 | list_del_rcu(&walk->list); |
| 160 | kfree(walk); | 174 | call_rcu(&walk->rcu, whitelist_item_free); |
| 161 | } | 175 | } |
| 162 | } | 176 | } |
| 163 | spin_unlock(&dev_cgroup->lock); | 177 | spin_unlock(&dev_cgroup->lock); |
| @@ -188,7 +202,7 @@ static struct cgroup_subsys_state *devcgroup_create(struct cgroup_subsys *ss, | |||
| 188 | } | 202 | } |
| 189 | wh->minor = wh->major = ~0; | 203 | wh->minor = wh->major = ~0; |
| 190 | wh->type = DEV_ALL; | 204 | wh->type = DEV_ALL; |
| 191 | wh->access = ACC_MKNOD | ACC_READ | ACC_WRITE; | 205 | wh->access = ACC_MASK; |
| 192 | list_add(&wh->list, &dev_cgroup->whitelist); | 206 | list_add(&wh->list, &dev_cgroup->whitelist); |
| 193 | } else { | 207 | } else { |
| 194 | parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup); | 208 | parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup); |
| @@ -250,11 +264,10 @@ static char type_to_char(short type) | |||
| 250 | 264 | ||
| 251 | static void set_majmin(char *str, unsigned m) | 265 | static void set_majmin(char *str, unsigned m) |
| 252 | { | 266 | { |
| 253 | memset(str, 0, MAJMINLEN); | ||
| 254 | if (m == ~0) | 267 | if (m == ~0) |
| 255 | sprintf(str, "*"); | 268 | strcpy(str, "*"); |
| 256 | else | 269 | else |
| 257 | snprintf(str, MAJMINLEN, "%u", m); | 270 | sprintf(str, "%u", m); |
| 258 | } | 271 | } |
| 259 | 272 | ||
| 260 | static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft, | 273 | static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft, |
| @@ -264,15 +277,15 @@ static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft, | |||
| 264 | struct dev_whitelist_item *wh; | 277 | struct dev_whitelist_item *wh; |
| 265 | char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN]; | 278 | char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN]; |
| 266 | 279 | ||
| 267 | spin_lock(&devcgroup->lock); | 280 | rcu_read_lock(); |
| 268 | list_for_each_entry(wh, &devcgroup->whitelist, list) { | 281 | list_for_each_entry_rcu(wh, &devcgroup->whitelist, list) { |
| 269 | set_access(acc, wh->access); | 282 | set_access(acc, wh->access); |
| 270 | set_majmin(maj, wh->major); | 283 | set_majmin(maj, wh->major); |
| 271 | set_majmin(min, wh->minor); | 284 | set_majmin(min, wh->minor); |
| 272 | seq_printf(m, "%c %s:%s %s\n", type_to_char(wh->type), | 285 | seq_printf(m, "%c %s:%s %s\n", type_to_char(wh->type), |
| 273 | maj, min, acc); | 286 | maj, min, acc); |
| 274 | } | 287 | } |
| 275 | spin_unlock(&devcgroup->lock); | 288 | rcu_read_unlock(); |
| 276 | 289 | ||
| 277 | return 0; | 290 | return 0; |
| 278 | } | 291 | } |
| @@ -312,10 +325,10 @@ static int may_access_whitelist(struct dev_cgroup *c, | |||
| 312 | * when adding a new allow rule to a device whitelist, the rule | 325 | * when adding a new allow rule to a device whitelist, the rule |
| 313 | * must be allowed in the parent device | 326 | * must be allowed in the parent device |
| 314 | */ | 327 | */ |
| 315 | static int parent_has_perm(struct cgroup *childcg, | 328 | static int parent_has_perm(struct dev_cgroup *childcg, |
| 316 | struct dev_whitelist_item *wh) | 329 | struct dev_whitelist_item *wh) |
| 317 | { | 330 | { |
| 318 | struct cgroup *pcg = childcg->parent; | 331 | struct cgroup *pcg = childcg->css.cgroup->parent; |
| 319 | struct dev_cgroup *parent; | 332 | struct dev_cgroup *parent; |
| 320 | int ret; | 333 | int ret; |
| 321 | 334 | ||
| @@ -341,39 +354,19 @@ static int parent_has_perm(struct cgroup *childcg, | |||
| 341 | * new access is only allowed if you're in the top-level cgroup, or your | 354 | * new access is only allowed if you're in the top-level cgroup, or your |
| 342 | * parent cgroup has the access you're asking for. | 355 | * parent cgroup has the access you're asking for. |
| 343 | */ | 356 | */ |
| 344 | static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft, | 357 | static int devcgroup_update_access(struct dev_cgroup *devcgroup, |
| 345 | struct file *file, const char __user *userbuf, | 358 | int filetype, const char *buffer) |
| 346 | size_t nbytes, loff_t *ppos) | ||
| 347 | { | 359 | { |
| 348 | struct cgroup *cur_cgroup; | 360 | struct dev_cgroup *cur_devcgroup; |
| 349 | struct dev_cgroup *devcgroup, *cur_devcgroup; | 361 | const char *b; |
| 350 | int filetype = cft->private; | 362 | char *endp; |
| 351 | char *buffer, *b; | ||
| 352 | int retval = 0, count; | 363 | int retval = 0, count; |
| 353 | struct dev_whitelist_item wh; | 364 | struct dev_whitelist_item wh; |
| 354 | 365 | ||
| 355 | if (!capable(CAP_SYS_ADMIN)) | 366 | if (!capable(CAP_SYS_ADMIN)) |
| 356 | return -EPERM; | 367 | return -EPERM; |
| 357 | 368 | ||
| 358 | devcgroup = cgroup_to_devcgroup(cgroup); | 369 | cur_devcgroup = task_devcgroup(current); |
| 359 | cur_cgroup = task_cgroup(current, devices_subsys.subsys_id); | ||
| 360 | cur_devcgroup = cgroup_to_devcgroup(cur_cgroup); | ||
| 361 | |||
| 362 | buffer = kmalloc(nbytes+1, GFP_KERNEL); | ||
| 363 | if (!buffer) | ||
| 364 | return -ENOMEM; | ||
| 365 | |||
| 366 | if (copy_from_user(buffer, userbuf, nbytes)) { | ||
| 367 | retval = -EFAULT; | ||
| 368 | goto out1; | ||
| 369 | } | ||
| 370 | buffer[nbytes] = 0; /* nul-terminate */ | ||
| 371 | |||
| 372 | cgroup_lock(); | ||
| 373 | if (cgroup_is_removed(cgroup)) { | ||
| 374 | retval = -ENODEV; | ||
| 375 | goto out2; | ||
| 376 | } | ||
| 377 | 370 | ||
| 378 | memset(&wh, 0, sizeof(wh)); | 371 | memset(&wh, 0, sizeof(wh)); |
| 379 | b = buffer; | 372 | b = buffer; |
| @@ -392,32 +385,23 @@ static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft, | |||
| 392 | wh.type = DEV_CHAR; | 385 | wh.type = DEV_CHAR; |
| 393 | break; | 386 | break; |
| 394 | default: | 387 | default: |
| 395 | retval = -EINVAL; | 388 | return -EINVAL; |
| 396 | goto out2; | ||
| 397 | } | 389 | } |
| 398 | b++; | 390 | b++; |
| 399 | if (!isspace(*b)) { | 391 | if (!isspace(*b)) |
| 400 | retval = -EINVAL; | 392 | return -EINVAL; |
| 401 | goto out2; | ||
| 402 | } | ||
| 403 | b++; | 393 | b++; |
| 404 | if (*b == '*') { | 394 | if (*b == '*') { |
| 405 | wh.major = ~0; | 395 | wh.major = ~0; |
| 406 | b++; | 396 | b++; |
| 407 | } else if (isdigit(*b)) { | 397 | } else if (isdigit(*b)) { |
| 408 | wh.major = 0; | 398 | wh.major = simple_strtoul(b, &endp, 10); |
| 409 | while (isdigit(*b)) { | 399 | b = endp; |
| 410 | wh.major = wh.major*10+(*b-'0'); | ||
| 411 | b++; | ||
| 412 | } | ||
| 413 | } else { | 400 | } else { |
| 414 | retval = -EINVAL; | 401 | return -EINVAL; |
| 415 | goto out2; | ||
| 416 | } | ||
| 417 | if (*b != ':') { | ||
| 418 | retval = -EINVAL; | ||
| 419 | goto out2; | ||
| 420 | } | 402 | } |
| 403 | if (*b != ':') | ||
| 404 | return -EINVAL; | ||
| 421 | b++; | 405 | b++; |
| 422 | 406 | ||
| 423 | /* read minor */ | 407 | /* read minor */ |
| @@ -425,19 +409,13 @@ static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft, | |||
| 425 | wh.minor = ~0; | 409 | wh.minor = ~0; |
| 426 | b++; | 410 | b++; |
| 427 | } else if (isdigit(*b)) { | 411 | } else if (isdigit(*b)) { |
| 428 | wh.minor = 0; | 412 | wh.minor = simple_strtoul(b, &endp, 10); |
| 429 | while (isdigit(*b)) { | 413 | b = endp; |
| 430 | wh.minor = wh.minor*10+(*b-'0'); | ||
| 431 | b++; | ||
| 432 | } | ||
| 433 | } else { | 414 | } else { |
| 434 | retval = -EINVAL; | 415 | return -EINVAL; |
| 435 | goto out2; | ||
| 436 | } | ||
| 437 | if (!isspace(*b)) { | ||
| 438 | retval = -EINVAL; | ||
| 439 | goto out2; | ||
| 440 | } | 416 | } |
| 417 | if (!isspace(*b)) | ||
| 418 | return -EINVAL; | ||
| 441 | for (b++, count = 0; count < 3; count++, b++) { | 419 | for (b++, count = 0; count < 3; count++, b++) { |
| 442 | switch (*b) { | 420 | switch (*b) { |
| 443 | case 'r': | 421 | case 'r': |
| @@ -454,8 +432,7 @@ static ssize_t devcgroup_access_write(struct cgroup *cgroup, struct cftype *cft, | |||
| 454 | count = 3; | 432 | count = 3; |
| 455 | break; | 433 | break; |
| 456 | default: | 434 | default: |
| 457 | retval = -EINVAL; | 435 | return -EINVAL; |
| 458 | goto out2; | ||
| 459 | } | 436 | } |
| 460 | } | 437 | } |
| 461 | 438 | ||
| @@ -463,38 +440,39 @@ handle: | |||
| 463 | retval = 0; | 440 | retval = 0; |
| 464 | switch (filetype) { | 441 | switch (filetype) { |
| 465 | case DEVCG_ALLOW: | 442 | case DEVCG_ALLOW: |
| 466 | if (!parent_has_perm(cgroup, &wh)) | 443 | if (!parent_has_perm(devcgroup, &wh)) |
| 467 | retval = -EPERM; | 444 | return -EPERM; |
| 468 | else | 445 | return dev_whitelist_add(devcgroup, &wh); |
| 469 | retval = dev_whitelist_add(devcgroup, &wh); | ||
| 470 | break; | ||
| 471 | case DEVCG_DENY: | 446 | case DEVCG_DENY: |
| 472 | dev_whitelist_rm(devcgroup, &wh); | 447 | dev_whitelist_rm(devcgroup, &wh); |
| 473 | break; | 448 | break; |
| 474 | default: | 449 | default: |
| 475 | retval = -EINVAL; | 450 | return -EINVAL; |
| 476 | goto out2; | ||
| 477 | } | 451 | } |
| 452 | return 0; | ||
| 453 | } | ||
| 478 | 454 | ||
| 479 | if (retval == 0) | 455 | static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft, |
| 480 | retval = nbytes; | 456 | const char *buffer) |
| 481 | 457 | { | |
| 482 | out2: | 458 | int retval; |
| 459 | if (!cgroup_lock_live_group(cgrp)) | ||
| 460 | return -ENODEV; | ||
| 461 | retval = devcgroup_update_access(cgroup_to_devcgroup(cgrp), | ||
| 462 | cft->private, buffer); | ||
| 483 | cgroup_unlock(); | 463 | cgroup_unlock(); |
| 484 | out1: | ||
| 485 | kfree(buffer); | ||
| 486 | return retval; | 464 | return retval; |
| 487 | } | 465 | } |
| 488 | 466 | ||
| 489 | static struct cftype dev_cgroup_files[] = { | 467 | static struct cftype dev_cgroup_files[] = { |
| 490 | { | 468 | { |
| 491 | .name = "allow", | 469 | .name = "allow", |
| 492 | .write = devcgroup_access_write, | 470 | .write_string = devcgroup_access_write, |
| 493 | .private = DEVCG_ALLOW, | 471 | .private = DEVCG_ALLOW, |
| 494 | }, | 472 | }, |
| 495 | { | 473 | { |
| 496 | .name = "deny", | 474 | .name = "deny", |
| 497 | .write = devcgroup_access_write, | 475 | .write_string = devcgroup_access_write, |
| 498 | .private = DEVCG_DENY, | 476 | .private = DEVCG_DENY, |
| 499 | }, | 477 | }, |
| 500 | { | 478 | { |
| @@ -535,8 +513,8 @@ int devcgroup_inode_permission(struct inode *inode, int mask) | |||
| 535 | if (!dev_cgroup) | 513 | if (!dev_cgroup) |
| 536 | return 0; | 514 | return 0; |
| 537 | 515 | ||
| 538 | spin_lock(&dev_cgroup->lock); | 516 | rcu_read_lock(); |
| 539 | list_for_each_entry(wh, &dev_cgroup->whitelist, list) { | 517 | list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) { |
| 540 | if (wh->type & DEV_ALL) | 518 | if (wh->type & DEV_ALL) |
| 541 | goto acc_check; | 519 | goto acc_check; |
| 542 | if ((wh->type & DEV_BLOCK) && !S_ISBLK(inode->i_mode)) | 520 | if ((wh->type & DEV_BLOCK) && !S_ISBLK(inode->i_mode)) |
| @@ -552,10 +530,10 @@ acc_check: | |||
| 552 | continue; | 530 | continue; |
| 553 | if ((mask & MAY_READ) && !(wh->access & ACC_READ)) | 531 | if ((mask & MAY_READ) && !(wh->access & ACC_READ)) |
| 554 | continue; | 532 | continue; |
| 555 | spin_unlock(&dev_cgroup->lock); | 533 | rcu_read_unlock(); |
| 556 | return 0; | 534 | return 0; |
| 557 | } | 535 | } |
| 558 | spin_unlock(&dev_cgroup->lock); | 536 | rcu_read_unlock(); |
| 559 | 537 | ||
| 560 | return -EPERM; | 538 | return -EPERM; |
| 561 | } | 539 | } |
| @@ -570,7 +548,7 @@ int devcgroup_inode_mknod(int mode, dev_t dev) | |||
| 570 | if (!dev_cgroup) | 548 | if (!dev_cgroup) |
| 571 | return 0; | 549 | return 0; |
| 572 | 550 | ||
| 573 | spin_lock(&dev_cgroup->lock); | 551 | rcu_read_lock(); |
| 574 | list_for_each_entry(wh, &dev_cgroup->whitelist, list) { | 552 | list_for_each_entry(wh, &dev_cgroup->whitelist, list) { |
| 575 | if (wh->type & DEV_ALL) | 553 | if (wh->type & DEV_ALL) |
| 576 | goto acc_check; | 554 | goto acc_check; |
| @@ -585,9 +563,9 @@ int devcgroup_inode_mknod(int mode, dev_t dev) | |||
| 585 | acc_check: | 563 | acc_check: |
| 586 | if (!(wh->access & ACC_MKNOD)) | 564 | if (!(wh->access & ACC_MKNOD)) |
| 587 | continue; | 565 | continue; |
| 588 | spin_unlock(&dev_cgroup->lock); | 566 | rcu_read_unlock(); |
| 589 | return 0; | 567 | return 0; |
| 590 | } | 568 | } |
| 591 | spin_unlock(&dev_cgroup->lock); | 569 | rcu_read_unlock(); |
| 592 | return -EPERM; | 570 | return -EPERM; |
| 593 | } | 571 | } |
diff --git a/security/security.c b/security/security.c index 59f23b5918b3..ff7068727757 100644 --- a/security/security.c +++ b/security/security.c | |||
| @@ -429,11 +429,11 @@ int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
| 429 | return security_ops->inode_follow_link(dentry, nd); | 429 | return security_ops->inode_follow_link(dentry, nd); |
| 430 | } | 430 | } |
| 431 | 431 | ||
| 432 | int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd) | 432 | int security_inode_permission(struct inode *inode, int mask) |
| 433 | { | 433 | { |
| 434 | if (unlikely(IS_PRIVATE(inode))) | 434 | if (unlikely(IS_PRIVATE(inode))) |
| 435 | return 0; | 435 | return 0; |
| 436 | return security_ops->inode_permission(inode, mask, nd); | 436 | return security_ops->inode_permission(inode, mask); |
| 437 | } | 437 | } |
| 438 | 438 | ||
| 439 | int security_inode_setattr(struct dentry *dentry, struct iattr *attr) | 439 | int security_inode_setattr(struct dentry *dentry, struct iattr *attr) |
| @@ -442,6 +442,7 @@ int security_inode_setattr(struct dentry *dentry, struct iattr *attr) | |||
| 442 | return 0; | 442 | return 0; |
| 443 | return security_ops->inode_setattr(dentry, attr); | 443 | return security_ops->inode_setattr(dentry, attr); |
| 444 | } | 444 | } |
| 445 | EXPORT_SYMBOL_GPL(security_inode_setattr); | ||
| 445 | 446 | ||
| 446 | int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) | 447 | int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) |
| 447 | { | 448 | { |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 63f131fc42e4..40d06c533f89 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
| @@ -25,7 +25,7 @@ | |||
| 25 | 25 | ||
| 26 | #include <linux/init.h> | 26 | #include <linux/init.h> |
| 27 | #include <linux/kernel.h> | 27 | #include <linux/kernel.h> |
| 28 | #include <linux/ptrace.h> | 28 | #include <linux/tracehook.h> |
| 29 | #include <linux/errno.h> | 29 | #include <linux/errno.h> |
| 30 | #include <linux/sched.h> | 30 | #include <linux/sched.h> |
| 31 | #include <linux/security.h> | 31 | #include <linux/security.h> |
| @@ -1971,22 +1971,6 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) | |||
| 1971 | return __vm_enough_memory(mm, pages, cap_sys_admin); | 1971 | return __vm_enough_memory(mm, pages, cap_sys_admin); |
| 1972 | } | 1972 | } |
| 1973 | 1973 | ||
| 1974 | /** | ||
| 1975 | * task_tracer_task - return the task that is tracing the given task | ||
| 1976 | * @task: task to consider | ||
| 1977 | * | ||
| 1978 | * Returns NULL if noone is tracing @task, or the &struct task_struct | ||
| 1979 | * pointer to its tracer. | ||
| 1980 | * | ||
| 1981 | * Must be called under rcu_read_lock(). | ||
| 1982 | */ | ||
| 1983 | static struct task_struct *task_tracer_task(struct task_struct *task) | ||
| 1984 | { | ||
| 1985 | if (task->ptrace & PT_PTRACED) | ||
| 1986 | return rcu_dereference(task->parent); | ||
| 1987 | return NULL; | ||
| 1988 | } | ||
| 1989 | |||
| 1990 | /* binprm security operations */ | 1974 | /* binprm security operations */ |
| 1991 | 1975 | ||
| 1992 | static int selinux_bprm_alloc_security(struct linux_binprm *bprm) | 1976 | static int selinux_bprm_alloc_security(struct linux_binprm *bprm) |
| @@ -2238,7 +2222,7 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) | |||
| 2238 | u32 ptsid = 0; | 2222 | u32 ptsid = 0; |
| 2239 | 2223 | ||
| 2240 | rcu_read_lock(); | 2224 | rcu_read_lock(); |
| 2241 | tracer = task_tracer_task(current); | 2225 | tracer = tracehook_tracer_task(current); |
| 2242 | if (likely(tracer != NULL)) { | 2226 | if (likely(tracer != NULL)) { |
| 2243 | sec = tracer->security; | 2227 | sec = tracer->security; |
| 2244 | ptsid = sec->sid; | 2228 | ptsid = sec->sid; |
| @@ -2640,12 +2624,11 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na | |||
| 2640 | return dentry_has_perm(current, NULL, dentry, FILE__READ); | 2624 | return dentry_has_perm(current, NULL, dentry, FILE__READ); |
| 2641 | } | 2625 | } |
| 2642 | 2626 | ||
| 2643 | static int selinux_inode_permission(struct inode *inode, int mask, | 2627 | static int selinux_inode_permission(struct inode *inode, int mask) |
| 2644 | struct nameidata *nd) | ||
| 2645 | { | 2628 | { |
| 2646 | int rc; | 2629 | int rc; |
| 2647 | 2630 | ||
| 2648 | rc = secondary_ops->inode_permission(inode, mask, nd); | 2631 | rc = secondary_ops->inode_permission(inode, mask); |
| 2649 | if (rc) | 2632 | if (rc) |
| 2650 | return rc; | 2633 | return rc; |
| 2651 | 2634 | ||
| @@ -5247,7 +5230,7 @@ static int selinux_setprocattr(struct task_struct *p, | |||
| 5247 | Otherwise, leave SID unchanged and fail. */ | 5230 | Otherwise, leave SID unchanged and fail. */ |
| 5248 | task_lock(p); | 5231 | task_lock(p); |
| 5249 | rcu_read_lock(); | 5232 | rcu_read_lock(); |
| 5250 | tracer = task_tracer_task(p); | 5233 | tracer = tracehook_tracer_task(p); |
| 5251 | if (tracer != NULL) { | 5234 | if (tracer != NULL) { |
| 5252 | struct task_security_struct *ptsec = tracer->security; | 5235 | struct task_security_struct *ptsec = tracer->security; |
| 5253 | u32 ptsid = ptsec->sid; | 5236 | u32 ptsid = ptsec->sid; |
| @@ -5670,27 +5653,20 @@ static struct nf_hook_ops selinux_ipv6_ops[] = { | |||
| 5670 | static int __init selinux_nf_ip_init(void) | 5653 | static int __init selinux_nf_ip_init(void) |
| 5671 | { | 5654 | { |
| 5672 | int err = 0; | 5655 | int err = 0; |
| 5673 | u32 iter; | ||
| 5674 | 5656 | ||
| 5675 | if (!selinux_enabled) | 5657 | if (!selinux_enabled) |
| 5676 | goto out; | 5658 | goto out; |
| 5677 | 5659 | ||
| 5678 | printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n"); | 5660 | printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n"); |
| 5679 | 5661 | ||
| 5680 | for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++) { | 5662 | err = nf_register_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops)); |
| 5681 | err = nf_register_hook(&selinux_ipv4_ops[iter]); | 5663 | if (err) |
| 5682 | if (err) | 5664 | panic("SELinux: nf_register_hooks for IPv4: error %d\n", err); |
| 5683 | panic("SELinux: nf_register_hook for IPv4: error %d\n", | ||
| 5684 | err); | ||
| 5685 | } | ||
| 5686 | 5665 | ||
| 5687 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 5666 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
| 5688 | for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++) { | 5667 | err = nf_register_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops)); |
| 5689 | err = nf_register_hook(&selinux_ipv6_ops[iter]); | 5668 | if (err) |
| 5690 | if (err) | 5669 | panic("SELinux: nf_register_hooks for IPv6: error %d\n", err); |
| 5691 | panic("SELinux: nf_register_hook for IPv6: error %d\n", | ||
| 5692 | err); | ||
| 5693 | } | ||
| 5694 | #endif /* IPV6 */ | 5670 | #endif /* IPV6 */ |
| 5695 | 5671 | ||
| 5696 | out: | 5672 | out: |
| @@ -5702,15 +5678,11 @@ __initcall(selinux_nf_ip_init); | |||
| 5702 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE | 5678 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE |
| 5703 | static void selinux_nf_ip_exit(void) | 5679 | static void selinux_nf_ip_exit(void) |
| 5704 | { | 5680 | { |
| 5705 | u32 iter; | ||
| 5706 | |||
| 5707 | printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n"); | 5681 | printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n"); |
| 5708 | 5682 | ||
| 5709 | for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++) | 5683 | nf_unregister_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops)); |
| 5710 | nf_unregister_hook(&selinux_ipv4_ops[iter]); | ||
| 5711 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 5684 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
| 5712 | for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++) | 5685 | nf_unregister_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops)); |
| 5713 | nf_unregister_hook(&selinux_ipv6_ops[iter]); | ||
| 5714 | #endif /* IPV6 */ | 5686 | #endif /* IPV6 */ |
| 5715 | } | 5687 | } |
| 5716 | #endif | 5688 | #endif |
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index ee5a51cbc5eb..1b40e558f983 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c | |||
| @@ -522,8 +522,7 @@ static int smack_inode_rename(struct inode *old_inode, | |||
| 522 | * | 522 | * |
| 523 | * Returns 0 if access is permitted, -EACCES otherwise | 523 | * Returns 0 if access is permitted, -EACCES otherwise |
| 524 | */ | 524 | */ |
| 525 | static int smack_inode_permission(struct inode *inode, int mask, | 525 | static int smack_inode_permission(struct inode *inode, int mask) |
| 526 | struct nameidata *nd) | ||
| 527 | { | 526 | { |
| 528 | /* | 527 | /* |
| 529 | * No permission to check. Existence test. Yup, it's there. | 528 | * No permission to check. Existence test. Yup, it's there. |
