diff options
Diffstat (limited to 'security/apparmor/policy_unpack.c')
-rw-r--r-- | security/apparmor/policy_unpack.c | 135 |
1 files changed, 104 insertions, 31 deletions
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 | } |