diff options
-rw-r--r-- | security/apparmor/apparmorfs.c | 1 | ||||
-rw-r--r-- | security/apparmor/domain.c | 163 | ||||
-rw-r--r-- | security/apparmor/include/task.h | 4 | ||||
-rw-r--r-- | security/apparmor/task.c | 7 |
4 files changed, 110 insertions, 65 deletions
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 874c1bf6b84a..07623fb41e32 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c | |||
@@ -2156,6 +2156,7 @@ static struct aa_sfs_entry aa_sfs_entry_domain[] = { | |||
2156 | AA_SFS_FILE_BOOLEAN("change_profile", 1), | 2156 | AA_SFS_FILE_BOOLEAN("change_profile", 1), |
2157 | AA_SFS_FILE_BOOLEAN("stack", 1), | 2157 | AA_SFS_FILE_BOOLEAN("stack", 1), |
2158 | AA_SFS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1), | 2158 | AA_SFS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1), |
2159 | AA_SFS_FILE_BOOLEAN("post_nnp_subset", 1), | ||
2159 | AA_SFS_FILE_STRING("version", "1.2"), | 2160 | AA_SFS_FILE_STRING("version", "1.2"), |
2160 | { } | 2161 | { } |
2161 | }; | 2162 | }; |
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index cd58eef4eb8d..9d1936519cfd 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c | |||
@@ -592,22 +592,6 @@ static struct aa_label *profile_transition(struct aa_profile *profile, | |||
592 | if (!new) | 592 | if (!new) |
593 | goto audit; | 593 | goto audit; |
594 | 594 | ||
595 | /* Policy has specified a domain transitions. if no_new_privs and | ||
596 | * confined and not transitioning to the current domain fail. | ||
597 | * | ||
598 | * NOTE: Domain transitions from unconfined and to stritly stacked | ||
599 | * subsets are allowed even when no_new_privs is set because this | ||
600 | * aways results in a further reduction of permissions. | ||
601 | */ | ||
602 | if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && | ||
603 | !profile_unconfined(profile) && | ||
604 | !aa_label_is_subset(new, &profile->label)) { | ||
605 | error = -EPERM; | ||
606 | info = "no new privs"; | ||
607 | nonewprivs = true; | ||
608 | perms.allow &= ~MAY_EXEC; | ||
609 | goto audit; | ||
610 | } | ||
611 | 595 | ||
612 | if (!(perms.xindex & AA_X_UNSAFE)) { | 596 | if (!(perms.xindex & AA_X_UNSAFE)) { |
613 | if (DEBUG_ON) { | 597 | if (DEBUG_ON) { |
@@ -684,21 +668,6 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec, | |||
684 | perms.allow &= ~AA_MAY_ONEXEC; | 668 | perms.allow &= ~AA_MAY_ONEXEC; |
685 | goto audit; | 669 | goto audit; |
686 | } | 670 | } |
687 | /* Policy has specified a domain transitions. if no_new_privs and | ||
688 | * confined and not transitioning to the current domain fail. | ||
689 | * | ||
690 | * NOTE: Domain transitions from unconfined and to stritly stacked | ||
691 | * subsets are allowed even when no_new_privs is set because this | ||
692 | * aways results in a further reduction of permissions. | ||
693 | */ | ||
694 | if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && | ||
695 | !profile_unconfined(profile) && | ||
696 | !aa_label_is_subset(onexec, &profile->label)) { | ||
697 | error = -EPERM; | ||
698 | info = "no new privs"; | ||
699 | perms.allow &= ~AA_MAY_ONEXEC; | ||
700 | goto audit; | ||
701 | } | ||
702 | 671 | ||
703 | if (!(perms.xindex & AA_X_UNSAFE)) { | 672 | if (!(perms.xindex & AA_X_UNSAFE)) { |
704 | if (DEBUG_ON) { | 673 | if (DEBUG_ON) { |
@@ -800,6 +769,17 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
800 | 769 | ||
801 | label = aa_get_newest_label(cred_label(bprm->cred)); | 770 | label = aa_get_newest_label(cred_label(bprm->cred)); |
802 | 771 | ||
772 | /* | ||
773 | * Detect no new privs being set, and store the label it | ||
774 | * occurred under. Ideally this would happen when nnp | ||
775 | * is set but there isn't a good way to do that yet. | ||
776 | * | ||
777 | * Testing for unconfined must be done before the subset test | ||
778 | */ | ||
779 | if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && !unconfined(label) && | ||
780 | !ctx->nnp) | ||
781 | ctx->nnp = aa_get_label(label); | ||
782 | |||
803 | /* buffer freed below, name is pointer into buffer */ | 783 | /* buffer freed below, name is pointer into buffer */ |
804 | get_buffers(buffer); | 784 | get_buffers(buffer); |
805 | /* Test for onexec first as onexec override other x transitions. */ | 785 | /* Test for onexec first as onexec override other x transitions. */ |
@@ -820,7 +800,20 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
820 | goto done; | 800 | goto done; |
821 | } | 801 | } |
822 | 802 | ||
823 | /* TODO: Add ns level no_new_privs subset test */ | 803 | /* Policy has specified a domain transitions. If no_new_privs and |
804 | * confined ensure the transition is to confinement that is subset | ||
805 | * of the confinement when the task entered no new privs. | ||
806 | * | ||
807 | * NOTE: Domain transitions from unconfined and to stacked | ||
808 | * subsets are allowed even when no_new_privs is set because this | ||
809 | * aways results in a further reduction of permissions. | ||
810 | */ | ||
811 | if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && | ||
812 | !unconfined(label) && !aa_label_is_subset(new, ctx->nnp)) { | ||
813 | error = -EPERM; | ||
814 | info = "no new privs"; | ||
815 | goto audit; | ||
816 | } | ||
824 | 817 | ||
825 | if (bprm->unsafe & LSM_UNSAFE_SHARE) { | 818 | if (bprm->unsafe & LSM_UNSAFE_SHARE) { |
826 | /* FIXME: currently don't mediate shared state */ | 819 | /* FIXME: currently don't mediate shared state */ |
@@ -1047,30 +1040,28 @@ build: | |||
1047 | int aa_change_hat(const char *hats[], int count, u64 token, int flags) | 1040 | int aa_change_hat(const char *hats[], int count, u64 token, int flags) |
1048 | { | 1041 | { |
1049 | const struct cred *cred; | 1042 | const struct cred *cred; |
1050 | struct aa_task_ctx *ctx; | 1043 | struct aa_task_ctx *ctx = task_ctx(current); |
1051 | struct aa_label *label, *previous, *new = NULL, *target = NULL; | 1044 | struct aa_label *label, *previous, *new = NULL, *target = NULL; |
1052 | struct aa_profile *profile; | 1045 | struct aa_profile *profile; |
1053 | struct aa_perms perms = {}; | 1046 | struct aa_perms perms = {}; |
1054 | const char *info = NULL; | 1047 | const char *info = NULL; |
1055 | int error = 0; | 1048 | int error = 0; |
1056 | 1049 | ||
1057 | /* | ||
1058 | * Fail explicitly requested domain transitions if no_new_privs. | ||
1059 | * There is no exception for unconfined as change_hat is not | ||
1060 | * available. | ||
1061 | */ | ||
1062 | if (task_no_new_privs(current)) { | ||
1063 | /* not an apparmor denial per se, so don't log it */ | ||
1064 | AA_DEBUG("no_new_privs - change_hat denied"); | ||
1065 | return -EPERM; | ||
1066 | } | ||
1067 | |||
1068 | /* released below */ | 1050 | /* released below */ |
1069 | cred = get_current_cred(); | 1051 | cred = get_current_cred(); |
1070 | ctx = task_ctx(current); | ||
1071 | label = aa_get_newest_cred_label(cred); | 1052 | label = aa_get_newest_cred_label(cred); |
1072 | previous = aa_get_newest_label(ctx->previous); | 1053 | previous = aa_get_newest_label(ctx->previous); |
1073 | 1054 | ||
1055 | /* | ||
1056 | * Detect no new privs being set, and store the label it | ||
1057 | * occurred under. Ideally this would happen when nnp | ||
1058 | * is set but there isn't a good way to do that yet. | ||
1059 | * | ||
1060 | * Testing for unconfined must be done before the subset test | ||
1061 | */ | ||
1062 | if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp) | ||
1063 | ctx->nnp = aa_get_label(label); | ||
1064 | |||
1074 | if (unconfined(label)) { | 1065 | if (unconfined(label)) { |
1075 | info = "unconfined can not change_hat"; | 1066 | info = "unconfined can not change_hat"; |
1076 | error = -EPERM; | 1067 | error = -EPERM; |
@@ -1091,6 +1082,18 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) | |||
1091 | if (error) | 1082 | if (error) |
1092 | goto fail; | 1083 | goto fail; |
1093 | 1084 | ||
1085 | /* | ||
1086 | * no new privs prevents domain transitions that would | ||
1087 | * reduce restrictions. | ||
1088 | */ | ||
1089 | if (task_no_new_privs(current) && !unconfined(label) && | ||
1090 | !aa_label_is_subset(new, ctx->nnp)) { | ||
1091 | /* not an apparmor denial per se, so don't log it */ | ||
1092 | AA_DEBUG("no_new_privs - change_hat denied"); | ||
1093 | error = -EPERM; | ||
1094 | goto out; | ||
1095 | } | ||
1096 | |||
1094 | if (flags & AA_CHANGE_TEST) | 1097 | if (flags & AA_CHANGE_TEST) |
1095 | goto out; | 1098 | goto out; |
1096 | 1099 | ||
@@ -1100,6 +1103,18 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) | |||
1100 | /* kill task in case of brute force attacks */ | 1103 | /* kill task in case of brute force attacks */ |
1101 | goto kill; | 1104 | goto kill; |
1102 | } else if (previous && !(flags & AA_CHANGE_TEST)) { | 1105 | } else if (previous && !(flags & AA_CHANGE_TEST)) { |
1106 | /* | ||
1107 | * no new privs prevents domain transitions that would | ||
1108 | * reduce restrictions. | ||
1109 | */ | ||
1110 | if (task_no_new_privs(current) && !unconfined(label) && | ||
1111 | !aa_label_is_subset(previous, ctx->nnp)) { | ||
1112 | /* not an apparmor denial per se, so don't log it */ | ||
1113 | AA_DEBUG("no_new_privs - change_hat denied"); | ||
1114 | error = -EPERM; | ||
1115 | goto out; | ||
1116 | } | ||
1117 | |||
1103 | /* Return to saved label. Kill task if restore fails | 1118 | /* Return to saved label. Kill task if restore fails |
1104 | * to avoid brute force attacks | 1119 | * to avoid brute force attacks |
1105 | */ | 1120 | */ |
@@ -1142,21 +1157,6 @@ static int change_profile_perms_wrapper(const char *op, const char *name, | |||
1142 | const char *info = NULL; | 1157 | const char *info = NULL; |
1143 | int error = 0; | 1158 | int error = 0; |
1144 | 1159 | ||
1145 | /* | ||
1146 | * Fail explicitly requested domain transitions when no_new_privs | ||
1147 | * and not unconfined OR the transition results in a stack on | ||
1148 | * the current label. | ||
1149 | * Stacking domain transitions and transitions from unconfined are | ||
1150 | * allowed even when no_new_privs is set because this aways results | ||
1151 | * in a reduction of permissions. | ||
1152 | */ | ||
1153 | if (task_no_new_privs(current) && !stack && | ||
1154 | !profile_unconfined(profile) && | ||
1155 | !aa_label_is_subset(target, &profile->label)) { | ||
1156 | info = "no new privs"; | ||
1157 | error = -EPERM; | ||
1158 | } | ||
1159 | |||
1160 | if (!error) | 1160 | if (!error) |
1161 | error = change_profile_perms(profile, target, stack, request, | 1161 | error = change_profile_perms(profile, target, stack, request, |
1162 | profile->file.start, perms); | 1162 | profile->file.start, perms); |
@@ -1190,10 +1190,23 @@ int aa_change_profile(const char *fqname, int flags) | |||
1190 | const char *info = NULL; | 1190 | const char *info = NULL; |
1191 | const char *auditname = fqname; /* retain leading & if stack */ | 1191 | const char *auditname = fqname; /* retain leading & if stack */ |
1192 | bool stack = flags & AA_CHANGE_STACK; | 1192 | bool stack = flags & AA_CHANGE_STACK; |
1193 | struct aa_task_ctx *ctx = task_ctx(current); | ||
1193 | int error = 0; | 1194 | int error = 0; |
1194 | char *op; | 1195 | char *op; |
1195 | u32 request; | 1196 | u32 request; |
1196 | 1197 | ||
1198 | label = aa_get_current_label(); | ||
1199 | |||
1200 | /* | ||
1201 | * Detect no new privs being set, and store the label it | ||
1202 | * occurred under. Ideally this would happen when nnp | ||
1203 | * is set but there isn't a good way to do that yet. | ||
1204 | * | ||
1205 | * Testing for unconfined must be done before the subset test | ||
1206 | */ | ||
1207 | if (task_no_new_privs(current) && !unconfined(label) && !ctx->nnp) | ||
1208 | ctx->nnp = aa_get_label(label); | ||
1209 | |||
1197 | if (!fqname || !*fqname) { | 1210 | if (!fqname || !*fqname) { |
1198 | AA_DEBUG("no profile name"); | 1211 | AA_DEBUG("no profile name"); |
1199 | return -EINVAL; | 1212 | return -EINVAL; |
@@ -1281,14 +1294,28 @@ check: | |||
1281 | if (flags & AA_CHANGE_TEST) | 1294 | if (flags & AA_CHANGE_TEST) |
1282 | goto out; | 1295 | goto out; |
1283 | 1296 | ||
1297 | /* stacking is always a subset, so only check the nonstack case */ | ||
1298 | if (!stack) { | ||
1299 | new = fn_label_build_in_ns(label, profile, GFP_KERNEL, | ||
1300 | aa_get_label(target), | ||
1301 | aa_get_label(&profile->label)); | ||
1302 | /* | ||
1303 | * no new privs prevents domain transitions that would | ||
1304 | * reduce restrictions. | ||
1305 | */ | ||
1306 | if (task_no_new_privs(current) && !unconfined(label) && | ||
1307 | !aa_label_is_subset(new, ctx->nnp)) { | ||
1308 | /* not an apparmor denial per se, so don't log it */ | ||
1309 | AA_DEBUG("no_new_privs - change_hat denied"); | ||
1310 | error = -EPERM; | ||
1311 | goto out; | ||
1312 | } | ||
1313 | } | ||
1314 | |||
1284 | if (!(flags & AA_CHANGE_ONEXEC)) { | 1315 | if (!(flags & AA_CHANGE_ONEXEC)) { |
1285 | /* only transition profiles in the current ns */ | 1316 | /* only transition profiles in the current ns */ |
1286 | if (stack) | 1317 | if (stack) |
1287 | new = aa_label_merge(label, target, GFP_KERNEL); | 1318 | new = aa_label_merge(label, target, GFP_KERNEL); |
1288 | else | ||
1289 | new = fn_label_build_in_ns(label, profile, GFP_KERNEL, | ||
1290 | aa_get_label(target), | ||
1291 | aa_get_label(&profile->label)); | ||
1292 | if (IS_ERR_OR_NULL(new)) { | 1319 | if (IS_ERR_OR_NULL(new)) { |
1293 | info = "failed to build target label"; | 1320 | info = "failed to build target label"; |
1294 | error = PTR_ERR(new); | 1321 | error = PTR_ERR(new); |
@@ -1297,9 +1324,15 @@ check: | |||
1297 | goto audit; | 1324 | goto audit; |
1298 | } | 1325 | } |
1299 | error = aa_replace_current_label(new); | 1326 | error = aa_replace_current_label(new); |
1300 | } else | 1327 | } else { |
1328 | if (new) { | ||
1329 | aa_put_label(new); | ||
1330 | new = NULL; | ||
1331 | } | ||
1332 | |||
1301 | /* full transition will be built in exec path */ | 1333 | /* full transition will be built in exec path */ |
1302 | error = aa_set_current_onexec(target, stack); | 1334 | error = aa_set_current_onexec(target, stack); |
1335 | } | ||
1303 | 1336 | ||
1304 | audit: | 1337 | audit: |
1305 | error = fn_for_each_in_ns(label, profile, | 1338 | error = fn_for_each_in_ns(label, profile, |
diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h index d222197db299..55edaa1d83f8 100644 --- a/security/apparmor/include/task.h +++ b/security/apparmor/include/task.h | |||
@@ -18,11 +18,13 @@ | |||
18 | 18 | ||
19 | /* | 19 | /* |
20 | * struct aa_task_ctx - information for current task label change | 20 | * struct aa_task_ctx - information for current task label change |
21 | * @nnp: snapshot of label at time of no_new_privs | ||
21 | * @onexec: profile to transition to on next exec (MAY BE NULL) | 22 | * @onexec: profile to transition to on next exec (MAY BE NULL) |
22 | * @previous: profile the task may return to (MAY BE NULL) | 23 | * @previous: profile the task may return to (MAY BE NULL) |
23 | * @token: magic value the task must know for returning to @previous_profile | 24 | * @token: magic value the task must know for returning to @previous_profile |
24 | */ | 25 | */ |
25 | struct aa_task_ctx { | 26 | struct aa_task_ctx { |
27 | struct aa_label *nnp; | ||
26 | struct aa_label *onexec; | 28 | struct aa_label *onexec; |
27 | struct aa_label *previous; | 29 | struct aa_label *previous; |
28 | u64 token; | 30 | u64 token; |
@@ -52,6 +54,7 @@ static inline struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags) | |||
52 | static inline void aa_free_task_ctx(struct aa_task_ctx *ctx) | 54 | static inline void aa_free_task_ctx(struct aa_task_ctx *ctx) |
53 | { | 55 | { |
54 | if (ctx) { | 56 | if (ctx) { |
57 | aa_put_label(ctx->nnp); | ||
55 | aa_put_label(ctx->previous); | 58 | aa_put_label(ctx->previous); |
56 | aa_put_label(ctx->onexec); | 59 | aa_put_label(ctx->onexec); |
57 | 60 | ||
@@ -68,6 +71,7 @@ static inline void aa_dup_task_ctx(struct aa_task_ctx *new, | |||
68 | const struct aa_task_ctx *old) | 71 | const struct aa_task_ctx *old) |
69 | { | 72 | { |
70 | *new = *old; | 73 | *new = *old; |
74 | aa_get_label(new->nnp); | ||
71 | aa_get_label(new->previous); | 75 | aa_get_label(new->previous); |
72 | aa_get_label(new->onexec); | 76 | aa_get_label(new->onexec); |
73 | } | 77 | } |
diff --git a/security/apparmor/task.c b/security/apparmor/task.c index 44b9b938e06d..c6b78a14da91 100644 --- a/security/apparmor/task.c +++ b/security/apparmor/task.c | |||
@@ -45,6 +45,7 @@ struct aa_label *aa_get_task_label(struct task_struct *task) | |||
45 | int aa_replace_current_label(struct aa_label *label) | 45 | int aa_replace_current_label(struct aa_label *label) |
46 | { | 46 | { |
47 | struct aa_label *old = aa_current_raw_label(); | 47 | struct aa_label *old = aa_current_raw_label(); |
48 | struct aa_task_ctx *ctx = task_ctx(current); | ||
48 | struct cred *new; | 49 | struct cred *new; |
49 | 50 | ||
50 | AA_BUG(!label); | 51 | AA_BUG(!label); |
@@ -59,6 +60,12 @@ int aa_replace_current_label(struct aa_label *label) | |||
59 | if (!new) | 60 | if (!new) |
60 | return -ENOMEM; | 61 | return -ENOMEM; |
61 | 62 | ||
63 | if (ctx->nnp && label_is_stale(ctx->nnp)) { | ||
64 | struct aa_label *tmp = ctx->nnp; | ||
65 | |||
66 | ctx->nnp = aa_get_newest_label(tmp); | ||
67 | aa_put_label(tmp); | ||
68 | } | ||
62 | if (unconfined(label) || (labels_ns(old) != labels_ns(label))) | 69 | if (unconfined(label) || (labels_ns(old) != labels_ns(label))) |
63 | /* | 70 | /* |
64 | * if switching to unconfined or a different label namespace | 71 | * if switching to unconfined or a different label namespace |