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 | struct aa_profile *p; | 1692 | struct aa_profile *p; |
| 1693 | p = aa_deref_parent(profile); | 1693 | p = aa_deref_parent(profile); |
| 1694 | dent = prof_dir(p); | 1694 | dent = prof_dir(p); |
| 1695 | if (!dent) { | ||
| 1696 | error = -ENOENT; | ||
| 1697 | goto fail2; | ||
| 1698 | } | ||
| 1695 | /* adding to parent that previously didn't have children */ | 1699 | /* adding to parent that previously didn't have children */ |
| 1696 | dent = aafs_create_dir("profiles", dent); | 1700 | dent = aafs_create_dir("profiles", dent); |
| 1697 | if (IS_ERR(dent)) | 1701 | if (IS_ERR(dent)) |
| @@ -144,19 +144,6 @@ int aa_audit_file(const struct cred *subj_cred, · 143 unmodified lines | |||
| 144 | return aa_audit(type, profile, &ad, file_audit_cb); | 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 | static int path_name(const char *op, const struct cred *subj_cred, | 147 | static int path_name(const char *op, const struct cred *subj_cred, |
| 161 | struct aa_label *label, | 148 | struct aa_label *label, |
| 162 | const struct path *path, int flags, char *buffer, | 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 | return aa_get_newest_label(aa_cred_raw_label(cred)); | 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 | * aa_current_raw_label - find the current tasks confining label | 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 | struct aa_file_ctx *fctx = file_ctx(file); | 461 | struct aa_file_ctx *fctx = file_ctx(file); |
| 462 | struct aa_label *label; | 462 | struct aa_label *label; |
| 463 | int error = 0; | 463 | int error = 0; |
| 464 | bool needput; | ||
| 464 | 465 | ||
| 465 | if (!path_mediated_fs(file->f_path.dentry)) | 466 | if (!path_mediated_fs(file->f_path.dentry)) |
| 466 | return 0; | 467 | return 0; |
| @@ -477,7 +478,7 @@ static int apparmor_file_open(struct file *file) · 10 unmodified lines | |||
| 477 | return 0; | 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 | if (!unconfined(label)) { | 482 | if (!unconfined(label)) { |
| 482 | struct mnt_idmap *idmap = file_mnt_idmap(file); | 483 | struct mnt_idmap *idmap = file_mnt_idmap(file); |
| 483 | struct inode *inode = file_inode(file); | 484 | struct inode *inode = file_inode(file); |
| @@ -494,7 +495,7 @@ static int apparmor_file_open(struct file *file) · 10 unmodified lines | |||
| 494 | /* todo cache full allowed permissions set and state */ | 495 | /* todo cache full allowed permissions set and state */ |
| 495 | fctx->allow = aa_map_file_to_perms(file); | 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 | return error; | 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 | * @sock: socket that is being setup | 1125 | * @sock: socket that is being setup |
| 1125 | * @family: family of socket being created | 1126 | * @family: family of socket being created |
| 1126 | * @type: type of the socket | 1127 | * @type: type of the socket |
| 1127 | * @ptotocol: protocol of the socket | 1128 | * @protocol: protocol of the socket |
| 1128 | * @kern: socket is a special kernel socket | 1129 | * @kern: socket is a special kernel socket |
| 1129 | * | 1130 | * |
| 1130 | * Note: | 1131 | * Note: |
| @@ -1304,6 +1305,13 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) · 173 unmodified lines | |||
| 1304 | if (!skb->secmark) | 1305 | if (!skb->secmark) |
| 1305 | return 0; | 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 | return apparmor_secmark_check(ctx->label, OP_RECVMSG, AA_MAY_RECEIVE, | 1315 | return apparmor_secmark_check(ctx->label, OP_RECVMSG, AA_MAY_RECEIVE, |
| 1308 | skb->secmark, sk); | 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 | audit_log_format(ab, ", mand"); | 44 | audit_log_format(ab, ", mand"); |
| 45 | if (flags & MS_DIRSYNC) | 45 | if (flags & MS_DIRSYNC) |
| 46 | audit_log_format(ab, ", dirsync"); | 46 | audit_log_format(ab, ", dirsync"); |
| 47 | if (flags & MS_NOSYMFOLLOW) | ||
| 48 | audit_log_format(ab, ", nosymfollow"); | ||
| 47 | if (flags & MS_NOATIME) | 49 | if (flags & MS_NOATIME) |
| 48 | audit_log_format(ab, ", noatime"); | 50 | audit_log_format(ab, ", noatime"); |
| 49 | if (flags & MS_NODIRATIME) | 51 | if (flags & MS_NODIRATIME) |
| @@ -225,7 +225,7 @@ static void aa_free_data(void *ptr, void *arg) · 224 unmodified lines | |||
| 225 | { | 225 | { |
| 226 | struct aa_data *data = ptr; | 226 | struct aa_data *data = ptr; |
| 227 | 227 | ||
| 228 | kfree_sensitive(data->data); | 228 | kvfree_sensitive(data->data, data->size); |
| 229 | kfree_sensitive(data->key); | 229 | kfree_sensitive(data->key); |
| 230 | kfree_sensitive(data); | 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 | *info = "missing required dfa"; | 747 | *info = "missing required dfa"; |
| 748 | goto fail; | 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 | 771 | * Unfortunately due to a bug in earlier userspaces, a |
| 755 | * | 772 | * transition table may be present even when the dfa is |
| 756 | * sadly start was given different names for file and policydb | 773 | * not. For compatibility reasons unpack and discard. |
| 757 | * but since it is optional we can try both | ||
| 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 | if (!unpack_trans_table(e, &pdb->trans) && required_trans) { | 775 | if (!unpack_trans_table(e, &pdb->trans) && required_trans) { |
| 771 | *info = "failed to unpack profile transition table"; | 776 | *info = "failed to unpack profile transition table"; |
| 772 | goto fail; | 777 | goto fail; |
| 773 | } | 778 | } |
| 774 | 779 | ||
| 780 | if (!pdb->dfa && pdb->trans.table) | ||
| 781 | aa_free_str_table(&pdb->trans); | ||
| 782 | |||
| 775 | /* TODO: move compat mapping here, requires dfa merging first */ | 783 | /* TODO: move compat mapping here, requires dfa merging first */ |
| 776 | /* TODO: move verify here, it has to be done after compat mappings */ | 784 | /* TODO: move verify here, it has to be done after compat mappings */ |
| 777 | out: | 785 | |
| 778 | *policy = pdb; | 786 | *policy = pdb; |
| 779 | return 0; | 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 | if (rhashtable_insert_fast(profile->data, &data->head, | 1080 | if (rhashtable_insert_fast(profile->data, &data->head, |
| 1073 | profile->data->p)) { | 1081 | profile->data->p)) { |
| 1082 | kvfree_sensitive(data->data, data->size); | ||
| 1074 | kfree_sensitive(data->key); | 1083 | kfree_sensitive(data->key); |
| 1075 | kfree_sensitive(data); | 1084 | kfree_sensitive(data); |
| 1076 | info = "failed to insert data to table"; | 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 | kunit_test_suite(apparmor_policy_unpack_test_module); | 605 | kunit_test_suite(apparmor_policy_unpack_test_module); |
| 606 | 606 | ||
| 607 | MODULE_DESCRIPTION("KUnit tests for AppArmor's policy unpack"); | ||
| 607 | MODULE_LICENSE("GPL"); | 608 | MODULE_LICENSE("GPL"); |