xref: /openbmc/linux/security/apparmor/resource.c (revision 690f33e1)
1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20ed3b28aSJohn Johansen /*
30ed3b28aSJohn Johansen  * AppArmor security module
40ed3b28aSJohn Johansen  *
50ed3b28aSJohn Johansen  * This file contains AppArmor resource mediation and attachment
60ed3b28aSJohn Johansen  *
70ed3b28aSJohn Johansen  * Copyright (C) 1998-2008 Novell/SUSE
80ed3b28aSJohn Johansen  * Copyright 2009-2010 Canonical Ltd.
90ed3b28aSJohn Johansen  */
100ed3b28aSJohn Johansen 
110ed3b28aSJohn Johansen #include <linux/audit.h>
1286b92cb7SJohn Johansen #include <linux/security.h>
130ed3b28aSJohn Johansen 
140ed3b28aSJohn Johansen #include "include/audit.h"
15d8889d49SJohn Johansen #include "include/cred.h"
160ed3b28aSJohn Johansen #include "include/resource.h"
170ed3b28aSJohn Johansen #include "include/policy.h"
180ed3b28aSJohn Johansen 
190ed3b28aSJohn Johansen /*
200ed3b28aSJohn Johansen  * Table of rlimit names: we generate it from resource.h.
210ed3b28aSJohn Johansen  */
220ed3b28aSJohn Johansen #include "rlim_names.h"
230ed3b28aSJohn Johansen 
24c97204baSJohn Johansen struct aa_sfs_entry aa_sfs_entry_rlimit[] = {
25c97204baSJohn Johansen 	AA_SFS_FILE_STRING("mask", AA_SFS_RLIMIT_MASK),
26d384b0a1SKees Cook 	{ }
27d384b0a1SKees Cook };
28d384b0a1SKees Cook 
290ed3b28aSJohn Johansen /* audit callback for resource specific fields */
audit_cb(struct audit_buffer * ab,void * va)300ed3b28aSJohn Johansen static void audit_cb(struct audit_buffer *ab, void *va)
310ed3b28aSJohn Johansen {
320ed3b28aSJohn Johansen 	struct common_audit_data *sa = va;
33c57bc80fSJohn Johansen 	struct apparmor_audit_data *ad = aad(sa);
340ed3b28aSJohn Johansen 
350ed3b28aSJohn Johansen 	audit_log_format(ab, " rlimit=%s value=%lu",
36c57bc80fSJohn Johansen 			 rlim_names[ad->rlim.rlim], ad->rlim.max);
37c57bc80fSJohn Johansen 	if (ad->peer) {
3886b92cb7SJohn Johansen 		audit_log_format(ab, " peer=");
3930b3669dSJohn Johansen 		aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer,
4086b92cb7SJohn Johansen 				FLAGS_NONE, GFP_ATOMIC);
4186b92cb7SJohn Johansen 	}
420ed3b28aSJohn Johansen }
430ed3b28aSJohn Johansen 
440ed3b28aSJohn Johansen /**
450ed3b28aSJohn Johansen  * audit_resource - audit setting resource limit
46*690f33e1SJohn Johansen  * @subj_cred: cred setting the resource
470ed3b28aSJohn Johansen  * @profile: profile being enforced  (NOT NULL)
485933a627SColin Ian King  * @resource: rlimit being auditing
490ed3b28aSJohn Johansen  * @value: value being set
501f2bc06aSGaosheng Cui  * @peer: aa_albel of the task being set
511f2bc06aSGaosheng Cui  * @info: info being auditing
520ed3b28aSJohn Johansen  * @error: error value
530ed3b28aSJohn Johansen  *
54c57bc80fSJohn Johansen  * Returns: 0 or ad->error else other error code on failure
550ed3b28aSJohn Johansen  */
audit_resource(const struct cred * subj_cred,struct aa_profile * profile,unsigned int resource,unsigned long value,struct aa_label * peer,const char * info,int error)56*690f33e1SJohn Johansen static int audit_resource(const struct cred *subj_cred,
57*690f33e1SJohn Johansen 			  struct aa_profile *profile, unsigned int resource,
5886b92cb7SJohn Johansen 			  unsigned long value, struct aa_label *peer,
5986b92cb7SJohn Johansen 			  const char *info, int error)
600ed3b28aSJohn Johansen {
61c57bc80fSJohn Johansen 	DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_RLIMITS,
628c4b785aSJohn Johansen 			  OP_SETRLIMIT);
630ed3b28aSJohn Johansen 
64*690f33e1SJohn Johansen 	ad.subj_cred = subj_cred;
65c57bc80fSJohn Johansen 	ad.rlim.rlim = resource;
66c57bc80fSJohn Johansen 	ad.rlim.max = value;
67c57bc80fSJohn Johansen 	ad.peer = peer;
68c57bc80fSJohn Johansen 	ad.info = info;
69c57bc80fSJohn Johansen 	ad.error = error;
7086b92cb7SJohn Johansen 
71c57bc80fSJohn Johansen 	return aa_audit(AUDIT_APPARMOR_AUTO, profile, &ad, audit_cb);
720ed3b28aSJohn Johansen }
730ed3b28aSJohn Johansen 
740ed3b28aSJohn Johansen /**
75d44c6923SYang Li  * aa_map_resource - map compiled policy resource to internal #
760ed3b28aSJohn Johansen  * @resource: flattened policy resource number
770ed3b28aSJohn Johansen  *
780ed3b28aSJohn Johansen  * Returns: resource # for the current architecture.
790ed3b28aSJohn Johansen  *
800ed3b28aSJohn Johansen  * rlimit resource can vary based on architecture, map the compiled policy
810ed3b28aSJohn Johansen  * resource # to the internal representation for the architecture.
820ed3b28aSJohn Johansen  */
aa_map_resource(int resource)830ed3b28aSJohn Johansen int aa_map_resource(int resource)
840ed3b28aSJohn Johansen {
850ed3b28aSJohn Johansen 	return rlim_map[resource];
860ed3b28aSJohn Johansen }
870ed3b28aSJohn Johansen 
profile_setrlimit(const struct cred * subj_cred,struct aa_profile * profile,unsigned int resource,struct rlimit * new_rlim)88*690f33e1SJohn Johansen static int profile_setrlimit(const struct cred *subj_cred,
89*690f33e1SJohn Johansen 			     struct aa_profile *profile, unsigned int resource,
9086b92cb7SJohn Johansen 			     struct rlimit *new_rlim)
9186b92cb7SJohn Johansen {
921ad22fccSJohn Johansen 	struct aa_ruleset *rules = list_first_entry(&profile->rules,
931ad22fccSJohn Johansen 						    typeof(*rules), list);
9486b92cb7SJohn Johansen 	int e = 0;
9586b92cb7SJohn Johansen 
96217af7e2SJohn Johansen 	if (rules->rlimits.mask & (1 << resource) && new_rlim->rlim_max >
97217af7e2SJohn Johansen 	    rules->rlimits.limits[resource].rlim_max)
9886b92cb7SJohn Johansen 		e = -EACCES;
99*690f33e1SJohn Johansen 	return audit_resource(subj_cred, profile, resource, new_rlim->rlim_max,
100*690f33e1SJohn Johansen 			      NULL, NULL, e);
10186b92cb7SJohn Johansen }
10286b92cb7SJohn Johansen 
1030ed3b28aSJohn Johansen /**
1040ed3b28aSJohn Johansen  * aa_task_setrlimit - test permission to set an rlimit
105*690f33e1SJohn Johansen  * @subj_cred: cred setting the limit
1060e4721f5SGaosheng Cui  * @label: label confining the task  (NOT NULL)
1070e4721f5SGaosheng Cui  * @task: task the resource is being set on
1080e4721f5SGaosheng Cui  * @resource: the resource being set
1090e4721f5SGaosheng Cui  * @new_rlim: the new resource limit  (NOT NULL)
1100ed3b28aSJohn Johansen  *
1110ed3b28aSJohn Johansen  * Control raising the processes hard limit.
1120ed3b28aSJohn Johansen  *
1130ed3b28aSJohn Johansen  * Returns: 0 or error code if setting resource failed
1140ed3b28aSJohn Johansen  */
aa_task_setrlimit(const struct cred * subj_cred,struct aa_label * label,struct task_struct * task,unsigned int resource,struct rlimit * new_rlim)115*690f33e1SJohn Johansen int aa_task_setrlimit(const struct cred *subj_cred, struct aa_label *label,
116*690f33e1SJohn Johansen 		      struct task_struct *task,
1173a2dc838SJohn Johansen 		      unsigned int resource, struct rlimit *new_rlim)
1180ed3b28aSJohn Johansen {
11986b92cb7SJohn Johansen 	struct aa_profile *profile;
12086b92cb7SJohn Johansen 	struct aa_label *peer;
1210ed3b28aSJohn Johansen 	int error = 0;
1220ed3b28aSJohn Johansen 
123cf47aedeSJohn Johansen 	rcu_read_lock();
12486b92cb7SJohn Johansen 	peer = aa_get_newest_cred_label(__task_cred(task));
125cf47aedeSJohn Johansen 	rcu_read_unlock();
126cf47aedeSJohn Johansen 
1273a2dc838SJohn Johansen 	/* TODO: extend resource control to handle other (non current)
128cf47aedeSJohn Johansen 	 * profiles.  AppArmor rules currently have the implicit assumption
129cf47aedeSJohn Johansen 	 * that the task is setting the resource of a task confined with
130ff118479SJeff Mahoney 	 * the same profile or that the task setting the resource of another
131ff118479SJeff Mahoney 	 * task has CAP_SYS_RESOURCE.
1323a2dc838SJohn Johansen 	 */
1330ed3b28aSJohn Johansen 
13486b92cb7SJohn Johansen 	if (label != peer &&
135*690f33e1SJohn Johansen 	    aa_capable(subj_cred, label, CAP_SYS_RESOURCE, CAP_OPT_NOAUDIT) != 0)
13686b92cb7SJohn Johansen 		error = fn_for_each(label, profile,
137*690f33e1SJohn Johansen 				audit_resource(subj_cred, profile, resource,
13886b92cb7SJohn Johansen 					       new_rlim->rlim_max, peer,
1395933a627SColin Ian King 					       "cap_sys_resource", -EACCES));
14086b92cb7SJohn Johansen 	else
14186b92cb7SJohn Johansen 		error = fn_for_each_confined(label, profile,
142*690f33e1SJohn Johansen 				profile_setrlimit(subj_cred, profile, resource,
143*690f33e1SJohn Johansen 						  new_rlim));
14486b92cb7SJohn Johansen 	aa_put_label(peer);
145cf47aedeSJohn Johansen 
14686b92cb7SJohn Johansen 	return error;
1470ed3b28aSJohn Johansen }
1480ed3b28aSJohn Johansen 
1490ed3b28aSJohn Johansen /**
1500ed3b28aSJohn Johansen  * __aa_transition_rlimits - apply new profile rlimits
15186b92cb7SJohn Johansen  * @old_l: old label on task  (NOT NULL)
15286b92cb7SJohn Johansen  * @new_l: new label with rlimits to apply  (NOT NULL)
1530ed3b28aSJohn Johansen  */
__aa_transition_rlimits(struct aa_label * old_l,struct aa_label * new_l)15486b92cb7SJohn Johansen void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l)
1550ed3b28aSJohn Johansen {
1560ed3b28aSJohn Johansen 	unsigned int mask = 0;
1570ed3b28aSJohn Johansen 	struct rlimit *rlim, *initrlim;
15886b92cb7SJohn Johansen 	struct aa_profile *old, *new;
15986b92cb7SJohn Johansen 	struct label_it i;
1600ed3b28aSJohn Johansen 
16186b92cb7SJohn Johansen 	old = labels_profile(old_l);
16286b92cb7SJohn Johansen 	new = labels_profile(new_l);
16386b92cb7SJohn Johansen 
16486b92cb7SJohn Johansen 	/* for any rlimits the profile controlled, reset the soft limit
16586b92cb7SJohn Johansen 	 * to the lesser of the tasks hard limit and the init tasks soft limit
1660ed3b28aSJohn Johansen 	 */
16786b92cb7SJohn Johansen 	label_for_each_confined(i, old_l, old) {
1681ad22fccSJohn Johansen 		struct aa_ruleset *rules = list_first_entry(&old->rules,
1691ad22fccSJohn Johansen 							    typeof(*rules),
1701ad22fccSJohn Johansen 							    list);
1711ad22fccSJohn Johansen 		if (rules->rlimits.mask) {
17286b92cb7SJohn Johansen 			int j;
17386b92cb7SJohn Johansen 
17486b92cb7SJohn Johansen 			for (j = 0, mask = 1; j < RLIM_NLIMITS; j++,
17586b92cb7SJohn Johansen 				     mask <<= 1) {
1761ad22fccSJohn Johansen 				if (rules->rlimits.mask & mask) {
17786b92cb7SJohn Johansen 					rlim = current->signal->rlim + j;
17886b92cb7SJohn Johansen 					initrlim = init_task.signal->rlim + j;
1790ed3b28aSJohn Johansen 					rlim->rlim_cur = min(rlim->rlim_max,
1800ed3b28aSJohn Johansen 							    initrlim->rlim_cur);
1810ed3b28aSJohn Johansen 				}
1820ed3b28aSJohn Johansen 			}
1830ed3b28aSJohn Johansen 		}
18486b92cb7SJohn Johansen 	}
1850ed3b28aSJohn Johansen 
1860ed3b28aSJohn Johansen 	/* set any new hard limits as dictated by the new profile */
18786b92cb7SJohn Johansen 	label_for_each_confined(i, new_l, new) {
1881ad22fccSJohn Johansen 		struct aa_ruleset *rules = list_first_entry(&new->rules,
1891ad22fccSJohn Johansen 							    typeof(*rules),
1901ad22fccSJohn Johansen 							    list);
19186b92cb7SJohn Johansen 		int j;
19286b92cb7SJohn Johansen 
1931ad22fccSJohn Johansen 		if (!rules->rlimits.mask)
19486b92cb7SJohn Johansen 			continue;
19586b92cb7SJohn Johansen 		for (j = 0, mask = 1; j < RLIM_NLIMITS; j++, mask <<= 1) {
1961ad22fccSJohn Johansen 			if (!(rules->rlimits.mask & mask))
1970ed3b28aSJohn Johansen 				continue;
1980ed3b28aSJohn Johansen 
19986b92cb7SJohn Johansen 			rlim = current->signal->rlim + j;
2000ed3b28aSJohn Johansen 			rlim->rlim_max = min(rlim->rlim_max,
2011ad22fccSJohn Johansen 					     rules->rlimits.limits[j].rlim_max);
2020ed3b28aSJohn Johansen 			/* soft limit should not exceed hard limit */
2030ed3b28aSJohn Johansen 			rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max);
2040ed3b28aSJohn Johansen 		}
2050ed3b28aSJohn Johansen 	}
20686b92cb7SJohn Johansen }
207