diff options
Diffstat (limited to 'security')
33 files changed, 1720 insertions, 619 deletions
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 9b9013b2e321..d49c53960b60 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig | |||
| @@ -29,3 +29,15 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE | |||
| 29 | boot. | 29 | boot. |
| 30 | 30 | ||
| 31 | If you are unsure how to answer this question, answer 1. | 31 | If you are unsure how to answer this question, answer 1. |
| 32 | |||
| 33 | config SECURITY_APPARMOR_HASH | ||
| 34 | bool "SHA1 hash of loaded profiles" | ||
| 35 | depends on SECURITY_APPARMOR | ||
| 36 | depends on CRYPTO | ||
| 37 | select CRYPTO_SHA1 | ||
| 38 | default y | ||
| 39 | |||
| 40 | help | ||
| 41 | This option selects whether sha1 hashing is done against loaded | ||
| 42 | profiles and exported for inspection to user space via the apparmor | ||
| 43 | filesystem. | ||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 5706b74c857f..d693df874818 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile | |||
| @@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o | |||
| 5 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ | 5 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ |
| 6 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ | 6 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ |
| 7 | resource.o sid.o file.o | 7 | resource.o sid.o file.o |
| 8 | apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o | ||
| 8 | 9 | ||
| 9 | clean-files := capability_names.h rlim_names.h | 10 | clean-files := capability_names.h rlim_names.h |
| 10 | 11 | ||
| @@ -18,7 +19,11 @@ quiet_cmd_make-caps = GEN $@ | |||
| 18 | cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ | 19 | cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ |
| 19 | sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \ | 20 | sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \ |
| 20 | -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ | 21 | -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\ |
| 21 | echo "};" >> $@ | 22 | echo "};" >> $@ ;\ |
| 23 | echo -n '\#define AA_FS_CAPS_MASK "' >> $@ ;\ | ||
| 24 | sed $< -r -n -e '/CAP_FS_MASK/d' \ | ||
| 25 | -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ | ||
| 26 | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ | ||
| 22 | 27 | ||
| 23 | 28 | ||
| 24 | # Build a lower case string table of rlimit names. | 29 | # Build a lower case string table of rlimit names. |
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 16c15ec6f670..7db9954f1af2 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | * License. | 12 | * License. |
| 13 | */ | 13 | */ |
| 14 | 14 | ||
| 15 | #include <linux/ctype.h> | ||
| 15 | #include <linux/security.h> | 16 | #include <linux/security.h> |
| 16 | #include <linux/vmalloc.h> | 17 | #include <linux/vmalloc.h> |
| 17 | #include <linux/module.h> | 18 | #include <linux/module.h> |
| @@ -19,15 +20,56 @@ | |||
| 19 | #include <linux/uaccess.h> | 20 | #include <linux/uaccess.h> |
| 20 | #include <linux/namei.h> | 21 | #include <linux/namei.h> |
| 21 | #include <linux/capability.h> | 22 | #include <linux/capability.h> |
| 23 | #include <linux/rcupdate.h> | ||
| 22 | 24 | ||
| 23 | #include "include/apparmor.h" | 25 | #include "include/apparmor.h" |
| 24 | #include "include/apparmorfs.h" | 26 | #include "include/apparmorfs.h" |
| 25 | #include "include/audit.h" | 27 | #include "include/audit.h" |
| 26 | #include "include/context.h" | 28 | #include "include/context.h" |
| 29 | #include "include/crypto.h" | ||
| 27 | #include "include/policy.h" | 30 | #include "include/policy.h" |
| 28 | #include "include/resource.h" | 31 | #include "include/resource.h" |
| 29 | 32 | ||
| 30 | /** | 33 | /** |
| 34 | * aa_mangle_name - mangle a profile name to std profile layout form | ||
| 35 | * @name: profile name to mangle (NOT NULL) | ||
| 36 | * @target: buffer to store mangled name, same length as @name (MAYBE NULL) | ||
| 37 | * | ||
| 38 | * Returns: length of mangled name | ||
| 39 | */ | ||
| 40 | static int mangle_name(char *name, char *target) | ||
| 41 | { | ||
| 42 | char *t = target; | ||
| 43 | |||
| 44 | while (*name == '/' || *name == '.') | ||
| 45 | name++; | ||
| 46 | |||
| 47 | if (target) { | ||
| 48 | for (; *name; name++) { | ||
| 49 | if (*name == '/') | ||
| 50 | *(t)++ = '.'; | ||
| 51 | else if (isspace(*name)) | ||
| 52 | *(t)++ = '_'; | ||
| 53 | else if (isalnum(*name) || strchr("._-", *name)) | ||
| 54 | *(t)++ = *name; | ||
| 55 | } | ||
| 56 | |||
| 57 | *t = 0; | ||
| 58 | } else { | ||
| 59 | int len = 0; | ||
| 60 | for (; *name; name++) { | ||
| 61 | if (isalnum(*name) || isspace(*name) || | ||
| 62 | strchr("/._-", *name)) | ||
| 63 | len++; | ||
| 64 | } | ||
| 65 | |||
| 66 | return len; | ||
| 67 | } | ||
| 68 | |||
| 69 | return t - target; | ||
| 70 | } | ||
| 71 | |||
| 72 | /** | ||
| 31 | * aa_simple_write_to_buffer - common routine for getting policy from user | 73 | * aa_simple_write_to_buffer - common routine for getting policy from user |
| 32 | * @op: operation doing the user buffer copy | 74 | * @op: operation doing the user buffer copy |
| 33 | * @userbuf: user buffer to copy data from (NOT NULL) | 75 | * @userbuf: user buffer to copy data from (NOT NULL) |
| @@ -182,8 +224,565 @@ const struct file_operations aa_fs_seq_file_ops = { | |||
| 182 | .release = single_release, | 224 | .release = single_release, |
| 183 | }; | 225 | }; |
| 184 | 226 | ||
| 185 | /** Base file system setup **/ | 227 | static int aa_fs_seq_profile_open(struct inode *inode, struct file *file, |
| 228 | int (*show)(struct seq_file *, void *)) | ||
| 229 | { | ||
| 230 | struct aa_replacedby *r = aa_get_replacedby(inode->i_private); | ||
| 231 | int error = single_open(file, show, r); | ||
| 232 | |||
| 233 | if (error) { | ||
| 234 | file->private_data = NULL; | ||
| 235 | aa_put_replacedby(r); | ||
| 236 | } | ||
| 237 | |||
| 238 | return error; | ||
| 239 | } | ||
| 240 | |||
| 241 | static int aa_fs_seq_profile_release(struct inode *inode, struct file *file) | ||
| 242 | { | ||
| 243 | struct seq_file *seq = (struct seq_file *) file->private_data; | ||
| 244 | if (seq) | ||
| 245 | aa_put_replacedby(seq->private); | ||
| 246 | return single_release(inode, file); | ||
| 247 | } | ||
| 248 | |||
| 249 | static int aa_fs_seq_profname_show(struct seq_file *seq, void *v) | ||
| 250 | { | ||
| 251 | struct aa_replacedby *r = seq->private; | ||
| 252 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | ||
| 253 | seq_printf(seq, "%s\n", profile->base.name); | ||
| 254 | aa_put_profile(profile); | ||
| 255 | |||
| 256 | return 0; | ||
| 257 | } | ||
| 258 | |||
| 259 | static int aa_fs_seq_profname_open(struct inode *inode, struct file *file) | ||
| 260 | { | ||
| 261 | return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profname_show); | ||
| 262 | } | ||
| 263 | |||
| 264 | static const struct file_operations aa_fs_profname_fops = { | ||
| 265 | .owner = THIS_MODULE, | ||
| 266 | .open = aa_fs_seq_profname_open, | ||
| 267 | .read = seq_read, | ||
| 268 | .llseek = seq_lseek, | ||
| 269 | .release = aa_fs_seq_profile_release, | ||
| 270 | }; | ||
| 271 | |||
| 272 | static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v) | ||
| 273 | { | ||
| 274 | struct aa_replacedby *r = seq->private; | ||
| 275 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | ||
| 276 | seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]); | ||
| 277 | aa_put_profile(profile); | ||
| 278 | |||
| 279 | return 0; | ||
| 280 | } | ||
| 281 | |||
| 282 | static int aa_fs_seq_profmode_open(struct inode *inode, struct file *file) | ||
| 283 | { | ||
| 284 | return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profmode_show); | ||
| 285 | } | ||
| 286 | |||
| 287 | static const struct file_operations aa_fs_profmode_fops = { | ||
| 288 | .owner = THIS_MODULE, | ||
| 289 | .open = aa_fs_seq_profmode_open, | ||
| 290 | .read = seq_read, | ||
| 291 | .llseek = seq_lseek, | ||
| 292 | .release = aa_fs_seq_profile_release, | ||
| 293 | }; | ||
| 294 | |||
| 295 | static int aa_fs_seq_profattach_show(struct seq_file *seq, void *v) | ||
| 296 | { | ||
| 297 | struct aa_replacedby *r = seq->private; | ||
| 298 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | ||
| 299 | if (profile->attach) | ||
| 300 | seq_printf(seq, "%s\n", profile->attach); | ||
| 301 | else if (profile->xmatch) | ||
| 302 | seq_puts(seq, "<unknown>\n"); | ||
| 303 | else | ||
| 304 | seq_printf(seq, "%s\n", profile->base.name); | ||
| 305 | aa_put_profile(profile); | ||
| 306 | |||
| 307 | return 0; | ||
| 308 | } | ||
| 309 | |||
| 310 | static int aa_fs_seq_profattach_open(struct inode *inode, struct file *file) | ||
| 311 | { | ||
| 312 | return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profattach_show); | ||
| 313 | } | ||
| 314 | |||
| 315 | static const struct file_operations aa_fs_profattach_fops = { | ||
| 316 | .owner = THIS_MODULE, | ||
| 317 | .open = aa_fs_seq_profattach_open, | ||
| 318 | .read = seq_read, | ||
| 319 | .llseek = seq_lseek, | ||
| 320 | .release = aa_fs_seq_profile_release, | ||
| 321 | }; | ||
| 322 | |||
| 323 | static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) | ||
| 324 | { | ||
| 325 | struct aa_replacedby *r = seq->private; | ||
| 326 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | ||
| 327 | unsigned int i, size = aa_hash_size(); | ||
| 328 | |||
| 329 | if (profile->hash) { | ||
| 330 | for (i = 0; i < size; i++) | ||
| 331 | seq_printf(seq, "%.2x", profile->hash[i]); | ||
| 332 | seq_puts(seq, "\n"); | ||
| 333 | } | ||
| 334 | |||
| 335 | return 0; | ||
| 336 | } | ||
| 337 | |||
| 338 | static int aa_fs_seq_hash_open(struct inode *inode, struct file *file) | ||
| 339 | { | ||
| 340 | return single_open(file, aa_fs_seq_hash_show, inode->i_private); | ||
| 341 | } | ||
| 342 | |||
| 343 | static const struct file_operations aa_fs_seq_hash_fops = { | ||
| 344 | .owner = THIS_MODULE, | ||
| 345 | .open = aa_fs_seq_hash_open, | ||
| 346 | .read = seq_read, | ||
| 347 | .llseek = seq_lseek, | ||
| 348 | .release = single_release, | ||
| 349 | }; | ||
| 350 | |||
| 351 | /** fns to setup dynamic per profile/namespace files **/ | ||
| 352 | void __aa_fs_profile_rmdir(struct aa_profile *profile) | ||
| 353 | { | ||
| 354 | struct aa_profile *child; | ||
| 355 | int i; | ||
| 356 | |||
| 357 | if (!profile) | ||
| 358 | return; | ||
| 359 | |||
| 360 | list_for_each_entry(child, &profile->base.profiles, base.list) | ||
| 361 | __aa_fs_profile_rmdir(child); | ||
| 362 | |||
| 363 | for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) { | ||
| 364 | struct aa_replacedby *r; | ||
| 365 | if (!profile->dents[i]) | ||
| 366 | continue; | ||
| 367 | |||
| 368 | r = profile->dents[i]->d_inode->i_private; | ||
| 369 | securityfs_remove(profile->dents[i]); | ||
| 370 | aa_put_replacedby(r); | ||
| 371 | profile->dents[i] = NULL; | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | void __aa_fs_profile_migrate_dents(struct aa_profile *old, | ||
| 376 | struct aa_profile *new) | ||
| 377 | { | ||
| 378 | int i; | ||
| 379 | |||
| 380 | for (i = 0; i < AAFS_PROF_SIZEOF; i++) { | ||
| 381 | new->dents[i] = old->dents[i]; | ||
| 382 | old->dents[i] = NULL; | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 386 | static struct dentry *create_profile_file(struct dentry *dir, const char *name, | ||
| 387 | struct aa_profile *profile, | ||
| 388 | const struct file_operations *fops) | ||
| 389 | { | ||
| 390 | struct aa_replacedby *r = aa_get_replacedby(profile->replacedby); | ||
| 391 | struct dentry *dent; | ||
| 392 | |||
| 393 | dent = securityfs_create_file(name, S_IFREG | 0444, dir, r, fops); | ||
| 394 | if (IS_ERR(dent)) | ||
| 395 | aa_put_replacedby(r); | ||
| 396 | |||
| 397 | return dent; | ||
| 398 | } | ||
| 399 | |||
| 400 | /* requires lock be held */ | ||
| 401 | int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) | ||
| 402 | { | ||
| 403 | struct aa_profile *child; | ||
| 404 | struct dentry *dent = NULL, *dir; | ||
| 405 | int error; | ||
| 406 | |||
| 407 | if (!parent) { | ||
| 408 | struct aa_profile *p; | ||
| 409 | p = aa_deref_parent(profile); | ||
| 410 | dent = prof_dir(p); | ||
| 411 | /* adding to parent that previously didn't have children */ | ||
| 412 | dent = securityfs_create_dir("profiles", dent); | ||
| 413 | if (IS_ERR(dent)) | ||
| 414 | goto fail; | ||
| 415 | prof_child_dir(p) = parent = dent; | ||
| 416 | } | ||
| 417 | |||
| 418 | if (!profile->dirname) { | ||
| 419 | int len, id_len; | ||
| 420 | len = mangle_name(profile->base.name, NULL); | ||
| 421 | id_len = snprintf(NULL, 0, ".%ld", profile->ns->uniq_id); | ||
| 422 | |||
| 423 | profile->dirname = kmalloc(len + id_len + 1, GFP_KERNEL); | ||
| 424 | if (!profile->dirname) | ||
| 425 | goto fail; | ||
| 426 | |||
| 427 | mangle_name(profile->base.name, profile->dirname); | ||
| 428 | sprintf(profile->dirname + len, ".%ld", profile->ns->uniq_id++); | ||
| 429 | } | ||
| 430 | |||
| 431 | dent = securityfs_create_dir(profile->dirname, parent); | ||
| 432 | if (IS_ERR(dent)) | ||
| 433 | goto fail; | ||
| 434 | prof_dir(profile) = dir = dent; | ||
| 435 | |||
| 436 | dent = create_profile_file(dir, "name", profile, &aa_fs_profname_fops); | ||
| 437 | if (IS_ERR(dent)) | ||
| 438 | goto fail; | ||
| 439 | profile->dents[AAFS_PROF_NAME] = dent; | ||
| 440 | |||
| 441 | dent = create_profile_file(dir, "mode", profile, &aa_fs_profmode_fops); | ||
| 442 | if (IS_ERR(dent)) | ||
| 443 | goto fail; | ||
| 444 | profile->dents[AAFS_PROF_MODE] = dent; | ||
| 445 | |||
| 446 | dent = create_profile_file(dir, "attach", profile, | ||
| 447 | &aa_fs_profattach_fops); | ||
| 448 | if (IS_ERR(dent)) | ||
| 449 | goto fail; | ||
| 450 | profile->dents[AAFS_PROF_ATTACH] = dent; | ||
| 451 | |||
| 452 | if (profile->hash) { | ||
| 453 | dent = create_profile_file(dir, "sha1", profile, | ||
| 454 | &aa_fs_seq_hash_fops); | ||
| 455 | if (IS_ERR(dent)) | ||
| 456 | goto fail; | ||
| 457 | profile->dents[AAFS_PROF_HASH] = dent; | ||
| 458 | } | ||
| 459 | |||
| 460 | list_for_each_entry(child, &profile->base.profiles, base.list) { | ||
| 461 | error = __aa_fs_profile_mkdir(child, prof_child_dir(profile)); | ||
| 462 | if (error) | ||
| 463 | goto fail2; | ||
| 464 | } | ||
| 465 | |||
| 466 | return 0; | ||
| 467 | |||
| 468 | fail: | ||
| 469 | error = PTR_ERR(dent); | ||
| 470 | |||
| 471 | fail2: | ||
| 472 | __aa_fs_profile_rmdir(profile); | ||
| 473 | |||
| 474 | return error; | ||
| 475 | } | ||
| 476 | |||
| 477 | void __aa_fs_namespace_rmdir(struct aa_namespace *ns) | ||
| 478 | { | ||
| 479 | struct aa_namespace *sub; | ||
| 480 | struct aa_profile *child; | ||
| 481 | int i; | ||
| 482 | |||
| 483 | if (!ns) | ||
| 484 | return; | ||
| 485 | |||
| 486 | list_for_each_entry(child, &ns->base.profiles, base.list) | ||
| 487 | __aa_fs_profile_rmdir(child); | ||
| 488 | |||
| 489 | list_for_each_entry(sub, &ns->sub_ns, base.list) { | ||
| 490 | mutex_lock(&sub->lock); | ||
| 491 | __aa_fs_namespace_rmdir(sub); | ||
| 492 | mutex_unlock(&sub->lock); | ||
| 493 | } | ||
| 186 | 494 | ||
| 495 | for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) { | ||
| 496 | securityfs_remove(ns->dents[i]); | ||
| 497 | ns->dents[i] = NULL; | ||
| 498 | } | ||
| 499 | } | ||
| 500 | |||
| 501 | int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, | ||
| 502 | const char *name) | ||
| 503 | { | ||
| 504 | struct aa_namespace *sub; | ||
| 505 | struct aa_profile *child; | ||
| 506 | struct dentry *dent, *dir; | ||
| 507 | int error; | ||
| 508 | |||
| 509 | if (!name) | ||
| 510 | name = ns->base.name; | ||
| 511 | |||
| 512 | dent = securityfs_create_dir(name, parent); | ||
| 513 | if (IS_ERR(dent)) | ||
| 514 | goto fail; | ||
| 515 | ns_dir(ns) = dir = dent; | ||
| 516 | |||
| 517 | dent = securityfs_create_dir("profiles", dir); | ||
| 518 | if (IS_ERR(dent)) | ||
| 519 | goto fail; | ||
| 520 | ns_subprofs_dir(ns) = dent; | ||
| 521 | |||
| 522 | dent = securityfs_create_dir("namespaces", dir); | ||
| 523 | if (IS_ERR(dent)) | ||
| 524 | goto fail; | ||
| 525 | ns_subns_dir(ns) = dent; | ||
| 526 | |||
| 527 | list_for_each_entry(child, &ns->base.profiles, base.list) { | ||
| 528 | error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns)); | ||
| 529 | if (error) | ||
| 530 | goto fail2; | ||
| 531 | } | ||
| 532 | |||
| 533 | list_for_each_entry(sub, &ns->sub_ns, base.list) { | ||
| 534 | mutex_lock(&sub->lock); | ||
| 535 | error = __aa_fs_namespace_mkdir(sub, ns_subns_dir(ns), NULL); | ||
| 536 | mutex_unlock(&sub->lock); | ||
| 537 | if (error) | ||
| 538 | goto fail2; | ||
| 539 | } | ||
| 540 | |||
| 541 | return 0; | ||
| 542 | |||
| 543 | fail: | ||
| 544 | error = PTR_ERR(dent); | ||
| 545 | |||
| 546 | fail2: | ||
| 547 | __aa_fs_namespace_rmdir(ns); | ||
| 548 | |||
| 549 | return error; | ||
| 550 | } | ||
| 551 | |||
| 552 | |||
| 553 | #define list_entry_next(pos, member) \ | ||
| 554 | list_entry(pos->member.next, typeof(*pos), member) | ||
| 555 | #define list_entry_is_head(pos, head, member) (&pos->member == (head)) | ||
| 556 | |||
| 557 | /** | ||
| 558 | * __next_namespace - find the next namespace to list | ||
| 559 | * @root: root namespace to stop search at (NOT NULL) | ||
| 560 | * @ns: current ns position (NOT NULL) | ||
| 561 | * | ||
| 562 | * Find the next namespace from @ns under @root and handle all locking needed | ||
| 563 | * while switching current namespace. | ||
| 564 | * | ||
| 565 | * Returns: next namespace or NULL if at last namespace under @root | ||
| 566 | * Requires: ns->parent->lock to be held | ||
| 567 | * NOTE: will not unlock root->lock | ||
| 568 | */ | ||
| 569 | static struct aa_namespace *__next_namespace(struct aa_namespace *root, | ||
| 570 | struct aa_namespace *ns) | ||
| 571 | { | ||
| 572 | struct aa_namespace *parent, *next; | ||
| 573 | |||
| 574 | /* is next namespace a child */ | ||
| 575 | if (!list_empty(&ns->sub_ns)) { | ||
| 576 | next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); | ||
| 577 | mutex_lock(&next->lock); | ||
| 578 | return next; | ||
| 579 | } | ||
| 580 | |||
| 581 | /* check if the next ns is a sibling, parent, gp, .. */ | ||
| 582 | parent = ns->parent; | ||
| 583 | while (ns != root) { | ||
| 584 | mutex_unlock(&ns->lock); | ||
| 585 | next = list_entry_next(ns, base.list); | ||
| 586 | if (!list_entry_is_head(next, &parent->sub_ns, base.list)) { | ||
| 587 | mutex_lock(&next->lock); | ||
| 588 | return next; | ||
| 589 | } | ||
| 590 | ns = parent; | ||
| 591 | parent = parent->parent; | ||
| 592 | } | ||
| 593 | |||
| 594 | return NULL; | ||
| 595 | } | ||
| 596 | |||
| 597 | /** | ||
| 598 | * __first_profile - find the first profile in a namespace | ||
| 599 | * @root: namespace that is root of profiles being displayed (NOT NULL) | ||
| 600 | * @ns: namespace to start in (NOT NULL) | ||
| 601 | * | ||
| 602 | * Returns: unrefcounted profile or NULL if no profile | ||
| 603 | * Requires: profile->ns.lock to be held | ||
| 604 | */ | ||
| 605 | static struct aa_profile *__first_profile(struct aa_namespace *root, | ||
| 606 | struct aa_namespace *ns) | ||
| 607 | { | ||
| 608 | for (; ns; ns = __next_namespace(root, ns)) { | ||
| 609 | if (!list_empty(&ns->base.profiles)) | ||
| 610 | return list_first_entry(&ns->base.profiles, | ||
| 611 | struct aa_profile, base.list); | ||
| 612 | } | ||
| 613 | return NULL; | ||
| 614 | } | ||
| 615 | |||
| 616 | /** | ||
| 617 | * __next_profile - step to the next profile in a profile tree | ||
| 618 | * @profile: current profile in tree (NOT NULL) | ||
| 619 | * | ||
| 620 | * Perform a depth first traversal on the profile tree in a namespace | ||
| 621 | * | ||
| 622 | * Returns: next profile or NULL if done | ||
| 623 | * Requires: profile->ns.lock to be held | ||
| 624 | */ | ||
| 625 | static struct aa_profile *__next_profile(struct aa_profile *p) | ||
| 626 | { | ||
| 627 | struct aa_profile *parent; | ||
| 628 | struct aa_namespace *ns = p->ns; | ||
| 629 | |||
| 630 | /* is next profile a child */ | ||
| 631 | if (!list_empty(&p->base.profiles)) | ||
| 632 | return list_first_entry(&p->base.profiles, typeof(*p), | ||
| 633 | base.list); | ||
| 634 | |||
| 635 | /* is next profile a sibling, parent sibling, gp, sibling, .. */ | ||
| 636 | parent = rcu_dereference_protected(p->parent, | ||
| 637 | mutex_is_locked(&p->ns->lock)); | ||
| 638 | while (parent) { | ||
| 639 | p = list_entry_next(p, base.list); | ||
| 640 | if (!list_entry_is_head(p, &parent->base.profiles, base.list)) | ||
| 641 | return p; | ||
| 642 | p = parent; | ||
| 643 | parent = rcu_dereference_protected(parent->parent, | ||
| 644 | mutex_is_locked(&parent->ns->lock)); | ||
| 645 | } | ||
| 646 | |||
| 647 | /* is next another profile in the namespace */ | ||
| 648 | p = list_entry_next(p, base.list); | ||
| 649 | if (!list_entry_is_head(p, &ns->base.profiles, base.list)) | ||
| 650 | return p; | ||
| 651 | |||
| 652 | return NULL; | ||
| 653 | } | ||
| 654 | |||
| 655 | /** | ||
| 656 | * next_profile - step to the next profile in where ever it may be | ||
| 657 | * @root: root namespace (NOT NULL) | ||
| 658 | * @profile: current profile (NOT NULL) | ||
| 659 | * | ||
| 660 | * Returns: next profile or NULL if there isn't one | ||
| 661 | */ | ||
| 662 | static struct aa_profile *next_profile(struct aa_namespace *root, | ||
| 663 | struct aa_profile *profile) | ||
| 664 | { | ||
| 665 | struct aa_profile *next = __next_profile(profile); | ||
| 666 | if (next) | ||
| 667 | return next; | ||
| 668 | |||
| 669 | /* finished all profiles in namespace move to next namespace */ | ||
| 670 | return __first_profile(root, __next_namespace(root, profile->ns)); | ||
| 671 | } | ||
| 672 | |||
| 673 | /** | ||
| 674 | * p_start - start a depth first traversal of profile tree | ||
| 675 | * @f: seq_file to fill | ||
| 676 | * @pos: current position | ||
| 677 | * | ||
| 678 | * Returns: first profile under current namespace or NULL if none found | ||
| 679 | * | ||
| 680 | * acquires first ns->lock | ||
| 681 | */ | ||
| 682 | static void *p_start(struct seq_file *f, loff_t *pos) | ||
| 683 | { | ||
| 684 | struct aa_profile *profile = NULL; | ||
| 685 | struct aa_namespace *root = aa_current_profile()->ns; | ||
| 686 | loff_t l = *pos; | ||
| 687 | f->private = aa_get_namespace(root); | ||
| 688 | |||
| 689 | |||
| 690 | /* find the first profile */ | ||
| 691 | mutex_lock(&root->lock); | ||
| 692 | profile = __first_profile(root, root); | ||
| 693 | |||
| 694 | /* skip to position */ | ||
| 695 | for (; profile && l > 0; l--) | ||
| 696 | profile = next_profile(root, profile); | ||
| 697 | |||
| 698 | return profile; | ||
| 699 | } | ||
| 700 | |||
| 701 | /** | ||
| 702 | * p_next - read the next profile entry | ||
| 703 | * @f: seq_file to fill | ||
| 704 | * @p: profile previously returned | ||
| 705 | * @pos: current position | ||
| 706 | * | ||
| 707 | * Returns: next profile after @p or NULL if none | ||
| 708 | * | ||
| 709 | * may acquire/release locks in namespace tree as necessary | ||
| 710 | */ | ||
| 711 | static void *p_next(struct seq_file *f, void *p, loff_t *pos) | ||
| 712 | { | ||
| 713 | struct aa_profile *profile = p; | ||
| 714 | struct aa_namespace *ns = f->private; | ||
| 715 | (*pos)++; | ||
| 716 | |||
| 717 | return next_profile(ns, profile); | ||
| 718 | } | ||
| 719 | |||
| 720 | /** | ||
| 721 | * p_stop - stop depth first traversal | ||
| 722 | * @f: seq_file we are filling | ||
| 723 | * @p: the last profile writen | ||
| 724 | * | ||
| 725 | * Release all locking done by p_start/p_next on namespace tree | ||
| 726 | */ | ||
| 727 | static void p_stop(struct seq_file *f, void *p) | ||
| 728 | { | ||
| 729 | struct aa_profile *profile = p; | ||
| 730 | struct aa_namespace *root = f->private, *ns; | ||
| 731 | |||
| 732 | if (profile) { | ||
| 733 | for (ns = profile->ns; ns && ns != root; ns = ns->parent) | ||
| 734 | mutex_unlock(&ns->lock); | ||
| 735 | } | ||
| 736 | mutex_unlock(&root->lock); | ||
| 737 | aa_put_namespace(root); | ||
| 738 | } | ||
| 739 | |||
| 740 | /** | ||
| 741 | * seq_show_profile - show a profile entry | ||
| 742 | * @f: seq_file to file | ||
| 743 | * @p: current position (profile) (NOT NULL) | ||
| 744 | * | ||
| 745 | * Returns: error on failure | ||
| 746 | */ | ||
| 747 | static int seq_show_profile(struct seq_file *f, void *p) | ||
| 748 | { | ||
| 749 | struct aa_profile *profile = (struct aa_profile *)p; | ||
| 750 | struct aa_namespace *root = f->private; | ||
| 751 | |||
| 752 | if (profile->ns != root) | ||
| 753 | seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); | ||
| 754 | seq_printf(f, "%s (%s)\n", profile->base.hname, | ||
| 755 | aa_profile_mode_names[profile->mode]); | ||
| 756 | |||
| 757 | return 0; | ||
| 758 | } | ||
| 759 | |||
| 760 | static const struct seq_operations aa_fs_profiles_op = { | ||
| 761 | .start = p_start, | ||
| 762 | .next = p_next, | ||
| 763 | .stop = p_stop, | ||
| 764 | .show = seq_show_profile, | ||
| 765 | }; | ||
| 766 | |||
| 767 | static int profiles_open(struct inode *inode, struct file *file) | ||
| 768 | { | ||
| 769 | return seq_open(file, &aa_fs_profiles_op); | ||
| 770 | } | ||
| 771 | |||
| 772 | static int profiles_release(struct inode *inode, struct file *file) | ||
| 773 | { | ||
| 774 | return seq_release(inode, file); | ||
| 775 | } | ||
| 776 | |||
| 777 | static const struct file_operations aa_fs_profiles_fops = { | ||
| 778 | .open = profiles_open, | ||
| 779 | .read = seq_read, | ||
| 780 | .llseek = seq_lseek, | ||
| 781 | .release = profiles_release, | ||
| 782 | }; | ||
| 783 | |||
| 784 | |||
| 785 | /** Base file system setup **/ | ||
| 187 | static struct aa_fs_entry aa_fs_entry_file[] = { | 786 | static struct aa_fs_entry aa_fs_entry_file[] = { |
| 188 | AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \ | 787 | AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \ |
| 189 | "link lock"), | 788 | "link lock"), |
| @@ -198,11 +797,18 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { | |||
| 198 | { } | 797 | { } |
| 199 | }; | 798 | }; |
| 200 | 799 | ||
| 800 | static struct aa_fs_entry aa_fs_entry_policy[] = { | ||
| 801 | AA_FS_FILE_BOOLEAN("set_load", 1), | ||
| 802 | {} | ||
| 803 | }; | ||
| 804 | |||
| 201 | static struct aa_fs_entry aa_fs_entry_features[] = { | 805 | static struct aa_fs_entry aa_fs_entry_features[] = { |
| 806 | AA_FS_DIR("policy", aa_fs_entry_policy), | ||
| 202 | AA_FS_DIR("domain", aa_fs_entry_domain), | 807 | AA_FS_DIR("domain", aa_fs_entry_domain), |
| 203 | AA_FS_DIR("file", aa_fs_entry_file), | 808 | AA_FS_DIR("file", aa_fs_entry_file), |
| 204 | AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), | 809 | AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), |
| 205 | AA_FS_DIR("rlimit", aa_fs_entry_rlimit), | 810 | AA_FS_DIR("rlimit", aa_fs_entry_rlimit), |
| 811 | AA_FS_DIR("caps", aa_fs_entry_caps), | ||
| 206 | { } | 812 | { } |
| 207 | }; | 813 | }; |
| 208 | 814 | ||
| @@ -210,6 +816,7 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { | |||
| 210 | AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), | 816 | AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), |
| 211 | AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), | 817 | AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), |
| 212 | AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), | 818 | AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), |
| 819 | AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), | ||
| 213 | AA_FS_DIR("features", aa_fs_entry_features), | 820 | AA_FS_DIR("features", aa_fs_entry_features), |
| 214 | { } | 821 | { } |
| 215 | }; | 822 | }; |
| @@ -240,6 +847,7 @@ static int __init aafs_create_file(struct aa_fs_entry *fs_file, | |||
| 240 | return error; | 847 | return error; |
| 241 | } | 848 | } |
| 242 | 849 | ||
| 850 | static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir); | ||
| 243 | /** | 851 | /** |
| 244 | * aafs_create_dir - recursively create a directory entry in the securityfs | 852 | * aafs_create_dir - recursively create a directory entry in the securityfs |
| 245 | * @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL) | 853 | * @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL) |
| @@ -250,17 +858,16 @@ static int __init aafs_create_file(struct aa_fs_entry *fs_file, | |||
| 250 | static int __init aafs_create_dir(struct aa_fs_entry *fs_dir, | 858 | static int __init aafs_create_dir(struct aa_fs_entry *fs_dir, |
| 251 | struct dentry *parent) | 859 | struct dentry *parent) |
| 252 | { | 860 | { |
| 253 | int error; | ||
| 254 | struct aa_fs_entry *fs_file; | 861 | struct aa_fs_entry *fs_file; |
| 862 | struct dentry *dir; | ||
| 863 | int error; | ||
| 255 | 864 | ||
| 256 | fs_dir->dentry = securityfs_create_dir(fs_dir->name, parent); | 865 | dir = securityfs_create_dir(fs_dir->name, parent); |
| 257 | if (IS_ERR(fs_dir->dentry)) { | 866 | if (IS_ERR(dir)) |
| 258 | error = PTR_ERR(fs_dir->dentry); | 867 | return PTR_ERR(dir); |
| 259 | fs_dir->dentry = NULL; | 868 | fs_dir->dentry = dir; |
| 260 | goto failed; | ||
| 261 | } | ||
| 262 | 869 | ||
| 263 | for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) { | 870 | for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) { |
| 264 | if (fs_file->v_type == AA_FS_TYPE_DIR) | 871 | if (fs_file->v_type == AA_FS_TYPE_DIR) |
| 265 | error = aafs_create_dir(fs_file, fs_dir->dentry); | 872 | error = aafs_create_dir(fs_file, fs_dir->dentry); |
| 266 | else | 873 | else |
| @@ -272,6 +879,8 @@ static int __init aafs_create_dir(struct aa_fs_entry *fs_dir, | |||
| 272 | return 0; | 879 | return 0; |
| 273 | 880 | ||
| 274 | failed: | 881 | failed: |
| 882 | aafs_remove_dir(fs_dir); | ||
| 883 | |||
| 275 | return error; | 884 | return error; |
| 276 | } | 885 | } |
| 277 | 886 | ||
| @@ -296,7 +905,7 @@ static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir) | |||
| 296 | { | 905 | { |
| 297 | struct aa_fs_entry *fs_file; | 906 | struct aa_fs_entry *fs_file; |
| 298 | 907 | ||
| 299 | for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) { | 908 | for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) { |
| 300 | if (fs_file->v_type == AA_FS_TYPE_DIR) | 909 | if (fs_file->v_type == AA_FS_TYPE_DIR) |
| 301 | aafs_remove_dir(fs_file); | 910 | aafs_remove_dir(fs_file); |
| 302 | else | 911 | else |
| @@ -340,6 +949,11 @@ static int __init aa_create_aafs(void) | |||
| 340 | if (error) | 949 | if (error) |
| 341 | goto error; | 950 | goto error; |
| 342 | 951 | ||
| 952 | error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry.dentry, | ||
| 953 | "policy"); | ||
| 954 | if (error) | ||
| 955 | goto error; | ||
| 956 | |||
| 343 | /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ | 957 | /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ |
| 344 | 958 | ||
| 345 | /* Report that AppArmor fs is enabled */ | 959 | /* Report that AppArmor fs is enabled */ |
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 887a5e948945..84d1f5f53877 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c | |||
| @@ -27,6 +27,11 @@ | |||
| 27 | */ | 27 | */ |
| 28 | #include "capability_names.h" | 28 | #include "capability_names.h" |
| 29 | 29 | ||
| 30 | struct aa_fs_entry aa_fs_entry_caps[] = { | ||
| 31 | AA_FS_FILE_STRING("mask", AA_FS_CAPS_MASK), | ||
| 32 | { } | ||
| 33 | }; | ||
| 34 | |||
| 30 | struct audit_cache { | 35 | struct audit_cache { |
| 31 | struct aa_profile *profile; | 36 | struct aa_profile *profile; |
| 32 | kernel_cap_t caps; | 37 | kernel_cap_t caps; |
diff --git a/security/apparmor/context.c b/security/apparmor/context.c index d5af1d15f26d..3064c6ced87c 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c | |||
| @@ -112,9 +112,9 @@ int aa_replace_current_profile(struct aa_profile *profile) | |||
| 112 | aa_clear_task_cxt_trans(cxt); | 112 | aa_clear_task_cxt_trans(cxt); |
| 113 | 113 | ||
| 114 | /* be careful switching cxt->profile, when racing replacement it | 114 | /* be careful switching cxt->profile, when racing replacement it |
| 115 | * is possible that cxt->profile->replacedby is the reference keeping | 115 | * is possible that cxt->profile->replacedby->profile is the reference |
| 116 | * @profile valid, so make sure to get its reference before dropping | 116 | * keeping @profile valid, so make sure to get its reference before |
| 117 | * the reference on cxt->profile */ | 117 | * dropping the reference on cxt->profile */ |
| 118 | aa_get_profile(profile); | 118 | aa_get_profile(profile); |
| 119 | aa_put_profile(cxt->profile); | 119 | aa_put_profile(cxt->profile); |
| 120 | cxt->profile = profile; | 120 | cxt->profile = profile; |
| @@ -175,7 +175,7 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token) | |||
| 175 | abort_creds(new); | 175 | abort_creds(new); |
| 176 | return -EACCES; | 176 | return -EACCES; |
| 177 | } | 177 | } |
| 178 | cxt->profile = aa_get_profile(aa_newest_version(profile)); | 178 | cxt->profile = aa_get_newest_profile(profile); |
| 179 | /* clear exec on switching context */ | 179 | /* clear exec on switching context */ |
| 180 | aa_put_profile(cxt->onexec); | 180 | aa_put_profile(cxt->onexec); |
| 181 | cxt->onexec = NULL; | 181 | cxt->onexec = NULL; |
| @@ -212,14 +212,8 @@ int aa_restore_previous_profile(u64 token) | |||
| 212 | } | 212 | } |
| 213 | 213 | ||
| 214 | aa_put_profile(cxt->profile); | 214 | aa_put_profile(cxt->profile); |
| 215 | cxt->profile = aa_newest_version(cxt->previous); | 215 | cxt->profile = aa_get_newest_profile(cxt->previous); |
| 216 | BUG_ON(!cxt->profile); | 216 | BUG_ON(!cxt->profile); |
| 217 | if (unlikely(cxt->profile != cxt->previous)) { | ||
| 218 | aa_get_profile(cxt->profile); | ||
| 219 | aa_put_profile(cxt->previous); | ||
| 220 | } | ||
| 221 | /* ref has been transfered so avoid putting ref in clear_task_cxt */ | ||
| 222 | cxt->previous = NULL; | ||
| 223 | /* clear exec && prev information when restoring to previous context */ | 217 | /* clear exec && prev information when restoring to previous context */ |
| 224 | aa_clear_task_cxt_trans(cxt); | 218 | aa_clear_task_cxt_trans(cxt); |
| 225 | 219 | ||
diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c new file mode 100644 index 000000000000..532471d0b3a0 --- /dev/null +++ b/security/apparmor/crypto.c | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor policy loading interface function definitions. | ||
| 5 | * | ||
| 6 | * Copyright 2013 Canonical Ltd. | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License as | ||
| 10 | * published by the Free Software Foundation, version 2 of the | ||
| 11 | * License. | ||
| 12 | * | ||
| 13 | * Fns to provide a checksum of policy that has been loaded this can be | ||
| 14 | * compared to userspace policy compiles to check loaded policy is what | ||
| 15 | * it should be. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <crypto/hash.h> | ||
| 19 | |||
| 20 | #include "include/apparmor.h" | ||
| 21 | #include "include/crypto.h" | ||
| 22 | |||
| 23 | static unsigned int apparmor_hash_size; | ||
| 24 | |||
| 25 | static struct crypto_shash *apparmor_tfm; | ||
| 26 | |||
| 27 | unsigned int aa_hash_size(void) | ||
| 28 | { | ||
| 29 | return apparmor_hash_size; | ||
| 30 | } | ||
| 31 | |||
| 32 | int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, | ||
| 33 | size_t len) | ||
| 34 | { | ||
| 35 | struct { | ||
| 36 | struct shash_desc shash; | ||
| 37 | char ctx[crypto_shash_descsize(apparmor_tfm)]; | ||
| 38 | } desc; | ||
| 39 | int error = -ENOMEM; | ||
| 40 | u32 le32_version = cpu_to_le32(version); | ||
| 41 | |||
| 42 | if (!apparmor_tfm) | ||
| 43 | return 0; | ||
| 44 | |||
| 45 | profile->hash = kzalloc(apparmor_hash_size, GFP_KERNEL); | ||
| 46 | if (!profile->hash) | ||
| 47 | goto fail; | ||
| 48 | |||
| 49 | desc.shash.tfm = apparmor_tfm; | ||
| 50 | desc.shash.flags = 0; | ||
| 51 | |||
| 52 | error = crypto_shash_init(&desc.shash); | ||
| 53 | if (error) | ||
| 54 | goto fail; | ||
| 55 | error = crypto_shash_update(&desc.shash, (u8 *) &le32_version, 4); | ||
| 56 | if (error) | ||
| 57 | goto fail; | ||
| 58 | error = crypto_shash_update(&desc.shash, (u8 *) start, len); | ||
| 59 | if (error) | ||
| 60 | goto fail; | ||
| 61 | error = crypto_shash_final(&desc.shash, profile->hash); | ||
| 62 | if (error) | ||
| 63 | goto fail; | ||
| 64 | |||
| 65 | return 0; | ||
| 66 | |||
| 67 | fail: | ||
| 68 | kfree(profile->hash); | ||
| 69 | profile->hash = NULL; | ||
| 70 | |||
| 71 | return error; | ||
| 72 | } | ||
| 73 | |||
| 74 | static int __init init_profile_hash(void) | ||
| 75 | { | ||
| 76 | struct crypto_shash *tfm; | ||
| 77 | |||
| 78 | if (!apparmor_initialized) | ||
| 79 | return 0; | ||
| 80 | |||
| 81 | tfm = crypto_alloc_shash("sha1", 0, CRYPTO_ALG_ASYNC); | ||
| 82 | if (IS_ERR(tfm)) { | ||
| 83 | int error = PTR_ERR(tfm); | ||
| 84 | AA_ERROR("failed to setup profile sha1 hashing: %d\n", error); | ||
| 85 | return error; | ||
| 86 | } | ||
| 87 | apparmor_tfm = tfm; | ||
| 88 | apparmor_hash_size = crypto_shash_digestsize(apparmor_tfm); | ||
| 89 | |||
| 90 | aa_info_message("AppArmor sha1 policy hashing enabled"); | ||
| 91 | |||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | late_initcall(init_profile_hash); | ||
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 01b7bd669a88..26c607c971f5 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c | |||
| @@ -144,7 +144,7 @@ static struct aa_profile *__attach_match(const char *name, | |||
| 144 | int len = 0; | 144 | int len = 0; |
| 145 | struct aa_profile *profile, *candidate = NULL; | 145 | struct aa_profile *profile, *candidate = NULL; |
| 146 | 146 | ||
| 147 | list_for_each_entry(profile, head, base.list) { | 147 | list_for_each_entry_rcu(profile, head, base.list) { |
| 148 | if (profile->flags & PFLAG_NULL) | 148 | if (profile->flags & PFLAG_NULL) |
| 149 | continue; | 149 | continue; |
| 150 | if (profile->xmatch && profile->xmatch_len > len) { | 150 | if (profile->xmatch && profile->xmatch_len > len) { |
| @@ -177,9 +177,9 @@ static struct aa_profile *find_attach(struct aa_namespace *ns, | |||
| 177 | { | 177 | { |
| 178 | struct aa_profile *profile; | 178 | struct aa_profile *profile; |
| 179 | 179 | ||
| 180 | read_lock(&ns->lock); | 180 | rcu_read_lock(); |
| 181 | profile = aa_get_profile(__attach_match(name, list)); | 181 | profile = aa_get_profile(__attach_match(name, list)); |
| 182 | read_unlock(&ns->lock); | 182 | rcu_read_unlock(); |
| 183 | 183 | ||
| 184 | return profile; | 184 | return profile; |
| 185 | } | 185 | } |
| @@ -359,7 +359,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
| 359 | cxt = cred_cxt(bprm->cred); | 359 | cxt = cred_cxt(bprm->cred); |
| 360 | BUG_ON(!cxt); | 360 | BUG_ON(!cxt); |
| 361 | 361 | ||
| 362 | profile = aa_get_profile(aa_newest_version(cxt->profile)); | 362 | profile = aa_get_newest_profile(cxt->profile); |
| 363 | /* | 363 | /* |
| 364 | * get the namespace from the replacement profile as replacement | 364 | * get the namespace from the replacement profile as replacement |
| 365 | * can change the namespace | 365 | * can change the namespace |
| @@ -371,8 +371,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
| 371 | error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer, | 371 | error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer, |
| 372 | &name, &info); | 372 | &name, &info); |
| 373 | if (error) { | 373 | if (error) { |
| 374 | if (profile->flags & | 374 | if (unconfined(profile) || |
| 375 | (PFLAG_IX_ON_NAME_ERROR | PFLAG_UNCONFINED)) | 375 | (profile->flags & PFLAG_IX_ON_NAME_ERROR)) |
| 376 | error = 0; | 376 | error = 0; |
| 377 | name = bprm->filename; | 377 | name = bprm->filename; |
| 378 | goto audit; | 378 | goto audit; |
| @@ -417,7 +417,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
| 417 | 417 | ||
| 418 | if (!(cp.allow & AA_MAY_ONEXEC)) | 418 | if (!(cp.allow & AA_MAY_ONEXEC)) |
| 419 | goto audit; | 419 | goto audit; |
| 420 | new_profile = aa_get_profile(aa_newest_version(cxt->onexec)); | 420 | new_profile = aa_get_newest_profile(cxt->onexec); |
| 421 | goto apply; | 421 | goto apply; |
| 422 | } | 422 | } |
| 423 | 423 | ||
| @@ -434,7 +434,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
| 434 | new_profile = aa_get_profile(profile); | 434 | new_profile = aa_get_profile(profile); |
| 435 | goto x_clear; | 435 | goto x_clear; |
| 436 | } else if (perms.xindex & AA_X_UNCONFINED) { | 436 | } else if (perms.xindex & AA_X_UNCONFINED) { |
| 437 | new_profile = aa_get_profile(ns->unconfined); | 437 | new_profile = aa_get_newest_profile(ns->unconfined); |
| 438 | info = "ux fallback"; | 438 | info = "ux fallback"; |
| 439 | } else { | 439 | } else { |
| 440 | error = -ENOENT; | 440 | error = -ENOENT; |
| @@ -641,7 +641,10 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | |||
| 641 | if (count) { | 641 | if (count) { |
| 642 | /* attempting to change into a new hat or switch to a sibling */ | 642 | /* attempting to change into a new hat or switch to a sibling */ |
| 643 | struct aa_profile *root; | 643 | struct aa_profile *root; |
| 644 | root = PROFILE_IS_HAT(profile) ? profile->parent : profile; | 644 | if (PROFILE_IS_HAT(profile)) |
| 645 | root = aa_get_profile_rcu(&profile->parent); | ||
| 646 | else | ||
| 647 | root = aa_get_profile(profile); | ||
| 645 | 648 | ||
| 646 | /* find first matching hat */ | 649 | /* find first matching hat */ |
| 647 | for (i = 0; i < count && !hat; i++) | 650 | for (i = 0; i < count && !hat; i++) |
| @@ -653,6 +656,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | |||
| 653 | error = -ECHILD; | 656 | error = -ECHILD; |
| 654 | else | 657 | else |
| 655 | error = -ENOENT; | 658 | error = -ENOENT; |
| 659 | aa_put_profile(root); | ||
| 656 | goto out; | 660 | goto out; |
| 657 | } | 661 | } |
| 658 | 662 | ||
| @@ -667,6 +671,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | |||
| 667 | 671 | ||
| 668 | /* freed below */ | 672 | /* freed below */ |
| 669 | name = new_compound_name(root->base.hname, hats[0]); | 673 | name = new_compound_name(root->base.hname, hats[0]); |
| 674 | aa_put_profile(root); | ||
| 670 | target = name; | 675 | target = name; |
| 671 | /* released below */ | 676 | /* released below */ |
| 672 | hat = aa_new_null_profile(profile, 1); | 677 | hat = aa_new_null_profile(profile, 1); |
| @@ -676,6 +681,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | |||
| 676 | goto audit; | 681 | goto audit; |
| 677 | } | 682 | } |
| 678 | } else { | 683 | } else { |
| 684 | aa_put_profile(root); | ||
| 679 | target = hat->base.hname; | 685 | target = hat->base.hname; |
| 680 | if (!PROFILE_IS_HAT(hat)) { | 686 | if (!PROFILE_IS_HAT(hat)) { |
| 681 | info = "target not hat"; | 687 | info = "target not hat"; |
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 1ba2ca56a6ef..8fb1488a3cd4 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h | |||
| @@ -78,6 +78,12 @@ static inline void *kvzalloc(size_t size) | |||
| 78 | return __aa_kvmalloc(size, __GFP_ZERO); | 78 | return __aa_kvmalloc(size, __GFP_ZERO); |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | /* returns 0 if kref not incremented */ | ||
| 82 | static inline int kref_get_not0(struct kref *kref) | ||
| 83 | { | ||
| 84 | return atomic_inc_not_zero(&kref->refcount); | ||
| 85 | } | ||
| 86 | |||
| 81 | /** | 87 | /** |
| 82 | * aa_strneq - compare null terminated @str to a non null terminated substring | 88 | * aa_strneq - compare null terminated @str to a non null terminated substring |
| 83 | * @str: a null terminated string | 89 | * @str: a null terminated string |
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 7ea4769fab3f..414e56878dd0 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h | |||
| @@ -61,4 +61,44 @@ extern const struct file_operations aa_fs_seq_file_ops; | |||
| 61 | 61 | ||
| 62 | extern void __init aa_destroy_aafs(void); | 62 | extern void __init aa_destroy_aafs(void); |
| 63 | 63 | ||
| 64 | struct aa_profile; | ||
| 65 | struct aa_namespace; | ||
| 66 | |||
| 67 | enum aafs_ns_type { | ||
| 68 | AAFS_NS_DIR, | ||
| 69 | AAFS_NS_PROFS, | ||
| 70 | AAFS_NS_NS, | ||
| 71 | AAFS_NS_COUNT, | ||
| 72 | AAFS_NS_MAX_COUNT, | ||
| 73 | AAFS_NS_SIZE, | ||
| 74 | AAFS_NS_MAX_SIZE, | ||
| 75 | AAFS_NS_OWNER, | ||
| 76 | AAFS_NS_SIZEOF, | ||
| 77 | }; | ||
| 78 | |||
| 79 | enum aafs_prof_type { | ||
| 80 | AAFS_PROF_DIR, | ||
| 81 | AAFS_PROF_PROFS, | ||
| 82 | AAFS_PROF_NAME, | ||
| 83 | AAFS_PROF_MODE, | ||
| 84 | AAFS_PROF_ATTACH, | ||
| 85 | AAFS_PROF_HASH, | ||
| 86 | AAFS_PROF_SIZEOF, | ||
| 87 | }; | ||
| 88 | |||
| 89 | #define ns_dir(X) ((X)->dents[AAFS_NS_DIR]) | ||
| 90 | #define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS]) | ||
| 91 | #define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS]) | ||
| 92 | |||
| 93 | #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) | ||
| 94 | #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS]) | ||
| 95 | |||
| 96 | void __aa_fs_profile_rmdir(struct aa_profile *profile); | ||
| 97 | void __aa_fs_profile_migrate_dents(struct aa_profile *old, | ||
| 98 | struct aa_profile *new); | ||
| 99 | int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent); | ||
| 100 | void __aa_fs_namespace_rmdir(struct aa_namespace *ns); | ||
| 101 | int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, | ||
| 102 | const char *name); | ||
| 103 | |||
| 64 | #endif /* __AA_APPARMORFS_H */ | 104 | #endif /* __AA_APPARMORFS_H */ |
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 69d8cae634e7..30e8d7687259 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h | |||
| @@ -27,7 +27,6 @@ struct aa_profile; | |||
| 27 | 27 | ||
| 28 | extern const char *const audit_mode_names[]; | 28 | extern const char *const audit_mode_names[]; |
| 29 | #define AUDIT_MAX_INDEX 5 | 29 | #define AUDIT_MAX_INDEX 5 |
| 30 | |||
| 31 | enum audit_mode { | 30 | enum audit_mode { |
| 32 | AUDIT_NORMAL, /* follow normal auditing of accesses */ | 31 | AUDIT_NORMAL, /* follow normal auditing of accesses */ |
| 33 | AUDIT_QUIET_DENIED, /* quiet all denied access messages */ | 32 | AUDIT_QUIET_DENIED, /* quiet all denied access messages */ |
diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h index c24d2959ea02..2e7c9d6a2f3b 100644 --- a/security/apparmor/include/capability.h +++ b/security/apparmor/include/capability.h | |||
| @@ -17,6 +17,8 @@ | |||
| 17 | 17 | ||
| 18 | #include <linux/sched.h> | 18 | #include <linux/sched.h> |
| 19 | 19 | ||
| 20 | #include "apparmorfs.h" | ||
| 21 | |||
| 20 | struct aa_profile; | 22 | struct aa_profile; |
| 21 | 23 | ||
| 22 | /* aa_caps - confinement data for capabilities | 24 | /* aa_caps - confinement data for capabilities |
| @@ -34,6 +36,8 @@ struct aa_caps { | |||
| 34 | kernel_cap_t extended; | 36 | kernel_cap_t extended; |
| 35 | }; | 37 | }; |
| 36 | 38 | ||
| 39 | extern struct aa_fs_entry aa_fs_entry_caps[]; | ||
| 40 | |||
| 37 | int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, | 41 | int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, |
| 38 | int audit); | 42 | int audit); |
| 39 | 43 | ||
diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index d44ba5802e3d..6bf65798e5d1 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h | |||
| @@ -98,7 +98,7 @@ static inline struct aa_profile *aa_cred_profile(const struct cred *cred) | |||
| 98 | { | 98 | { |
| 99 | struct aa_task_cxt *cxt = cred_cxt(cred); | 99 | struct aa_task_cxt *cxt = cred_cxt(cred); |
| 100 | BUG_ON(!cxt || !cxt->profile); | 100 | BUG_ON(!cxt || !cxt->profile); |
| 101 | return aa_newest_version(cxt->profile); | 101 | return cxt->profile; |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | /** | 104 | /** |
| @@ -152,15 +152,14 @@ static inline struct aa_profile *aa_current_profile(void) | |||
| 152 | struct aa_profile *profile; | 152 | struct aa_profile *profile; |
| 153 | BUG_ON(!cxt || !cxt->profile); | 153 | BUG_ON(!cxt || !cxt->profile); |
| 154 | 154 | ||
| 155 | profile = aa_newest_version(cxt->profile); | 155 | if (PROFILE_INVALID(cxt->profile)) { |
| 156 | /* | 156 | profile = aa_get_newest_profile(cxt->profile); |
| 157 | * Whether or not replacement succeeds, use newest profile so | ||
| 158 | * there is no need to update it after replacement. | ||
| 159 | */ | ||
| 160 | if (unlikely((cxt->profile != profile))) | ||
| 161 | aa_replace_current_profile(profile); | 157 | aa_replace_current_profile(profile); |
| 158 | aa_put_profile(profile); | ||
| 159 | cxt = current_cxt(); | ||
| 160 | } | ||
| 162 | 161 | ||
| 163 | return profile; | 162 | return cxt->profile; |
| 164 | } | 163 | } |
| 165 | 164 | ||
| 166 | /** | 165 | /** |
diff --git a/security/apparmor/include/crypto.h b/security/apparmor/include/crypto.h new file mode 100644 index 000000000000..dc418e5024d9 --- /dev/null +++ b/security/apparmor/include/crypto.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | /* | ||
| 2 | * AppArmor security module | ||
| 3 | * | ||
| 4 | * This file contains AppArmor policy loading interface function definitions. | ||
| 5 | * | ||
| 6 | * Copyright 2013 Canonical Ltd. | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License as | ||
| 10 | * published by the Free Software Foundation, version 2 of the | ||
| 11 | * License. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #ifndef __APPARMOR_CRYPTO_H | ||
| 15 | #define __APPARMOR_CRYPTO_H | ||
| 16 | |||
| 17 | #include "policy.h" | ||
| 18 | |||
| 19 | #ifdef CONFIG_SECURITY_APPARMOR_HASH | ||
| 20 | unsigned int aa_hash_size(void); | ||
| 21 | int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, | ||
| 22 | size_t len); | ||
| 23 | #else | ||
| 24 | static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version, | ||
| 25 | void *start, size_t len) | ||
| 26 | { | ||
| 27 | return 0; | ||
| 28 | } | ||
| 29 | |||
| 30 | static inline unsigned int aa_hash_size(void) | ||
| 31 | { | ||
| 32 | return 0; | ||
| 33 | } | ||
| 34 | #endif | ||
| 35 | |||
| 36 | #endif /* __APPARMOR_CRYPTO_H */ | ||
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index b25491a3046a..c28b0f20ab53 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h | |||
| @@ -29,8 +29,8 @@ | |||
| 29 | #include "file.h" | 29 | #include "file.h" |
| 30 | #include "resource.h" | 30 | #include "resource.h" |
| 31 | 31 | ||
| 32 | extern const char *const profile_mode_names[]; | 32 | extern const char *const aa_profile_mode_names[]; |
| 33 | #define APPARMOR_NAMES_MAX_INDEX 3 | 33 | #define APPARMOR_MODE_NAMES_MAX_INDEX 4 |
| 34 | 34 | ||
| 35 | #define PROFILE_MODE(_profile, _mode) \ | 35 | #define PROFILE_MODE(_profile, _mode) \ |
| 36 | ((aa_g_profile_mode == (_mode)) || \ | 36 | ((aa_g_profile_mode == (_mode)) || \ |
| @@ -42,6 +42,10 @@ extern const char *const profile_mode_names[]; | |||
| 42 | 42 | ||
| 43 | #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) | 43 | #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) |
| 44 | 44 | ||
| 45 | #define PROFILE_INVALID(_profile) ((_profile)->flags & PFLAG_INVALID) | ||
| 46 | |||
| 47 | #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) | ||
| 48 | |||
| 45 | /* | 49 | /* |
| 46 | * FIXME: currently need a clean way to replace and remove profiles as a | 50 | * FIXME: currently need a clean way to replace and remove profiles as a |
| 47 | * set. It should be done at the namespace level. | 51 | * set. It should be done at the namespace level. |
| @@ -52,17 +56,19 @@ enum profile_mode { | |||
| 52 | APPARMOR_ENFORCE, /* enforce access rules */ | 56 | APPARMOR_ENFORCE, /* enforce access rules */ |
| 53 | APPARMOR_COMPLAIN, /* allow and log access violations */ | 57 | APPARMOR_COMPLAIN, /* allow and log access violations */ |
| 54 | APPARMOR_KILL, /* kill task on access violation */ | 58 | APPARMOR_KILL, /* kill task on access violation */ |
| 59 | APPARMOR_UNCONFINED, /* profile set to unconfined */ | ||
| 55 | }; | 60 | }; |
| 56 | 61 | ||
| 57 | enum profile_flags { | 62 | enum profile_flags { |
| 58 | PFLAG_HAT = 1, /* profile is a hat */ | 63 | PFLAG_HAT = 1, /* profile is a hat */ |
| 59 | PFLAG_UNCONFINED = 2, /* profile is an unconfined profile */ | ||
| 60 | PFLAG_NULL = 4, /* profile is null learning profile */ | 64 | PFLAG_NULL = 4, /* profile is null learning profile */ |
| 61 | PFLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */ | 65 | PFLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */ |
| 62 | PFLAG_IMMUTABLE = 0x10, /* don't allow changes/replacement */ | 66 | PFLAG_IMMUTABLE = 0x10, /* don't allow changes/replacement */ |
| 63 | PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ | 67 | PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ |
| 64 | PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ | 68 | PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ |
| 65 | PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ | 69 | PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ |
| 70 | PFLAG_INVALID = 0x200, /* profile replaced/removed */ | ||
| 71 | PFLAG_NS_COUNT = 0x400, /* carries NS ref count */ | ||
| 66 | 72 | ||
| 67 | /* These flags must correspond with PATH_flags */ | 73 | /* These flags must correspond with PATH_flags */ |
| 68 | PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ | 74 | PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ |
| @@ -73,14 +79,12 @@ struct aa_profile; | |||
| 73 | /* struct aa_policy - common part of both namespaces and profiles | 79 | /* struct aa_policy - common part of both namespaces and profiles |
| 74 | * @name: name of the object | 80 | * @name: name of the object |
| 75 | * @hname - The hierarchical name | 81 | * @hname - The hierarchical name |
| 76 | * @count: reference count of the obj | ||
| 77 | * @list: list policy object is on | 82 | * @list: list policy object is on |
| 78 | * @profiles: head of the profiles list contained in the object | 83 | * @profiles: head of the profiles list contained in the object |
| 79 | */ | 84 | */ |
| 80 | struct aa_policy { | 85 | struct aa_policy { |
| 81 | char *name; | 86 | char *name; |
| 82 | char *hname; | 87 | char *hname; |
| 83 | struct kref count; | ||
| 84 | struct list_head list; | 88 | struct list_head list; |
| 85 | struct list_head profiles; | 89 | struct list_head profiles; |
| 86 | }; | 90 | }; |
| @@ -106,6 +110,8 @@ struct aa_ns_acct { | |||
| 106 | * @unconfined: special unconfined profile for the namespace | 110 | * @unconfined: special unconfined profile for the namespace |
| 107 | * @sub_ns: list of namespaces under the current namespace. | 111 | * @sub_ns: list of namespaces under the current namespace. |
| 108 | * @uniq_null: uniq value used for null learning profiles | 112 | * @uniq_null: uniq value used for null learning profiles |
| 113 | * @uniq_id: a unique id count for the profiles in the namespace | ||
| 114 | * @dents: dentries for the namespaces file entries in apparmorfs | ||
| 109 | * | 115 | * |
| 110 | * An aa_namespace defines the set profiles that are searched to determine | 116 | * An aa_namespace defines the set profiles that are searched to determine |
| 111 | * which profile to attach to a task. Profiles can not be shared between | 117 | * which profile to attach to a task. Profiles can not be shared between |
| @@ -124,11 +130,14 @@ struct aa_ns_acct { | |||
| 124 | struct aa_namespace { | 130 | struct aa_namespace { |
| 125 | struct aa_policy base; | 131 | struct aa_policy base; |
| 126 | struct aa_namespace *parent; | 132 | struct aa_namespace *parent; |
| 127 | rwlock_t lock; | 133 | struct mutex lock; |
| 128 | struct aa_ns_acct acct; | 134 | struct aa_ns_acct acct; |
| 129 | struct aa_profile *unconfined; | 135 | struct aa_profile *unconfined; |
| 130 | struct list_head sub_ns; | 136 | struct list_head sub_ns; |
| 131 | atomic_t uniq_null; | 137 | atomic_t uniq_null; |
| 138 | long uniq_id; | ||
| 139 | |||
| 140 | struct dentry *dents[AAFS_NS_SIZEOF]; | ||
| 132 | }; | 141 | }; |
| 133 | 142 | ||
| 134 | /* struct aa_policydb - match engine for a policy | 143 | /* struct aa_policydb - match engine for a policy |
| @@ -142,12 +151,21 @@ struct aa_policydb { | |||
| 142 | 151 | ||
| 143 | }; | 152 | }; |
| 144 | 153 | ||
| 154 | struct aa_replacedby { | ||
| 155 | struct kref count; | ||
| 156 | struct aa_profile __rcu *profile; | ||
| 157 | }; | ||
| 158 | |||
| 159 | |||
| 145 | /* struct aa_profile - basic confinement data | 160 | /* struct aa_profile - basic confinement data |
| 146 | * @base - base components of the profile (name, refcount, lists, lock ...) | 161 | * @base - base components of the profile (name, refcount, lists, lock ...) |
| 162 | * @count: reference count of the obj | ||
| 163 | * @rcu: rcu head used when removing from @list | ||
| 147 | * @parent: parent of profile | 164 | * @parent: parent of profile |
| 148 | * @ns: namespace the profile is in | 165 | * @ns: namespace the profile is in |
| 149 | * @replacedby: is set to the profile that replaced this profile | 166 | * @replacedby: is set to the profile that replaced this profile |
| 150 | * @rename: optional profile name that this profile renamed | 167 | * @rename: optional profile name that this profile renamed |
| 168 | * @attach: human readable attachment string | ||
| 151 | * @xmatch: optional extended matching for unconfined executables names | 169 | * @xmatch: optional extended matching for unconfined executables names |
| 152 | * @xmatch_len: xmatch prefix len, used to determine xmatch priority | 170 | * @xmatch_len: xmatch prefix len, used to determine xmatch priority |
| 153 | * @audit: the auditing mode of the profile | 171 | * @audit: the auditing mode of the profile |
| @@ -160,13 +178,15 @@ struct aa_policydb { | |||
| 160 | * @caps: capabilities for the profile | 178 | * @caps: capabilities for the profile |
| 161 | * @rlimits: rlimits for the profile | 179 | * @rlimits: rlimits for the profile |
| 162 | * | 180 | * |
| 181 | * @dents: dentries for the profiles file entries in apparmorfs | ||
| 182 | * @dirname: name of the profile dir in apparmorfs | ||
| 183 | * | ||
| 163 | * The AppArmor profile contains the basic confinement data. Each profile | 184 | * The AppArmor profile contains the basic confinement data. Each profile |
| 164 | * has a name, and exists in a namespace. The @name and @exec_match are | 185 | * has a name, and exists in a namespace. The @name and @exec_match are |
| 165 | * used to determine profile attachment against unconfined tasks. All other | 186 | * used to determine profile attachment against unconfined tasks. All other |
| 166 | * attachments are determined by profile X transition rules. | 187 | * attachments are determined by profile X transition rules. |
| 167 | * | 188 | * |
| 168 | * The @replacedby field is write protected by the profile lock. Reads | 189 | * The @replacedby struct is write protected by the profile lock. |
| 169 | * are assumed to be atomic, and are done without locking. | ||
| 170 | * | 190 | * |
| 171 | * Profiles have a hierarchy where hats and children profiles keep | 191 | * Profiles have a hierarchy where hats and children profiles keep |
| 172 | * a reference to their parent. | 192 | * a reference to their parent. |
| @@ -177,17 +197,20 @@ struct aa_policydb { | |||
| 177 | */ | 197 | */ |
| 178 | struct aa_profile { | 198 | struct aa_profile { |
| 179 | struct aa_policy base; | 199 | struct aa_policy base; |
| 180 | struct aa_profile *parent; | 200 | struct kref count; |
| 201 | struct rcu_head rcu; | ||
| 202 | struct aa_profile __rcu *parent; | ||
| 181 | 203 | ||
| 182 | struct aa_namespace *ns; | 204 | struct aa_namespace *ns; |
| 183 | struct aa_profile *replacedby; | 205 | struct aa_replacedby *replacedby; |
| 184 | const char *rename; | 206 | const char *rename; |
| 185 | 207 | ||
| 208 | const char *attach; | ||
| 186 | struct aa_dfa *xmatch; | 209 | struct aa_dfa *xmatch; |
| 187 | int xmatch_len; | 210 | int xmatch_len; |
| 188 | enum audit_mode audit; | 211 | enum audit_mode audit; |
| 189 | enum profile_mode mode; | 212 | long mode; |
| 190 | u32 flags; | 213 | long flags; |
| 191 | u32 path_flags; | 214 | u32 path_flags; |
| 192 | int size; | 215 | int size; |
| 193 | 216 | ||
| @@ -195,6 +218,10 @@ struct aa_profile { | |||
| 195 | struct aa_file_rules file; | 218 | struct aa_file_rules file; |
| 196 | struct aa_caps caps; | 219 | struct aa_caps caps; |
| 197 | struct aa_rlimit rlimits; | 220 | struct aa_rlimit rlimits; |
| 221 | |||
| 222 | unsigned char *hash; | ||
| 223 | char *dirname; | ||
| 224 | struct dentry *dents[AAFS_PROF_SIZEOF]; | ||
| 198 | }; | 225 | }; |
| 199 | 226 | ||
| 200 | extern struct aa_namespace *root_ns; | 227 | extern struct aa_namespace *root_ns; |
| @@ -211,43 +238,11 @@ void aa_free_namespace_kref(struct kref *kref); | |||
| 211 | struct aa_namespace *aa_find_namespace(struct aa_namespace *root, | 238 | struct aa_namespace *aa_find_namespace(struct aa_namespace *root, |
| 212 | const char *name); | 239 | const char *name); |
| 213 | 240 | ||
| 214 | static inline struct aa_policy *aa_get_common(struct aa_policy *c) | ||
| 215 | { | ||
| 216 | if (c) | ||
| 217 | kref_get(&c->count); | ||
| 218 | |||
| 219 | return c; | ||
| 220 | } | ||
| 221 | |||
| 222 | /** | ||
| 223 | * aa_get_namespace - increment references count on @ns | ||
| 224 | * @ns: namespace to increment reference count of (MAYBE NULL) | ||
| 225 | * | ||
| 226 | * Returns: pointer to @ns, if @ns is NULL returns NULL | ||
| 227 | * Requires: @ns must be held with valid refcount when called | ||
| 228 | */ | ||
| 229 | static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) | ||
| 230 | { | ||
| 231 | if (ns) | ||
| 232 | kref_get(&(ns->base.count)); | ||
| 233 | |||
| 234 | return ns; | ||
| 235 | } | ||
| 236 | |||
| 237 | /** | ||
| 238 | * aa_put_namespace - decrement refcount on @ns | ||
| 239 | * @ns: namespace to put reference of | ||
| 240 | * | ||
| 241 | * Decrement reference count of @ns and if no longer in use free it | ||
| 242 | */ | ||
| 243 | static inline void aa_put_namespace(struct aa_namespace *ns) | ||
| 244 | { | ||
| 245 | if (ns) | ||
| 246 | kref_put(&ns->base.count, aa_free_namespace_kref); | ||
| 247 | } | ||
| 248 | 241 | ||
| 242 | void aa_free_replacedby_kref(struct kref *kref); | ||
| 249 | struct aa_profile *aa_alloc_profile(const char *name); | 243 | struct aa_profile *aa_alloc_profile(const char *name); |
| 250 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); | 244 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); |
| 245 | void aa_free_profile(struct aa_profile *profile); | ||
| 251 | void aa_free_profile_kref(struct kref *kref); | 246 | void aa_free_profile_kref(struct kref *kref); |
| 252 | struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); | 247 | struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); |
| 253 | struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name); | 248 | struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name); |
| @@ -259,25 +254,13 @@ ssize_t aa_remove_profiles(char *name, size_t size); | |||
| 259 | #define PROF_ADD 1 | 254 | #define PROF_ADD 1 |
| 260 | #define PROF_REPLACE 0 | 255 | #define PROF_REPLACE 0 |
| 261 | 256 | ||
| 262 | #define unconfined(X) ((X)->flags & PFLAG_UNCONFINED) | 257 | #define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED) |
| 263 | 258 | ||
| 264 | /** | ||
| 265 | * aa_newest_version - find the newest version of @profile | ||
| 266 | * @profile: the profile to check for newer versions of (NOT NULL) | ||
| 267 | * | ||
| 268 | * Returns: newest version of @profile, if @profile is the newest version | ||
| 269 | * return @profile. | ||
| 270 | * | ||
| 271 | * NOTE: the profile returned is not refcounted, The refcount on @profile | ||
| 272 | * must be held until the caller decides what to do with the returned newest | ||
| 273 | * version. | ||
| 274 | */ | ||
| 275 | static inline struct aa_profile *aa_newest_version(struct aa_profile *profile) | ||
| 276 | { | ||
| 277 | while (profile->replacedby) | ||
| 278 | profile = profile->replacedby; | ||
| 279 | 259 | ||
| 280 | return profile; | 260 | static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) |
| 261 | { | ||
| 262 | return rcu_dereference_protected(p->parent, | ||
| 263 | mutex_is_locked(&p->ns->lock)); | ||
| 281 | } | 264 | } |
| 282 | 265 | ||
| 283 | /** | 266 | /** |
| @@ -290,19 +273,126 @@ static inline struct aa_profile *aa_newest_version(struct aa_profile *profile) | |||
| 290 | static inline struct aa_profile *aa_get_profile(struct aa_profile *p) | 273 | static inline struct aa_profile *aa_get_profile(struct aa_profile *p) |
| 291 | { | 274 | { |
| 292 | if (p) | 275 | if (p) |
| 293 | kref_get(&(p->base.count)); | 276 | kref_get(&(p->count)); |
| 294 | 277 | ||
| 295 | return p; | 278 | return p; |
| 296 | } | 279 | } |
| 297 | 280 | ||
| 298 | /** | 281 | /** |
| 282 | * aa_get_profile_not0 - increment refcount on profile @p found via lookup | ||
| 283 | * @p: profile (MAYBE NULL) | ||
| 284 | * | ||
| 285 | * Returns: pointer to @p if @p is NULL will return NULL | ||
| 286 | * Requires: @p must be held with valid refcount when called | ||
| 287 | */ | ||
| 288 | static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p) | ||
| 289 | { | ||
| 290 | if (p && kref_get_not0(&p->count)) | ||
| 291 | return p; | ||
| 292 | |||
| 293 | return NULL; | ||
| 294 | } | ||
| 295 | |||
| 296 | /** | ||
| 297 | * aa_get_profile_rcu - increment a refcount profile that can be replaced | ||
| 298 | * @p: pointer to profile that can be replaced (NOT NULL) | ||
| 299 | * | ||
| 300 | * Returns: pointer to a refcounted profile. | ||
| 301 | * else NULL if no profile | ||
| 302 | */ | ||
| 303 | static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p) | ||
| 304 | { | ||
| 305 | struct aa_profile *c; | ||
| 306 | |||
| 307 | rcu_read_lock(); | ||
| 308 | do { | ||
| 309 | c = rcu_dereference(*p); | ||
| 310 | } while (c && !kref_get_not0(&c->count)); | ||
| 311 | rcu_read_unlock(); | ||
| 312 | |||
| 313 | return c; | ||
| 314 | } | ||
| 315 | |||
| 316 | /** | ||
| 317 | * aa_get_newest_profile - find the newest version of @profile | ||
| 318 | * @profile: the profile to check for newer versions of | ||
| 319 | * | ||
| 320 | * Returns: refcounted newest version of @profile taking into account | ||
| 321 | * replacement, renames and removals | ||
| 322 | * return @profile. | ||
| 323 | */ | ||
| 324 | static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p) | ||
| 325 | { | ||
| 326 | if (!p) | ||
| 327 | return NULL; | ||
| 328 | |||
| 329 | if (PROFILE_INVALID(p)) | ||
| 330 | return aa_get_profile_rcu(&p->replacedby->profile); | ||
| 331 | |||
| 332 | return aa_get_profile(p); | ||
| 333 | } | ||
| 334 | |||
| 335 | /** | ||
| 299 | * aa_put_profile - decrement refcount on profile @p | 336 | * aa_put_profile - decrement refcount on profile @p |
| 300 | * @p: profile (MAYBE NULL) | 337 | * @p: profile (MAYBE NULL) |
| 301 | */ | 338 | */ |
| 302 | static inline void aa_put_profile(struct aa_profile *p) | 339 | static inline void aa_put_profile(struct aa_profile *p) |
| 303 | { | 340 | { |
| 304 | if (p) | 341 | if (p) |
| 305 | kref_put(&p->base.count, aa_free_profile_kref); | 342 | kref_put(&p->count, aa_free_profile_kref); |
| 343 | } | ||
| 344 | |||
| 345 | static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p) | ||
| 346 | { | ||
| 347 | if (p) | ||
| 348 | kref_get(&(p->count)); | ||
| 349 | |||
| 350 | return p; | ||
| 351 | } | ||
| 352 | |||
| 353 | static inline void aa_put_replacedby(struct aa_replacedby *p) | ||
| 354 | { | ||
| 355 | if (p) | ||
| 356 | kref_put(&p->count, aa_free_replacedby_kref); | ||
| 357 | } | ||
| 358 | |||
| 359 | /* requires profile list write lock held */ | ||
| 360 | static inline void __aa_update_replacedby(struct aa_profile *orig, | ||
| 361 | struct aa_profile *new) | ||
| 362 | { | ||
| 363 | struct aa_profile *tmp; | ||
| 364 | tmp = rcu_dereference_protected(orig->replacedby->profile, | ||
| 365 | mutex_is_locked(&orig->ns->lock)); | ||
| 366 | rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new)); | ||
| 367 | orig->flags |= PFLAG_INVALID; | ||
| 368 | aa_put_profile(tmp); | ||
| 369 | } | ||
| 370 | |||
| 371 | /** | ||
| 372 | * aa_get_namespace - increment references count on @ns | ||
| 373 | * @ns: namespace to increment reference count of (MAYBE NULL) | ||
| 374 | * | ||
| 375 | * Returns: pointer to @ns, if @ns is NULL returns NULL | ||
| 376 | * Requires: @ns must be held with valid refcount when called | ||
| 377 | */ | ||
| 378 | static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) | ||
| 379 | { | ||
| 380 | if (ns) | ||
| 381 | aa_get_profile(ns->unconfined); | ||
| 382 | |||
| 383 | return ns; | ||
| 384 | } | ||
| 385 | |||
| 386 | /** | ||
| 387 | * aa_put_namespace - decrement refcount on @ns | ||
| 388 | * @ns: namespace to put reference of | ||
| 389 | * | ||
| 390 | * Decrement reference count of @ns and if no longer in use free it | ||
| 391 | */ | ||
| 392 | static inline void aa_put_namespace(struct aa_namespace *ns) | ||
| 393 | { | ||
| 394 | if (ns) | ||
| 395 | aa_put_profile(ns->unconfined); | ||
| 306 | } | 396 | } |
| 307 | 397 | ||
| 308 | static inline int AUDIT_MODE(struct aa_profile *profile) | 398 | static inline int AUDIT_MODE(struct aa_profile *profile) |
diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index a2dcccac45aa..c214fb88b1bc 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h | |||
| @@ -15,6 +15,25 @@ | |||
| 15 | #ifndef __POLICY_INTERFACE_H | 15 | #ifndef __POLICY_INTERFACE_H |
| 16 | #define __POLICY_INTERFACE_H | 16 | #define __POLICY_INTERFACE_H |
| 17 | 17 | ||
| 18 | struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns); | 18 | #include <linux/list.h> |
| 19 | |||
| 20 | struct aa_load_ent { | ||
| 21 | struct list_head list; | ||
| 22 | struct aa_profile *new; | ||
| 23 | struct aa_profile *old; | ||
| 24 | struct aa_profile *rename; | ||
| 25 | }; | ||
| 26 | |||
| 27 | void aa_load_ent_free(struct aa_load_ent *ent); | ||
| 28 | struct aa_load_ent *aa_load_ent_alloc(void); | ||
| 29 | |||
| 30 | #define PACKED_FLAG_HAT 1 | ||
| 31 | |||
| 32 | #define PACKED_MODE_ENFORCE 0 | ||
| 33 | #define PACKED_MODE_COMPLAIN 1 | ||
| 34 | #define PACKED_MODE_KILL 2 | ||
| 35 | #define PACKED_MODE_UNCONFINED 3 | ||
| 36 | |||
| 37 | int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns); | ||
| 19 | 38 | ||
| 20 | #endif /* __POLICY_INTERFACE_H */ | 39 | #endif /* __POLICY_INTERFACE_H */ |
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index fcfe0233574c..69689922c491 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c | |||
| @@ -97,11 +97,6 @@ void *__aa_kvmalloc(size_t size, gfp_t flags) | |||
| 97 | if (size <= (16*PAGE_SIZE)) | 97 | if (size <= (16*PAGE_SIZE)) |
| 98 | buffer = kmalloc(size, flags | GFP_NOIO | __GFP_NOWARN); | 98 | buffer = kmalloc(size, flags | GFP_NOIO | __GFP_NOWARN); |
| 99 | if (!buffer) { | 99 | if (!buffer) { |
| 100 | /* see kvfree for why size must be at least work_struct size | ||
| 101 | * when allocated via vmalloc | ||
| 102 | */ | ||
| 103 | if (size < sizeof(struct work_struct)) | ||
| 104 | size = sizeof(struct work_struct); | ||
| 105 | if (flags & __GFP_ZERO) | 100 | if (flags & __GFP_ZERO) |
| 106 | buffer = vzalloc(size); | 101 | buffer = vzalloc(size); |
| 107 | else | 102 | else |
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 2e2a0dd4a73f..fb99e18123b4 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c | |||
| @@ -508,19 +508,21 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, | |||
| 508 | /* released below */ | 508 | /* released below */ |
| 509 | const struct cred *cred = get_task_cred(task); | 509 | const struct cred *cred = get_task_cred(task); |
| 510 | struct aa_task_cxt *cxt = cred_cxt(cred); | 510 | struct aa_task_cxt *cxt = cred_cxt(cred); |
| 511 | struct aa_profile *profile = NULL; | ||
| 511 | 512 | ||
| 512 | if (strcmp(name, "current") == 0) | 513 | if (strcmp(name, "current") == 0) |
| 513 | error = aa_getprocattr(aa_newest_version(cxt->profile), | 514 | profile = aa_get_newest_profile(cxt->profile); |
| 514 | value); | ||
| 515 | else if (strcmp(name, "prev") == 0 && cxt->previous) | 515 | else if (strcmp(name, "prev") == 0 && cxt->previous) |
| 516 | error = aa_getprocattr(aa_newest_version(cxt->previous), | 516 | profile = aa_get_newest_profile(cxt->previous); |
| 517 | value); | ||
| 518 | else if (strcmp(name, "exec") == 0 && cxt->onexec) | 517 | else if (strcmp(name, "exec") == 0 && cxt->onexec) |
| 519 | error = aa_getprocattr(aa_newest_version(cxt->onexec), | 518 | profile = aa_get_newest_profile(cxt->onexec); |
| 520 | value); | ||
| 521 | else | 519 | else |
| 522 | error = -EINVAL; | 520 | error = -EINVAL; |
| 523 | 521 | ||
| 522 | if (profile) | ||
| 523 | error = aa_getprocattr(profile, value); | ||
| 524 | |||
| 525 | aa_put_profile(profile); | ||
| 524 | put_cred(cred); | 526 | put_cred(cred); |
| 525 | 527 | ||
| 526 | return error; | 528 | return error; |
| @@ -666,6 +668,7 @@ static int param_set_aabool(const char *val, const struct kernel_param *kp); | |||
| 666 | static int param_get_aabool(char *buffer, const struct kernel_param *kp); | 668 | static int param_get_aabool(char *buffer, const struct kernel_param *kp); |
| 667 | #define param_check_aabool param_check_bool | 669 | #define param_check_aabool param_check_bool |
| 668 | static struct kernel_param_ops param_ops_aabool = { | 670 | static struct kernel_param_ops param_ops_aabool = { |
| 671 | .flags = KERNEL_PARAM_FL_NOARG, | ||
| 669 | .set = param_set_aabool, | 672 | .set = param_set_aabool, |
| 670 | .get = param_get_aabool | 673 | .get = param_get_aabool |
| 671 | }; | 674 | }; |
| @@ -682,6 +685,7 @@ static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp | |||
| 682 | static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp); | 685 | static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp); |
| 683 | #define param_check_aalockpolicy param_check_bool | 686 | #define param_check_aalockpolicy param_check_bool |
| 684 | static struct kernel_param_ops param_ops_aalockpolicy = { | 687 | static struct kernel_param_ops param_ops_aalockpolicy = { |
| 688 | .flags = KERNEL_PARAM_FL_NOARG, | ||
| 685 | .set = param_set_aalockpolicy, | 689 | .set = param_set_aalockpolicy, |
| 686 | .get = param_get_aalockpolicy | 690 | .get = param_get_aalockpolicy |
| 687 | }; | 691 | }; |
| @@ -742,7 +746,7 @@ module_param_named(paranoid_load, aa_g_paranoid_load, aabool, | |||
| 742 | 746 | ||
| 743 | /* Boot time disable flag */ | 747 | /* Boot time disable flag */ |
| 744 | static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; | 748 | static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; |
| 745 | module_param_named(enabled, apparmor_enabled, aabool, S_IRUSR); | 749 | module_param_named(enabled, apparmor_enabled, bool, S_IRUGO); |
| 746 | 750 | ||
| 747 | static int __init apparmor_enabled_setup(char *str) | 751 | static int __init apparmor_enabled_setup(char *str) |
| 748 | { | 752 | { |
| @@ -841,7 +845,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) | |||
| 841 | if (!apparmor_enabled) | 845 | if (!apparmor_enabled) |
| 842 | return -EINVAL; | 846 | return -EINVAL; |
| 843 | 847 | ||
| 844 | return sprintf(buffer, "%s", profile_mode_names[aa_g_profile_mode]); | 848 | return sprintf(buffer, "%s", aa_profile_mode_names[aa_g_profile_mode]); |
| 845 | } | 849 | } |
| 846 | 850 | ||
| 847 | static int param_set_mode(const char *val, struct kernel_param *kp) | 851 | static int param_set_mode(const char *val, struct kernel_param *kp) |
| @@ -856,8 +860,8 @@ static int param_set_mode(const char *val, struct kernel_param *kp) | |||
| 856 | if (!val) | 860 | if (!val) |
| 857 | return -EINVAL; | 861 | return -EINVAL; |
| 858 | 862 | ||
| 859 | for (i = 0; i < APPARMOR_NAMES_MAX_INDEX; i++) { | 863 | for (i = 0; i < APPARMOR_MODE_NAMES_MAX_INDEX; i++) { |
| 860 | if (strcmp(val, profile_mode_names[i]) == 0) { | 864 | if (strcmp(val, aa_profile_mode_names[i]) == 0) { |
| 861 | aa_g_profile_mode = i; | 865 | aa_g_profile_mode = i; |
| 862 | return 0; | 866 | return 0; |
| 863 | } | 867 | } |
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 0f345c4dee5f..705c2879d3a9 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c | |||
| @@ -92,10 +92,11 @@ | |||
| 92 | /* root profile namespace */ | 92 | /* root profile namespace */ |
| 93 | struct aa_namespace *root_ns; | 93 | struct aa_namespace *root_ns; |
| 94 | 94 | ||
| 95 | const char *const profile_mode_names[] = { | 95 | const char *const aa_profile_mode_names[] = { |
| 96 | "enforce", | 96 | "enforce", |
| 97 | "complain", | 97 | "complain", |
| 98 | "kill", | 98 | "kill", |
| 99 | "unconfined", | ||
| 99 | }; | 100 | }; |
| 100 | 101 | ||
| 101 | /** | 102 | /** |
| @@ -141,7 +142,6 @@ static bool policy_init(struct aa_policy *policy, const char *prefix, | |||
| 141 | policy->name = (char *)hname_tail(policy->hname); | 142 | policy->name = (char *)hname_tail(policy->hname); |
| 142 | INIT_LIST_HEAD(&policy->list); | 143 | INIT_LIST_HEAD(&policy->list); |
| 143 | INIT_LIST_HEAD(&policy->profiles); | 144 | INIT_LIST_HEAD(&policy->profiles); |
| 144 | kref_init(&policy->count); | ||
| 145 | 145 | ||
| 146 | return 1; | 146 | return 1; |
| 147 | } | 147 | } |
| @@ -153,13 +153,13 @@ static bool policy_init(struct aa_policy *policy, const char *prefix, | |||
| 153 | static void policy_destroy(struct aa_policy *policy) | 153 | static void policy_destroy(struct aa_policy *policy) |
| 154 | { | 154 | { |
| 155 | /* still contains profiles -- invalid */ | 155 | /* still contains profiles -- invalid */ |
| 156 | if (!list_empty(&policy->profiles)) { | 156 | if (on_list_rcu(&policy->profiles)) { |
| 157 | AA_ERROR("%s: internal error, " | 157 | AA_ERROR("%s: internal error, " |
| 158 | "policy '%s' still contains profiles\n", | 158 | "policy '%s' still contains profiles\n", |
| 159 | __func__, policy->name); | 159 | __func__, policy->name); |
| 160 | BUG(); | 160 | BUG(); |
| 161 | } | 161 | } |
| 162 | if (!list_empty(&policy->list)) { | 162 | if (on_list_rcu(&policy->list)) { |
| 163 | AA_ERROR("%s: internal error, policy '%s' still on list\n", | 163 | AA_ERROR("%s: internal error, policy '%s' still on list\n", |
| 164 | __func__, policy->name); | 164 | __func__, policy->name); |
| 165 | BUG(); | 165 | BUG(); |
| @@ -174,7 +174,7 @@ static void policy_destroy(struct aa_policy *policy) | |||
| 174 | * @head: list to search (NOT NULL) | 174 | * @head: list to search (NOT NULL) |
| 175 | * @name: name to search for (NOT NULL) | 175 | * @name: name to search for (NOT NULL) |
| 176 | * | 176 | * |
| 177 | * Requires: correct locks for the @head list be held | 177 | * Requires: rcu_read_lock be held |
| 178 | * | 178 | * |
| 179 | * Returns: unrefcounted policy that match @name or NULL if not found | 179 | * Returns: unrefcounted policy that match @name or NULL if not found |
| 180 | */ | 180 | */ |
| @@ -182,7 +182,7 @@ static struct aa_policy *__policy_find(struct list_head *head, const char *name) | |||
| 182 | { | 182 | { |
| 183 | struct aa_policy *policy; | 183 | struct aa_policy *policy; |
| 184 | 184 | ||
| 185 | list_for_each_entry(policy, head, list) { | 185 | list_for_each_entry_rcu(policy, head, list) { |
| 186 | if (!strcmp(policy->name, name)) | 186 | if (!strcmp(policy->name, name)) |
| 187 | return policy; | 187 | return policy; |
| 188 | } | 188 | } |
| @@ -195,7 +195,7 @@ static struct aa_policy *__policy_find(struct list_head *head, const char *name) | |||
| 195 | * @str: string to search for (NOT NULL) | 195 | * @str: string to search for (NOT NULL) |
| 196 | * @len: length of match required | 196 | * @len: length of match required |
| 197 | * | 197 | * |
| 198 | * Requires: correct locks for the @head list be held | 198 | * Requires: rcu_read_lock be held |
| 199 | * | 199 | * |
| 200 | * Returns: unrefcounted policy that match @str or NULL if not found | 200 | * Returns: unrefcounted policy that match @str or NULL if not found |
| 201 | * | 201 | * |
| @@ -207,7 +207,7 @@ static struct aa_policy *__policy_strn_find(struct list_head *head, | |||
| 207 | { | 207 | { |
| 208 | struct aa_policy *policy; | 208 | struct aa_policy *policy; |
| 209 | 209 | ||
| 210 | list_for_each_entry(policy, head, list) { | 210 | list_for_each_entry_rcu(policy, head, list) { |
| 211 | if (aa_strneq(policy->name, str, len)) | 211 | if (aa_strneq(policy->name, str, len)) |
| 212 | return policy; | 212 | return policy; |
| 213 | } | 213 | } |
| @@ -284,22 +284,19 @@ static struct aa_namespace *alloc_namespace(const char *prefix, | |||
| 284 | goto fail_ns; | 284 | goto fail_ns; |
| 285 | 285 | ||
| 286 | INIT_LIST_HEAD(&ns->sub_ns); | 286 | INIT_LIST_HEAD(&ns->sub_ns); |
| 287 | rwlock_init(&ns->lock); | 287 | mutex_init(&ns->lock); |
| 288 | 288 | ||
| 289 | /* released by free_namespace */ | 289 | /* released by free_namespace */ |
| 290 | ns->unconfined = aa_alloc_profile("unconfined"); | 290 | ns->unconfined = aa_alloc_profile("unconfined"); |
| 291 | if (!ns->unconfined) | 291 | if (!ns->unconfined) |
| 292 | goto fail_unconfined; | 292 | goto fail_unconfined; |
| 293 | 293 | ||
| 294 | ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | | 294 | ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR | |
| 295 | PFLAG_IMMUTABLE; | 295 | PFLAG_IMMUTABLE | PFLAG_NS_COUNT; |
| 296 | ns->unconfined->mode = APPARMOR_UNCONFINED; | ||
| 296 | 297 | ||
| 297 | /* | 298 | /* ns and ns->unconfined share ns->unconfined refcount */ |
| 298 | * released by free_namespace, however __remove_namespace breaks | 299 | ns->unconfined->ns = ns; |
| 299 | * the cyclic references (ns->unconfined, and unconfined->ns) and | ||
| 300 | * replaces with refs to parent namespace unconfined | ||
| 301 | */ | ||
| 302 | ns->unconfined->ns = aa_get_namespace(ns); | ||
| 303 | 300 | ||
| 304 | atomic_set(&ns->uniq_null, 0); | 301 | atomic_set(&ns->uniq_null, 0); |
| 305 | 302 | ||
| @@ -327,30 +324,19 @@ static void free_namespace(struct aa_namespace *ns) | |||
| 327 | policy_destroy(&ns->base); | 324 | policy_destroy(&ns->base); |
| 328 | aa_put_namespace(ns->parent); | 325 | aa_put_namespace(ns->parent); |
| 329 | 326 | ||
| 330 | if (ns->unconfined && ns->unconfined->ns == ns) | 327 | ns->unconfined->ns = NULL; |
| 331 | ns->unconfined->ns = NULL; | 328 | aa_free_profile(ns->unconfined); |
| 332 | |||
| 333 | aa_put_profile(ns->unconfined); | ||
| 334 | kzfree(ns); | 329 | kzfree(ns); |
| 335 | } | 330 | } |
| 336 | 331 | ||
| 337 | /** | 332 | /** |
| 338 | * aa_free_namespace_kref - free aa_namespace by kref (see aa_put_namespace) | ||
| 339 | * @kr: kref callback for freeing of a namespace (NOT NULL) | ||
| 340 | */ | ||
| 341 | void aa_free_namespace_kref(struct kref *kref) | ||
| 342 | { | ||
| 343 | free_namespace(container_of(kref, struct aa_namespace, base.count)); | ||
| 344 | } | ||
| 345 | |||
| 346 | /** | ||
| 347 | * __aa_find_namespace - find a namespace on a list by @name | 333 | * __aa_find_namespace - find a namespace on a list by @name |
| 348 | * @head: list to search for namespace on (NOT NULL) | 334 | * @head: list to search for namespace on (NOT NULL) |
| 349 | * @name: name of namespace to look for (NOT NULL) | 335 | * @name: name of namespace to look for (NOT NULL) |
| 350 | * | 336 | * |
| 351 | * Returns: unrefcounted namespace | 337 | * Returns: unrefcounted namespace |
| 352 | * | 338 | * |
| 353 | * Requires: ns lock be held | 339 | * Requires: rcu_read_lock be held |
| 354 | */ | 340 | */ |
| 355 | static struct aa_namespace *__aa_find_namespace(struct list_head *head, | 341 | static struct aa_namespace *__aa_find_namespace(struct list_head *head, |
| 356 | const char *name) | 342 | const char *name) |
| @@ -373,9 +359,9 @@ struct aa_namespace *aa_find_namespace(struct aa_namespace *root, | |||
| 373 | { | 359 | { |
| 374 | struct aa_namespace *ns = NULL; | 360 | struct aa_namespace *ns = NULL; |
| 375 | 361 | ||
| 376 | read_lock(&root->lock); | 362 | rcu_read_lock(); |
| 377 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); | 363 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); |
| 378 | read_unlock(&root->lock); | 364 | rcu_read_unlock(); |
| 379 | 365 | ||
| 380 | return ns; | 366 | return ns; |
| 381 | } | 367 | } |
| @@ -392,7 +378,7 @@ static struct aa_namespace *aa_prepare_namespace(const char *name) | |||
| 392 | 378 | ||
| 393 | root = aa_current_profile()->ns; | 379 | root = aa_current_profile()->ns; |
| 394 | 380 | ||
| 395 | write_lock(&root->lock); | 381 | mutex_lock(&root->lock); |
| 396 | 382 | ||
| 397 | /* if name isn't specified the profile is loaded to the current ns */ | 383 | /* if name isn't specified the profile is loaded to the current ns */ |
| 398 | if (!name) { | 384 | if (!name) { |
| @@ -405,31 +391,23 @@ static struct aa_namespace *aa_prepare_namespace(const char *name) | |||
| 405 | /* released by caller */ | 391 | /* released by caller */ |
| 406 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); | 392 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); |
| 407 | if (!ns) { | 393 | if (!ns) { |
| 408 | /* namespace not found */ | 394 | ns = alloc_namespace(root->base.hname, name); |
| 409 | struct aa_namespace *new_ns; | 395 | if (!ns) |
| 410 | write_unlock(&root->lock); | 396 | goto out; |
| 411 | new_ns = alloc_namespace(root->base.hname, name); | 397 | if (__aa_fs_namespace_mkdir(ns, ns_subns_dir(root), name)) { |
| 412 | if (!new_ns) | 398 | AA_ERROR("Failed to create interface for ns %s\n", |
| 413 | return NULL; | 399 | ns->base.name); |
| 414 | write_lock(&root->lock); | 400 | free_namespace(ns); |
| 415 | /* test for race when new_ns was allocated */ | 401 | ns = NULL; |
| 416 | ns = __aa_find_namespace(&root->sub_ns, name); | 402 | goto out; |
| 417 | if (!ns) { | ||
| 418 | /* add parent ref */ | ||
| 419 | new_ns->parent = aa_get_namespace(root); | ||
| 420 | |||
| 421 | list_add(&new_ns->base.list, &root->sub_ns); | ||
| 422 | /* add list ref */ | ||
| 423 | ns = aa_get_namespace(new_ns); | ||
| 424 | } else { | ||
| 425 | /* raced so free the new one */ | ||
| 426 | free_namespace(new_ns); | ||
| 427 | /* get reference on namespace */ | ||
| 428 | aa_get_namespace(ns); | ||
| 429 | } | 403 | } |
| 404 | ns->parent = aa_get_namespace(root); | ||
| 405 | list_add_rcu(&ns->base.list, &root->sub_ns); | ||
| 406 | /* add list ref */ | ||
| 407 | aa_get_namespace(ns); | ||
| 430 | } | 408 | } |
| 431 | out: | 409 | out: |
| 432 | write_unlock(&root->lock); | 410 | mutex_unlock(&root->lock); |
| 433 | 411 | ||
| 434 | /* return ref */ | 412 | /* return ref */ |
| 435 | return ns; | 413 | return ns; |
| @@ -447,7 +425,7 @@ out: | |||
| 447 | static void __list_add_profile(struct list_head *list, | 425 | static void __list_add_profile(struct list_head *list, |
| 448 | struct aa_profile *profile) | 426 | struct aa_profile *profile) |
| 449 | { | 427 | { |
| 450 | list_add(&profile->base.list, list); | 428 | list_add_rcu(&profile->base.list, list); |
| 451 | /* get list reference */ | 429 | /* get list reference */ |
| 452 | aa_get_profile(profile); | 430 | aa_get_profile(profile); |
| 453 | } | 431 | } |
| @@ -466,49 +444,8 @@ static void __list_add_profile(struct list_head *list, | |||
| 466 | */ | 444 | */ |
| 467 | static void __list_remove_profile(struct aa_profile *profile) | 445 | static void __list_remove_profile(struct aa_profile *profile) |
| 468 | { | 446 | { |
| 469 | list_del_init(&profile->base.list); | 447 | list_del_rcu(&profile->base.list); |
| 470 | if (!(profile->flags & PFLAG_NO_LIST_REF)) | 448 | aa_put_profile(profile); |
| 471 | /* release list reference */ | ||
| 472 | aa_put_profile(profile); | ||
| 473 | } | ||
| 474 | |||
| 475 | /** | ||
| 476 | * __replace_profile - replace @old with @new on a list | ||
| 477 | * @old: profile to be replaced (NOT NULL) | ||
| 478 | * @new: profile to replace @old with (NOT NULL) | ||
| 479 | * | ||
| 480 | * Will duplicate and refcount elements that @new inherits from @old | ||
| 481 | * and will inherit @old children. | ||
| 482 | * | ||
| 483 | * refcount @new for list, put @old list refcount | ||
| 484 | * | ||
| 485 | * Requires: namespace list lock be held, or list not be shared | ||
| 486 | */ | ||
| 487 | static void __replace_profile(struct aa_profile *old, struct aa_profile *new) | ||
| 488 | { | ||
| 489 | struct aa_policy *policy; | ||
| 490 | struct aa_profile *child, *tmp; | ||
| 491 | |||
| 492 | if (old->parent) | ||
| 493 | policy = &old->parent->base; | ||
| 494 | else | ||
| 495 | policy = &old->ns->base; | ||
| 496 | |||
| 497 | /* released when @new is freed */ | ||
| 498 | new->parent = aa_get_profile(old->parent); | ||
| 499 | new->ns = aa_get_namespace(old->ns); | ||
| 500 | __list_add_profile(&policy->profiles, new); | ||
| 501 | /* inherit children */ | ||
| 502 | list_for_each_entry_safe(child, tmp, &old->base.profiles, base.list) { | ||
| 503 | aa_put_profile(child->parent); | ||
| 504 | child->parent = aa_get_profile(new); | ||
| 505 | /* list refcount transferred to @new*/ | ||
| 506 | list_move(&child->base.list, &new->base.profiles); | ||
| 507 | } | ||
| 508 | |||
| 509 | /* released by free_profile */ | ||
| 510 | old->replacedby = aa_get_profile(new); | ||
| 511 | __list_remove_profile(old); | ||
| 512 | } | 449 | } |
| 513 | 450 | ||
| 514 | static void __profile_list_release(struct list_head *head); | 451 | static void __profile_list_release(struct list_head *head); |
| @@ -524,7 +461,8 @@ static void __remove_profile(struct aa_profile *profile) | |||
| 524 | /* release any children lists first */ | 461 | /* release any children lists first */ |
| 525 | __profile_list_release(&profile->base.profiles); | 462 | __profile_list_release(&profile->base.profiles); |
| 526 | /* released by free_profile */ | 463 | /* released by free_profile */ |
| 527 | profile->replacedby = aa_get_profile(profile->ns->unconfined); | 464 | __aa_update_replacedby(profile, profile->ns->unconfined); |
| 465 | __aa_fs_profile_rmdir(profile); | ||
| 528 | __list_remove_profile(profile); | 466 | __list_remove_profile(profile); |
| 529 | } | 467 | } |
| 530 | 468 | ||
| @@ -552,14 +490,17 @@ static void destroy_namespace(struct aa_namespace *ns) | |||
| 552 | if (!ns) | 490 | if (!ns) |
| 553 | return; | 491 | return; |
| 554 | 492 | ||
| 555 | write_lock(&ns->lock); | 493 | mutex_lock(&ns->lock); |
| 556 | /* release all profiles in this namespace */ | 494 | /* release all profiles in this namespace */ |
| 557 | __profile_list_release(&ns->base.profiles); | 495 | __profile_list_release(&ns->base.profiles); |
| 558 | 496 | ||
| 559 | /* release all sub namespaces */ | 497 | /* release all sub namespaces */ |
| 560 | __ns_list_release(&ns->sub_ns); | 498 | __ns_list_release(&ns->sub_ns); |
| 561 | 499 | ||
| 562 | write_unlock(&ns->lock); | 500 | if (ns->parent) |
| 501 | __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); | ||
| 502 | __aa_fs_namespace_rmdir(ns); | ||
| 503 | mutex_unlock(&ns->lock); | ||
| 563 | } | 504 | } |
| 564 | 505 | ||
| 565 | /** | 506 | /** |
| @@ -570,25 +511,9 @@ static void destroy_namespace(struct aa_namespace *ns) | |||
| 570 | */ | 511 | */ |
| 571 | static void __remove_namespace(struct aa_namespace *ns) | 512 | static void __remove_namespace(struct aa_namespace *ns) |
| 572 | { | 513 | { |
| 573 | struct aa_profile *unconfined = ns->unconfined; | ||
| 574 | |||
| 575 | /* remove ns from namespace list */ | 514 | /* remove ns from namespace list */ |
| 576 | list_del_init(&ns->base.list); | 515 | list_del_rcu(&ns->base.list); |
| 577 | |||
| 578 | /* | ||
| 579 | * break the ns, unconfined profile cyclic reference and forward | ||
| 580 | * all new unconfined profiles requests to the parent namespace | ||
| 581 | * This will result in all confined tasks that have a profile | ||
| 582 | * being removed, inheriting the parent->unconfined profile. | ||
| 583 | */ | ||
| 584 | if (ns->parent) | ||
| 585 | ns->unconfined = aa_get_profile(ns->parent->unconfined); | ||
| 586 | |||
| 587 | destroy_namespace(ns); | 516 | destroy_namespace(ns); |
| 588 | |||
| 589 | /* release original ns->unconfined ref */ | ||
| 590 | aa_put_profile(unconfined); | ||
| 591 | /* release ns->base.list ref, from removal above */ | ||
| 592 | aa_put_namespace(ns); | 517 | aa_put_namespace(ns); |
| 593 | } | 518 | } |
| 594 | 519 | ||
| @@ -634,8 +559,26 @@ void __init aa_free_root_ns(void) | |||
| 634 | aa_put_namespace(ns); | 559 | aa_put_namespace(ns); |
| 635 | } | 560 | } |
| 636 | 561 | ||
| 562 | |||
| 563 | static void free_replacedby(struct aa_replacedby *r) | ||
| 564 | { | ||
| 565 | if (r) { | ||
| 566 | /* r->profile will not be updated any more as r is dead */ | ||
| 567 | aa_put_profile(rcu_dereference_protected(r->profile, true)); | ||
| 568 | kzfree(r); | ||
| 569 | } | ||
| 570 | } | ||
| 571 | |||
| 572 | |||
| 573 | void aa_free_replacedby_kref(struct kref *kref) | ||
| 574 | { | ||
| 575 | struct aa_replacedby *r = container_of(kref, struct aa_replacedby, | ||
| 576 | count); | ||
| 577 | free_replacedby(r); | ||
| 578 | } | ||
| 579 | |||
| 637 | /** | 580 | /** |
| 638 | * free_profile - free a profile | 581 | * aa_free_profile - free a profile |
| 639 | * @profile: the profile to free (MAYBE NULL) | 582 | * @profile: the profile to free (MAYBE NULL) |
| 640 | * | 583 | * |
| 641 | * Free a profile, its hats and null_profile. All references to the profile, | 584 | * Free a profile, its hats and null_profile. All references to the profile, |
| @@ -644,25 +587,16 @@ void __init aa_free_root_ns(void) | |||
| 644 | * If the profile was referenced from a task context, free_profile() will | 587 | * If the profile was referenced from a task context, free_profile() will |
| 645 | * be called from an rcu callback routine, so we must not sleep here. | 588 | * be called from an rcu callback routine, so we must not sleep here. |
| 646 | */ | 589 | */ |
| 647 | static void free_profile(struct aa_profile *profile) | 590 | void aa_free_profile(struct aa_profile *profile) |
| 648 | { | 591 | { |
| 649 | struct aa_profile *p; | ||
| 650 | |||
| 651 | AA_DEBUG("%s(%p)\n", __func__, profile); | 592 | AA_DEBUG("%s(%p)\n", __func__, profile); |
| 652 | 593 | ||
| 653 | if (!profile) | 594 | if (!profile) |
| 654 | return; | 595 | return; |
| 655 | 596 | ||
| 656 | if (!list_empty(&profile->base.list)) { | ||
| 657 | AA_ERROR("%s: internal error, " | ||
| 658 | "profile '%s' still on ns list\n", | ||
| 659 | __func__, profile->base.name); | ||
| 660 | BUG(); | ||
| 661 | } | ||
| 662 | |||
| 663 | /* free children profiles */ | 597 | /* free children profiles */ |
| 664 | policy_destroy(&profile->base); | 598 | policy_destroy(&profile->base); |
| 665 | aa_put_profile(profile->parent); | 599 | aa_put_profile(rcu_access_pointer(profile->parent)); |
| 666 | 600 | ||
| 667 | aa_put_namespace(profile->ns); | 601 | aa_put_namespace(profile->ns); |
| 668 | kzfree(profile->rename); | 602 | kzfree(profile->rename); |
| @@ -671,44 +605,36 @@ static void free_profile(struct aa_profile *profile) | |||
| 671 | aa_free_cap_rules(&profile->caps); | 605 | aa_free_cap_rules(&profile->caps); |
| 672 | aa_free_rlimit_rules(&profile->rlimits); | 606 | aa_free_rlimit_rules(&profile->rlimits); |
| 673 | 607 | ||
| 608 | kzfree(profile->dirname); | ||
| 674 | aa_put_dfa(profile->xmatch); | 609 | aa_put_dfa(profile->xmatch); |
| 675 | aa_put_dfa(profile->policy.dfa); | 610 | aa_put_dfa(profile->policy.dfa); |
| 611 | aa_put_replacedby(profile->replacedby); | ||
| 676 | 612 | ||
| 677 | /* put the profile reference for replacedby, but not via | 613 | kzfree(profile->hash); |
| 678 | * put_profile(kref_put). | ||
| 679 | * replacedby can form a long chain that can result in cascading | ||
| 680 | * frees that blows the stack because kref_put makes a nested fn | ||
| 681 | * call (it looks like recursion, with free_profile calling | ||
| 682 | * free_profile) for each profile in the chain lp#1056078. | ||
| 683 | */ | ||
| 684 | for (p = profile->replacedby; p; ) { | ||
| 685 | if (atomic_dec_and_test(&p->base.count.refcount)) { | ||
| 686 | /* no more refs on p, grab its replacedby */ | ||
| 687 | struct aa_profile *next = p->replacedby; | ||
| 688 | /* break the chain */ | ||
| 689 | p->replacedby = NULL; | ||
| 690 | /* now free p, chain is broken */ | ||
| 691 | free_profile(p); | ||
| 692 | |||
| 693 | /* follow up with next profile in the chain */ | ||
| 694 | p = next; | ||
| 695 | } else | ||
| 696 | break; | ||
| 697 | } | ||
| 698 | |||
| 699 | kzfree(profile); | 614 | kzfree(profile); |
| 700 | } | 615 | } |
| 701 | 616 | ||
| 702 | /** | 617 | /** |
| 618 | * aa_free_profile_rcu - free aa_profile by rcu (called by aa_free_profile_kref) | ||
| 619 | * @head: rcu_head callback for freeing of a profile (NOT NULL) | ||
| 620 | */ | ||
| 621 | static void aa_free_profile_rcu(struct rcu_head *head) | ||
| 622 | { | ||
| 623 | struct aa_profile *p = container_of(head, struct aa_profile, rcu); | ||
| 624 | if (p->flags & PFLAG_NS_COUNT) | ||
| 625 | free_namespace(p->ns); | ||
| 626 | else | ||
| 627 | aa_free_profile(p); | ||
| 628 | } | ||
| 629 | |||
| 630 | /** | ||
| 703 | * aa_free_profile_kref - free aa_profile by kref (called by aa_put_profile) | 631 | * aa_free_profile_kref - free aa_profile by kref (called by aa_put_profile) |
| 704 | * @kr: kref callback for freeing of a profile (NOT NULL) | 632 | * @kr: kref callback for freeing of a profile (NOT NULL) |
| 705 | */ | 633 | */ |
| 706 | void aa_free_profile_kref(struct kref *kref) | 634 | void aa_free_profile_kref(struct kref *kref) |
| 707 | { | 635 | { |
| 708 | struct aa_profile *p = container_of(kref, struct aa_profile, | 636 | struct aa_profile *p = container_of(kref, struct aa_profile, count); |
| 709 | base.count); | 637 | call_rcu(&p->rcu, aa_free_profile_rcu); |
| 710 | |||
| 711 | free_profile(p); | ||
| 712 | } | 638 | } |
| 713 | 639 | ||
| 714 | /** | 640 | /** |
| @@ -726,13 +652,23 @@ struct aa_profile *aa_alloc_profile(const char *hname) | |||
| 726 | if (!profile) | 652 | if (!profile) |
| 727 | return NULL; | 653 | return NULL; |
| 728 | 654 | ||
| 729 | if (!policy_init(&profile->base, NULL, hname)) { | 655 | profile->replacedby = kzalloc(sizeof(struct aa_replacedby), GFP_KERNEL); |
| 730 | kzfree(profile); | 656 | if (!profile->replacedby) |
| 731 | return NULL; | 657 | goto fail; |
| 732 | } | 658 | kref_init(&profile->replacedby->count); |
| 659 | |||
| 660 | if (!policy_init(&profile->base, NULL, hname)) | ||
| 661 | goto fail; | ||
| 662 | kref_init(&profile->count); | ||
| 733 | 663 | ||
| 734 | /* refcount released by caller */ | 664 | /* refcount released by caller */ |
| 735 | return profile; | 665 | return profile; |
| 666 | |||
| 667 | fail: | ||
| 668 | kzfree(profile->replacedby); | ||
| 669 | kzfree(profile); | ||
| 670 | |||
| 671 | return NULL; | ||
| 736 | } | 672 | } |
| 737 | 673 | ||
| 738 | /** | 674 | /** |
| @@ -772,12 +708,12 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) | |||
| 772 | profile->flags |= PFLAG_HAT; | 708 | profile->flags |= PFLAG_HAT; |
| 773 | 709 | ||
| 774 | /* released on free_profile */ | 710 | /* released on free_profile */ |
| 775 | profile->parent = aa_get_profile(parent); | 711 | rcu_assign_pointer(profile->parent, aa_get_profile(parent)); |
| 776 | profile->ns = aa_get_namespace(parent->ns); | 712 | profile->ns = aa_get_namespace(parent->ns); |
| 777 | 713 | ||
| 778 | write_lock(&profile->ns->lock); | 714 | mutex_lock(&profile->ns->lock); |
| 779 | __list_add_profile(&parent->base.profiles, profile); | 715 | __list_add_profile(&parent->base.profiles, profile); |
| 780 | write_unlock(&profile->ns->lock); | 716 | mutex_unlock(&profile->ns->lock); |
| 781 | 717 | ||
| 782 | /* refcount released by caller */ | 718 | /* refcount released by caller */ |
| 783 | return profile; | 719 | return profile; |
| @@ -793,7 +729,7 @@ fail: | |||
| 793 | * @head: list to search (NOT NULL) | 729 | * @head: list to search (NOT NULL) |
| 794 | * @name: name of profile (NOT NULL) | 730 | * @name: name of profile (NOT NULL) |
| 795 | * | 731 | * |
| 796 | * Requires: ns lock protecting list be held | 732 | * Requires: rcu_read_lock be held |
| 797 | * | 733 | * |
| 798 | * Returns: unrefcounted profile ptr, or NULL if not found | 734 | * Returns: unrefcounted profile ptr, or NULL if not found |
| 799 | */ | 735 | */ |
| @@ -808,7 +744,7 @@ static struct aa_profile *__find_child(struct list_head *head, const char *name) | |||
| 808 | * @name: name of profile (NOT NULL) | 744 | * @name: name of profile (NOT NULL) |
| 809 | * @len: length of @name substring to match | 745 | * @len: length of @name substring to match |
| 810 | * | 746 | * |
| 811 | * Requires: ns lock protecting list be held | 747 | * Requires: rcu_read_lock be held |
| 812 | * | 748 | * |
| 813 | * Returns: unrefcounted profile ptr, or NULL if not found | 749 | * Returns: unrefcounted profile ptr, or NULL if not found |
| 814 | */ | 750 | */ |
| @@ -829,9 +765,9 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) | |||
| 829 | { | 765 | { |
| 830 | struct aa_profile *profile; | 766 | struct aa_profile *profile; |
| 831 | 767 | ||
| 832 | read_lock(&parent->ns->lock); | 768 | rcu_read_lock(); |
| 833 | profile = aa_get_profile(__find_child(&parent->base.profiles, name)); | 769 | profile = aa_get_profile(__find_child(&parent->base.profiles, name)); |
| 834 | read_unlock(&parent->ns->lock); | 770 | rcu_read_unlock(); |
| 835 | 771 | ||
| 836 | /* refcount released by caller */ | 772 | /* refcount released by caller */ |
| 837 | return profile; | 773 | return profile; |
| @@ -846,7 +782,7 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) | |||
| 846 | * that matches hname does not need to exist, in general this | 782 | * that matches hname does not need to exist, in general this |
| 847 | * is used to load a new profile. | 783 | * is used to load a new profile. |
| 848 | * | 784 | * |
| 849 | * Requires: ns->lock be held | 785 | * Requires: rcu_read_lock be held |
| 850 | * | 786 | * |
| 851 | * Returns: unrefcounted policy or NULL if not found | 787 | * Returns: unrefcounted policy or NULL if not found |
| 852 | */ | 788 | */ |
| @@ -878,7 +814,7 @@ static struct aa_policy *__lookup_parent(struct aa_namespace *ns, | |||
| 878 | * @base: base list to start looking up profile name from (NOT NULL) | 814 | * @base: base list to start looking up profile name from (NOT NULL) |
| 879 | * @hname: hierarchical profile name (NOT NULL) | 815 | * @hname: hierarchical profile name (NOT NULL) |
| 880 | * | 816 | * |
| 881 | * Requires: ns->lock be held | 817 | * Requires: rcu_read_lock be held |
| 882 | * | 818 | * |
| 883 | * Returns: unrefcounted profile pointer or NULL if not found | 819 | * Returns: unrefcounted profile pointer or NULL if not found |
| 884 | * | 820 | * |
| @@ -917,13 +853,15 @@ struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname) | |||
| 917 | { | 853 | { |
| 918 | struct aa_profile *profile; | 854 | struct aa_profile *profile; |
| 919 | 855 | ||
| 920 | read_lock(&ns->lock); | 856 | rcu_read_lock(); |
| 921 | profile = aa_get_profile(__lookup_profile(&ns->base, hname)); | 857 | do { |
| 922 | read_unlock(&ns->lock); | 858 | profile = __lookup_profile(&ns->base, hname); |
| 859 | } while (profile && !aa_get_profile_not0(profile)); | ||
| 860 | rcu_read_unlock(); | ||
| 923 | 861 | ||
| 924 | /* the unconfined profile is not in the regular profile list */ | 862 | /* the unconfined profile is not in the regular profile list */ |
| 925 | if (!profile && strcmp(hname, "unconfined") == 0) | 863 | if (!profile && strcmp(hname, "unconfined") == 0) |
| 926 | profile = aa_get_profile(ns->unconfined); | 864 | profile = aa_get_newest_profile(ns->unconfined); |
| 927 | 865 | ||
| 928 | /* refcount released by caller */ | 866 | /* refcount released by caller */ |
| 929 | return profile; | 867 | return profile; |
| @@ -953,25 +891,6 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace, | |||
| 953 | } | 891 | } |
| 954 | 892 | ||
| 955 | /** | 893 | /** |
| 956 | * __add_new_profile - simple wrapper around __list_add_profile | ||
| 957 | * @ns: namespace that profile is being added to (NOT NULL) | ||
| 958 | * @policy: the policy container to add the profile to (NOT NULL) | ||
| 959 | * @profile: profile to add (NOT NULL) | ||
| 960 | * | ||
| 961 | * add a profile to a list and do other required basic allocations | ||
| 962 | */ | ||
| 963 | static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy, | ||
| 964 | struct aa_profile *profile) | ||
| 965 | { | ||
| 966 | if (policy != &ns->base) | ||
| 967 | /* released on profile replacement or free_profile */ | ||
| 968 | profile->parent = aa_get_profile((struct aa_profile *) policy); | ||
| 969 | __list_add_profile(&policy->profiles, profile); | ||
| 970 | /* released on free_profile */ | ||
| 971 | profile->ns = aa_get_namespace(ns); | ||
| 972 | } | ||
| 973 | |||
| 974 | /** | ||
| 975 | * aa_audit_policy - Do auditing of policy changes | 894 | * aa_audit_policy - Do auditing of policy changes |
| 976 | * @op: policy operation being performed | 895 | * @op: policy operation being performed |
| 977 | * @gfp: memory allocation flags | 896 | * @gfp: memory allocation flags |
| @@ -1019,6 +938,121 @@ bool aa_may_manage_policy(int op) | |||
| 1019 | return 1; | 938 | return 1; |
| 1020 | } | 939 | } |
| 1021 | 940 | ||
| 941 | static struct aa_profile *__list_lookup_parent(struct list_head *lh, | ||
| 942 | struct aa_profile *profile) | ||
| 943 | { | ||
| 944 | const char *base = hname_tail(profile->base.hname); | ||
| 945 | long len = base - profile->base.hname; | ||
| 946 | struct aa_load_ent *ent; | ||
| 947 | |||
| 948 | /* parent won't have trailing // so remove from len */ | ||
| 949 | if (len <= 2) | ||
| 950 | return NULL; | ||
| 951 | len -= 2; | ||
| 952 | |||
| 953 | list_for_each_entry(ent, lh, list) { | ||
| 954 | if (ent->new == profile) | ||
| 955 | continue; | ||
| 956 | if (strncmp(ent->new->base.hname, profile->base.hname, len) == | ||
| 957 | 0 && ent->new->base.hname[len] == 0) | ||
| 958 | return ent->new; | ||
| 959 | } | ||
| 960 | |||
| 961 | return NULL; | ||
| 962 | } | ||
| 963 | |||
| 964 | /** | ||
| 965 | * __replace_profile - replace @old with @new on a list | ||
| 966 | * @old: profile to be replaced (NOT NULL) | ||
| 967 | * @new: profile to replace @old with (NOT NULL) | ||
| 968 | * @share_replacedby: transfer @old->replacedby to @new | ||
| 969 | * | ||
| 970 | * Will duplicate and refcount elements that @new inherits from @old | ||
| 971 | * and will inherit @old children. | ||
| 972 | * | ||
| 973 | * refcount @new for list, put @old list refcount | ||
| 974 | * | ||
| 975 | * Requires: namespace list lock be held, or list not be shared | ||
| 976 | */ | ||
| 977 | static void __replace_profile(struct aa_profile *old, struct aa_profile *new, | ||
| 978 | bool share_replacedby) | ||
| 979 | { | ||
| 980 | struct aa_profile *child, *tmp; | ||
| 981 | |||
| 982 | if (!list_empty(&old->base.profiles)) { | ||
| 983 | LIST_HEAD(lh); | ||
| 984 | list_splice_init_rcu(&old->base.profiles, &lh, synchronize_rcu); | ||
| 985 | |||
| 986 | list_for_each_entry_safe(child, tmp, &lh, base.list) { | ||
| 987 | struct aa_profile *p; | ||
| 988 | |||
| 989 | list_del_init(&child->base.list); | ||
| 990 | p = __find_child(&new->base.profiles, child->base.name); | ||
| 991 | if (p) { | ||
| 992 | /* @p replaces @child */ | ||
| 993 | __replace_profile(child, p, share_replacedby); | ||
| 994 | continue; | ||
| 995 | } | ||
| 996 | |||
| 997 | /* inherit @child and its children */ | ||
| 998 | /* TODO: update hname of inherited children */ | ||
| 999 | /* list refcount transferred to @new */ | ||
| 1000 | p = aa_deref_parent(child); | ||
| 1001 | rcu_assign_pointer(child->parent, aa_get_profile(new)); | ||
| 1002 | list_add_rcu(&child->base.list, &new->base.profiles); | ||
| 1003 | aa_put_profile(p); | ||
| 1004 | } | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | if (!rcu_access_pointer(new->parent)) { | ||
| 1008 | struct aa_profile *parent = aa_deref_parent(old); | ||
| 1009 | rcu_assign_pointer(new->parent, aa_get_profile(parent)); | ||
| 1010 | } | ||
| 1011 | __aa_update_replacedby(old, new); | ||
| 1012 | if (share_replacedby) { | ||
| 1013 | aa_put_replacedby(new->replacedby); | ||
| 1014 | new->replacedby = aa_get_replacedby(old->replacedby); | ||
| 1015 | } else if (!rcu_access_pointer(new->replacedby->profile)) | ||
| 1016 | /* aafs interface uses replacedby */ | ||
| 1017 | rcu_assign_pointer(new->replacedby->profile, | ||
| 1018 | aa_get_profile(new)); | ||
| 1019 | __aa_fs_profile_migrate_dents(old, new); | ||
| 1020 | |||
| 1021 | if (list_empty(&new->base.list)) { | ||
| 1022 | /* new is not on a list already */ | ||
| 1023 | list_replace_rcu(&old->base.list, &new->base.list); | ||
| 1024 | aa_get_profile(new); | ||
| 1025 | aa_put_profile(old); | ||
| 1026 | } else | ||
| 1027 | __list_remove_profile(old); | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | /** | ||
| 1031 | * __lookup_replace - lookup replacement information for a profile | ||
| 1032 | * @ns - namespace the lookup occurs in | ||
| 1033 | * @hname - name of profile to lookup | ||
| 1034 | * @noreplace - true if not replacing an existing profile | ||
| 1035 | * @p - Returns: profile to be replaced | ||
| 1036 | * @info - Returns: info string on why lookup failed | ||
| 1037 | * | ||
| 1038 | * Returns: profile to replace (no ref) on success else ptr error | ||
| 1039 | */ | ||
| 1040 | static int __lookup_replace(struct aa_namespace *ns, const char *hname, | ||
| 1041 | bool noreplace, struct aa_profile **p, | ||
| 1042 | const char **info) | ||
| 1043 | { | ||
| 1044 | *p = aa_get_profile(__lookup_profile(&ns->base, hname)); | ||
| 1045 | if (*p) { | ||
| 1046 | int error = replacement_allowed(*p, noreplace, info); | ||
| 1047 | if (error) { | ||
| 1048 | *info = "profile can not be replaced"; | ||
| 1049 | return error; | ||
| 1050 | } | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | return 0; | ||
| 1054 | } | ||
| 1055 | |||
| 1022 | /** | 1056 | /** |
| 1023 | * aa_replace_profiles - replace profile(s) on the profile list | 1057 | * aa_replace_profiles - replace profile(s) on the profile list |
| 1024 | * @udata: serialized data stream (NOT NULL) | 1058 | * @udata: serialized data stream (NOT NULL) |
| @@ -1033,21 +1067,17 @@ bool aa_may_manage_policy(int op) | |||
| 1033 | */ | 1067 | */ |
| 1034 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | 1068 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) |
| 1035 | { | 1069 | { |
| 1036 | struct aa_policy *policy; | ||
| 1037 | struct aa_profile *old_profile = NULL, *new_profile = NULL; | ||
| 1038 | struct aa_profile *rename_profile = NULL; | ||
| 1039 | struct aa_namespace *ns = NULL; | ||
| 1040 | const char *ns_name, *name = NULL, *info = NULL; | 1070 | const char *ns_name, *name = NULL, *info = NULL; |
| 1071 | struct aa_namespace *ns = NULL; | ||
| 1072 | struct aa_load_ent *ent, *tmp; | ||
| 1041 | int op = OP_PROF_REPL; | 1073 | int op = OP_PROF_REPL; |
| 1042 | ssize_t error; | 1074 | ssize_t error; |
| 1075 | LIST_HEAD(lh); | ||
| 1043 | 1076 | ||
| 1044 | /* released below */ | 1077 | /* released below */ |
| 1045 | new_profile = aa_unpack(udata, size, &ns_name); | 1078 | error = aa_unpack(udata, size, &lh, &ns_name); |
| 1046 | if (IS_ERR(new_profile)) { | 1079 | if (error) |
| 1047 | error = PTR_ERR(new_profile); | 1080 | goto out; |
| 1048 | new_profile = NULL; | ||
| 1049 | goto fail; | ||
| 1050 | } | ||
| 1051 | 1081 | ||
| 1052 | /* released below */ | 1082 | /* released below */ |
| 1053 | ns = aa_prepare_namespace(ns_name); | 1083 | ns = aa_prepare_namespace(ns_name); |
| @@ -1058,71 +1088,140 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |||
| 1058 | goto fail; | 1088 | goto fail; |
| 1059 | } | 1089 | } |
| 1060 | 1090 | ||
| 1061 | name = new_profile->base.hname; | 1091 | mutex_lock(&ns->lock); |
| 1062 | 1092 | /* setup parent and ns info */ | |
| 1063 | write_lock(&ns->lock); | 1093 | list_for_each_entry(ent, &lh, list) { |
| 1064 | /* no ref on policy only use inside lock */ | 1094 | struct aa_policy *policy; |
| 1065 | policy = __lookup_parent(ns, new_profile->base.hname); | 1095 | |
| 1096 | name = ent->new->base.hname; | ||
| 1097 | error = __lookup_replace(ns, ent->new->base.hname, noreplace, | ||
| 1098 | &ent->old, &info); | ||
| 1099 | if (error) | ||
| 1100 | goto fail_lock; | ||
| 1101 | |||
| 1102 | if (ent->new->rename) { | ||
| 1103 | error = __lookup_replace(ns, ent->new->rename, | ||
| 1104 | noreplace, &ent->rename, | ||
| 1105 | &info); | ||
| 1106 | if (error) | ||
| 1107 | goto fail_lock; | ||
| 1108 | } | ||
| 1066 | 1109 | ||
| 1067 | if (!policy) { | 1110 | /* released when @new is freed */ |
| 1068 | info = "parent does not exist"; | 1111 | ent->new->ns = aa_get_namespace(ns); |
| 1069 | error = -ENOENT; | 1112 | |
| 1070 | goto audit; | 1113 | if (ent->old || ent->rename) |
| 1114 | continue; | ||
| 1115 | |||
| 1116 | /* no ref on policy only use inside lock */ | ||
| 1117 | policy = __lookup_parent(ns, ent->new->base.hname); | ||
| 1118 | if (!policy) { | ||
| 1119 | struct aa_profile *p; | ||
| 1120 | p = __list_lookup_parent(&lh, ent->new); | ||
| 1121 | if (!p) { | ||
| 1122 | error = -ENOENT; | ||
| 1123 | info = "parent does not exist"; | ||
| 1124 | name = ent->new->base.hname; | ||
| 1125 | goto fail_lock; | ||
| 1126 | } | ||
| 1127 | rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); | ||
| 1128 | } else if (policy != &ns->base) { | ||
| 1129 | /* released on profile replacement or free_profile */ | ||
| 1130 | struct aa_profile *p = (struct aa_profile *) policy; | ||
| 1131 | rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); | ||
| 1132 | } | ||
| 1071 | } | 1133 | } |
| 1072 | 1134 | ||
| 1073 | old_profile = __find_child(&policy->profiles, new_profile->base.name); | 1135 | /* create new fs entries for introspection if needed */ |
| 1074 | /* released below */ | 1136 | list_for_each_entry(ent, &lh, list) { |
| 1075 | aa_get_profile(old_profile); | 1137 | if (ent->old) { |
| 1138 | /* inherit old interface files */ | ||
| 1076 | 1139 | ||
| 1077 | if (new_profile->rename) { | 1140 | /* if (ent->rename) |
| 1078 | rename_profile = __lookup_profile(&ns->base, | 1141 | TODO: support rename */ |
| 1079 | new_profile->rename); | 1142 | /* } else if (ent->rename) { |
| 1080 | /* released below */ | 1143 | TODO: support rename */ |
| 1081 | aa_get_profile(rename_profile); | 1144 | } else { |
| 1145 | struct dentry *parent; | ||
| 1146 | if (rcu_access_pointer(ent->new->parent)) { | ||
| 1147 | struct aa_profile *p; | ||
| 1148 | p = aa_deref_parent(ent->new); | ||
| 1149 | parent = prof_child_dir(p); | ||
| 1150 | } else | ||
| 1151 | parent = ns_subprofs_dir(ent->new->ns); | ||
| 1152 | error = __aa_fs_profile_mkdir(ent->new, parent); | ||
| 1153 | } | ||
| 1082 | 1154 | ||
| 1083 | if (!rename_profile) { | 1155 | if (error) { |
| 1084 | info = "profile to rename does not exist"; | 1156 | info = "failed to create "; |
| 1085 | name = new_profile->rename; | 1157 | goto fail_lock; |
| 1086 | error = -ENOENT; | ||
| 1087 | goto audit; | ||
| 1088 | } | 1158 | } |
| 1089 | } | 1159 | } |
| 1090 | 1160 | ||
| 1091 | error = replacement_allowed(old_profile, noreplace, &info); | 1161 | /* Done with checks that may fail - do actual replacement */ |
| 1092 | if (error) | 1162 | list_for_each_entry_safe(ent, tmp, &lh, list) { |
| 1093 | goto audit; | 1163 | list_del_init(&ent->list); |
| 1094 | 1164 | op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; | |
| 1095 | error = replacement_allowed(rename_profile, noreplace, &info); | 1165 | |
| 1096 | if (error) | 1166 | audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error); |
| 1097 | goto audit; | 1167 | |
| 1098 | 1168 | if (ent->old) { | |
| 1099 | audit: | 1169 | __replace_profile(ent->old, ent->new, 1); |
| 1100 | if (!old_profile && !rename_profile) | 1170 | if (ent->rename) { |
| 1101 | op = OP_PROF_LOAD; | 1171 | /* aafs interface uses replacedby */ |
| 1102 | 1172 | struct aa_replacedby *r = ent->new->replacedby; | |
| 1103 | error = audit_policy(op, GFP_ATOMIC, name, info, error); | 1173 | rcu_assign_pointer(r->profile, |
| 1104 | 1174 | aa_get_profile(ent->new)); | |
| 1105 | if (!error) { | 1175 | __replace_profile(ent->rename, ent->new, 0); |
| 1106 | if (rename_profile) | 1176 | } |
| 1107 | __replace_profile(rename_profile, new_profile); | 1177 | } else if (ent->rename) { |
| 1108 | if (old_profile) | 1178 | /* aafs interface uses replacedby */ |
| 1109 | __replace_profile(old_profile, new_profile); | 1179 | rcu_assign_pointer(ent->new->replacedby->profile, |
| 1110 | if (!(old_profile || rename_profile)) | 1180 | aa_get_profile(ent->new)); |
| 1111 | __add_new_profile(ns, policy, new_profile); | 1181 | __replace_profile(ent->rename, ent->new, 0); |
| 1182 | } else if (ent->new->parent) { | ||
| 1183 | struct aa_profile *parent, *newest; | ||
| 1184 | parent = aa_deref_parent(ent->new); | ||
| 1185 | newest = aa_get_newest_profile(parent); | ||
| 1186 | |||
| 1187 | /* parent replaced in this atomic set? */ | ||
| 1188 | if (newest != parent) { | ||
| 1189 | aa_get_profile(newest); | ||
| 1190 | aa_put_profile(parent); | ||
| 1191 | rcu_assign_pointer(ent->new->parent, newest); | ||
| 1192 | } else | ||
| 1193 | aa_put_profile(newest); | ||
| 1194 | /* aafs interface uses replacedby */ | ||
| 1195 | rcu_assign_pointer(ent->new->replacedby->profile, | ||
| 1196 | aa_get_profile(ent->new)); | ||
| 1197 | __list_add_profile(&parent->base.profiles, ent->new); | ||
| 1198 | } else { | ||
| 1199 | /* aafs interface uses replacedby */ | ||
| 1200 | rcu_assign_pointer(ent->new->replacedby->profile, | ||
| 1201 | aa_get_profile(ent->new)); | ||
| 1202 | __list_add_profile(&ns->base.profiles, ent->new); | ||
| 1203 | } | ||
| 1204 | aa_load_ent_free(ent); | ||
| 1112 | } | 1205 | } |
| 1113 | write_unlock(&ns->lock); | 1206 | mutex_unlock(&ns->lock); |
| 1114 | 1207 | ||
| 1115 | out: | 1208 | out: |
| 1116 | aa_put_namespace(ns); | 1209 | aa_put_namespace(ns); |
| 1117 | aa_put_profile(rename_profile); | 1210 | |
| 1118 | aa_put_profile(old_profile); | ||
| 1119 | aa_put_profile(new_profile); | ||
| 1120 | if (error) | 1211 | if (error) |
| 1121 | return error; | 1212 | return error; |
| 1122 | return size; | 1213 | return size; |
| 1123 | 1214 | ||
| 1215 | fail_lock: | ||
| 1216 | mutex_unlock(&ns->lock); | ||
| 1124 | fail: | 1217 | fail: |
| 1125 | error = audit_policy(op, GFP_KERNEL, name, info, error); | 1218 | error = audit_policy(op, GFP_KERNEL, name, info, error); |
| 1219 | |||
| 1220 | list_for_each_entry_safe(ent, tmp, &lh, list) { | ||
| 1221 | list_del_init(&ent->list); | ||
| 1222 | aa_load_ent_free(ent); | ||
| 1223 | } | ||
| 1224 | |||
| 1126 | goto out; | 1225 | goto out; |
| 1127 | } | 1226 | } |
| 1128 | 1227 | ||
| @@ -1169,12 +1268,12 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) | |||
| 1169 | 1268 | ||
| 1170 | if (!name) { | 1269 | if (!name) { |
| 1171 | /* remove namespace - can only happen if fqname[0] == ':' */ | 1270 | /* remove namespace - can only happen if fqname[0] == ':' */ |
| 1172 | write_lock(&ns->parent->lock); | 1271 | mutex_lock(&ns->parent->lock); |
| 1173 | __remove_namespace(ns); | 1272 | __remove_namespace(ns); |
| 1174 | write_unlock(&ns->parent->lock); | 1273 | mutex_unlock(&ns->parent->lock); |
| 1175 | } else { | 1274 | } else { |
| 1176 | /* remove profile */ | 1275 | /* remove profile */ |
| 1177 | write_lock(&ns->lock); | 1276 | mutex_lock(&ns->lock); |
| 1178 | profile = aa_get_profile(__lookup_profile(&ns->base, name)); | 1277 | profile = aa_get_profile(__lookup_profile(&ns->base, name)); |
| 1179 | if (!profile) { | 1278 | if (!profile) { |
| 1180 | error = -ENOENT; | 1279 | error = -ENOENT; |
| @@ -1183,7 +1282,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) | |||
| 1183 | } | 1282 | } |
| 1184 | name = profile->base.hname; | 1283 | name = profile->base.hname; |
| 1185 | __remove_profile(profile); | 1284 | __remove_profile(profile); |
| 1186 | write_unlock(&ns->lock); | 1285 | mutex_unlock(&ns->lock); |
| 1187 | } | 1286 | } |
| 1188 | 1287 | ||
| 1189 | /* don't fail removal if audit fails */ | 1288 | /* don't fail removal if audit fails */ |
| @@ -1193,7 +1292,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) | |||
| 1193 | return size; | 1292 | return size; |
| 1194 | 1293 | ||
| 1195 | fail_ns_lock: | 1294 | fail_ns_lock: |
| 1196 | write_unlock(&ns->lock); | 1295 | mutex_unlock(&ns->lock); |
| 1197 | aa_put_namespace(ns); | 1296 | aa_put_namespace(ns); |
| 1198 | 1297 | ||
| 1199 | fail: | 1298 | fail: |
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 6dac7d77cb4d..a689f10930b5 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include "include/apparmor.h" | 24 | #include "include/apparmor.h" |
| 25 | #include "include/audit.h" | 25 | #include "include/audit.h" |
| 26 | #include "include/context.h" | 26 | #include "include/context.h" |
| 27 | #include "include/crypto.h" | ||
| 27 | #include "include/match.h" | 28 | #include "include/match.h" |
| 28 | #include "include/policy.h" | 29 | #include "include/policy.h" |
| 29 | #include "include/policy_unpack.h" | 30 | #include "include/policy_unpack.h" |
| @@ -333,8 +334,10 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e) | |||
| 333 | /* | 334 | /* |
| 334 | * The dfa is aligned with in the blob to 8 bytes | 335 | * The dfa is aligned with in the blob to 8 bytes |
| 335 | * from the beginning of the stream. | 336 | * from the beginning of the stream. |
| 337 | * alignment adjust needed by dfa unpack | ||
| 336 | */ | 338 | */ |
| 337 | size_t sz = blob - (char *)e->start; | 339 | size_t sz = blob - (char *) e->start - |
| 340 | ((e->pos - e->start) & 7); | ||
| 338 | size_t pad = ALIGN(sz, 8) - sz; | 341 | size_t pad = ALIGN(sz, 8) - sz; |
| 339 | int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | | 342 | int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | |
| 340 | TO_ACCEPT2_FLAG(YYTD_DATA32); | 343 | TO_ACCEPT2_FLAG(YYTD_DATA32); |
| @@ -490,6 +493,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) | |||
| 490 | /* profile renaming is optional */ | 493 | /* profile renaming is optional */ |
| 491 | (void) unpack_str(e, &profile->rename, "rename"); | 494 | (void) unpack_str(e, &profile->rename, "rename"); |
| 492 | 495 | ||
| 496 | /* attachment string is optional */ | ||
| 497 | (void) unpack_str(e, &profile->attach, "attach"); | ||
| 498 | |||
| 493 | /* xmatch is optional and may be NULL */ | 499 | /* xmatch is optional and may be NULL */ |
| 494 | profile->xmatch = unpack_dfa(e); | 500 | profile->xmatch = unpack_dfa(e); |
| 495 | if (IS_ERR(profile->xmatch)) { | 501 | if (IS_ERR(profile->xmatch)) { |
| @@ -509,12 +515,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) | |||
| 509 | goto fail; | 515 | goto fail; |
| 510 | if (!unpack_u32(e, &tmp, NULL)) | 516 | if (!unpack_u32(e, &tmp, NULL)) |
| 511 | goto fail; | 517 | goto fail; |
| 512 | if (tmp) | 518 | if (tmp & PACKED_FLAG_HAT) |
| 513 | profile->flags |= PFLAG_HAT; | 519 | profile->flags |= PFLAG_HAT; |
| 514 | if (!unpack_u32(e, &tmp, NULL)) | 520 | if (!unpack_u32(e, &tmp, NULL)) |
| 515 | goto fail; | 521 | goto fail; |
| 516 | if (tmp) | 522 | if (tmp == PACKED_MODE_COMPLAIN) |
| 517 | profile->mode = APPARMOR_COMPLAIN; | 523 | profile->mode = APPARMOR_COMPLAIN; |
| 524 | else if (tmp == PACKED_MODE_KILL) | ||
| 525 | profile->mode = APPARMOR_KILL; | ||
| 526 | else if (tmp == PACKED_MODE_UNCONFINED) | ||
| 527 | profile->mode = APPARMOR_UNCONFINED; | ||
| 518 | if (!unpack_u32(e, &tmp, NULL)) | 528 | if (!unpack_u32(e, &tmp, NULL)) |
| 519 | goto fail; | 529 | goto fail; |
| 520 | if (tmp) | 530 | if (tmp) |
| @@ -614,7 +624,7 @@ fail: | |||
| 614 | else if (!name) | 624 | else if (!name) |
| 615 | name = "unknown"; | 625 | name = "unknown"; |
| 616 | audit_iface(profile, name, "failed to unpack profile", e, error); | 626 | audit_iface(profile, name, "failed to unpack profile", e, error); |
| 617 | aa_put_profile(profile); | 627 | aa_free_profile(profile); |
| 618 | 628 | ||
| 619 | return ERR_PTR(error); | 629 | return ERR_PTR(error); |
| 620 | } | 630 | } |
| @@ -622,29 +632,41 @@ fail: | |||
| 622 | /** | 632 | /** |
| 623 | * verify_head - unpack serialized stream header | 633 | * verify_head - unpack serialized stream header |
| 624 | * @e: serialized data read head (NOT NULL) | 634 | * @e: serialized data read head (NOT NULL) |
| 635 | * @required: whether the header is required or optional | ||
| 625 | * @ns: Returns - namespace if one is specified else NULL (NOT NULL) | 636 | * @ns: Returns - namespace if one is specified else NULL (NOT NULL) |
| 626 | * | 637 | * |
| 627 | * Returns: error or 0 if header is good | 638 | * Returns: error or 0 if header is good |
| 628 | */ | 639 | */ |
| 629 | static int verify_header(struct aa_ext *e, const char **ns) | 640 | static int verify_header(struct aa_ext *e, int required, const char **ns) |
| 630 | { | 641 | { |
| 631 | int error = -EPROTONOSUPPORT; | 642 | int error = -EPROTONOSUPPORT; |
| 643 | const char *name = NULL; | ||
| 644 | *ns = NULL; | ||
| 645 | |||
| 632 | /* get the interface version */ | 646 | /* get the interface version */ |
| 633 | if (!unpack_u32(e, &e->version, "version")) { | 647 | if (!unpack_u32(e, &e->version, "version")) { |
| 634 | audit_iface(NULL, NULL, "invalid profile format", e, error); | 648 | if (required) { |
| 635 | return error; | 649 | audit_iface(NULL, NULL, "invalid profile format", e, |
| 636 | } | 650 | error); |
| 651 | return error; | ||
| 652 | } | ||
| 637 | 653 | ||
| 638 | /* check that the interface version is currently supported */ | 654 | /* check that the interface version is currently supported */ |
| 639 | if (e->version != 5) { | 655 | if (e->version != 5) { |
| 640 | audit_iface(NULL, NULL, "unsupported interface version", e, | 656 | audit_iface(NULL, NULL, "unsupported interface version", |
| 641 | error); | 657 | e, error); |
| 642 | return error; | 658 | return error; |
| 659 | } | ||
| 643 | } | 660 | } |
| 644 | 661 | ||
| 662 | |||
| 645 | /* read the namespace if present */ | 663 | /* read the namespace if present */ |
| 646 | if (!unpack_str(e, ns, "namespace")) | 664 | if (unpack_str(e, &name, "namespace")) { |
| 647 | *ns = NULL; | 665 | if (*ns && strcmp(*ns, name)) |
| 666 | audit_iface(NULL, NULL, "invalid ns change", e, error); | ||
| 667 | else if (!*ns) | ||
| 668 | *ns = name; | ||
| 669 | } | ||
| 648 | 670 | ||
| 649 | return 0; | 671 | return 0; |
| 650 | } | 672 | } |
| @@ -693,18 +715,40 @@ static int verify_profile(struct aa_profile *profile) | |||
| 693 | return 0; | 715 | return 0; |
| 694 | } | 716 | } |
| 695 | 717 | ||
| 718 | void aa_load_ent_free(struct aa_load_ent *ent) | ||
| 719 | { | ||
| 720 | if (ent) { | ||
| 721 | aa_put_profile(ent->rename); | ||
| 722 | aa_put_profile(ent->old); | ||
| 723 | aa_put_profile(ent->new); | ||
| 724 | kzfree(ent); | ||
| 725 | } | ||
| 726 | } | ||
| 727 | |||
| 728 | struct aa_load_ent *aa_load_ent_alloc(void) | ||
| 729 | { | ||
| 730 | struct aa_load_ent *ent = kzalloc(sizeof(*ent), GFP_KERNEL); | ||
| 731 | if (ent) | ||
| 732 | INIT_LIST_HEAD(&ent->list); | ||
| 733 | return ent; | ||
| 734 | } | ||
| 735 | |||
| 696 | /** | 736 | /** |
| 697 | * aa_unpack - unpack packed binary profile data loaded from user space | 737 | * aa_unpack - unpack packed binary profile(s) data loaded from user space |
| 698 | * @udata: user data copied to kmem (NOT NULL) | 738 | * @udata: user data copied to kmem (NOT NULL) |
| 699 | * @size: the size of the user data | 739 | * @size: the size of the user data |
| 740 | * @lh: list to place unpacked profiles in a aa_repl_ws | ||
| 700 | * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) | 741 | * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) |
| 701 | * | 742 | * |
| 702 | * Unpack user data and return refcounted allocated profile or ERR_PTR | 743 | * Unpack user data and return refcounted allocated profile(s) stored in |
| 744 | * @lh in order of discovery, with the list chain stored in base.list | ||
| 745 | * or error | ||
| 703 | * | 746 | * |
| 704 | * Returns: profile else error pointer if fails to unpack | 747 | * Returns: profile(s) on @lh else error pointer if fails to unpack |
| 705 | */ | 748 | */ |
| 706 | struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) | 749 | int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) |
| 707 | { | 750 | { |
| 751 | struct aa_load_ent *tmp, *ent; | ||
| 708 | struct aa_profile *profile = NULL; | 752 | struct aa_profile *profile = NULL; |
| 709 | int error; | 753 | int error; |
| 710 | struct aa_ext e = { | 754 | struct aa_ext e = { |
| @@ -713,20 +757,49 @@ struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) | |||
| 713 | .pos = udata, | 757 | .pos = udata, |
| 714 | }; | 758 | }; |
| 715 | 759 | ||
| 716 | error = verify_header(&e, ns); | 760 | *ns = NULL; |
| 717 | if (error) | 761 | while (e.pos < e.end) { |
| 718 | return ERR_PTR(error); | 762 | void *start; |
| 763 | error = verify_header(&e, e.pos == e.start, ns); | ||
| 764 | if (error) | ||
| 765 | goto fail; | ||
| 766 | |||
| 767 | start = e.pos; | ||
| 768 | profile = unpack_profile(&e); | ||
| 769 | if (IS_ERR(profile)) { | ||
| 770 | error = PTR_ERR(profile); | ||
| 771 | goto fail; | ||
| 772 | } | ||
| 773 | |||
| 774 | error = verify_profile(profile); | ||
| 775 | if (error) | ||
| 776 | goto fail_profile; | ||
| 777 | |||
| 778 | error = aa_calc_profile_hash(profile, e.version, start, | ||
| 779 | e.pos - start); | ||
| 780 | if (error) | ||
| 781 | goto fail_profile; | ||
| 782 | |||
| 783 | ent = aa_load_ent_alloc(); | ||
| 784 | if (!ent) { | ||
| 785 | error = -ENOMEM; | ||
| 786 | goto fail_profile; | ||
| 787 | } | ||
| 788 | |||
| 789 | ent->new = profile; | ||
| 790 | list_add_tail(&ent->list, lh); | ||
| 791 | } | ||
| 792 | |||
| 793 | return 0; | ||
| 719 | 794 | ||
| 720 | profile = unpack_profile(&e); | 795 | fail_profile: |
| 721 | if (IS_ERR(profile)) | 796 | aa_put_profile(profile); |
| 722 | return profile; | ||
| 723 | 797 | ||
| 724 | error = verify_profile(profile); | 798 | fail: |
| 725 | if (error) { | 799 | list_for_each_entry_safe(ent, tmp, lh, list) { |
| 726 | aa_put_profile(profile); | 800 | list_del_init(&ent->list); |
| 727 | profile = ERR_PTR(error); | 801 | aa_load_ent_free(ent); |
| 728 | } | 802 | } |
| 729 | 803 | ||
| 730 | /* return refcount */ | 804 | return error; |
| 731 | return profile; | ||
| 732 | } | 805 | } |
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index 6c9390179b89..b125acc9aa26 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c | |||
| @@ -37,7 +37,7 @@ int aa_getprocattr(struct aa_profile *profile, char **string) | |||
| 37 | { | 37 | { |
| 38 | char *str; | 38 | char *str; |
| 39 | int len = 0, mode_len = 0, ns_len = 0, name_len; | 39 | int len = 0, mode_len = 0, ns_len = 0, name_len; |
| 40 | const char *mode_str = profile_mode_names[profile->mode]; | 40 | const char *mode_str = aa_profile_mode_names[profile->mode]; |
| 41 | const char *ns_name = NULL; | 41 | const char *ns_name = NULL; |
| 42 | struct aa_namespace *ns = profile->ns; | 42 | struct aa_namespace *ns = profile->ns; |
| 43 | struct aa_namespace *current_ns = __aa_current_profile()->ns; | 43 | struct aa_namespace *current_ns = __aa_current_profile()->ns; |
diff --git a/security/capability.c b/security/capability.c index 32b515766df1..dbeb9bc27b24 100644 --- a/security/capability.c +++ b/security/capability.c | |||
| @@ -129,7 +129,7 @@ static void cap_inode_free_security(struct inode *inode) | |||
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | static int cap_inode_init_security(struct inode *inode, struct inode *dir, | 131 | static int cap_inode_init_security(struct inode *inode, struct inode *dir, |
| 132 | const struct qstr *qstr, char **name, | 132 | const struct qstr *qstr, const char **name, |
| 133 | void **value, size_t *len) | 133 | void **value, size_t *len) |
| 134 | { | 134 | { |
| 135 | return -EOPNOTSUPP; | 135 | return -EOPNOTSUPP; |
diff --git a/security/commoncap.c b/security/commoncap.c index c44b6fe6648e..b9d613e0ef14 100644 --- a/security/commoncap.c +++ b/security/commoncap.c | |||
| @@ -768,16 +768,16 @@ int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags) | |||
| 768 | */ | 768 | */ |
| 769 | static int cap_safe_nice(struct task_struct *p) | 769 | static int cap_safe_nice(struct task_struct *p) |
| 770 | { | 770 | { |
| 771 | int is_subset; | 771 | int is_subset, ret = 0; |
| 772 | 772 | ||
| 773 | rcu_read_lock(); | 773 | rcu_read_lock(); |
| 774 | is_subset = cap_issubset(__task_cred(p)->cap_permitted, | 774 | is_subset = cap_issubset(__task_cred(p)->cap_permitted, |
| 775 | current_cred()->cap_permitted); | 775 | current_cred()->cap_permitted); |
| 776 | if (!is_subset && !ns_capable(__task_cred(p)->user_ns, CAP_SYS_NICE)) | ||
| 777 | ret = -EPERM; | ||
| 776 | rcu_read_unlock(); | 778 | rcu_read_unlock(); |
| 777 | 779 | ||
| 778 | if (!is_subset && !capable(CAP_SYS_NICE)) | 780 | return ret; |
| 779 | return -EPERM; | ||
| 780 | return 0; | ||
| 781 | } | 781 | } |
| 782 | 782 | ||
| 783 | /** | 783 | /** |
| @@ -824,7 +824,7 @@ int cap_task_setnice(struct task_struct *p, int nice) | |||
| 824 | */ | 824 | */ |
| 825 | static long cap_prctl_drop(struct cred *new, unsigned long cap) | 825 | static long cap_prctl_drop(struct cred *new, unsigned long cap) |
| 826 | { | 826 | { |
| 827 | if (!capable(CAP_SETPCAP)) | 827 | if (!ns_capable(current_user_ns(), CAP_SETPCAP)) |
| 828 | return -EPERM; | 828 | return -EPERM; |
| 829 | if (!cap_valid(cap)) | 829 | if (!cap_valid(cap)) |
| 830 | return -EINVAL; | 830 | return -EINVAL; |
diff --git a/security/device_cgroup.c b/security/device_cgroup.c index e8aad69f0d69..c123628d3f84 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c | |||
| @@ -53,22 +53,17 @@ struct dev_cgroup { | |||
| 53 | 53 | ||
| 54 | static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s) | 54 | static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s) |
| 55 | { | 55 | { |
| 56 | return container_of(s, struct dev_cgroup, css); | 56 | return s ? container_of(s, struct dev_cgroup, css) : NULL; |
| 57 | } | ||
| 58 | |||
| 59 | static inline struct dev_cgroup *cgroup_to_devcgroup(struct cgroup *cgroup) | ||
| 60 | { | ||
| 61 | return css_to_devcgroup(cgroup_subsys_state(cgroup, devices_subsys_id)); | ||
| 62 | } | 57 | } |
| 63 | 58 | ||
| 64 | static inline struct dev_cgroup *task_devcgroup(struct task_struct *task) | 59 | static inline struct dev_cgroup *task_devcgroup(struct task_struct *task) |
| 65 | { | 60 | { |
| 66 | return css_to_devcgroup(task_subsys_state(task, devices_subsys_id)); | 61 | return css_to_devcgroup(task_css(task, devices_subsys_id)); |
| 67 | } | 62 | } |
| 68 | 63 | ||
| 69 | struct cgroup_subsys devices_subsys; | 64 | struct cgroup_subsys devices_subsys; |
| 70 | 65 | ||
| 71 | static int devcgroup_can_attach(struct cgroup *new_cgrp, | 66 | static int devcgroup_can_attach(struct cgroup_subsys_state *new_css, |
| 72 | struct cgroup_taskset *set) | 67 | struct cgroup_taskset *set) |
| 73 | { | 68 | { |
| 74 | struct task_struct *task = cgroup_taskset_first(set); | 69 | struct task_struct *task = cgroup_taskset_first(set); |
| @@ -193,18 +188,16 @@ static inline bool is_devcg_online(const struct dev_cgroup *devcg) | |||
| 193 | /** | 188 | /** |
| 194 | * devcgroup_online - initializes devcgroup's behavior and exceptions based on | 189 | * devcgroup_online - initializes devcgroup's behavior and exceptions based on |
| 195 | * parent's | 190 | * parent's |
| 196 | * @cgroup: cgroup getting online | 191 | * @css: css getting online |
| 197 | * returns 0 in case of success, error code otherwise | 192 | * returns 0 in case of success, error code otherwise |
| 198 | */ | 193 | */ |
| 199 | static int devcgroup_online(struct cgroup *cgroup) | 194 | static int devcgroup_online(struct cgroup_subsys_state *css) |
| 200 | { | 195 | { |
| 201 | struct dev_cgroup *dev_cgroup, *parent_dev_cgroup = NULL; | 196 | struct dev_cgroup *dev_cgroup = css_to_devcgroup(css); |
| 197 | struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css_parent(css)); | ||
| 202 | int ret = 0; | 198 | int ret = 0; |
| 203 | 199 | ||
| 204 | mutex_lock(&devcgroup_mutex); | 200 | mutex_lock(&devcgroup_mutex); |
| 205 | dev_cgroup = cgroup_to_devcgroup(cgroup); | ||
| 206 | if (cgroup->parent) | ||
| 207 | parent_dev_cgroup = cgroup_to_devcgroup(cgroup->parent); | ||
| 208 | 201 | ||
| 209 | if (parent_dev_cgroup == NULL) | 202 | if (parent_dev_cgroup == NULL) |
| 210 | dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW; | 203 | dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW; |
| @@ -219,9 +212,9 @@ static int devcgroup_online(struct cgroup *cgroup) | |||
| 219 | return ret; | 212 | return ret; |
| 220 | } | 213 | } |
| 221 | 214 | ||
| 222 | static void devcgroup_offline(struct cgroup *cgroup) | 215 | static void devcgroup_offline(struct cgroup_subsys_state *css) |
| 223 | { | 216 | { |
| 224 | struct dev_cgroup *dev_cgroup = cgroup_to_devcgroup(cgroup); | 217 | struct dev_cgroup *dev_cgroup = css_to_devcgroup(css); |
| 225 | 218 | ||
| 226 | mutex_lock(&devcgroup_mutex); | 219 | mutex_lock(&devcgroup_mutex); |
| 227 | dev_cgroup->behavior = DEVCG_DEFAULT_NONE; | 220 | dev_cgroup->behavior = DEVCG_DEFAULT_NONE; |
| @@ -231,7 +224,8 @@ static void devcgroup_offline(struct cgroup *cgroup) | |||
| 231 | /* | 224 | /* |
| 232 | * called from kernel/cgroup.c with cgroup_lock() held. | 225 | * called from kernel/cgroup.c with cgroup_lock() held. |
| 233 | */ | 226 | */ |
| 234 | static struct cgroup_subsys_state *devcgroup_css_alloc(struct cgroup *cgroup) | 227 | static struct cgroup_subsys_state * |
| 228 | devcgroup_css_alloc(struct cgroup_subsys_state *parent_css) | ||
| 235 | { | 229 | { |
| 236 | struct dev_cgroup *dev_cgroup; | 230 | struct dev_cgroup *dev_cgroup; |
| 237 | 231 | ||
| @@ -244,11 +238,10 @@ static struct cgroup_subsys_state *devcgroup_css_alloc(struct cgroup *cgroup) | |||
| 244 | return &dev_cgroup->css; | 238 | return &dev_cgroup->css; |
| 245 | } | 239 | } |
| 246 | 240 | ||
| 247 | static void devcgroup_css_free(struct cgroup *cgroup) | 241 | static void devcgroup_css_free(struct cgroup_subsys_state *css) |
| 248 | { | 242 | { |
| 249 | struct dev_cgroup *dev_cgroup; | 243 | struct dev_cgroup *dev_cgroup = css_to_devcgroup(css); |
| 250 | 244 | ||
| 251 | dev_cgroup = cgroup_to_devcgroup(cgroup); | ||
| 252 | __dev_exception_clean(dev_cgroup); | 245 | __dev_exception_clean(dev_cgroup); |
| 253 | kfree(dev_cgroup); | 246 | kfree(dev_cgroup); |
| 254 | } | 247 | } |
| @@ -291,10 +284,10 @@ static void set_majmin(char *str, unsigned m) | |||
| 291 | sprintf(str, "%u", m); | 284 | sprintf(str, "%u", m); |
| 292 | } | 285 | } |
| 293 | 286 | ||
| 294 | static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft, | 287 | static int devcgroup_seq_read(struct cgroup_subsys_state *css, |
| 295 | struct seq_file *m) | 288 | struct cftype *cft, struct seq_file *m) |
| 296 | { | 289 | { |
| 297 | struct dev_cgroup *devcgroup = cgroup_to_devcgroup(cgroup); | 290 | struct dev_cgroup *devcgroup = css_to_devcgroup(css); |
| 298 | struct dev_exception_item *ex; | 291 | struct dev_exception_item *ex; |
| 299 | char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN]; | 292 | char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN]; |
| 300 | 293 | ||
| @@ -394,12 +387,10 @@ static bool may_access(struct dev_cgroup *dev_cgroup, | |||
| 394 | static int parent_has_perm(struct dev_cgroup *childcg, | 387 | static int parent_has_perm(struct dev_cgroup *childcg, |
| 395 | struct dev_exception_item *ex) | 388 | struct dev_exception_item *ex) |
| 396 | { | 389 | { |
| 397 | struct cgroup *pcg = childcg->css.cgroup->parent; | 390 | struct dev_cgroup *parent = css_to_devcgroup(css_parent(&childcg->css)); |
| 398 | struct dev_cgroup *parent; | ||
| 399 | 391 | ||
| 400 | if (!pcg) | 392 | if (!parent) |
| 401 | return 1; | 393 | return 1; |
| 402 | parent = cgroup_to_devcgroup(pcg); | ||
| 403 | return may_access(parent, ex, childcg->behavior); | 394 | return may_access(parent, ex, childcg->behavior); |
| 404 | } | 395 | } |
| 405 | 396 | ||
| @@ -451,13 +442,13 @@ static void revalidate_active_exceptions(struct dev_cgroup *devcg) | |||
| 451 | static int propagate_exception(struct dev_cgroup *devcg_root, | 442 | static int propagate_exception(struct dev_cgroup *devcg_root, |
| 452 | struct dev_exception_item *ex) | 443 | struct dev_exception_item *ex) |
| 453 | { | 444 | { |
| 454 | struct cgroup *root = devcg_root->css.cgroup, *pos; | 445 | struct cgroup_subsys_state *pos; |
| 455 | int rc = 0; | 446 | int rc = 0; |
| 456 | 447 | ||
| 457 | rcu_read_lock(); | 448 | rcu_read_lock(); |
| 458 | 449 | ||
| 459 | cgroup_for_each_descendant_pre(pos, root) { | 450 | css_for_each_descendant_pre(pos, &devcg_root->css) { |
| 460 | struct dev_cgroup *devcg = cgroup_to_devcgroup(pos); | 451 | struct dev_cgroup *devcg = css_to_devcgroup(pos); |
| 461 | 452 | ||
| 462 | /* | 453 | /* |
| 463 | * Because devcgroup_mutex is held, no devcg will become | 454 | * Because devcgroup_mutex is held, no devcg will become |
| @@ -465,7 +456,7 @@ static int propagate_exception(struct dev_cgroup *devcg_root, | |||
| 465 | * methods), and online ones are safe to access outside RCU | 456 | * methods), and online ones are safe to access outside RCU |
| 466 | * read lock without bumping refcnt. | 457 | * read lock without bumping refcnt. |
| 467 | */ | 458 | */ |
| 468 | if (!is_devcg_online(devcg)) | 459 | if (pos == &devcg_root->css || !is_devcg_online(devcg)) |
| 469 | continue; | 460 | continue; |
| 470 | 461 | ||
| 471 | rcu_read_unlock(); | 462 | rcu_read_unlock(); |
| @@ -524,15 +515,11 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, | |||
| 524 | char temp[12]; /* 11 + 1 characters needed for a u32 */ | 515 | char temp[12]; /* 11 + 1 characters needed for a u32 */ |
| 525 | int count, rc = 0; | 516 | int count, rc = 0; |
| 526 | struct dev_exception_item ex; | 517 | struct dev_exception_item ex; |
| 527 | struct cgroup *p = devcgroup->css.cgroup; | 518 | struct dev_cgroup *parent = css_to_devcgroup(css_parent(&devcgroup->css)); |
| 528 | struct dev_cgroup *parent = NULL; | ||
| 529 | 519 | ||
| 530 | if (!capable(CAP_SYS_ADMIN)) | 520 | if (!capable(CAP_SYS_ADMIN)) |
| 531 | return -EPERM; | 521 | return -EPERM; |
| 532 | 522 | ||
| 533 | if (p->parent) | ||
| 534 | parent = cgroup_to_devcgroup(p->parent); | ||
| 535 | |||
| 536 | memset(&ex, 0, sizeof(ex)); | 523 | memset(&ex, 0, sizeof(ex)); |
| 537 | b = buffer; | 524 | b = buffer; |
| 538 | 525 | ||
| @@ -677,13 +664,13 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, | |||
| 677 | return rc; | 664 | return rc; |
| 678 | } | 665 | } |
| 679 | 666 | ||
| 680 | static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft, | 667 | static int devcgroup_access_write(struct cgroup_subsys_state *css, |
| 681 | const char *buffer) | 668 | struct cftype *cft, const char *buffer) |
| 682 | { | 669 | { |
| 683 | int retval; | 670 | int retval; |
| 684 | 671 | ||
| 685 | mutex_lock(&devcgroup_mutex); | 672 | mutex_lock(&devcgroup_mutex); |
| 686 | retval = devcgroup_update_access(cgroup_to_devcgroup(cgrp), | 673 | retval = devcgroup_update_access(css_to_devcgroup(css), |
| 687 | cft->private, buffer); | 674 | cft->private, buffer); |
| 688 | mutex_unlock(&devcgroup_mutex); | 675 | mutex_unlock(&devcgroup_mutex); |
| 689 | return retval; | 676 | return retval; |
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index df0fa451a871..af9b6852f4e1 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c | |||
| @@ -418,7 +418,7 @@ int evm_inode_init_security(struct inode *inode, | |||
| 418 | 418 | ||
| 419 | evm_xattr->value = xattr_data; | 419 | evm_xattr->value = xattr_data; |
| 420 | evm_xattr->value_len = sizeof(*xattr_data); | 420 | evm_xattr->value_len = sizeof(*xattr_data); |
| 421 | evm_xattr->name = kstrdup(XATTR_EVM_SUFFIX, GFP_NOFS); | 421 | evm_xattr->name = XATTR_EVM_SUFFIX; |
| 422 | return 0; | 422 | return 0; |
| 423 | out: | 423 | out: |
| 424 | kfree(xattr_data); | 424 | kfree(xattr_data); |
diff --git a/security/security.c b/security/security.c index 94b35aef6871..4dc31f4f2700 100644 --- a/security/security.c +++ b/security/security.c | |||
| @@ -348,10 +348,10 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, | |||
| 348 | if (unlikely(IS_PRIVATE(inode))) | 348 | if (unlikely(IS_PRIVATE(inode))) |
| 349 | return 0; | 349 | return 0; |
| 350 | 350 | ||
| 351 | memset(new_xattrs, 0, sizeof new_xattrs); | ||
| 352 | if (!initxattrs) | 351 | if (!initxattrs) |
| 353 | return security_ops->inode_init_security(inode, dir, qstr, | 352 | return security_ops->inode_init_security(inode, dir, qstr, |
| 354 | NULL, NULL, NULL); | 353 | NULL, NULL, NULL); |
| 354 | memset(new_xattrs, 0, sizeof(new_xattrs)); | ||
| 355 | lsm_xattr = new_xattrs; | 355 | lsm_xattr = new_xattrs; |
| 356 | ret = security_ops->inode_init_security(inode, dir, qstr, | 356 | ret = security_ops->inode_init_security(inode, dir, qstr, |
| 357 | &lsm_xattr->name, | 357 | &lsm_xattr->name, |
| @@ -366,16 +366,14 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, | |||
| 366 | goto out; | 366 | goto out; |
| 367 | ret = initxattrs(inode, new_xattrs, fs_data); | 367 | ret = initxattrs(inode, new_xattrs, fs_data); |
| 368 | out: | 368 | out: |
| 369 | for (xattr = new_xattrs; xattr->name != NULL; xattr++) { | 369 | for (xattr = new_xattrs; xattr->value != NULL; xattr++) |
| 370 | kfree(xattr->name); | ||
| 371 | kfree(xattr->value); | 370 | kfree(xattr->value); |
| 372 | } | ||
| 373 | return (ret == -EOPNOTSUPP) ? 0 : ret; | 371 | return (ret == -EOPNOTSUPP) ? 0 : ret; |
| 374 | } | 372 | } |
| 375 | EXPORT_SYMBOL(security_inode_init_security); | 373 | EXPORT_SYMBOL(security_inode_init_security); |
| 376 | 374 | ||
| 377 | int security_old_inode_init_security(struct inode *inode, struct inode *dir, | 375 | int security_old_inode_init_security(struct inode *inode, struct inode *dir, |
| 378 | const struct qstr *qstr, char **name, | 376 | const struct qstr *qstr, const char **name, |
| 379 | void **value, size_t *len) | 377 | void **value, size_t *len) |
| 380 | { | 378 | { |
| 381 | if (unlikely(IS_PRIVATE(inode))) | 379 | if (unlikely(IS_PRIVATE(inode))) |
diff --git a/security/selinux/avc.c b/security/selinux/avc.c index dad36a6ab45f..fc3e6628a864 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c | |||
| @@ -746,7 +746,6 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid, | |||
| 746 | * @tclass: target security class | 746 | * @tclass: target security class |
| 747 | * @requested: requested permissions, interpreted based on @tclass | 747 | * @requested: requested permissions, interpreted based on @tclass |
| 748 | * @auditdata: auxiliary audit data | 748 | * @auditdata: auxiliary audit data |
| 749 | * @flags: VFS walk flags | ||
| 750 | * | 749 | * |
| 751 | * Check the AVC to determine whether the @requested permissions are granted | 750 | * Check the AVC to determine whether the @requested permissions are granted |
| 752 | * for the SID pair (@ssid, @tsid), interpreting the permissions | 751 | * for the SID pair (@ssid, @tsid), interpreting the permissions |
| @@ -756,17 +755,15 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid, | |||
| 756 | * permissions are granted, -%EACCES if any permissions are denied, or | 755 | * permissions are granted, -%EACCES if any permissions are denied, or |
| 757 | * another -errno upon other errors. | 756 | * another -errno upon other errors. |
| 758 | */ | 757 | */ |
| 759 | int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass, | 758 | int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, |
| 760 | u32 requested, struct common_audit_data *auditdata, | 759 | u32 requested, struct common_audit_data *auditdata) |
| 761 | unsigned flags) | ||
| 762 | { | 760 | { |
| 763 | struct av_decision avd; | 761 | struct av_decision avd; |
| 764 | int rc, rc2; | 762 | int rc, rc2; |
| 765 | 763 | ||
| 766 | rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd); | 764 | rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd); |
| 767 | 765 | ||
| 768 | rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata, | 766 | rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata); |
| 769 | flags); | ||
| 770 | if (rc2) | 767 | if (rc2) |
| 771 | return rc2; | 768 | return rc2; |
| 772 | return rc; | 769 | return rc; |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c956390a9136..5b5231068516 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
| @@ -1502,7 +1502,7 @@ static int cred_has_capability(const struct cred *cred, | |||
| 1502 | 1502 | ||
| 1503 | rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd); | 1503 | rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd); |
| 1504 | if (audit == SECURITY_CAP_AUDIT) { | 1504 | if (audit == SECURITY_CAP_AUDIT) { |
| 1505 | int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0); | 1505 | int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad); |
| 1506 | if (rc2) | 1506 | if (rc2) |
| 1507 | return rc2; | 1507 | return rc2; |
| 1508 | } | 1508 | } |
| @@ -1525,8 +1525,7 @@ static int task_has_system(struct task_struct *tsk, | |||
| 1525 | static int inode_has_perm(const struct cred *cred, | 1525 | static int inode_has_perm(const struct cred *cred, |
| 1526 | struct inode *inode, | 1526 | struct inode *inode, |
| 1527 | u32 perms, | 1527 | u32 perms, |
| 1528 | struct common_audit_data *adp, | 1528 | struct common_audit_data *adp) |
| 1529 | unsigned flags) | ||
| 1530 | { | 1529 | { |
| 1531 | struct inode_security_struct *isec; | 1530 | struct inode_security_struct *isec; |
| 1532 | u32 sid; | 1531 | u32 sid; |
| @@ -1539,7 +1538,7 @@ static int inode_has_perm(const struct cred *cred, | |||
| 1539 | sid = cred_sid(cred); | 1538 | sid = cred_sid(cred); |
| 1540 | isec = inode->i_security; | 1539 | isec = inode->i_security; |
| 1541 | 1540 | ||
| 1542 | return avc_has_perm_flags(sid, isec->sid, isec->sclass, perms, adp, flags); | 1541 | return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp); |
| 1543 | } | 1542 | } |
| 1544 | 1543 | ||
| 1545 | /* Same as inode_has_perm, but pass explicit audit data containing | 1544 | /* Same as inode_has_perm, but pass explicit audit data containing |
| @@ -1554,7 +1553,7 @@ static inline int dentry_has_perm(const struct cred *cred, | |||
| 1554 | 1553 | ||
| 1555 | ad.type = LSM_AUDIT_DATA_DENTRY; | 1554 | ad.type = LSM_AUDIT_DATA_DENTRY; |
| 1556 | ad.u.dentry = dentry; | 1555 | ad.u.dentry = dentry; |
| 1557 | return inode_has_perm(cred, inode, av, &ad, 0); | 1556 | return inode_has_perm(cred, inode, av, &ad); |
| 1558 | } | 1557 | } |
| 1559 | 1558 | ||
| 1560 | /* Same as inode_has_perm, but pass explicit audit data containing | 1559 | /* Same as inode_has_perm, but pass explicit audit data containing |
| @@ -1569,7 +1568,7 @@ static inline int path_has_perm(const struct cred *cred, | |||
| 1569 | 1568 | ||
| 1570 | ad.type = LSM_AUDIT_DATA_PATH; | 1569 | ad.type = LSM_AUDIT_DATA_PATH; |
| 1571 | ad.u.path = *path; | 1570 | ad.u.path = *path; |
| 1572 | return inode_has_perm(cred, inode, av, &ad, 0); | 1571 | return inode_has_perm(cred, inode, av, &ad); |
| 1573 | } | 1572 | } |
| 1574 | 1573 | ||
| 1575 | /* Same as path_has_perm, but uses the inode from the file struct. */ | 1574 | /* Same as path_has_perm, but uses the inode from the file struct. */ |
| @@ -1581,7 +1580,7 @@ static inline int file_path_has_perm(const struct cred *cred, | |||
| 1581 | 1580 | ||
| 1582 | ad.type = LSM_AUDIT_DATA_PATH; | 1581 | ad.type = LSM_AUDIT_DATA_PATH; |
| 1583 | ad.u.path = file->f_path; | 1582 | ad.u.path = file->f_path; |
| 1584 | return inode_has_perm(cred, file_inode(file), av, &ad, 0); | 1583 | return inode_has_perm(cred, file_inode(file), av, &ad); |
| 1585 | } | 1584 | } |
| 1586 | 1585 | ||
| 1587 | /* Check whether a task can use an open file descriptor to | 1586 | /* Check whether a task can use an open file descriptor to |
| @@ -1617,7 +1616,7 @@ static int file_has_perm(const struct cred *cred, | |||
| 1617 | /* av is zero if only checking access to the descriptor. */ | 1616 | /* av is zero if only checking access to the descriptor. */ |
| 1618 | rc = 0; | 1617 | rc = 0; |
| 1619 | if (av) | 1618 | if (av) |
| 1620 | rc = inode_has_perm(cred, inode, av, &ad, 0); | 1619 | rc = inode_has_perm(cred, inode, av, &ad); |
| 1621 | 1620 | ||
| 1622 | out: | 1621 | out: |
| 1623 | return rc; | 1622 | return rc; |
| @@ -2587,7 +2586,8 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode, | |||
| 2587 | } | 2586 | } |
| 2588 | 2587 | ||
| 2589 | static int selinux_inode_init_security(struct inode *inode, struct inode *dir, | 2588 | static int selinux_inode_init_security(struct inode *inode, struct inode *dir, |
| 2590 | const struct qstr *qstr, char **name, | 2589 | const struct qstr *qstr, |
| 2590 | const char **name, | ||
| 2591 | void **value, size_t *len) | 2591 | void **value, size_t *len) |
| 2592 | { | 2592 | { |
| 2593 | const struct task_security_struct *tsec = current_security(); | 2593 | const struct task_security_struct *tsec = current_security(); |
| @@ -2595,7 +2595,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, | |||
| 2595 | struct superblock_security_struct *sbsec; | 2595 | struct superblock_security_struct *sbsec; |
| 2596 | u32 sid, newsid, clen; | 2596 | u32 sid, newsid, clen; |
| 2597 | int rc; | 2597 | int rc; |
| 2598 | char *namep = NULL, *context; | 2598 | char *context; |
| 2599 | 2599 | ||
| 2600 | dsec = dir->i_security; | 2600 | dsec = dir->i_security; |
| 2601 | sbsec = dir->i_sb->s_security; | 2601 | sbsec = dir->i_sb->s_security; |
| @@ -2631,19 +2631,13 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, | |||
| 2631 | if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP)) | 2631 | if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP)) |
| 2632 | return -EOPNOTSUPP; | 2632 | return -EOPNOTSUPP; |
| 2633 | 2633 | ||
| 2634 | if (name) { | 2634 | if (name) |
| 2635 | namep = kstrdup(XATTR_SELINUX_SUFFIX, GFP_NOFS); | 2635 | *name = XATTR_SELINUX_SUFFIX; |
| 2636 | if (!namep) | ||
| 2637 | return -ENOMEM; | ||
| 2638 | *name = namep; | ||
| 2639 | } | ||
| 2640 | 2636 | ||
| 2641 | if (value && len) { | 2637 | if (value && len) { |
| 2642 | rc = security_sid_to_context_force(newsid, &context, &clen); | 2638 | rc = security_sid_to_context_force(newsid, &context, &clen); |
| 2643 | if (rc) { | 2639 | if (rc) |
| 2644 | kfree(namep); | ||
| 2645 | return rc; | 2640 | return rc; |
| 2646 | } | ||
| 2647 | *value = context; | 2641 | *value = context; |
| 2648 | *len = clen; | 2642 | *len = clen; |
| 2649 | } | 2643 | } |
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 92d0ab561db8..f53ee3c58d0f 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h | |||
| @@ -130,7 +130,7 @@ static inline int avc_audit(u32 ssid, u32 tsid, | |||
| 130 | u16 tclass, u32 requested, | 130 | u16 tclass, u32 requested, |
| 131 | struct av_decision *avd, | 131 | struct av_decision *avd, |
| 132 | int result, | 132 | int result, |
| 133 | struct common_audit_data *a, unsigned flags) | 133 | struct common_audit_data *a) |
| 134 | { | 134 | { |
| 135 | u32 audited, denied; | 135 | u32 audited, denied; |
| 136 | audited = avc_audit_required(requested, avd, result, 0, &denied); | 136 | audited = avc_audit_required(requested, avd, result, 0, &denied); |
| @@ -138,7 +138,7 @@ static inline int avc_audit(u32 ssid, u32 tsid, | |||
| 138 | return 0; | 138 | return 0; |
| 139 | return slow_avc_audit(ssid, tsid, tclass, | 139 | return slow_avc_audit(ssid, tsid, tclass, |
| 140 | requested, audited, denied, | 140 | requested, audited, denied, |
| 141 | a, flags); | 141 | a, 0); |
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | #define AVC_STRICT 1 /* Ignore permissive mode. */ | 144 | #define AVC_STRICT 1 /* Ignore permissive mode. */ |
| @@ -147,17 +147,9 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, | |||
| 147 | unsigned flags, | 147 | unsigned flags, |
| 148 | struct av_decision *avd); | 148 | struct av_decision *avd); |
| 149 | 149 | ||
| 150 | int avc_has_perm_flags(u32 ssid, u32 tsid, | 150 | int avc_has_perm(u32 ssid, u32 tsid, |
| 151 | u16 tclass, u32 requested, | 151 | u16 tclass, u32 requested, |
| 152 | struct common_audit_data *auditdata, | 152 | struct common_audit_data *auditdata); |
| 153 | unsigned); | ||
| 154 | |||
| 155 | static inline int avc_has_perm(u32 ssid, u32 tsid, | ||
| 156 | u16 tclass, u32 requested, | ||
| 157 | struct common_audit_data *auditdata) | ||
| 158 | { | ||
| 159 | return avc_has_perm_flags(ssid, tsid, tclass, requested, auditdata, 0); | ||
| 160 | } | ||
| 161 | 153 | ||
| 162 | u32 avc_policy_seqno(void); | 154 | u32 avc_policy_seqno(void); |
| 163 | 155 | ||
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 65f67cb0aefb..6713f04e30ba 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h | |||
| @@ -50,8 +50,13 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall); | |||
| 50 | 50 | ||
| 51 | static inline void selinux_xfrm_notify_policyload(void) | 51 | static inline void selinux_xfrm_notify_policyload(void) |
| 52 | { | 52 | { |
| 53 | struct net *net; | ||
| 54 | |||
| 53 | atomic_inc(&flow_cache_genid); | 55 | atomic_inc(&flow_cache_genid); |
| 54 | rt_genid_bump(&init_net); | 56 | rtnl_lock(); |
| 57 | for_each_net(net) | ||
| 58 | rt_genid_bump_all(net); | ||
| 59 | rtnl_unlock(); | ||
| 55 | } | 60 | } |
| 56 | #else | 61 | #else |
| 57 | static inline int selinux_xfrm_enabled(void) | 62 | static inline int selinux_xfrm_enabled(void) |
diff --git a/security/smack/smack.h b/security/smack/smack.h index 339614c76e63..076b8e8a51ab 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h | |||
| @@ -53,6 +53,7 @@ | |||
| 53 | */ | 53 | */ |
| 54 | struct smack_known { | 54 | struct smack_known { |
| 55 | struct list_head list; | 55 | struct list_head list; |
| 56 | struct hlist_node smk_hashed; | ||
| 56 | char *smk_known; | 57 | char *smk_known; |
| 57 | u32 smk_secid; | 58 | u32 smk_secid; |
| 58 | struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */ | 59 | struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */ |
| @@ -167,9 +168,13 @@ struct smk_port_label { | |||
| 167 | #define SMACK_CIPSO_DOI_INVALID -1 /* Not a DOI */ | 168 | #define SMACK_CIPSO_DOI_INVALID -1 /* Not a DOI */ |
| 168 | #define SMACK_CIPSO_DIRECT_DEFAULT 250 /* Arbitrary */ | 169 | #define SMACK_CIPSO_DIRECT_DEFAULT 250 /* Arbitrary */ |
| 169 | #define SMACK_CIPSO_MAPPED_DEFAULT 251 /* Also arbitrary */ | 170 | #define SMACK_CIPSO_MAPPED_DEFAULT 251 /* Also arbitrary */ |
| 170 | #define SMACK_CIPSO_MAXCATVAL 63 /* Bigger gets harder */ | ||
| 171 | #define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */ | 171 | #define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */ |
| 172 | #define SMACK_CIPSO_MAXCATNUM 239 /* CIPSO 2.2 standard */ | 172 | /* |
| 173 | * CIPSO 2.2 standard is 239, but Smack wants to use the | ||
| 174 | * categories in a structured way that limits the value to | ||
| 175 | * the bits in 23 bytes, hence the unusual number. | ||
| 176 | */ | ||
| 177 | #define SMACK_CIPSO_MAXCATNUM 184 /* 23 * 8 */ | ||
| 173 | 178 | ||
| 174 | /* | 179 | /* |
| 175 | * Flag for transmute access | 180 | * Flag for transmute access |
| @@ -222,6 +227,7 @@ char *smk_parse_smack(const char *string, int len); | |||
| 222 | int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); | 227 | int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); |
| 223 | char *smk_import(const char *, int); | 228 | char *smk_import(const char *, int); |
| 224 | struct smack_known *smk_import_entry(const char *, int); | 229 | struct smack_known *smk_import_entry(const char *, int); |
| 230 | void smk_insert_entry(struct smack_known *skp); | ||
| 225 | struct smack_known *smk_find_entry(const char *); | 231 | struct smack_known *smk_find_entry(const char *); |
| 226 | u32 smack_to_secid(const char *); | 232 | u32 smack_to_secid(const char *); |
| 227 | 233 | ||
| @@ -247,6 +253,9 @@ extern struct list_head smk_netlbladdr_list; | |||
| 247 | 253 | ||
| 248 | extern struct security_operations smack_ops; | 254 | extern struct security_operations smack_ops; |
| 249 | 255 | ||
| 256 | #define SMACK_HASH_SLOTS 16 | ||
| 257 | extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS]; | ||
| 258 | |||
| 250 | /* | 259 | /* |
| 251 | * Is the directory transmuting? | 260 | * Is the directory transmuting? |
| 252 | */ | 261 | */ |
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 6a0377f38620..b3b59b1e93d6 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c | |||
| @@ -325,6 +325,25 @@ void smack_log(char *subject_label, char *object_label, int request, | |||
| 325 | 325 | ||
| 326 | DEFINE_MUTEX(smack_known_lock); | 326 | DEFINE_MUTEX(smack_known_lock); |
| 327 | 327 | ||
| 328 | struct hlist_head smack_known_hash[SMACK_HASH_SLOTS]; | ||
| 329 | |||
| 330 | /** | ||
| 331 | * smk_insert_entry - insert a smack label into a hash map, | ||
| 332 | * | ||
| 333 | * this function must be called under smack_known_lock | ||
| 334 | */ | ||
| 335 | void smk_insert_entry(struct smack_known *skp) | ||
| 336 | { | ||
| 337 | unsigned int hash; | ||
| 338 | struct hlist_head *head; | ||
| 339 | |||
| 340 | hash = full_name_hash(skp->smk_known, strlen(skp->smk_known)); | ||
| 341 | head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)]; | ||
| 342 | |||
| 343 | hlist_add_head_rcu(&skp->smk_hashed, head); | ||
| 344 | list_add_rcu(&skp->list, &smack_known_list); | ||
| 345 | } | ||
| 346 | |||
| 328 | /** | 347 | /** |
| 329 | * smk_find_entry - find a label on the list, return the list entry | 348 | * smk_find_entry - find a label on the list, return the list entry |
| 330 | * @string: a text string that might be a Smack label | 349 | * @string: a text string that might be a Smack label |
| @@ -334,12 +353,16 @@ DEFINE_MUTEX(smack_known_lock); | |||
| 334 | */ | 353 | */ |
| 335 | struct smack_known *smk_find_entry(const char *string) | 354 | struct smack_known *smk_find_entry(const char *string) |
| 336 | { | 355 | { |
| 356 | unsigned int hash; | ||
| 357 | struct hlist_head *head; | ||
| 337 | struct smack_known *skp; | 358 | struct smack_known *skp; |
| 338 | 359 | ||
| 339 | list_for_each_entry_rcu(skp, &smack_known_list, list) { | 360 | hash = full_name_hash(string, strlen(string)); |
| 361 | head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)]; | ||
| 362 | |||
| 363 | hlist_for_each_entry_rcu(skp, head, smk_hashed) | ||
| 340 | if (strcmp(skp->smk_known, string) == 0) | 364 | if (strcmp(skp->smk_known, string) == 0) |
| 341 | return skp; | 365 | return skp; |
| 342 | } | ||
| 343 | 366 | ||
| 344 | return NULL; | 367 | return NULL; |
| 345 | } | 368 | } |
| @@ -475,7 +498,7 @@ struct smack_known *smk_import_entry(const char *string, int len) | |||
| 475 | * Make sure that the entry is actually | 498 | * Make sure that the entry is actually |
| 476 | * filled before putting it on the list. | 499 | * filled before putting it on the list. |
| 477 | */ | 500 | */ |
| 478 | list_add_rcu(&skp->list, &smack_known_list); | 501 | smk_insert_entry(skp); |
| 479 | goto unlockout; | 502 | goto unlockout; |
| 480 | } | 503 | } |
| 481 | /* | 504 | /* |
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index eefbd10e408f..8825375cc031 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c | |||
| @@ -582,7 +582,7 @@ static void smack_inode_free_security(struct inode *inode) | |||
| 582 | * Returns 0 if it all works out, -ENOMEM if there's no memory | 582 | * Returns 0 if it all works out, -ENOMEM if there's no memory |
| 583 | */ | 583 | */ |
| 584 | static int smack_inode_init_security(struct inode *inode, struct inode *dir, | 584 | static int smack_inode_init_security(struct inode *inode, struct inode *dir, |
| 585 | const struct qstr *qstr, char **name, | 585 | const struct qstr *qstr, const char **name, |
| 586 | void **value, size_t *len) | 586 | void **value, size_t *len) |
| 587 | { | 587 | { |
| 588 | struct inode_smack *issp = inode->i_security; | 588 | struct inode_smack *issp = inode->i_security; |
| @@ -591,11 +591,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, | |||
| 591 | char *dsp = smk_of_inode(dir); | 591 | char *dsp = smk_of_inode(dir); |
| 592 | int may; | 592 | int may; |
| 593 | 593 | ||
| 594 | if (name) { | 594 | if (name) |
| 595 | *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_NOFS); | 595 | *name = XATTR_SMACK_SUFFIX; |
| 596 | if (*name == NULL) | ||
| 597 | return -ENOMEM; | ||
| 598 | } | ||
| 599 | 596 | ||
| 600 | if (value) { | 597 | if (value) { |
| 601 | rcu_read_lock(); | 598 | rcu_read_lock(); |
| @@ -3065,6 +3062,8 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, | |||
| 3065 | { | 3062 | { |
| 3066 | struct smack_known *skp; | 3063 | struct smack_known *skp; |
| 3067 | int found = 0; | 3064 | int found = 0; |
| 3065 | int acat; | ||
| 3066 | int kcat; | ||
| 3068 | 3067 | ||
| 3069 | if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) { | 3068 | if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) { |
| 3070 | /* | 3069 | /* |
| @@ -3081,12 +3080,28 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, | |||
| 3081 | list_for_each_entry(skp, &smack_known_list, list) { | 3080 | list_for_each_entry(skp, &smack_known_list, list) { |
| 3082 | if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl) | 3081 | if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl) |
| 3083 | continue; | 3082 | continue; |
| 3084 | if (memcmp(sap->attr.mls.cat, | 3083 | /* |
| 3085 | skp->smk_netlabel.attr.mls.cat, | 3084 | * Compare the catsets. Use the netlbl APIs. |
| 3086 | SMK_CIPSOLEN) != 0) | 3085 | */ |
| 3087 | continue; | 3086 | if ((sap->flags & NETLBL_SECATTR_MLS_CAT) == 0) { |
| 3088 | found = 1; | 3087 | if ((skp->smk_netlabel.flags & |
| 3089 | break; | 3088 | NETLBL_SECATTR_MLS_CAT) == 0) |
| 3089 | found = 1; | ||
| 3090 | break; | ||
| 3091 | } | ||
| 3092 | for (acat = -1, kcat = -1; acat == kcat; ) { | ||
| 3093 | acat = netlbl_secattr_catmap_walk( | ||
| 3094 | sap->attr.mls.cat, acat + 1); | ||
| 3095 | kcat = netlbl_secattr_catmap_walk( | ||
| 3096 | skp->smk_netlabel.attr.mls.cat, | ||
| 3097 | kcat + 1); | ||
| 3098 | if (acat < 0 || kcat < 0) | ||
| 3099 | break; | ||
| 3100 | } | ||
| 3101 | if (acat == kcat) { | ||
| 3102 | found = 1; | ||
| 3103 | break; | ||
| 3104 | } | ||
| 3090 | } | 3105 | } |
| 3091 | rcu_read_unlock(); | 3106 | rcu_read_unlock(); |
| 3092 | 3107 | ||
| @@ -3877,12 +3892,12 @@ static __init void init_smack_known_list(void) | |||
| 3877 | /* | 3892 | /* |
| 3878 | * Create the known labels list | 3893 | * Create the known labels list |
| 3879 | */ | 3894 | */ |
| 3880 | list_add(&smack_known_huh.list, &smack_known_list); | 3895 | smk_insert_entry(&smack_known_huh); |
| 3881 | list_add(&smack_known_hat.list, &smack_known_list); | 3896 | smk_insert_entry(&smack_known_hat); |
| 3882 | list_add(&smack_known_star.list, &smack_known_list); | 3897 | smk_insert_entry(&smack_known_star); |
| 3883 | list_add(&smack_known_floor.list, &smack_known_list); | 3898 | smk_insert_entry(&smack_known_floor); |
| 3884 | list_add(&smack_known_invalid.list, &smack_known_list); | 3899 | smk_insert_entry(&smack_known_invalid); |
| 3885 | list_add(&smack_known_web.list, &smack_known_list); | 3900 | smk_insert_entry(&smack_known_web); |
| 3886 | } | 3901 | } |
| 3887 | 3902 | ||
| 3888 | /** | 3903 | /** |
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index ab167037b2dd..80f4b4a45725 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c | |||
| @@ -368,56 +368,43 @@ static int smk_parse_rule(const char *data, struct smack_parsed_rule *rule, | |||
| 368 | * @data: string to be parsed, null terminated | 368 | * @data: string to be parsed, null terminated |
| 369 | * @rule: Will be filled with Smack parsed rule | 369 | * @rule: Will be filled with Smack parsed rule |
| 370 | * @import: if non-zero, import labels | 370 | * @import: if non-zero, import labels |
| 371 | * @change: if non-zero, data is from /smack/change-rule | 371 | * @tokens: numer of substrings expected in data |
| 372 | * | 372 | * |
| 373 | * Returns 0 on success, -1 on failure | 373 | * Returns number of processed bytes on success, -1 on failure. |
| 374 | */ | 374 | */ |
| 375 | static int smk_parse_long_rule(const char *data, struct smack_parsed_rule *rule, | 375 | static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule, |
| 376 | int import, int change) | 376 | int import, int tokens) |
| 377 | { | 377 | { |
| 378 | char *subject; | 378 | ssize_t cnt = 0; |
| 379 | char *object; | 379 | char *tok[4]; |
| 380 | char *access1; | 380 | int i; |
| 381 | char *access2; | ||
| 382 | int datalen; | ||
| 383 | int rc = -1; | ||
| 384 | 381 | ||
| 385 | /* This is inefficient */ | 382 | /* |
| 386 | datalen = strlen(data); | 383 | * Parsing the rule in-place, filling all white-spaces with '\0' |
| 384 | */ | ||
| 385 | for (i = 0; i < tokens; ++i) { | ||
| 386 | while (isspace(data[cnt])) | ||
| 387 | data[cnt++] = '\0'; | ||
| 387 | 388 | ||
| 388 | /* Our first element can be 64 + \0 with no spaces */ | 389 | if (data[cnt] == '\0') |
| 389 | subject = kzalloc(datalen + 1, GFP_KERNEL); | 390 | /* Unexpected end of data */ |
| 390 | if (subject == NULL) | 391 | return -1; |
| 391 | return -1; | 392 | |
| 392 | object = kzalloc(datalen, GFP_KERNEL); | 393 | tok[i] = data + cnt; |
| 393 | if (object == NULL) | 394 | |
| 394 | goto free_out_s; | 395 | while (data[cnt] && !isspace(data[cnt])) |
| 395 | access1 = kzalloc(datalen, GFP_KERNEL); | 396 | ++cnt; |
| 396 | if (access1 == NULL) | ||
| 397 | goto free_out_o; | ||
| 398 | access2 = kzalloc(datalen, GFP_KERNEL); | ||
| 399 | if (access2 == NULL) | ||
| 400 | goto free_out_a; | ||
| 401 | |||
| 402 | if (change) { | ||
| 403 | if (sscanf(data, "%s %s %s %s", | ||
| 404 | subject, object, access1, access2) == 4) | ||
| 405 | rc = smk_fill_rule(subject, object, access1, access2, | ||
| 406 | rule, import, 0); | ||
| 407 | } else { | ||
| 408 | if (sscanf(data, "%s %s %s", subject, object, access1) == 3) | ||
| 409 | rc = smk_fill_rule(subject, object, access1, NULL, | ||
| 410 | rule, import, 0); | ||
| 411 | } | 397 | } |
| 398 | while (isspace(data[cnt])) | ||
| 399 | data[cnt++] = '\0'; | ||
| 412 | 400 | ||
| 413 | kfree(access2); | 401 | while (i < 4) |
| 414 | free_out_a: | 402 | tok[i++] = NULL; |
| 415 | kfree(access1); | 403 | |
| 416 | free_out_o: | 404 | if (smk_fill_rule(tok[0], tok[1], tok[2], tok[3], rule, import, 0)) |
| 417 | kfree(object); | 405 | return -1; |
| 418 | free_out_s: | 406 | |
| 419 | kfree(subject); | 407 | return cnt; |
| 420 | return rc; | ||
| 421 | } | 408 | } |
| 422 | 409 | ||
| 423 | #define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */ | 410 | #define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */ |
| @@ -447,11 +434,12 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, | |||
| 447 | struct list_head *rule_list, | 434 | struct list_head *rule_list, |
| 448 | struct mutex *rule_lock, int format) | 435 | struct mutex *rule_lock, int format) |
| 449 | { | 436 | { |
| 450 | struct smack_parsed_rule *rule; | 437 | struct smack_parsed_rule rule; |
| 451 | char *data; | 438 | char *data; |
| 452 | int datalen; | 439 | int rc; |
| 453 | int rc = -EINVAL; | 440 | int trunc = 0; |
| 454 | int load = 0; | 441 | int tokens; |
| 442 | ssize_t cnt = 0; | ||
| 455 | 443 | ||
| 456 | /* | 444 | /* |
| 457 | * No partial writes. | 445 | * No partial writes. |
| @@ -466,11 +454,14 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, | |||
| 466 | */ | 454 | */ |
| 467 | if (count != SMK_OLOADLEN && count != SMK_LOADLEN) | 455 | if (count != SMK_OLOADLEN && count != SMK_LOADLEN) |
| 468 | return -EINVAL; | 456 | return -EINVAL; |
| 469 | datalen = SMK_LOADLEN; | 457 | } else { |
| 470 | } else | 458 | if (count >= PAGE_SIZE) { |
| 471 | datalen = count + 1; | 459 | count = PAGE_SIZE - 1; |
| 460 | trunc = 1; | ||
| 461 | } | ||
| 462 | } | ||
| 472 | 463 | ||
| 473 | data = kzalloc(datalen, GFP_KERNEL); | 464 | data = kmalloc(count + 1, GFP_KERNEL); |
| 474 | if (data == NULL) | 465 | if (data == NULL) |
| 475 | return -ENOMEM; | 466 | return -ENOMEM; |
| 476 | 467 | ||
| @@ -479,47 +470,49 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, | |||
| 479 | goto out; | 470 | goto out; |
| 480 | } | 471 | } |
| 481 | 472 | ||
| 482 | rule = kzalloc(sizeof(*rule), GFP_KERNEL); | 473 | /* |
| 483 | if (rule == NULL) { | 474 | * In case of parsing only part of user buf, |
| 484 | rc = -ENOMEM; | 475 | * avoid having partial rule at the data buffer |
| 485 | goto out; | 476 | */ |
| 477 | if (trunc) { | ||
| 478 | while (count > 0 && (data[count - 1] != '\n')) | ||
| 479 | --count; | ||
| 480 | if (count == 0) { | ||
| 481 | rc = -EINVAL; | ||
| 482 | goto out; | ||
| 483 | } | ||
| 486 | } | 484 | } |
| 487 | 485 | ||
| 488 | if (format == SMK_LONG_FMT) { | 486 | data[count] = '\0'; |
| 489 | /* | 487 | tokens = (format == SMK_CHANGE_FMT ? 4 : 3); |
| 490 | * Be sure the data string is terminated. | 488 | while (cnt < count) { |
| 491 | */ | 489 | if (format == SMK_FIXED24_FMT) { |
| 492 | data[count] = '\0'; | 490 | rc = smk_parse_rule(data, &rule, 1); |
| 493 | if (smk_parse_long_rule(data, rule, 1, 0)) | 491 | if (rc != 0) { |
| 494 | goto out_free_rule; | 492 | rc = -EINVAL; |
| 495 | } else if (format == SMK_CHANGE_FMT) { | 493 | goto out; |
| 496 | data[count] = '\0'; | 494 | } |
| 497 | if (smk_parse_long_rule(data, rule, 1, 1)) | 495 | cnt = count; |
| 498 | goto out_free_rule; | 496 | } else { |
| 499 | } else { | 497 | rc = smk_parse_long_rule(data + cnt, &rule, 1, tokens); |
| 500 | /* | 498 | if (rc <= 0) { |
| 501 | * More on the minor hack for backward compatibility | 499 | rc = -EINVAL; |
| 502 | */ | 500 | goto out; |
| 503 | if (count == (SMK_OLOADLEN)) | 501 | } |
| 504 | data[SMK_OLOADLEN] = '-'; | 502 | cnt += rc; |
| 505 | if (smk_parse_rule(data, rule, 1)) | 503 | } |
| 506 | goto out_free_rule; | ||
| 507 | } | ||
| 508 | 504 | ||
| 509 | if (rule_list == NULL) { | 505 | if (rule_list == NULL) |
| 510 | load = 1; | 506 | rc = smk_set_access(&rule, &rule.smk_subject->smk_rules, |
| 511 | rule_list = &rule->smk_subject->smk_rules; | 507 | &rule.smk_subject->smk_rules_lock, 1); |
| 512 | rule_lock = &rule->smk_subject->smk_rules_lock; | 508 | else |
| 513 | } | 509 | rc = smk_set_access(&rule, rule_list, rule_lock, 0); |
| 514 | 510 | ||
| 515 | rc = smk_set_access(rule, rule_list, rule_lock, load); | 511 | if (rc) |
| 516 | if (rc == 0) { | 512 | goto out; |
| 517 | rc = count; | ||
| 518 | goto out; | ||
| 519 | } | 513 | } |
| 520 | 514 | ||
| 521 | out_free_rule: | 515 | rc = cnt; |
| 522 | kfree(rule); | ||
| 523 | out: | 516 | out: |
| 524 | kfree(data); | 517 | kfree(data); |
| 525 | return rc; | 518 | return rc; |
| @@ -901,7 +894,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, | |||
| 901 | for (i = 0; i < catlen; i++) { | 894 | for (i = 0; i < catlen; i++) { |
| 902 | rule += SMK_DIGITLEN; | 895 | rule += SMK_DIGITLEN; |
| 903 | ret = sscanf(rule, "%u", &cat); | 896 | ret = sscanf(rule, "%u", &cat); |
| 904 | if (ret != 1 || cat > SMACK_CIPSO_MAXCATVAL) | 897 | if (ret != 1 || cat > SMACK_CIPSO_MAXCATNUM) |
| 905 | goto out; | 898 | goto out; |
| 906 | 899 | ||
| 907 | smack_catset_bit(cat, mapcatset); | 900 | smack_catset_bit(cat, mapcatset); |
| @@ -1840,7 +1833,6 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, | |||
| 1840 | { | 1833 | { |
| 1841 | struct smack_parsed_rule rule; | 1834 | struct smack_parsed_rule rule; |
| 1842 | char *data; | 1835 | char *data; |
| 1843 | char *cod; | ||
| 1844 | int res; | 1836 | int res; |
| 1845 | 1837 | ||
| 1846 | data = simple_transaction_get(file, buf, count); | 1838 | data = simple_transaction_get(file, buf, count); |
| @@ -1853,18 +1845,12 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, | |||
| 1853 | res = smk_parse_rule(data, &rule, 0); | 1845 | res = smk_parse_rule(data, &rule, 0); |
| 1854 | } else { | 1846 | } else { |
| 1855 | /* | 1847 | /* |
| 1856 | * Copy the data to make sure the string is terminated. | 1848 | * simple_transaction_get() returns null-terminated data |
| 1857 | */ | 1849 | */ |
| 1858 | cod = kzalloc(count + 1, GFP_KERNEL); | 1850 | res = smk_parse_long_rule(data, &rule, 0, 3); |
| 1859 | if (cod == NULL) | ||
| 1860 | return -ENOMEM; | ||
| 1861 | memcpy(cod, data, count); | ||
| 1862 | cod[count] = '\0'; | ||
| 1863 | res = smk_parse_long_rule(cod, &rule, 0, 0); | ||
| 1864 | kfree(cod); | ||
| 1865 | } | 1851 | } |
| 1866 | 1852 | ||
| 1867 | if (res) | 1853 | if (res < 0) |
| 1868 | return -EINVAL; | 1854 | return -EINVAL; |
| 1869 | 1855 | ||
| 1870 | res = smk_access(rule.smk_subject, rule.smk_object, | 1856 | res = smk_access(rule.smk_subject, rule.smk_object, |
