Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

apparmor: allow restricting unprivileged change_profile

unprivileged unconfined can use change_profile to alter the confinement
set by the mac admin.

Allow restricting unprivileged unconfined by still allowing change_profile
but stacking the change against unconfined. This allows unconfined to
still apply system policy but allows the task to enter the new confinement.

If unprivileged unconfined is required a sysctl is provided to switch
to the previous behavior.

Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>

+39
+6
security/apparmor/apparmorfs.c
··· 2341 2341 { } 2342 2342 }; 2343 2343 2344 + static struct aa_sfs_entry aa_sfs_entry_unconfined[] = { 2345 + AA_SFS_FILE_BOOLEAN("change_profile", 1), 2346 + { } 2347 + }; 2348 + 2344 2349 static struct aa_sfs_entry aa_sfs_entry_versions[] = { 2345 2350 AA_SFS_FILE_BOOLEAN("v5", 1), 2346 2351 AA_SFS_FILE_BOOLEAN("v6", 1), ··· 2363 2358 AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED), 2364 2359 AA_SFS_FILE_U64("permstable32_version", 1), 2365 2360 AA_SFS_FILE_STRING("permstable32", PERMS32STR), 2361 + AA_SFS_DIR("unconfined_restrictions", aa_sfs_entry_unconfined), 2366 2362 { } 2367 2363 }; 2368 2364
+24
security/apparmor/domain.c
··· 1311 1311 return error; 1312 1312 } 1313 1313 1314 + const char *stack_msg = "change_profile unprivileged unconfined converted to stacking"; 1315 + 1314 1316 /** 1315 1317 * aa_change_profile - perform a one-way profile transition 1316 1318 * @fqname: name of profile may include namespace (NOT NULL) ··· 1370 1368 op = OP_STACK; 1371 1369 else 1372 1370 op = OP_CHANGE_PROFILE; 1371 + } 1372 + 1373 + /* This should move to a per profile test. Requires pushing build 1374 + * into callback 1375 + */ 1376 + if (!stack && unconfined(label) && 1377 + label == &labels_ns(label)->unconfined->label && 1378 + aa_unprivileged_unconfined_restricted && 1379 + /* TODO: refactor so this check is a fn */ 1380 + cap_capable(current_cred(), &init_user_ns, CAP_MAC_OVERRIDE, 1381 + CAP_OPT_NOAUDIT)) { 1382 + /* regardless of the request in this case apparmor 1383 + * stacks against unconfined so admin set policy can't be 1384 + * by-passed 1385 + */ 1386 + stack = true; 1387 + perms.audit = request; 1388 + (void) fn_for_each_in_ns(label, profile, 1389 + aa_audit_file(subj_cred, profile, &perms, op, 1390 + request, auditname, NULL, target, 1391 + GLOBAL_ROOT_UID, stack_msg, 0)); 1392 + perms.audit = 0; 1373 1393 } 1374 1394 1375 1395 if (*fqname == '&') {
+1
security/apparmor/include/policy.h
··· 34 34 struct aa_ns; 35 35 36 36 extern int unprivileged_userns_apparmor_policy; 37 + extern int aa_unprivileged_unconfined_restricted; 37 38 38 39 extern const char *const aa_profile_mode_names[]; 39 40 #define APPARMOR_MODE_NAMES_MAX_INDEX 4
+7
security/apparmor/lsm.c
··· 1798 1798 .mode = 0600, 1799 1799 .proc_handler = apparmor_dointvec, 1800 1800 }, 1801 + { 1802 + .procname = "apparmor_restrict_unprivileged_unconfined", 1803 + .data = &aa_unprivileged_unconfined_restricted, 1804 + .maxlen = sizeof(int), 1805 + .mode = 0600, 1806 + .proc_handler = apparmor_dointvec, 1807 + }, 1801 1808 1802 1809 { } 1803 1810 };
+1
security/apparmor/policy.c
··· 88 88 #include "include/resource.h" 89 89 90 90 int unprivileged_userns_apparmor_policy = 1; 91 + int aa_unprivileged_unconfined_restricted; 91 92 92 93 const char *const aa_profile_mode_names[] = { 93 94 "enforce",