18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * AppArmor security module 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This file contains AppArmor mediation of files 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 1998-2008 Novell/SUSE 88c2ecf20Sopenharmony_ci * Copyright 2009-2010 Canonical Ltd. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/tty.h> 128c2ecf20Sopenharmony_ci#include <linux/fdtable.h> 138c2ecf20Sopenharmony_ci#include <linux/file.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "include/apparmor.h" 168c2ecf20Sopenharmony_ci#include "include/audit.h" 178c2ecf20Sopenharmony_ci#include "include/cred.h" 188c2ecf20Sopenharmony_ci#include "include/file.h" 198c2ecf20Sopenharmony_ci#include "include/match.h" 208c2ecf20Sopenharmony_ci#include "include/net.h" 218c2ecf20Sopenharmony_ci#include "include/path.h" 228c2ecf20Sopenharmony_ci#include "include/policy.h" 238c2ecf20Sopenharmony_ci#include "include/label.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic u32 map_mask_to_chr_mask(u32 mask) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci u32 m = mask & PERMS_CHRS_MASK; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci if (mask & AA_MAY_GETATTR) 308c2ecf20Sopenharmony_ci m |= MAY_READ; 318c2ecf20Sopenharmony_ci if (mask & (AA_MAY_SETATTR | AA_MAY_CHMOD | AA_MAY_CHOWN)) 328c2ecf20Sopenharmony_ci m |= MAY_WRITE; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci return m; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/** 388c2ecf20Sopenharmony_ci * file_audit_cb - call back for file specific audit fields 398c2ecf20Sopenharmony_ci * @ab: audit_buffer (NOT NULL) 408c2ecf20Sopenharmony_ci * @va: audit struct to audit values of (NOT NULL) 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_cistatic void file_audit_cb(struct audit_buffer *ab, void *va) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct common_audit_data *sa = va; 458c2ecf20Sopenharmony_ci kuid_t fsuid = current_fsuid(); 468c2ecf20Sopenharmony_ci char str[10]; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (aad(sa)->request & AA_AUDIT_FILE_MASK) { 498c2ecf20Sopenharmony_ci aa_perm_mask_to_str(str, sizeof(str), aa_file_perm_chrs, 508c2ecf20Sopenharmony_ci map_mask_to_chr_mask(aad(sa)->request)); 518c2ecf20Sopenharmony_ci audit_log_format(ab, " requested_mask=\"%s\"", str); 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci if (aad(sa)->denied & AA_AUDIT_FILE_MASK) { 548c2ecf20Sopenharmony_ci aa_perm_mask_to_str(str, sizeof(str), aa_file_perm_chrs, 558c2ecf20Sopenharmony_ci map_mask_to_chr_mask(aad(sa)->denied)); 568c2ecf20Sopenharmony_ci audit_log_format(ab, " denied_mask=\"%s\"", str); 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci if (aad(sa)->request & AA_AUDIT_FILE_MASK) { 598c2ecf20Sopenharmony_ci audit_log_format(ab, " fsuid=%d", 608c2ecf20Sopenharmony_ci from_kuid(&init_user_ns, fsuid)); 618c2ecf20Sopenharmony_ci audit_log_format(ab, " ouid=%d", 628c2ecf20Sopenharmony_ci from_kuid(&init_user_ns, aad(sa)->fs.ouid)); 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (aad(sa)->peer) { 668c2ecf20Sopenharmony_ci audit_log_format(ab, " target="); 678c2ecf20Sopenharmony_ci aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, 688c2ecf20Sopenharmony_ci FLAG_VIEW_SUBNS, GFP_KERNEL); 698c2ecf20Sopenharmony_ci } else if (aad(sa)->fs.target) { 708c2ecf20Sopenharmony_ci audit_log_format(ab, " target="); 718c2ecf20Sopenharmony_ci audit_log_untrustedstring(ab, aad(sa)->fs.target); 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/** 768c2ecf20Sopenharmony_ci * aa_audit_file - handle the auditing of file operations 778c2ecf20Sopenharmony_ci * @profile: the profile being enforced (NOT NULL) 788c2ecf20Sopenharmony_ci * @perms: the permissions computed for the request (NOT NULL) 798c2ecf20Sopenharmony_ci * @op: operation being mediated 808c2ecf20Sopenharmony_ci * @request: permissions requested 818c2ecf20Sopenharmony_ci * @name: name of object being mediated (MAYBE NULL) 828c2ecf20Sopenharmony_ci * @target: name of target (MAYBE NULL) 838c2ecf20Sopenharmony_ci * @tlabel: target label (MAY BE NULL) 848c2ecf20Sopenharmony_ci * @ouid: object uid 858c2ecf20Sopenharmony_ci * @info: extra information message (MAYBE NULL) 868c2ecf20Sopenharmony_ci * @error: 0 if operation allowed else failure error code 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * Returns: %0 or error on failure 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ciint aa_audit_file(struct aa_profile *profile, struct aa_perms *perms, 918c2ecf20Sopenharmony_ci const char *op, u32 request, const char *name, 928c2ecf20Sopenharmony_ci const char *target, struct aa_label *tlabel, 938c2ecf20Sopenharmony_ci kuid_t ouid, const char *info, int error) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci int type = AUDIT_APPARMOR_AUTO; 968c2ecf20Sopenharmony_ci DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_TASK, op); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci sa.u.tsk = NULL; 998c2ecf20Sopenharmony_ci aad(&sa)->request = request; 1008c2ecf20Sopenharmony_ci aad(&sa)->name = name; 1018c2ecf20Sopenharmony_ci aad(&sa)->fs.target = target; 1028c2ecf20Sopenharmony_ci aad(&sa)->peer = tlabel; 1038c2ecf20Sopenharmony_ci aad(&sa)->fs.ouid = ouid; 1048c2ecf20Sopenharmony_ci aad(&sa)->info = info; 1058c2ecf20Sopenharmony_ci aad(&sa)->error = error; 1068c2ecf20Sopenharmony_ci sa.u.tsk = NULL; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (likely(!aad(&sa)->error)) { 1098c2ecf20Sopenharmony_ci u32 mask = perms->audit; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) 1128c2ecf20Sopenharmony_ci mask = 0xffff; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* mask off perms that are not being force audited */ 1158c2ecf20Sopenharmony_ci aad(&sa)->request &= mask; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (likely(!aad(&sa)->request)) 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci type = AUDIT_APPARMOR_AUDIT; 1208c2ecf20Sopenharmony_ci } else { 1218c2ecf20Sopenharmony_ci /* only report permissions that were denied */ 1228c2ecf20Sopenharmony_ci aad(&sa)->request = aad(&sa)->request & ~perms->allow; 1238c2ecf20Sopenharmony_ci AA_BUG(!aad(&sa)->request); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (aad(&sa)->request & perms->kill) 1268c2ecf20Sopenharmony_ci type = AUDIT_APPARMOR_KILL; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* quiet known rejects, assumes quiet and kill do not overlap */ 1298c2ecf20Sopenharmony_ci if ((aad(&sa)->request & perms->quiet) && 1308c2ecf20Sopenharmony_ci AUDIT_MODE(profile) != AUDIT_NOQUIET && 1318c2ecf20Sopenharmony_ci AUDIT_MODE(profile) != AUDIT_ALL) 1328c2ecf20Sopenharmony_ci aad(&sa)->request &= ~perms->quiet; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (!aad(&sa)->request) 1358c2ecf20Sopenharmony_ci return aad(&sa)->error; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci aad(&sa)->denied = aad(&sa)->request & ~perms->allow; 1398c2ecf20Sopenharmony_ci return aa_audit(type, profile, &sa, file_audit_cb); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/** 1438c2ecf20Sopenharmony_ci * is_deleted - test if a file has been completely unlinked 1448c2ecf20Sopenharmony_ci * @dentry: dentry of file to test for deletion (NOT NULL) 1458c2ecf20Sopenharmony_ci * 1468c2ecf20Sopenharmony_ci * Returns: true if deleted else false 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_cistatic inline bool is_deleted(struct dentry *dentry) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0) 1518c2ecf20Sopenharmony_ci return true; 1528c2ecf20Sopenharmony_ci return false; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int path_name(const char *op, struct aa_label *label, 1568c2ecf20Sopenharmony_ci const struct path *path, int flags, char *buffer, 1578c2ecf20Sopenharmony_ci const char **name, struct path_cond *cond, u32 request) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct aa_profile *profile; 1608c2ecf20Sopenharmony_ci const char *info = NULL; 1618c2ecf20Sopenharmony_ci int error; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci error = aa_path_name(path, flags, buffer, name, &info, 1648c2ecf20Sopenharmony_ci labels_profile(label)->disconnected); 1658c2ecf20Sopenharmony_ci if (error) { 1668c2ecf20Sopenharmony_ci fn_for_each_confined(label, profile, 1678c2ecf20Sopenharmony_ci aa_audit_file(profile, &nullperms, op, request, *name, 1688c2ecf20Sopenharmony_ci NULL, NULL, cond->uid, info, error)); 1698c2ecf20Sopenharmony_ci return error; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/** 1768c2ecf20Sopenharmony_ci * map_old_perms - map old file perms layout to the new layout 1778c2ecf20Sopenharmony_ci * @old: permission set in old mapping 1788c2ecf20Sopenharmony_ci * 1798c2ecf20Sopenharmony_ci * Returns: new permission mapping 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_cistatic u32 map_old_perms(u32 old) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci u32 new = old & 0xf; 1848c2ecf20Sopenharmony_ci if (old & MAY_READ) 1858c2ecf20Sopenharmony_ci new |= AA_MAY_GETATTR | AA_MAY_OPEN; 1868c2ecf20Sopenharmony_ci if (old & MAY_WRITE) 1878c2ecf20Sopenharmony_ci new |= AA_MAY_SETATTR | AA_MAY_CREATE | AA_MAY_DELETE | 1888c2ecf20Sopenharmony_ci AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_OPEN; 1898c2ecf20Sopenharmony_ci if (old & 0x10) 1908c2ecf20Sopenharmony_ci new |= AA_MAY_LINK; 1918c2ecf20Sopenharmony_ci /* the old mapping lock and link_subset flags where overlaid 1928c2ecf20Sopenharmony_ci * and use was determined by part of a pair that they were in 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci if (old & 0x20) 1958c2ecf20Sopenharmony_ci new |= AA_MAY_LOCK | AA_LINK_SUBSET; 1968c2ecf20Sopenharmony_ci if (old & 0x40) /* AA_EXEC_MMAP */ 1978c2ecf20Sopenharmony_ci new |= AA_EXEC_MMAP; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return new; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/** 2038c2ecf20Sopenharmony_ci * aa_compute_fperms - convert dfa compressed perms to internal perms 2048c2ecf20Sopenharmony_ci * @dfa: dfa to compute perms for (NOT NULL) 2058c2ecf20Sopenharmony_ci * @state: state in dfa 2068c2ecf20Sopenharmony_ci * @cond: conditions to consider (NOT NULL) 2078c2ecf20Sopenharmony_ci * 2088c2ecf20Sopenharmony_ci * TODO: convert from dfa + state to permission entry, do computation conversion 2098c2ecf20Sopenharmony_ci * at load time. 2108c2ecf20Sopenharmony_ci * 2118c2ecf20Sopenharmony_ci * Returns: computed permission set 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_cistruct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state, 2148c2ecf20Sopenharmony_ci struct path_cond *cond) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci /* FIXME: change over to new dfa format 2178c2ecf20Sopenharmony_ci * currently file perms are encoded in the dfa, new format 2188c2ecf20Sopenharmony_ci * splits the permissions from the dfa. This mapping can be 2198c2ecf20Sopenharmony_ci * done at profile load 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci struct aa_perms perms = { }; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (uid_eq(current_fsuid(), cond->uid)) { 2248c2ecf20Sopenharmony_ci perms.allow = map_old_perms(dfa_user_allow(dfa, state)); 2258c2ecf20Sopenharmony_ci perms.audit = map_old_perms(dfa_user_audit(dfa, state)); 2268c2ecf20Sopenharmony_ci perms.quiet = map_old_perms(dfa_user_quiet(dfa, state)); 2278c2ecf20Sopenharmony_ci perms.xindex = dfa_user_xindex(dfa, state); 2288c2ecf20Sopenharmony_ci } else { 2298c2ecf20Sopenharmony_ci perms.allow = map_old_perms(dfa_other_allow(dfa, state)); 2308c2ecf20Sopenharmony_ci perms.audit = map_old_perms(dfa_other_audit(dfa, state)); 2318c2ecf20Sopenharmony_ci perms.quiet = map_old_perms(dfa_other_quiet(dfa, state)); 2328c2ecf20Sopenharmony_ci perms.xindex = dfa_other_xindex(dfa, state); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci perms.allow |= AA_MAY_GETATTR; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* change_profile wasn't determined by ownership in old mapping */ 2378c2ecf20Sopenharmony_ci if (ACCEPT_TABLE(dfa)[state] & 0x80000000) 2388c2ecf20Sopenharmony_ci perms.allow |= AA_MAY_CHANGE_PROFILE; 2398c2ecf20Sopenharmony_ci if (ACCEPT_TABLE(dfa)[state] & 0x40000000) 2408c2ecf20Sopenharmony_ci perms.allow |= AA_MAY_ONEXEC; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return perms; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/** 2468c2ecf20Sopenharmony_ci * aa_str_perms - find permission that match @name 2478c2ecf20Sopenharmony_ci * @dfa: to match against (MAYBE NULL) 2488c2ecf20Sopenharmony_ci * @state: state to start matching in 2498c2ecf20Sopenharmony_ci * @name: string to match against dfa (NOT NULL) 2508c2ecf20Sopenharmony_ci * @cond: conditions to consider for permission set computation (NOT NULL) 2518c2ecf20Sopenharmony_ci * @perms: Returns - the permissions found when matching @name 2528c2ecf20Sopenharmony_ci * 2538c2ecf20Sopenharmony_ci * Returns: the final state in @dfa when beginning @start and walking @name 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ciunsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, 2568c2ecf20Sopenharmony_ci const char *name, struct path_cond *cond, 2578c2ecf20Sopenharmony_ci struct aa_perms *perms) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci unsigned int state; 2608c2ecf20Sopenharmony_ci state = aa_dfa_match(dfa, start, name); 2618c2ecf20Sopenharmony_ci *perms = aa_compute_fperms(dfa, state, cond); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return state; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ciint __aa_path_perm(const char *op, struct aa_profile *profile, const char *name, 2678c2ecf20Sopenharmony_ci u32 request, struct path_cond *cond, int flags, 2688c2ecf20Sopenharmony_ci struct aa_perms *perms) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci int e = 0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (profile_unconfined(profile)) 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms); 2758c2ecf20Sopenharmony_ci if (request & ~perms->allow) 2768c2ecf20Sopenharmony_ci e = -EACCES; 2778c2ecf20Sopenharmony_ci return aa_audit_file(profile, perms, op, request, name, NULL, NULL, 2788c2ecf20Sopenharmony_ci cond->uid, NULL, e); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int profile_path_perm(const char *op, struct aa_profile *profile, 2838c2ecf20Sopenharmony_ci const struct path *path, char *buffer, u32 request, 2848c2ecf20Sopenharmony_ci struct path_cond *cond, int flags, 2858c2ecf20Sopenharmony_ci struct aa_perms *perms) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci const char *name; 2888c2ecf20Sopenharmony_ci int error; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (profile_unconfined(profile)) 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci error = path_name(op, &profile->label, path, 2948c2ecf20Sopenharmony_ci flags | profile->path_flags, buffer, &name, cond, 2958c2ecf20Sopenharmony_ci request); 2968c2ecf20Sopenharmony_ci if (error) 2978c2ecf20Sopenharmony_ci return error; 2988c2ecf20Sopenharmony_ci return __aa_path_perm(op, profile, name, request, cond, flags, 2998c2ecf20Sopenharmony_ci perms); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/** 3038c2ecf20Sopenharmony_ci * aa_path_perm - do permissions check & audit for @path 3048c2ecf20Sopenharmony_ci * @op: operation being checked 3058c2ecf20Sopenharmony_ci * @label: profile being enforced (NOT NULL) 3068c2ecf20Sopenharmony_ci * @path: path to check permissions of (NOT NULL) 3078c2ecf20Sopenharmony_ci * @flags: any additional path flags beyond what the profile specifies 3088c2ecf20Sopenharmony_ci * @request: requested permissions 3098c2ecf20Sopenharmony_ci * @cond: conditional info for this request (NOT NULL) 3108c2ecf20Sopenharmony_ci * 3118c2ecf20Sopenharmony_ci * Returns: %0 else error if access denied or other error 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ciint aa_path_perm(const char *op, struct aa_label *label, 3148c2ecf20Sopenharmony_ci const struct path *path, int flags, u32 request, 3158c2ecf20Sopenharmony_ci struct path_cond *cond) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct aa_perms perms = {}; 3188c2ecf20Sopenharmony_ci struct aa_profile *profile; 3198c2ecf20Sopenharmony_ci char *buffer = NULL; 3208c2ecf20Sopenharmony_ci int error; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci flags |= PATH_DELEGATE_DELETED | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 3238c2ecf20Sopenharmony_ci 0); 3248c2ecf20Sopenharmony_ci buffer = aa_get_buffer(false); 3258c2ecf20Sopenharmony_ci if (!buffer) 3268c2ecf20Sopenharmony_ci return -ENOMEM; 3278c2ecf20Sopenharmony_ci error = fn_for_each_confined(label, profile, 3288c2ecf20Sopenharmony_ci profile_path_perm(op, profile, path, buffer, request, 3298c2ecf20Sopenharmony_ci cond, flags, &perms)); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci aa_put_buffer(buffer); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return error; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/** 3378c2ecf20Sopenharmony_ci * xindex_is_subset - helper for aa_path_link 3388c2ecf20Sopenharmony_ci * @link: link permission set 3398c2ecf20Sopenharmony_ci * @target: target permission set 3408c2ecf20Sopenharmony_ci * 3418c2ecf20Sopenharmony_ci * test target x permissions are equal OR a subset of link x permissions 3428c2ecf20Sopenharmony_ci * this is done as part of the subset test, where a hardlink must have 3438c2ecf20Sopenharmony_ci * a subset of permissions that the target has. 3448c2ecf20Sopenharmony_ci * 3458c2ecf20Sopenharmony_ci * Returns: true if subset else false 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_cistatic inline bool xindex_is_subset(u32 link, u32 target) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci if (((link & ~AA_X_UNSAFE) != (target & ~AA_X_UNSAFE)) || 3508c2ecf20Sopenharmony_ci ((link & AA_X_UNSAFE) && !(target & AA_X_UNSAFE))) 3518c2ecf20Sopenharmony_ci return false; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return true; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int profile_path_link(struct aa_profile *profile, 3578c2ecf20Sopenharmony_ci const struct path *link, char *buffer, 3588c2ecf20Sopenharmony_ci const struct path *target, char *buffer2, 3598c2ecf20Sopenharmony_ci struct path_cond *cond) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci const char *lname, *tname = NULL; 3628c2ecf20Sopenharmony_ci struct aa_perms lperms = {}, perms; 3638c2ecf20Sopenharmony_ci const char *info = NULL; 3648c2ecf20Sopenharmony_ci u32 request = AA_MAY_LINK; 3658c2ecf20Sopenharmony_ci unsigned int state; 3668c2ecf20Sopenharmony_ci int error; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci error = path_name(OP_LINK, &profile->label, link, profile->path_flags, 3698c2ecf20Sopenharmony_ci buffer, &lname, cond, AA_MAY_LINK); 3708c2ecf20Sopenharmony_ci if (error) 3718c2ecf20Sopenharmony_ci goto audit; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* buffer2 freed below, tname is pointer in buffer2 */ 3748c2ecf20Sopenharmony_ci error = path_name(OP_LINK, &profile->label, target, profile->path_flags, 3758c2ecf20Sopenharmony_ci buffer2, &tname, cond, AA_MAY_LINK); 3768c2ecf20Sopenharmony_ci if (error) 3778c2ecf20Sopenharmony_ci goto audit; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci error = -EACCES; 3808c2ecf20Sopenharmony_ci /* aa_str_perms - handles the case of the dfa being NULL */ 3818c2ecf20Sopenharmony_ci state = aa_str_perms(profile->file.dfa, profile->file.start, lname, 3828c2ecf20Sopenharmony_ci cond, &lperms); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (!(lperms.allow & AA_MAY_LINK)) 3858c2ecf20Sopenharmony_ci goto audit; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* test to see if target can be paired with link */ 3888c2ecf20Sopenharmony_ci state = aa_dfa_null_transition(profile->file.dfa, state); 3898c2ecf20Sopenharmony_ci aa_str_perms(profile->file.dfa, state, tname, cond, &perms); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* force audit/quiet masks for link are stored in the second entry 3928c2ecf20Sopenharmony_ci * in the link pair. 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci lperms.audit = perms.audit; 3958c2ecf20Sopenharmony_ci lperms.quiet = perms.quiet; 3968c2ecf20Sopenharmony_ci lperms.kill = perms.kill; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (!(perms.allow & AA_MAY_LINK)) { 3998c2ecf20Sopenharmony_ci info = "target restricted"; 4008c2ecf20Sopenharmony_ci lperms = perms; 4018c2ecf20Sopenharmony_ci goto audit; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* done if link subset test is not required */ 4058c2ecf20Sopenharmony_ci if (!(perms.allow & AA_LINK_SUBSET)) 4068c2ecf20Sopenharmony_ci goto done_tests; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* Do link perm subset test requiring allowed permission on link are 4098c2ecf20Sopenharmony_ci * a subset of the allowed permissions on target. 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci aa_str_perms(profile->file.dfa, profile->file.start, tname, cond, 4128c2ecf20Sopenharmony_ci &perms); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* AA_MAY_LINK is not considered in the subset test */ 4158c2ecf20Sopenharmony_ci request = lperms.allow & ~AA_MAY_LINK; 4168c2ecf20Sopenharmony_ci lperms.allow &= perms.allow | AA_MAY_LINK; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci request |= AA_AUDIT_FILE_MASK & (lperms.allow & ~perms.allow); 4198c2ecf20Sopenharmony_ci if (request & ~lperms.allow) { 4208c2ecf20Sopenharmony_ci goto audit; 4218c2ecf20Sopenharmony_ci } else if ((lperms.allow & MAY_EXEC) && 4228c2ecf20Sopenharmony_ci !xindex_is_subset(lperms.xindex, perms.xindex)) { 4238c2ecf20Sopenharmony_ci lperms.allow &= ~MAY_EXEC; 4248c2ecf20Sopenharmony_ci request |= MAY_EXEC; 4258c2ecf20Sopenharmony_ci info = "link not subset of target"; 4268c2ecf20Sopenharmony_ci goto audit; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cidone_tests: 4308c2ecf20Sopenharmony_ci error = 0; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ciaudit: 4338c2ecf20Sopenharmony_ci return aa_audit_file(profile, &lperms, OP_LINK, request, lname, tname, 4348c2ecf20Sopenharmony_ci NULL, cond->uid, info, error); 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci/** 4388c2ecf20Sopenharmony_ci * aa_path_link - Handle hard link permission check 4398c2ecf20Sopenharmony_ci * @label: the label being enforced (NOT NULL) 4408c2ecf20Sopenharmony_ci * @old_dentry: the target dentry (NOT NULL) 4418c2ecf20Sopenharmony_ci * @new_dir: directory the new link will be created in (NOT NULL) 4428c2ecf20Sopenharmony_ci * @new_dentry: the link being created (NOT NULL) 4438c2ecf20Sopenharmony_ci * 4448c2ecf20Sopenharmony_ci * Handle the permission test for a link & target pair. Permission 4458c2ecf20Sopenharmony_ci * is encoded as a pair where the link permission is determined 4468c2ecf20Sopenharmony_ci * first, and if allowed, the target is tested. The target test 4478c2ecf20Sopenharmony_ci * is done from the point of the link match (not start of DFA) 4488c2ecf20Sopenharmony_ci * making the target permission dependent on the link permission match. 4498c2ecf20Sopenharmony_ci * 4508c2ecf20Sopenharmony_ci * The subset test if required forces that permissions granted 4518c2ecf20Sopenharmony_ci * on link are a subset of the permission granted to target. 4528c2ecf20Sopenharmony_ci * 4538c2ecf20Sopenharmony_ci * Returns: %0 if allowed else error 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_ciint aa_path_link(struct aa_label *label, struct dentry *old_dentry, 4568c2ecf20Sopenharmony_ci const struct path *new_dir, struct dentry *new_dentry) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry }; 4598c2ecf20Sopenharmony_ci struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry }; 4608c2ecf20Sopenharmony_ci struct path_cond cond = { 4618c2ecf20Sopenharmony_ci d_backing_inode(old_dentry)->i_uid, 4628c2ecf20Sopenharmony_ci d_backing_inode(old_dentry)->i_mode 4638c2ecf20Sopenharmony_ci }; 4648c2ecf20Sopenharmony_ci char *buffer = NULL, *buffer2 = NULL; 4658c2ecf20Sopenharmony_ci struct aa_profile *profile; 4668c2ecf20Sopenharmony_ci int error; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* buffer freed below, lname is pointer in buffer */ 4698c2ecf20Sopenharmony_ci buffer = aa_get_buffer(false); 4708c2ecf20Sopenharmony_ci buffer2 = aa_get_buffer(false); 4718c2ecf20Sopenharmony_ci error = -ENOMEM; 4728c2ecf20Sopenharmony_ci if (!buffer || !buffer2) 4738c2ecf20Sopenharmony_ci goto out; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci error = fn_for_each_confined(label, profile, 4768c2ecf20Sopenharmony_ci profile_path_link(profile, &link, buffer, &target, 4778c2ecf20Sopenharmony_ci buffer2, &cond)); 4788c2ecf20Sopenharmony_ciout: 4798c2ecf20Sopenharmony_ci aa_put_buffer(buffer); 4808c2ecf20Sopenharmony_ci aa_put_buffer(buffer2); 4818c2ecf20Sopenharmony_ci return error; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic void update_file_ctx(struct aa_file_ctx *fctx, struct aa_label *label, 4858c2ecf20Sopenharmony_ci u32 request) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct aa_label *l, *old; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* update caching of label on file_ctx */ 4908c2ecf20Sopenharmony_ci spin_lock(&fctx->lock); 4918c2ecf20Sopenharmony_ci old = rcu_dereference_protected(fctx->label, 4928c2ecf20Sopenharmony_ci lockdep_is_held(&fctx->lock)); 4938c2ecf20Sopenharmony_ci l = aa_label_merge(old, label, GFP_ATOMIC); 4948c2ecf20Sopenharmony_ci if (l) { 4958c2ecf20Sopenharmony_ci if (l != old) { 4968c2ecf20Sopenharmony_ci rcu_assign_pointer(fctx->label, l); 4978c2ecf20Sopenharmony_ci aa_put_label(old); 4988c2ecf20Sopenharmony_ci } else 4998c2ecf20Sopenharmony_ci aa_put_label(l); 5008c2ecf20Sopenharmony_ci fctx->allow |= request; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci spin_unlock(&fctx->lock); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic int __file_path_perm(const char *op, struct aa_label *label, 5068c2ecf20Sopenharmony_ci struct aa_label *flabel, struct file *file, 5078c2ecf20Sopenharmony_ci u32 request, u32 denied, bool in_atomic) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct aa_profile *profile; 5108c2ecf20Sopenharmony_ci struct aa_perms perms = {}; 5118c2ecf20Sopenharmony_ci struct path_cond cond = { 5128c2ecf20Sopenharmony_ci .uid = file_inode(file)->i_uid, 5138c2ecf20Sopenharmony_ci .mode = file_inode(file)->i_mode 5148c2ecf20Sopenharmony_ci }; 5158c2ecf20Sopenharmony_ci char *buffer; 5168c2ecf20Sopenharmony_ci int flags, error; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* revalidation due to label out of date. No revocation at this time */ 5198c2ecf20Sopenharmony_ci if (!denied && aa_label_is_subset(flabel, label)) 5208c2ecf20Sopenharmony_ci /* TODO: check for revocation on stale profiles */ 5218c2ecf20Sopenharmony_ci return 0; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci flags = PATH_DELEGATE_DELETED | (S_ISDIR(cond.mode) ? PATH_IS_DIR : 0); 5248c2ecf20Sopenharmony_ci buffer = aa_get_buffer(in_atomic); 5258c2ecf20Sopenharmony_ci if (!buffer) 5268c2ecf20Sopenharmony_ci return -ENOMEM; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* check every profile in task label not in current cache */ 5298c2ecf20Sopenharmony_ci error = fn_for_each_not_in_set(flabel, label, profile, 5308c2ecf20Sopenharmony_ci profile_path_perm(op, profile, &file->f_path, buffer, 5318c2ecf20Sopenharmony_ci request, &cond, flags, &perms)); 5328c2ecf20Sopenharmony_ci if (denied && !error) { 5338c2ecf20Sopenharmony_ci /* 5348c2ecf20Sopenharmony_ci * check every profile in file label that was not tested 5358c2ecf20Sopenharmony_ci * in the initial check above. 5368c2ecf20Sopenharmony_ci * 5378c2ecf20Sopenharmony_ci * TODO: cache full perms so this only happens because of 5388c2ecf20Sopenharmony_ci * conditionals 5398c2ecf20Sopenharmony_ci * TODO: don't audit here 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_ci if (label == flabel) 5428c2ecf20Sopenharmony_ci error = fn_for_each(label, profile, 5438c2ecf20Sopenharmony_ci profile_path_perm(op, profile, &file->f_path, 5448c2ecf20Sopenharmony_ci buffer, request, &cond, flags, 5458c2ecf20Sopenharmony_ci &perms)); 5468c2ecf20Sopenharmony_ci else 5478c2ecf20Sopenharmony_ci error = fn_for_each_not_in_set(label, flabel, profile, 5488c2ecf20Sopenharmony_ci profile_path_perm(op, profile, &file->f_path, 5498c2ecf20Sopenharmony_ci buffer, request, &cond, flags, 5508c2ecf20Sopenharmony_ci &perms)); 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci if (!error) 5538c2ecf20Sopenharmony_ci update_file_ctx(file_ctx(file), label, request); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci aa_put_buffer(buffer); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci return error; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic int __file_sock_perm(const char *op, struct aa_label *label, 5618c2ecf20Sopenharmony_ci struct aa_label *flabel, struct file *file, 5628c2ecf20Sopenharmony_ci u32 request, u32 denied) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci struct socket *sock = (struct socket *) file->private_data; 5658c2ecf20Sopenharmony_ci int error; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci AA_BUG(!sock); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* revalidation due to label out of date. No revocation at this time */ 5708c2ecf20Sopenharmony_ci if (!denied && aa_label_is_subset(flabel, label)) 5718c2ecf20Sopenharmony_ci return 0; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* TODO: improve to skip profiles cached in flabel */ 5748c2ecf20Sopenharmony_ci error = aa_sock_file_perm(label, op, request, sock); 5758c2ecf20Sopenharmony_ci if (denied) { 5768c2ecf20Sopenharmony_ci /* TODO: improve to skip profiles checked above */ 5778c2ecf20Sopenharmony_ci /* check every profile in file label to is cached */ 5788c2ecf20Sopenharmony_ci last_error(error, aa_sock_file_perm(flabel, op, request, sock)); 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci if (!error) 5818c2ecf20Sopenharmony_ci update_file_ctx(file_ctx(file), label, request); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci return error; 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci/** 5878c2ecf20Sopenharmony_ci * aa_file_perm - do permission revalidation check & audit for @file 5888c2ecf20Sopenharmony_ci * @op: operation being checked 5898c2ecf20Sopenharmony_ci * @label: label being enforced (NOT NULL) 5908c2ecf20Sopenharmony_ci * @file: file to revalidate access permissions on (NOT NULL) 5918c2ecf20Sopenharmony_ci * @request: requested permissions 5928c2ecf20Sopenharmony_ci * @in_atomic: whether allocations need to be done in atomic context 5938c2ecf20Sopenharmony_ci * 5948c2ecf20Sopenharmony_ci * Returns: %0 if access allowed else error 5958c2ecf20Sopenharmony_ci */ 5968c2ecf20Sopenharmony_ciint aa_file_perm(const char *op, struct aa_label *label, struct file *file, 5978c2ecf20Sopenharmony_ci u32 request, bool in_atomic) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct aa_file_ctx *fctx; 6008c2ecf20Sopenharmony_ci struct aa_label *flabel; 6018c2ecf20Sopenharmony_ci u32 denied; 6028c2ecf20Sopenharmony_ci int error = 0; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci AA_BUG(!label); 6058c2ecf20Sopenharmony_ci AA_BUG(!file); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci fctx = file_ctx(file); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci rcu_read_lock(); 6108c2ecf20Sopenharmony_ci flabel = rcu_dereference(fctx->label); 6118c2ecf20Sopenharmony_ci AA_BUG(!flabel); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* revalidate access, if task is unconfined, or the cached cred 6148c2ecf20Sopenharmony_ci * doesn't match or if the request is for more permissions than 6158c2ecf20Sopenharmony_ci * was granted. 6168c2ecf20Sopenharmony_ci * 6178c2ecf20Sopenharmony_ci * Note: the test for !unconfined(flabel) is to handle file 6188c2ecf20Sopenharmony_ci * delegation from unconfined tasks 6198c2ecf20Sopenharmony_ci */ 6208c2ecf20Sopenharmony_ci denied = request & ~fctx->allow; 6218c2ecf20Sopenharmony_ci if (unconfined(label) || unconfined(flabel) || 6228c2ecf20Sopenharmony_ci (!denied && aa_label_is_subset(flabel, label))) { 6238c2ecf20Sopenharmony_ci rcu_read_unlock(); 6248c2ecf20Sopenharmony_ci goto done; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci flabel = aa_get_newest_label(flabel); 6288c2ecf20Sopenharmony_ci rcu_read_unlock(); 6298c2ecf20Sopenharmony_ci /* TODO: label cross check */ 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (file->f_path.mnt && path_mediated_fs(file->f_path.dentry)) 6328c2ecf20Sopenharmony_ci error = __file_path_perm(op, label, flabel, file, request, 6338c2ecf20Sopenharmony_ci denied, in_atomic); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci else if (S_ISSOCK(file_inode(file)->i_mode)) 6368c2ecf20Sopenharmony_ci error = __file_sock_perm(op, label, flabel, file, request, 6378c2ecf20Sopenharmony_ci denied); 6388c2ecf20Sopenharmony_ci aa_put_label(flabel); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cidone: 6418c2ecf20Sopenharmony_ci return error; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic void revalidate_tty(struct aa_label *label) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct tty_struct *tty; 6478c2ecf20Sopenharmony_ci int drop_tty = 0; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci tty = get_current_tty(); 6508c2ecf20Sopenharmony_ci if (!tty) 6518c2ecf20Sopenharmony_ci return; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci spin_lock(&tty->files_lock); 6548c2ecf20Sopenharmony_ci if (!list_empty(&tty->tty_files)) { 6558c2ecf20Sopenharmony_ci struct tty_file_private *file_priv; 6568c2ecf20Sopenharmony_ci struct file *file; 6578c2ecf20Sopenharmony_ci /* TODO: Revalidate access to controlling tty. */ 6588c2ecf20Sopenharmony_ci file_priv = list_first_entry(&tty->tty_files, 6598c2ecf20Sopenharmony_ci struct tty_file_private, list); 6608c2ecf20Sopenharmony_ci file = file_priv->file; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (aa_file_perm(OP_INHERIT, label, file, MAY_READ | MAY_WRITE, 6638c2ecf20Sopenharmony_ci IN_ATOMIC)) 6648c2ecf20Sopenharmony_ci drop_tty = 1; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci spin_unlock(&tty->files_lock); 6678c2ecf20Sopenharmony_ci tty_kref_put(tty); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci if (drop_tty) 6708c2ecf20Sopenharmony_ci no_tty(); 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic int match_file(const void *p, struct file *file, unsigned int fd) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct aa_label *label = (struct aa_label *)p; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (aa_file_perm(OP_INHERIT, label, file, aa_map_file_to_perms(file), 6788c2ecf20Sopenharmony_ci IN_ATOMIC)) 6798c2ecf20Sopenharmony_ci return fd + 1; 6808c2ecf20Sopenharmony_ci return 0; 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci/* based on selinux's flush_unauthorized_files */ 6858c2ecf20Sopenharmony_civoid aa_inherit_files(const struct cred *cred, struct files_struct *files) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci struct aa_label *label = aa_get_newest_cred_label(cred); 6888c2ecf20Sopenharmony_ci struct file *devnull = NULL; 6898c2ecf20Sopenharmony_ci unsigned int n; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci revalidate_tty(label); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci /* Revalidate access to inherited open files. */ 6948c2ecf20Sopenharmony_ci n = iterate_fd(files, 0, match_file, label); 6958c2ecf20Sopenharmony_ci if (!n) /* none found? */ 6968c2ecf20Sopenharmony_ci goto out; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci devnull = dentry_open(&aa_null, O_RDWR, cred); 6998c2ecf20Sopenharmony_ci if (IS_ERR(devnull)) 7008c2ecf20Sopenharmony_ci devnull = NULL; 7018c2ecf20Sopenharmony_ci /* replace all the matching ones with this */ 7028c2ecf20Sopenharmony_ci do { 7038c2ecf20Sopenharmony_ci replace_fd(n - 1, devnull, 0); 7048c2ecf20Sopenharmony_ci } while ((n = iterate_fd(files, n, match_file, label)) != 0); 7058c2ecf20Sopenharmony_ci if (devnull) 7068c2ecf20Sopenharmony_ci fput(devnull); 7078c2ecf20Sopenharmony_ciout: 7088c2ecf20Sopenharmony_ci aa_put_label(label); 7098c2ecf20Sopenharmony_ci} 710