18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * AppArmor security module 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This file contains AppArmor resource mediation and attachment 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/audit.h> 128c2ecf20Sopenharmony_ci#include <linux/security.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "include/audit.h" 158c2ecf20Sopenharmony_ci#include "include/cred.h" 168c2ecf20Sopenharmony_ci#include "include/resource.h" 178c2ecf20Sopenharmony_ci#include "include/policy.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * Table of rlimit names: we generate it from resource.h. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci#include "rlim_names.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct aa_sfs_entry aa_sfs_entry_rlimit[] = { 258c2ecf20Sopenharmony_ci AA_SFS_FILE_STRING("mask", AA_SFS_RLIMIT_MASK), 268c2ecf20Sopenharmony_ci { } 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* audit callback for resource specific fields */ 308c2ecf20Sopenharmony_cistatic void audit_cb(struct audit_buffer *ab, void *va) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct common_audit_data *sa = va; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci audit_log_format(ab, " rlimit=%s value=%lu", 358c2ecf20Sopenharmony_ci rlim_names[aad(sa)->rlim.rlim], aad(sa)->rlim.max); 368c2ecf20Sopenharmony_ci if (aad(sa)->peer) { 378c2ecf20Sopenharmony_ci audit_log_format(ab, " peer="); 388c2ecf20Sopenharmony_ci aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, 398c2ecf20Sopenharmony_ci FLAGS_NONE, GFP_ATOMIC); 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/** 448c2ecf20Sopenharmony_ci * audit_resource - audit setting resource limit 458c2ecf20Sopenharmony_ci * @profile: profile being enforced (NOT NULL) 468c2ecf20Sopenharmony_ci * @resource: rlimit being auditing 478c2ecf20Sopenharmony_ci * @value: value being set 488c2ecf20Sopenharmony_ci * @error: error value 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * Returns: 0 or sa->error else other error code on failure 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_cistatic int audit_resource(struct aa_profile *profile, unsigned int resource, 538c2ecf20Sopenharmony_ci unsigned long value, struct aa_label *peer, 548c2ecf20Sopenharmony_ci const char *info, int error) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETRLIMIT); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci aad(&sa)->rlim.rlim = resource; 598c2ecf20Sopenharmony_ci aad(&sa)->rlim.max = value; 608c2ecf20Sopenharmony_ci aad(&sa)->peer = peer; 618c2ecf20Sopenharmony_ci aad(&sa)->info = info; 628c2ecf20Sopenharmony_ci aad(&sa)->error = error; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa, audit_cb); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/** 688c2ecf20Sopenharmony_ci * aa_map_resouce - map compiled policy resource to internal # 698c2ecf20Sopenharmony_ci * @resource: flattened policy resource number 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * Returns: resource # for the current architecture. 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * rlimit resource can vary based on architecture, map the compiled policy 748c2ecf20Sopenharmony_ci * resource # to the internal representation for the architecture. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ciint aa_map_resource(int resource) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci return rlim_map[resource]; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int profile_setrlimit(struct aa_profile *profile, unsigned int resource, 828c2ecf20Sopenharmony_ci struct rlimit *new_rlim) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci int e = 0; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (profile->rlimits.mask & (1 << resource) && new_rlim->rlim_max > 878c2ecf20Sopenharmony_ci profile->rlimits.limits[resource].rlim_max) 888c2ecf20Sopenharmony_ci e = -EACCES; 898c2ecf20Sopenharmony_ci return audit_resource(profile, resource, new_rlim->rlim_max, NULL, NULL, 908c2ecf20Sopenharmony_ci e); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/** 948c2ecf20Sopenharmony_ci * aa_task_setrlimit - test permission to set an rlimit 958c2ecf20Sopenharmony_ci * @label - label confining the task (NOT NULL) 968c2ecf20Sopenharmony_ci * @task - task the resource is being set on 978c2ecf20Sopenharmony_ci * @resource - the resource being set 988c2ecf20Sopenharmony_ci * @new_rlim - the new resource limit (NOT NULL) 998c2ecf20Sopenharmony_ci * 1008c2ecf20Sopenharmony_ci * Control raising the processes hard limit. 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * Returns: 0 or error code if setting resource failed 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ciint aa_task_setrlimit(struct aa_label *label, struct task_struct *task, 1058c2ecf20Sopenharmony_ci unsigned int resource, struct rlimit *new_rlim) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct aa_profile *profile; 1088c2ecf20Sopenharmony_ci struct aa_label *peer; 1098c2ecf20Sopenharmony_ci int error = 0; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci rcu_read_lock(); 1128c2ecf20Sopenharmony_ci peer = aa_get_newest_cred_label(__task_cred(task)); 1138c2ecf20Sopenharmony_ci rcu_read_unlock(); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* TODO: extend resource control to handle other (non current) 1168c2ecf20Sopenharmony_ci * profiles. AppArmor rules currently have the implicit assumption 1178c2ecf20Sopenharmony_ci * that the task is setting the resource of a task confined with 1188c2ecf20Sopenharmony_ci * the same profile or that the task setting the resource of another 1198c2ecf20Sopenharmony_ci * task has CAP_SYS_RESOURCE. 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (label != peer && 1238c2ecf20Sopenharmony_ci aa_capable(label, CAP_SYS_RESOURCE, CAP_OPT_NOAUDIT) != 0) 1248c2ecf20Sopenharmony_ci error = fn_for_each(label, profile, 1258c2ecf20Sopenharmony_ci audit_resource(profile, resource, 1268c2ecf20Sopenharmony_ci new_rlim->rlim_max, peer, 1278c2ecf20Sopenharmony_ci "cap_sys_resource", -EACCES)); 1288c2ecf20Sopenharmony_ci else 1298c2ecf20Sopenharmony_ci error = fn_for_each_confined(label, profile, 1308c2ecf20Sopenharmony_ci profile_setrlimit(profile, resource, new_rlim)); 1318c2ecf20Sopenharmony_ci aa_put_label(peer); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return error; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/** 1378c2ecf20Sopenharmony_ci * __aa_transition_rlimits - apply new profile rlimits 1388c2ecf20Sopenharmony_ci * @old_l: old label on task (NOT NULL) 1398c2ecf20Sopenharmony_ci * @new_l: new label with rlimits to apply (NOT NULL) 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_civoid __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci unsigned int mask = 0; 1448c2ecf20Sopenharmony_ci struct rlimit *rlim, *initrlim; 1458c2ecf20Sopenharmony_ci struct aa_profile *old, *new; 1468c2ecf20Sopenharmony_ci struct label_it i; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci old = labels_profile(old_l); 1498c2ecf20Sopenharmony_ci new = labels_profile(new_l); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* for any rlimits the profile controlled, reset the soft limit 1528c2ecf20Sopenharmony_ci * to the lesser of the tasks hard limit and the init tasks soft limit 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci label_for_each_confined(i, old_l, old) { 1558c2ecf20Sopenharmony_ci if (old->rlimits.mask) { 1568c2ecf20Sopenharmony_ci int j; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci for (j = 0, mask = 1; j < RLIM_NLIMITS; j++, 1598c2ecf20Sopenharmony_ci mask <<= 1) { 1608c2ecf20Sopenharmony_ci if (old->rlimits.mask & mask) { 1618c2ecf20Sopenharmony_ci rlim = current->signal->rlim + j; 1628c2ecf20Sopenharmony_ci initrlim = init_task.signal->rlim + j; 1638c2ecf20Sopenharmony_ci rlim->rlim_cur = min(rlim->rlim_max, 1648c2ecf20Sopenharmony_ci initrlim->rlim_cur); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* set any new hard limits as dictated by the new profile */ 1718c2ecf20Sopenharmony_ci label_for_each_confined(i, new_l, new) { 1728c2ecf20Sopenharmony_ci int j; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (!new->rlimits.mask) 1758c2ecf20Sopenharmony_ci continue; 1768c2ecf20Sopenharmony_ci for (j = 0, mask = 1; j < RLIM_NLIMITS; j++, mask <<= 1) { 1778c2ecf20Sopenharmony_ci if (!(new->rlimits.mask & mask)) 1788c2ecf20Sopenharmony_ci continue; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci rlim = current->signal->rlim + j; 1818c2ecf20Sopenharmony_ci rlim->rlim_max = min(rlim->rlim_max, 1828c2ecf20Sopenharmony_ci new->rlimits.limits[j].rlim_max); 1838c2ecf20Sopenharmony_ci /* soft limit should not exceed hard limit */ 1848c2ecf20Sopenharmony_ci rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci} 188