Merge tag 'apparmor-pr-2024-07-25' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor

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

+65 -34
+4
security/apparmor/apparmorfs.c
··· 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))
-13
security/apparmor/file.c
··· 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,
+20
security/apparmor/include/cred.h
··· 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 *
+11 -3
security/apparmor/lsm.c
··· 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; ··· 478 477 return 0; 479 478 } 480 479 481 - label = aa_get_newest_cred_label(file->f_cred); 480 + label = aa_get_newest_cred_label_condref(file->f_cred, &needput); 482 481 if (!unconfined(label)) { 483 482 struct mnt_idmap *idmap = file_mnt_idmap(file); 484 483 struct inode *inode = file_inode(file); ··· 495 494 /* todo cache full allowed permissions set and state */ 496 495 fctx->allow = aa_map_file_to_perms(file); 497 496 } 498 - aa_put_label(label); 497 + aa_put_label_condref(label, needput); 499 498 500 499 return error; 501 500 } ··· 1125 1124 * @sock: socket that is being setup 1126 1125 * @family: family of socket being created 1127 1126 * @type: type of the socket 1128 - * @ptotocol: protocol of the socket 1127 + * @protocol: protocol of the socket 1129 1128 * @kern: socket is a special kernel socket 1130 1129 * 1131 1130 * Note: ··· 1304 1303 1305 1304 if (!skb->secmark) 1306 1305 return 0; 1306 + 1307 + /* 1308 + * If reach here before socket_post_create hook is called, in which 1309 + * case label is null, drop the packet. 1310 + */ 1311 + if (!ctx->label) 1312 + return -EACCES; 1307 1313 1308 1314 return apparmor_secmark_check(ctx->label, OP_RECVMSG, AA_MAY_RECEIVE, 1309 1315 skb->secmark, sk);
+2
security/apparmor/mount.c
··· 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)
+1 -1
security/apparmor/policy.c
··· 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 }
+26 -17
security/apparmor/policy_unpack.c
··· 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 ··· 1079 1071 1080 1072 if (rhashtable_insert_fast(profile->data, &data->head, 1081 1073 profile->data->p)) { 1074 + kvfree_sensitive(data->data, data->size); 1082 1075 kfree_sensitive(data->key); 1083 1076 kfree_sensitive(data); 1084 1077 info = "failed to insert data to table";
+1
security/apparmor/policy_unpack_test.c
··· 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");