Pull apparmor updates from John Johansen: "Cleanups - optimization: try to avoid refing the label in apparmor_file_open - remove useless static inline function is_deleted - use kvfree_sensitive to free data->data - fix typo in kernel doc Bug fixes: - unpack transition table if dfa is not present - test: add MODULE_DESCRIPTION() - take nosymfollow flag into account - fix possible NULL pointer dereference - fix null pointer deref when receiving skb during sock creation" * tag 'apparmor-pr-2024-07-25' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor: apparmor: unpack transition table if dfa is not present apparmor: try to avoid refing the label in apparmor_file_open apparmor: test: add MODULE_DESCRIPTION() apparmor: take nosymfollow flag into account apparmor: fix possible NULL pointer dereference apparmor: fix typo in kernel doc apparmor: remove useless static inline function is_deleted apparmor: use kvfree_sensitive to free data->data apparmor: Fix null pointer deref when receiving skb during sock creation
Unified · Split
| @@ -1692,6 +1692,10 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) · 1691 unmodified lines | ||
| 1692 | 1692 | struct aa_profile *p; |
| 1693 | 1693 | p = aa_deref_parent(profile); |
| 1694 | 1694 | dent = prof_dir(p); |
| 1695 | + if (!dent) { | |
| 1696 | + error = -ENOENT; | |
| 1697 | + goto fail2; | |
| 1698 | + } | |
| 1695 | 1699 | /* adding to parent that previously didn't have children */ |
| 1696 | 1700 | dent = aafs_create_dir("profiles", dent); |
| 1697 | 1701 | if (IS_ERR(dent)) |
| @@ -144,19 +144,6 @@ int aa_audit_file(const struct cred *subj_cred, · 143 unmodified lines | ||
| 144 | 144 | return aa_audit(type, profile, &ad, file_audit_cb); |
| 145 | 145 | } |
| 146 | 146 | |
| 147 | −/** | |
| 148 | − * is_deleted - test if a file has been completely unlinked | |
| 149 | − * @dentry: dentry of file to test for deletion (NOT NULL) | |
| 150 | − * | |
| 151 | − * Returns: true if deleted else false | |
| 152 | − */ | |
| 153 | −static inline bool is_deleted(struct dentry *dentry) | |
| 154 | −{ | |
| 155 | − if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0) | |
| 156 | − return true; | |
| 157 | − return false; | |
| 158 | −} | |
| 159 | − | |
| 160 | 147 | static int path_name(const char *op, const struct cred *subj_cred, |
| 161 | 148 | struct aa_label *label, |
| 162 | 149 | const struct path *path, int flags, char *buffer, |
| @@ -63,6 +63,26 @@ static inline struct aa_label *aa_get_newest_cred_label(const struct cred *cred) · 62 unmodified lines | ||
| 63 | 63 | return aa_get_newest_label(aa_cred_raw_label(cred)); |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | +static inline struct aa_label *aa_get_newest_cred_label_condref(const struct cred *cred, | |
| 67 | + bool *needput) | |
| 68 | +{ | |
| 69 | + struct aa_label *l = aa_cred_raw_label(cred); | |
| 70 | + | |
| 71 | + if (unlikely(label_is_stale(l))) { | |
| 72 | + *needput = true; | |
| 73 | + return aa_get_newest_label(l); | |
| 74 | + } | |
| 75 | + | |
| 76 | + *needput = false; | |
| 77 | + return l; | |
| 78 | +} | |
| 79 | + | |
| 80 | +static inline void aa_put_label_condref(struct aa_label *l, bool needput) | |
| 81 | +{ | |
| 82 | + if (unlikely(needput)) | |
| 83 | + aa_put_label(l); | |
| 84 | +} | |
| 85 | + | |
| 66 | 86 | /** |
| 67 | 87 | * aa_current_raw_label - find the current tasks confining label |
| 68 | 88 | * |
| @@ -461,6 +461,7 @@ static int apparmor_file_open(struct file *file) · 460 unmodified lines | ||
| 461 | 461 | struct aa_file_ctx *fctx = file_ctx(file); |
| 462 | 462 | struct aa_label *label; |
| 463 | 463 | int error = 0; |
| 464 | + bool needput; | |
| 464 | 465 | |
| 465 | 466 | if (!path_mediated_fs(file->f_path.dentry)) |
| 466 | 467 | return 0; |
| @@ -477,7 +478,7 @@ static int apparmor_file_open(struct file *file) · 10 unmodified lines | ||
| 477 | 478 | return 0; |
| 478 | 479 | } |
| 479 | 480 | |
| 480 | − label = aa_get_newest_cred_label(file->f_cred); | |
| 481 | + label = aa_get_newest_cred_label_condref(file->f_cred, &needput); | |
| 481 | 482 | if (!unconfined(label)) { |
| 482 | 483 | struct mnt_idmap *idmap = file_mnt_idmap(file); |
| 483 | 484 | struct inode *inode = file_inode(file); |
| @@ -494,7 +495,7 @@ static int apparmor_file_open(struct file *file) · 10 unmodified lines | ||
| 494 | 495 | /* todo cache full allowed permissions set and state */ |
| 495 | 496 | fctx->allow = aa_map_file_to_perms(file); |
| 496 | 497 | } |
| 497 | − aa_put_label(label); | |
| 498 | + aa_put_label_condref(label, needput); | |
| 498 | 499 | |
| 499 | 500 | return error; |
| 500 | 501 | } |
| @@ -1124,7 +1125,7 @@ static int apparmor_socket_create(int family, int type, int protocol, int kern) · 623 unmodified lines | ||
| 1124 | 1125 | * @sock: socket that is being setup |
| 1125 | 1126 | * @family: family of socket being created |
| 1126 | 1127 | * @type: type of the socket |
| 1127 | − * @ptotocol: protocol of the socket | |
| 1128 | + * @protocol: protocol of the socket | |
| 1128 | 1129 | * @kern: socket is a special kernel socket |
| 1129 | 1130 | * |
| 1130 | 1131 | * Note: |
| @@ -1304,6 +1305,13 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) · 173 unmodified lines | ||
| 1304 | 1305 | if (!skb->secmark) |
| 1305 | 1306 | return 0; |
| 1306 | 1307 | |
| 1308 | + /* | |
| 1309 | + * If reach here before socket_post_create hook is called, in which | |
| 1310 | + * case label is null, drop the packet. | |
| 1311 | + */ | |
| 1312 | + if (!ctx->label) | |
| 1313 | + return -EACCES; | |
| 1314 | + | |
| 1307 | 1315 | return apparmor_secmark_check(ctx->label, OP_RECVMSG, AA_MAY_RECEIVE, |
| 1308 | 1316 | skb->secmark, sk); |
| 1309 | 1317 | } |
| @@ -44,6 +44,8 @@ static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) · 43 unmodified lines | ||
| 44 | 44 | audit_log_format(ab, ", mand"); |
| 45 | 45 | if (flags & MS_DIRSYNC) |
| 46 | 46 | audit_log_format(ab, ", dirsync"); |
| 47 | + if (flags & MS_NOSYMFOLLOW) | |
| 48 | + audit_log_format(ab, ", nosymfollow"); | |
| 47 | 49 | if (flags & MS_NOATIME) |
| 48 | 50 | audit_log_format(ab, ", noatime"); |
| 49 | 51 | if (flags & MS_NODIRATIME) |
| @@ -225,7 +225,7 @@ static void aa_free_data(void *ptr, void *arg) · 224 unmodified lines | ||
| 225 | 225 | { |
| 226 | 226 | struct aa_data *data = ptr; |
| 227 | 227 | |
| 228 | − kfree_sensitive(data->data); | |
| 228 | + kvfree_sensitive(data->data, data->size); | |
| 229 | 229 | kfree_sensitive(data->key); |
| 230 | 230 | kfree_sensitive(data); |
| 231 | 231 | } |
| @@ -747,34 +747,42 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, · 746 unmodified lines | ||
| 747 | 747 | *info = "missing required dfa"; |
| 748 | 748 | goto fail; |
| 749 | 749 | } |
| 750 | − goto out; | |
| 750 | + } else { | |
| 751 | + /* | |
| 752 | + * only unpack the following if a dfa is present | |
| 753 | + * | |
| 754 | + * sadly start was given different names for file and policydb | |
| 755 | + * but since it is optional we can try both | |
| 756 | + */ | |
| 757 | + if (!aa_unpack_u32(e, &pdb->start[0], "start")) | |
| 758 | + /* default start state */ | |
| 759 | + pdb->start[0] = DFA_START; | |
| 760 | + if (!aa_unpack_u32(e, &pdb->start[AA_CLASS_FILE], "dfa_start")) { | |
| 761 | + /* default start state for xmatch and file dfa */ | |
| 762 | + pdb->start[AA_CLASS_FILE] = DFA_START; | |
| 763 | + } /* setup class index */ | |
| 764 | + for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) { | |
| 765 | + pdb->start[i] = aa_dfa_next(pdb->dfa, pdb->start[0], | |
| 766 | + i); | |
| 767 | + } | |
| 751 | 768 | } |
| 752 | 769 | |
| 753 | 770 | /* |
| 754 | − * only unpack the following if a dfa is present | |
| 755 | − * | |
| 756 | − * sadly start was given different names for file and policydb | |
| 757 | − * but since it is optional we can try both | |
| 771 | + * Unfortunately due to a bug in earlier userspaces, a | |
| 772 | + * transition table may be present even when the dfa is | |
| 773 | + * not. For compatibility reasons unpack and discard. | |
| 758 | 774 | */ |
| 759 | − if (!aa_unpack_u32(e, &pdb->start[0], "start")) | |
| 760 | − /* default start state */ | |
| 761 | − pdb->start[0] = DFA_START; | |
| 762 | − if (!aa_unpack_u32(e, &pdb->start[AA_CLASS_FILE], "dfa_start")) { | |
| 763 | − /* default start state for xmatch and file dfa */ | |
| 764 | − pdb->start[AA_CLASS_FILE] = DFA_START; | |
| 765 | − } /* setup class index */ | |
| 766 | − for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) { | |
| 767 | − pdb->start[i] = aa_dfa_next(pdb->dfa, pdb->start[0], | |
| 768 | − i); | |
| 769 | − } | |
| 770 | 775 | if (!unpack_trans_table(e, &pdb->trans) && required_trans) { |
| 771 | 776 | *info = "failed to unpack profile transition table"; |
| 772 | 777 | goto fail; |
| 773 | 778 | } |
| 774 | 779 | |
| 780 | + if (!pdb->dfa && pdb->trans.table) | |
| 781 | + aa_free_str_table(&pdb->trans); | |
| 782 | + | |
| 775 | 783 | /* TODO: move compat mapping here, requires dfa merging first */ |
| 776 | 784 | /* TODO: move verify here, it has to be done after compat mappings */ |
| 777 | −out: | |
| 785 | + | |
| 778 | 786 | *policy = pdb; |
| 779 | 787 | return 0; |
| 780 | 788 | |
| @@ -1071,6 +1079,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) · 290 unmodified lines | ||
| 1071 | 1079 | |
| 1072 | 1080 | if (rhashtable_insert_fast(profile->data, &data->head, |
| 1073 | 1081 | profile->data->p)) { |
| 1082 | + kvfree_sensitive(data->data, data->size); | |
| 1074 | 1083 | kfree_sensitive(data->key); |
| 1075 | 1084 | kfree_sensitive(data); |
| 1076 | 1085 | info = "failed to insert data to table"; |
| @@ -604,4 +604,5 @@ static struct kunit_suite apparmor_policy_unpack_test_module = { · 603 unmodified lines | ||
| 604 | 604 | |
| 605 | 605 | kunit_test_suite(apparmor_policy_unpack_test_module); |
| 606 | 606 | |
| 607 | +MODULE_DESCRIPTION("KUnit tests for AppArmor's policy unpack"); | |
| 607 | 608 | MODULE_LICENSE("GPL"); |