18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * AppArmor security module
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This file contains AppArmor capability mediation functions
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/capability.h>
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/gfp.h>
148c2ecf20Sopenharmony_ci#include <linux/security.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "include/apparmor.h"
178c2ecf20Sopenharmony_ci#include "include/capability.h"
188c2ecf20Sopenharmony_ci#include "include/cred.h"
198c2ecf20Sopenharmony_ci#include "include/policy.h"
208c2ecf20Sopenharmony_ci#include "include/audit.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/*
238c2ecf20Sopenharmony_ci * Table of capability names: we generate it from capabilities.h.
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ci#include "capability_names.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct aa_sfs_entry aa_sfs_entry_caps[] = {
288c2ecf20Sopenharmony_ci	AA_SFS_FILE_STRING("mask", AA_SFS_CAPS_MASK),
298c2ecf20Sopenharmony_ci	{ }
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct audit_cache {
338c2ecf20Sopenharmony_ci	struct aa_profile *profile;
348c2ecf20Sopenharmony_ci	kernel_cap_t caps;
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct audit_cache, audit_cache);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/**
408c2ecf20Sopenharmony_ci * audit_cb - call back for capability components of audit struct
418c2ecf20Sopenharmony_ci * @ab - audit buffer   (NOT NULL)
428c2ecf20Sopenharmony_ci * @va - audit struct to audit data from  (NOT NULL)
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_cistatic void audit_cb(struct audit_buffer *ab, void *va)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct common_audit_data *sa = va;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	audit_log_format(ab, " capname=");
498c2ecf20Sopenharmony_ci	audit_log_untrustedstring(ab, capability_names[sa->u.cap]);
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/**
538c2ecf20Sopenharmony_ci * audit_caps - audit a capability
548c2ecf20Sopenharmony_ci * @sa: audit data
558c2ecf20Sopenharmony_ci * @profile: profile being tested for confinement (NOT NULL)
568c2ecf20Sopenharmony_ci * @cap: capability tested
578c2ecf20Sopenharmony_ci * @error: error code returned by test
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci * Do auditing of capability and handle, audit/complain/kill modes switching
608c2ecf20Sopenharmony_ci * and duplicate message elimination.
618c2ecf20Sopenharmony_ci *
628c2ecf20Sopenharmony_ci * Returns: 0 or sa->error on success,  error code on failure
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_cistatic int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
658c2ecf20Sopenharmony_ci		      int cap, int error)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct audit_cache *ent;
688c2ecf20Sopenharmony_ci	int type = AUDIT_APPARMOR_AUTO;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	aad(sa)->error = error;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (likely(!error)) {
738c2ecf20Sopenharmony_ci		/* test if auditing is being forced */
748c2ecf20Sopenharmony_ci		if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
758c2ecf20Sopenharmony_ci			   !cap_raised(profile->caps.audit, cap)))
768c2ecf20Sopenharmony_ci			return 0;
778c2ecf20Sopenharmony_ci		type = AUDIT_APPARMOR_AUDIT;
788c2ecf20Sopenharmony_ci	} else if (KILL_MODE(profile) ||
798c2ecf20Sopenharmony_ci		   cap_raised(profile->caps.kill, cap)) {
808c2ecf20Sopenharmony_ci		type = AUDIT_APPARMOR_KILL;
818c2ecf20Sopenharmony_ci	} else if (cap_raised(profile->caps.quiet, cap) &&
828c2ecf20Sopenharmony_ci		   AUDIT_MODE(profile) != AUDIT_NOQUIET &&
838c2ecf20Sopenharmony_ci		   AUDIT_MODE(profile) != AUDIT_ALL) {
848c2ecf20Sopenharmony_ci		/* quiet auditing */
858c2ecf20Sopenharmony_ci		return error;
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	/* Do simple duplicate message elimination */
898c2ecf20Sopenharmony_ci	ent = &get_cpu_var(audit_cache);
908c2ecf20Sopenharmony_ci	if (profile == ent->profile && cap_raised(ent->caps, cap)) {
918c2ecf20Sopenharmony_ci		put_cpu_var(audit_cache);
928c2ecf20Sopenharmony_ci		if (COMPLAIN_MODE(profile))
938c2ecf20Sopenharmony_ci			return complain_error(error);
948c2ecf20Sopenharmony_ci		return error;
958c2ecf20Sopenharmony_ci	} else {
968c2ecf20Sopenharmony_ci		aa_put_profile(ent->profile);
978c2ecf20Sopenharmony_ci		ent->profile = aa_get_profile(profile);
988c2ecf20Sopenharmony_ci		cap_raise(ent->caps, cap);
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci	put_cpu_var(audit_cache);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return aa_audit(type, profile, sa, audit_cb);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/**
1068c2ecf20Sopenharmony_ci * profile_capable - test if profile allows use of capability @cap
1078c2ecf20Sopenharmony_ci * @profile: profile being enforced    (NOT NULL, NOT unconfined)
1088c2ecf20Sopenharmony_ci * @cap: capability to test if allowed
1098c2ecf20Sopenharmony_ci * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated
1108c2ecf20Sopenharmony_ci * @sa: audit data (MAY BE NULL indicating no auditing)
1118c2ecf20Sopenharmony_ci *
1128c2ecf20Sopenharmony_ci * Returns: 0 if allowed else -EPERM
1138c2ecf20Sopenharmony_ci */
1148c2ecf20Sopenharmony_cistatic int profile_capable(struct aa_profile *profile, int cap,
1158c2ecf20Sopenharmony_ci			   unsigned int opts, struct common_audit_data *sa)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	int error;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (cap_raised(profile->caps.allow, cap) &&
1208c2ecf20Sopenharmony_ci	    !cap_raised(profile->caps.denied, cap))
1218c2ecf20Sopenharmony_ci		error = 0;
1228c2ecf20Sopenharmony_ci	else
1238c2ecf20Sopenharmony_ci		error = -EPERM;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (opts & CAP_OPT_NOAUDIT) {
1268c2ecf20Sopenharmony_ci		if (!COMPLAIN_MODE(profile))
1278c2ecf20Sopenharmony_ci			return error;
1288c2ecf20Sopenharmony_ci		/* audit the cap request in complain mode but note that it
1298c2ecf20Sopenharmony_ci		 * should be optional.
1308c2ecf20Sopenharmony_ci		 */
1318c2ecf20Sopenharmony_ci		aad(sa)->info = "optional: no audit";
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	return audit_caps(sa, profile, cap, error);
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci/**
1388c2ecf20Sopenharmony_ci * aa_capable - test permission to use capability
1398c2ecf20Sopenharmony_ci * @label: label being tested for capability (NOT NULL)
1408c2ecf20Sopenharmony_ci * @cap: capability to be tested
1418c2ecf20Sopenharmony_ci * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated
1428c2ecf20Sopenharmony_ci *
1438c2ecf20Sopenharmony_ci * Look up capability in profile capability set.
1448c2ecf20Sopenharmony_ci *
1458c2ecf20Sopenharmony_ci * Returns: 0 on success, or else an error code.
1468c2ecf20Sopenharmony_ci */
1478c2ecf20Sopenharmony_ciint aa_capable(struct aa_label *label, int cap, unsigned int opts)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	struct aa_profile *profile;
1508c2ecf20Sopenharmony_ci	int error = 0;
1518c2ecf20Sopenharmony_ci	DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	sa.u.cap = cap;
1548c2ecf20Sopenharmony_ci	error = fn_for_each_confined(label, profile,
1558c2ecf20Sopenharmony_ci			profile_capable(profile, cap, opts, &sa));
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return error;
1588c2ecf20Sopenharmony_ci}
159