162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author:
662306a36Sopenharmony_ci *      Casey Schaufler <casey@schaufler-ca.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/sched.h>
1362306a36Sopenharmony_ci#include "smack.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistruct smack_known smack_known_huh = {
1662306a36Sopenharmony_ci	.smk_known	= "?",
1762306a36Sopenharmony_ci	.smk_secid	= 2,
1862306a36Sopenharmony_ci};
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct smack_known smack_known_hat = {
2162306a36Sopenharmony_ci	.smk_known	= "^",
2262306a36Sopenharmony_ci	.smk_secid	= 3,
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct smack_known smack_known_star = {
2662306a36Sopenharmony_ci	.smk_known	= "*",
2762306a36Sopenharmony_ci	.smk_secid	= 4,
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct smack_known smack_known_floor = {
3162306a36Sopenharmony_ci	.smk_known	= "_",
3262306a36Sopenharmony_ci	.smk_secid	= 5,
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct smack_known smack_known_web = {
3662306a36Sopenharmony_ci	.smk_known	= "@",
3762306a36Sopenharmony_ci	.smk_secid	= 7,
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ciLIST_HEAD(smack_known_list);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * The initial value needs to be bigger than any of the
4462306a36Sopenharmony_ci * known values above.
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_cistatic u32 smack_next_secid = 10;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * what events do we log
5062306a36Sopenharmony_ci * can be overwritten at run-time by /smack/logging
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_ciint log_policy = SMACK_AUDIT_DENIED;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/**
5562306a36Sopenharmony_ci * smk_access_entry - look up matching access rule
5662306a36Sopenharmony_ci * @subject_label: a pointer to the subject's Smack label
5762306a36Sopenharmony_ci * @object_label: a pointer to the object's Smack label
5862306a36Sopenharmony_ci * @rule_list: the list of rules to search
5962306a36Sopenharmony_ci *
6062306a36Sopenharmony_ci * This function looks up the subject/object pair in the
6162306a36Sopenharmony_ci * access rule list and returns the access mode. If no
6262306a36Sopenharmony_ci * entry is found returns -ENOENT.
6362306a36Sopenharmony_ci *
6462306a36Sopenharmony_ci * NOTE:
6562306a36Sopenharmony_ci *
6662306a36Sopenharmony_ci * Earlier versions of this function allowed for labels that
6762306a36Sopenharmony_ci * were not on the label list. This was done to allow for
6862306a36Sopenharmony_ci * labels to come over the network that had never been seen
6962306a36Sopenharmony_ci * before on this host. Unless the receiving socket has the
7062306a36Sopenharmony_ci * star label this will always result in a failure check. The
7162306a36Sopenharmony_ci * star labeled socket case is now handled in the networking
7262306a36Sopenharmony_ci * hooks so there is no case where the label is not on the
7362306a36Sopenharmony_ci * label list. Checking to see if the address of two labels
7462306a36Sopenharmony_ci * is the same is now a reliable test.
7562306a36Sopenharmony_ci *
7662306a36Sopenharmony_ci * Do the object check first because that is more
7762306a36Sopenharmony_ci * likely to differ.
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci * Allowing write access implies allowing locking.
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_ciint smk_access_entry(char *subject_label, char *object_label,
8262306a36Sopenharmony_ci			struct list_head *rule_list)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct smack_rule *srp;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	list_for_each_entry_rcu(srp, rule_list, list) {
8762306a36Sopenharmony_ci		if (srp->smk_object->smk_known == object_label &&
8862306a36Sopenharmony_ci		    srp->smk_subject->smk_known == subject_label) {
8962306a36Sopenharmony_ci			int may = srp->smk_access;
9062306a36Sopenharmony_ci			/*
9162306a36Sopenharmony_ci			 * MAY_WRITE implies MAY_LOCK.
9262306a36Sopenharmony_ci			 */
9362306a36Sopenharmony_ci			if ((may & MAY_WRITE) == MAY_WRITE)
9462306a36Sopenharmony_ci				may |= MAY_LOCK;
9562306a36Sopenharmony_ci			return may;
9662306a36Sopenharmony_ci		}
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	return -ENOENT;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/**
10362306a36Sopenharmony_ci * smk_access - determine if a subject has a specific access to an object
10462306a36Sopenharmony_ci * @subject: a pointer to the subject's Smack label entry
10562306a36Sopenharmony_ci * @object: a pointer to the object's Smack label entry
10662306a36Sopenharmony_ci * @request: the access requested, in "MAY" format
10762306a36Sopenharmony_ci * @a : a pointer to the audit data
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci * This function looks up the subject/object pair in the
11062306a36Sopenharmony_ci * access rule list and returns 0 if the access is permitted,
11162306a36Sopenharmony_ci * non zero otherwise.
11262306a36Sopenharmony_ci *
11362306a36Sopenharmony_ci * Smack labels are shared on smack_list
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_ciint smk_access(struct smack_known *subject, struct smack_known *object,
11662306a36Sopenharmony_ci	       int request, struct smk_audit_info *a)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	int may = MAY_NOT;
11962306a36Sopenharmony_ci	int rc = 0;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/*
12262306a36Sopenharmony_ci	 * Hardcoded comparisons.
12362306a36Sopenharmony_ci	 */
12462306a36Sopenharmony_ci	/*
12562306a36Sopenharmony_ci	 * A star subject can't access any object.
12662306a36Sopenharmony_ci	 */
12762306a36Sopenharmony_ci	if (subject == &smack_known_star) {
12862306a36Sopenharmony_ci		rc = -EACCES;
12962306a36Sopenharmony_ci		goto out_audit;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci	/*
13262306a36Sopenharmony_ci	 * An internet object can be accessed by any subject.
13362306a36Sopenharmony_ci	 * Tasks cannot be assigned the internet label.
13462306a36Sopenharmony_ci	 * An internet subject can access any object.
13562306a36Sopenharmony_ci	 */
13662306a36Sopenharmony_ci	if (object == &smack_known_web || subject == &smack_known_web)
13762306a36Sopenharmony_ci		goto out_audit;
13862306a36Sopenharmony_ci	/*
13962306a36Sopenharmony_ci	 * A star object can be accessed by any subject.
14062306a36Sopenharmony_ci	 */
14162306a36Sopenharmony_ci	if (object == &smack_known_star)
14262306a36Sopenharmony_ci		goto out_audit;
14362306a36Sopenharmony_ci	/*
14462306a36Sopenharmony_ci	 * An object can be accessed in any way by a subject
14562306a36Sopenharmony_ci	 * with the same label.
14662306a36Sopenharmony_ci	 */
14762306a36Sopenharmony_ci	if (subject->smk_known == object->smk_known)
14862306a36Sopenharmony_ci		goto out_audit;
14962306a36Sopenharmony_ci	/*
15062306a36Sopenharmony_ci	 * A hat subject can read or lock any object.
15162306a36Sopenharmony_ci	 * A floor object can be read or locked by any subject.
15262306a36Sopenharmony_ci	 */
15362306a36Sopenharmony_ci	if ((request & MAY_ANYREAD) == request ||
15462306a36Sopenharmony_ci	    (request & MAY_LOCK) == request) {
15562306a36Sopenharmony_ci		if (object == &smack_known_floor)
15662306a36Sopenharmony_ci			goto out_audit;
15762306a36Sopenharmony_ci		if (subject == &smack_known_hat)
15862306a36Sopenharmony_ci			goto out_audit;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci	/*
16162306a36Sopenharmony_ci	 * Beyond here an explicit relationship is required.
16262306a36Sopenharmony_ci	 * If the requested access is contained in the available
16362306a36Sopenharmony_ci	 * access (e.g. read is included in readwrite) it's
16462306a36Sopenharmony_ci	 * good. A negative response from smk_access_entry()
16562306a36Sopenharmony_ci	 * indicates there is no entry for this pair.
16662306a36Sopenharmony_ci	 */
16762306a36Sopenharmony_ci	rcu_read_lock();
16862306a36Sopenharmony_ci	may = smk_access_entry(subject->smk_known, object->smk_known,
16962306a36Sopenharmony_ci			       &subject->smk_rules);
17062306a36Sopenharmony_ci	rcu_read_unlock();
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (may <= 0 || (request & may) != request) {
17362306a36Sopenharmony_ci		rc = -EACCES;
17462306a36Sopenharmony_ci		goto out_audit;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SMACK_BRINGUP
17762306a36Sopenharmony_ci	/*
17862306a36Sopenharmony_ci	 * Return a positive value if using bringup mode.
17962306a36Sopenharmony_ci	 * This allows the hooks to identify checks that
18062306a36Sopenharmony_ci	 * succeed because of "b" rules.
18162306a36Sopenharmony_ci	 */
18262306a36Sopenharmony_ci	if (may & MAY_BRINGUP)
18362306a36Sopenharmony_ci		rc = SMACK_BRINGUP_ALLOW;
18462306a36Sopenharmony_ci#endif
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ciout_audit:
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SMACK_BRINGUP
18962306a36Sopenharmony_ci	if (rc < 0) {
19062306a36Sopenharmony_ci		if (object == smack_unconfined)
19162306a36Sopenharmony_ci			rc = SMACK_UNCONFINED_OBJECT;
19262306a36Sopenharmony_ci		if (subject == smack_unconfined)
19362306a36Sopenharmony_ci			rc = SMACK_UNCONFINED_SUBJECT;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci#endif
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci#ifdef CONFIG_AUDIT
19862306a36Sopenharmony_ci	if (a)
19962306a36Sopenharmony_ci		smack_log(subject->smk_known, object->smk_known,
20062306a36Sopenharmony_ci			  request, rc, a);
20162306a36Sopenharmony_ci#endif
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return rc;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci/**
20762306a36Sopenharmony_ci * smk_tskacc - determine if a task has a specific access to an object
20862306a36Sopenharmony_ci * @tsp: a pointer to the subject's task
20962306a36Sopenharmony_ci * @obj_known: a pointer to the object's label entry
21062306a36Sopenharmony_ci * @mode: the access requested, in "MAY" format
21162306a36Sopenharmony_ci * @a : common audit data
21262306a36Sopenharmony_ci *
21362306a36Sopenharmony_ci * This function checks the subject task's label/object label pair
21462306a36Sopenharmony_ci * in the access rule list and returns 0 if the access is permitted,
21562306a36Sopenharmony_ci * non zero otherwise. It allows that the task may have the capability
21662306a36Sopenharmony_ci * to override the rules.
21762306a36Sopenharmony_ci */
21862306a36Sopenharmony_ciint smk_tskacc(struct task_smack *tsp, struct smack_known *obj_known,
21962306a36Sopenharmony_ci	       u32 mode, struct smk_audit_info *a)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct smack_known *sbj_known = smk_of_task(tsp);
22262306a36Sopenharmony_ci	int may;
22362306a36Sopenharmony_ci	int rc;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/*
22662306a36Sopenharmony_ci	 * Check the global rule list
22762306a36Sopenharmony_ci	 */
22862306a36Sopenharmony_ci	rc = smk_access(sbj_known, obj_known, mode, NULL);
22962306a36Sopenharmony_ci	if (rc >= 0) {
23062306a36Sopenharmony_ci		/*
23162306a36Sopenharmony_ci		 * If there is an entry in the task's rule list
23262306a36Sopenharmony_ci		 * it can further restrict access.
23362306a36Sopenharmony_ci		 */
23462306a36Sopenharmony_ci		may = smk_access_entry(sbj_known->smk_known,
23562306a36Sopenharmony_ci				       obj_known->smk_known,
23662306a36Sopenharmony_ci				       &tsp->smk_rules);
23762306a36Sopenharmony_ci		if (may < 0)
23862306a36Sopenharmony_ci			goto out_audit;
23962306a36Sopenharmony_ci		if ((mode & may) == mode)
24062306a36Sopenharmony_ci			goto out_audit;
24162306a36Sopenharmony_ci		rc = -EACCES;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/*
24562306a36Sopenharmony_ci	 * Allow for priviliged to override policy.
24662306a36Sopenharmony_ci	 */
24762306a36Sopenharmony_ci	if (rc != 0 && smack_privileged(CAP_MAC_OVERRIDE))
24862306a36Sopenharmony_ci		rc = 0;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ciout_audit:
25162306a36Sopenharmony_ci#ifdef CONFIG_AUDIT
25262306a36Sopenharmony_ci	if (a)
25362306a36Sopenharmony_ci		smack_log(sbj_known->smk_known, obj_known->smk_known,
25462306a36Sopenharmony_ci			  mode, rc, a);
25562306a36Sopenharmony_ci#endif
25662306a36Sopenharmony_ci	return rc;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci/**
26062306a36Sopenharmony_ci * smk_curacc - determine if current has a specific access to an object
26162306a36Sopenharmony_ci * @obj_known: a pointer to the object's Smack label entry
26262306a36Sopenharmony_ci * @mode: the access requested, in "MAY" format
26362306a36Sopenharmony_ci * @a : common audit data
26462306a36Sopenharmony_ci *
26562306a36Sopenharmony_ci * This function checks the current subject label/object label pair
26662306a36Sopenharmony_ci * in the access rule list and returns 0 if the access is permitted,
26762306a36Sopenharmony_ci * non zero otherwise. It allows that current may have the capability
26862306a36Sopenharmony_ci * to override the rules.
26962306a36Sopenharmony_ci */
27062306a36Sopenharmony_ciint smk_curacc(struct smack_known *obj_known,
27162306a36Sopenharmony_ci	       u32 mode, struct smk_audit_info *a)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct task_smack *tsp = smack_cred(current_cred());
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return smk_tskacc(tsp, obj_known, mode, a);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci#ifdef CONFIG_AUDIT
27962306a36Sopenharmony_ci/**
28062306a36Sopenharmony_ci * smack_str_from_perm : helper to transalate an int to a
28162306a36Sopenharmony_ci * readable string
28262306a36Sopenharmony_ci * @string : the string to fill
28362306a36Sopenharmony_ci * @access : the int
28462306a36Sopenharmony_ci *
28562306a36Sopenharmony_ci */
28662306a36Sopenharmony_cistatic inline void smack_str_from_perm(char *string, int access)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	int i = 0;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (access & MAY_READ)
29162306a36Sopenharmony_ci		string[i++] = 'r';
29262306a36Sopenharmony_ci	if (access & MAY_WRITE)
29362306a36Sopenharmony_ci		string[i++] = 'w';
29462306a36Sopenharmony_ci	if (access & MAY_EXEC)
29562306a36Sopenharmony_ci		string[i++] = 'x';
29662306a36Sopenharmony_ci	if (access & MAY_APPEND)
29762306a36Sopenharmony_ci		string[i++] = 'a';
29862306a36Sopenharmony_ci	if (access & MAY_TRANSMUTE)
29962306a36Sopenharmony_ci		string[i++] = 't';
30062306a36Sopenharmony_ci	if (access & MAY_LOCK)
30162306a36Sopenharmony_ci		string[i++] = 'l';
30262306a36Sopenharmony_ci	string[i] = '\0';
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci/**
30562306a36Sopenharmony_ci * smack_log_callback - SMACK specific information
30662306a36Sopenharmony_ci * will be called by generic audit code
30762306a36Sopenharmony_ci * @ab : the audit_buffer
30862306a36Sopenharmony_ci * @a  : audit_data
30962306a36Sopenharmony_ci *
31062306a36Sopenharmony_ci */
31162306a36Sopenharmony_cistatic void smack_log_callback(struct audit_buffer *ab, void *a)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct common_audit_data *ad = a;
31462306a36Sopenharmony_ci	struct smack_audit_data *sad = ad->smack_audit_data;
31562306a36Sopenharmony_ci	audit_log_format(ab, "lsm=SMACK fn=%s action=%s",
31662306a36Sopenharmony_ci			 ad->smack_audit_data->function,
31762306a36Sopenharmony_ci			 sad->result ? "denied" : "granted");
31862306a36Sopenharmony_ci	audit_log_format(ab, " subject=");
31962306a36Sopenharmony_ci	audit_log_untrustedstring(ab, sad->subject);
32062306a36Sopenharmony_ci	audit_log_format(ab, " object=");
32162306a36Sopenharmony_ci	audit_log_untrustedstring(ab, sad->object);
32262306a36Sopenharmony_ci	if (sad->request[0] == '\0')
32362306a36Sopenharmony_ci		audit_log_format(ab, " labels_differ");
32462306a36Sopenharmony_ci	else
32562306a36Sopenharmony_ci		audit_log_format(ab, " requested=%s", sad->request);
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci/**
32962306a36Sopenharmony_ci *  smack_log - Audit the granting or denial of permissions.
33062306a36Sopenharmony_ci *  @subject_label : smack label of the requester
33162306a36Sopenharmony_ci *  @object_label  : smack label of the object being accessed
33262306a36Sopenharmony_ci *  @request: requested permissions
33362306a36Sopenharmony_ci *  @result: result from smk_access
33462306a36Sopenharmony_ci *  @ad:  auxiliary audit data
33562306a36Sopenharmony_ci *
33662306a36Sopenharmony_ci * Audit the granting or denial of permissions in accordance
33762306a36Sopenharmony_ci * with the policy.
33862306a36Sopenharmony_ci */
33962306a36Sopenharmony_civoid smack_log(char *subject_label, char *object_label, int request,
34062306a36Sopenharmony_ci	       int result, struct smk_audit_info *ad)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SMACK_BRINGUP
34362306a36Sopenharmony_ci	char request_buffer[SMK_NUM_ACCESS_TYPE + 5];
34462306a36Sopenharmony_ci#else
34562306a36Sopenharmony_ci	char request_buffer[SMK_NUM_ACCESS_TYPE + 1];
34662306a36Sopenharmony_ci#endif
34762306a36Sopenharmony_ci	struct smack_audit_data *sad;
34862306a36Sopenharmony_ci	struct common_audit_data *a = &ad->a;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* check if we have to log the current event */
35162306a36Sopenharmony_ci	if (result < 0 && (log_policy & SMACK_AUDIT_DENIED) == 0)
35262306a36Sopenharmony_ci		return;
35362306a36Sopenharmony_ci	if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0)
35462306a36Sopenharmony_ci		return;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	sad = a->smack_audit_data;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (sad->function == NULL)
35962306a36Sopenharmony_ci		sad->function = "unknown";
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/* end preparing the audit data */
36262306a36Sopenharmony_ci	smack_str_from_perm(request_buffer, request);
36362306a36Sopenharmony_ci	sad->subject = subject_label;
36462306a36Sopenharmony_ci	sad->object  = object_label;
36562306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SMACK_BRINGUP
36662306a36Sopenharmony_ci	/*
36762306a36Sopenharmony_ci	 * The result may be positive in bringup mode.
36862306a36Sopenharmony_ci	 * A positive result is an allow, but not for normal reasons.
36962306a36Sopenharmony_ci	 * Mark it as successful, but don't filter it out even if
37062306a36Sopenharmony_ci	 * the logging policy says to do so.
37162306a36Sopenharmony_ci	 */
37262306a36Sopenharmony_ci	if (result == SMACK_UNCONFINED_SUBJECT)
37362306a36Sopenharmony_ci		strcat(request_buffer, "(US)");
37462306a36Sopenharmony_ci	else if (result == SMACK_UNCONFINED_OBJECT)
37562306a36Sopenharmony_ci		strcat(request_buffer, "(UO)");
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (result > 0)
37862306a36Sopenharmony_ci		result = 0;
37962306a36Sopenharmony_ci#endif
38062306a36Sopenharmony_ci	sad->request = request_buffer;
38162306a36Sopenharmony_ci	sad->result  = result;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	common_lsm_audit(a, smack_log_callback, NULL);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci#else /* #ifdef CONFIG_AUDIT */
38662306a36Sopenharmony_civoid smack_log(char *subject_label, char *object_label, int request,
38762306a36Sopenharmony_ci               int result, struct smk_audit_info *ad)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci#endif
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ciDEFINE_MUTEX(smack_known_lock);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistruct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/**
39762306a36Sopenharmony_ci * smk_insert_entry - insert a smack label into a hash map,
39862306a36Sopenharmony_ci * @skp: smack label
39962306a36Sopenharmony_ci *
40062306a36Sopenharmony_ci * this function must be called under smack_known_lock
40162306a36Sopenharmony_ci */
40262306a36Sopenharmony_civoid smk_insert_entry(struct smack_known *skp)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	unsigned int hash;
40562306a36Sopenharmony_ci	struct hlist_head *head;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	hash = full_name_hash(NULL, skp->smk_known, strlen(skp->smk_known));
40862306a36Sopenharmony_ci	head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)];
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	hlist_add_head_rcu(&skp->smk_hashed, head);
41162306a36Sopenharmony_ci	list_add_rcu(&skp->list, &smack_known_list);
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci/**
41562306a36Sopenharmony_ci * smk_find_entry - find a label on the list, return the list entry
41662306a36Sopenharmony_ci * @string: a text string that might be a Smack label
41762306a36Sopenharmony_ci *
41862306a36Sopenharmony_ci * Returns a pointer to the entry in the label list that
41962306a36Sopenharmony_ci * matches the passed string or NULL if not found.
42062306a36Sopenharmony_ci */
42162306a36Sopenharmony_cistruct smack_known *smk_find_entry(const char *string)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	unsigned int hash;
42462306a36Sopenharmony_ci	struct hlist_head *head;
42562306a36Sopenharmony_ci	struct smack_known *skp;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	hash = full_name_hash(NULL, string, strlen(string));
42862306a36Sopenharmony_ci	head = &smack_known_hash[hash & (SMACK_HASH_SLOTS - 1)];
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	hlist_for_each_entry_rcu(skp, head, smk_hashed)
43162306a36Sopenharmony_ci		if (strcmp(skp->smk_known, string) == 0)
43262306a36Sopenharmony_ci			return skp;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return NULL;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci/**
43862306a36Sopenharmony_ci * smk_parse_smack - parse smack label from a text string
43962306a36Sopenharmony_ci * @string: a text string that might contain a Smack label
44062306a36Sopenharmony_ci * @len: the maximum size, or zero if it is NULL terminated.
44162306a36Sopenharmony_ci *
44262306a36Sopenharmony_ci * Returns a pointer to the clean label or an error code.
44362306a36Sopenharmony_ci */
44462306a36Sopenharmony_cichar *smk_parse_smack(const char *string, int len)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	char *smack;
44762306a36Sopenharmony_ci	int i;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (len <= 0)
45062306a36Sopenharmony_ci		len = strlen(string) + 1;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/*
45362306a36Sopenharmony_ci	 * Reserve a leading '-' as an indicator that
45462306a36Sopenharmony_ci	 * this isn't a label, but an option to interfaces
45562306a36Sopenharmony_ci	 * including /smack/cipso and /smack/cipso2
45662306a36Sopenharmony_ci	 */
45762306a36Sopenharmony_ci	if (string[0] == '-')
45862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	for (i = 0; i < len; i++)
46162306a36Sopenharmony_ci		if (string[i] > '~' || string[i] <= ' ' || string[i] == '/' ||
46262306a36Sopenharmony_ci		    string[i] == '"' || string[i] == '\\' || string[i] == '\'')
46362306a36Sopenharmony_ci			break;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	if (i == 0 || i >= SMK_LONGLABEL)
46662306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	smack = kstrndup(string, i, GFP_NOFS);
46962306a36Sopenharmony_ci	if (!smack)
47062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
47162306a36Sopenharmony_ci	return smack;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci/**
47562306a36Sopenharmony_ci * smk_netlbl_mls - convert a catset to netlabel mls categories
47662306a36Sopenharmony_ci * @level: MLS sensitivity level
47762306a36Sopenharmony_ci * @catset: the Smack categories
47862306a36Sopenharmony_ci * @sap: where to put the netlabel categories
47962306a36Sopenharmony_ci * @len: number of bytes for the levels in a CIPSO IP option
48062306a36Sopenharmony_ci *
48162306a36Sopenharmony_ci * Allocates and fills attr.mls
48262306a36Sopenharmony_ci * Returns 0 on success, error code on failure.
48362306a36Sopenharmony_ci */
48462306a36Sopenharmony_ciint smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap,
48562306a36Sopenharmony_ci			int len)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	unsigned char *cp;
48862306a36Sopenharmony_ci	unsigned char m;
48962306a36Sopenharmony_ci	int cat;
49062306a36Sopenharmony_ci	int rc;
49162306a36Sopenharmony_ci	int byte;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	sap->flags |= NETLBL_SECATTR_MLS_CAT;
49462306a36Sopenharmony_ci	sap->attr.mls.lvl = level;
49562306a36Sopenharmony_ci	sap->attr.mls.cat = NULL;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	for (cat = 1, cp = catset, byte = 0; byte < len; cp++, byte++)
49862306a36Sopenharmony_ci		for (m = 0x80; m != 0; m >>= 1, cat++) {
49962306a36Sopenharmony_ci			if ((m & *cp) == 0)
50062306a36Sopenharmony_ci				continue;
50162306a36Sopenharmony_ci			rc = netlbl_catmap_setbit(&sap->attr.mls.cat,
50262306a36Sopenharmony_ci						  cat, GFP_NOFS);
50362306a36Sopenharmony_ci			if (rc < 0) {
50462306a36Sopenharmony_ci				netlbl_catmap_free(sap->attr.mls.cat);
50562306a36Sopenharmony_ci				return rc;
50662306a36Sopenharmony_ci			}
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	return 0;
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci/**
51362306a36Sopenharmony_ci * smack_populate_secattr - fill in the smack_known netlabel information
51462306a36Sopenharmony_ci * @skp: pointer to the structure to fill
51562306a36Sopenharmony_ci *
51662306a36Sopenharmony_ci * Populate the netlabel secattr structure for a Smack label.
51762306a36Sopenharmony_ci *
51862306a36Sopenharmony_ci * Returns 0 unless creating the category mapping fails
51962306a36Sopenharmony_ci */
52062306a36Sopenharmony_ciint smack_populate_secattr(struct smack_known *skp)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	int slen;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	skp->smk_netlabel.attr.secid = skp->smk_secid;
52562306a36Sopenharmony_ci	skp->smk_netlabel.domain = skp->smk_known;
52662306a36Sopenharmony_ci	skp->smk_netlabel.cache = netlbl_secattr_cache_alloc(GFP_ATOMIC);
52762306a36Sopenharmony_ci	if (skp->smk_netlabel.cache != NULL) {
52862306a36Sopenharmony_ci		skp->smk_netlabel.flags |= NETLBL_SECATTR_CACHE;
52962306a36Sopenharmony_ci		skp->smk_netlabel.cache->free = NULL;
53062306a36Sopenharmony_ci		skp->smk_netlabel.cache->data = skp;
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci	skp->smk_netlabel.flags |= NETLBL_SECATTR_SECID |
53362306a36Sopenharmony_ci				   NETLBL_SECATTR_MLS_LVL |
53462306a36Sopenharmony_ci				   NETLBL_SECATTR_DOMAIN;
53562306a36Sopenharmony_ci	/*
53662306a36Sopenharmony_ci	 * If direct labeling works use it.
53762306a36Sopenharmony_ci	 * Otherwise use mapped labeling.
53862306a36Sopenharmony_ci	 */
53962306a36Sopenharmony_ci	slen = strlen(skp->smk_known);
54062306a36Sopenharmony_ci	if (slen < SMK_CIPSOLEN)
54162306a36Sopenharmony_ci		return smk_netlbl_mls(smack_cipso_direct, skp->smk_known,
54262306a36Sopenharmony_ci				      &skp->smk_netlabel, slen);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	return smk_netlbl_mls(smack_cipso_mapped, (char *)&skp->smk_secid,
54562306a36Sopenharmony_ci			      &skp->smk_netlabel, sizeof(skp->smk_secid));
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci/**
54962306a36Sopenharmony_ci * smk_import_entry - import a label, return the list entry
55062306a36Sopenharmony_ci * @string: a text string that might be a Smack label
55162306a36Sopenharmony_ci * @len: the maximum size, or zero if it is NULL terminated.
55262306a36Sopenharmony_ci *
55362306a36Sopenharmony_ci * Returns a pointer to the entry in the label list that
55462306a36Sopenharmony_ci * matches the passed string, adding it if necessary,
55562306a36Sopenharmony_ci * or an error code.
55662306a36Sopenharmony_ci */
55762306a36Sopenharmony_cistruct smack_known *smk_import_entry(const char *string, int len)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	struct smack_known *skp;
56062306a36Sopenharmony_ci	char *smack;
56162306a36Sopenharmony_ci	int rc;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	smack = smk_parse_smack(string, len);
56462306a36Sopenharmony_ci	if (IS_ERR(smack))
56562306a36Sopenharmony_ci		return ERR_CAST(smack);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	mutex_lock(&smack_known_lock);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	skp = smk_find_entry(smack);
57062306a36Sopenharmony_ci	if (skp != NULL)
57162306a36Sopenharmony_ci		goto freeout;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	skp = kzalloc(sizeof(*skp), GFP_NOFS);
57462306a36Sopenharmony_ci	if (skp == NULL) {
57562306a36Sopenharmony_ci		skp = ERR_PTR(-ENOMEM);
57662306a36Sopenharmony_ci		goto freeout;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	skp->smk_known = smack;
58062306a36Sopenharmony_ci	skp->smk_secid = smack_next_secid++;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	rc = smack_populate_secattr(skp);
58362306a36Sopenharmony_ci	if (rc >= 0) {
58462306a36Sopenharmony_ci		INIT_LIST_HEAD(&skp->smk_rules);
58562306a36Sopenharmony_ci		mutex_init(&skp->smk_rules_lock);
58662306a36Sopenharmony_ci		/*
58762306a36Sopenharmony_ci		 * Make sure that the entry is actually
58862306a36Sopenharmony_ci		 * filled before putting it on the list.
58962306a36Sopenharmony_ci		 */
59062306a36Sopenharmony_ci		smk_insert_entry(skp);
59162306a36Sopenharmony_ci		goto unlockout;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci	kfree(skp);
59462306a36Sopenharmony_ci	skp = ERR_PTR(rc);
59562306a36Sopenharmony_cifreeout:
59662306a36Sopenharmony_ci	kfree(smack);
59762306a36Sopenharmony_ciunlockout:
59862306a36Sopenharmony_ci	mutex_unlock(&smack_known_lock);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	return skp;
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci/**
60462306a36Sopenharmony_ci * smack_from_secid - find the Smack label associated with a secid
60562306a36Sopenharmony_ci * @secid: an integer that might be associated with a Smack label
60662306a36Sopenharmony_ci *
60762306a36Sopenharmony_ci * Returns a pointer to the appropriate Smack label entry if there is one,
60862306a36Sopenharmony_ci * otherwise a pointer to the invalid Smack label.
60962306a36Sopenharmony_ci */
61062306a36Sopenharmony_cistruct smack_known *smack_from_secid(const u32 secid)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct smack_known *skp;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	rcu_read_lock();
61562306a36Sopenharmony_ci	list_for_each_entry_rcu(skp, &smack_known_list, list) {
61662306a36Sopenharmony_ci		if (skp->smk_secid == secid) {
61762306a36Sopenharmony_ci			rcu_read_unlock();
61862306a36Sopenharmony_ci			return skp;
61962306a36Sopenharmony_ci		}
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	/*
62362306a36Sopenharmony_ci	 * If we got this far someone asked for the translation
62462306a36Sopenharmony_ci	 * of a secid that is not on the list.
62562306a36Sopenharmony_ci	 */
62662306a36Sopenharmony_ci	rcu_read_unlock();
62762306a36Sopenharmony_ci	return &smack_known_huh;
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci/*
63162306a36Sopenharmony_ci * Unless a process is running with one of these labels
63262306a36Sopenharmony_ci * even having CAP_MAC_OVERRIDE isn't enough to grant
63362306a36Sopenharmony_ci * privilege to violate MAC policy. If no labels are
63462306a36Sopenharmony_ci * designated (the empty list case) capabilities apply to
63562306a36Sopenharmony_ci * everyone.
63662306a36Sopenharmony_ci */
63762306a36Sopenharmony_ciLIST_HEAD(smack_onlycap_list);
63862306a36Sopenharmony_ciDEFINE_MUTEX(smack_onlycap_lock);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci/**
64162306a36Sopenharmony_ci * smack_privileged_cred - are all privilege requirements met by cred
64262306a36Sopenharmony_ci * @cap: The requested capability
64362306a36Sopenharmony_ci * @cred: the credential to use
64462306a36Sopenharmony_ci *
64562306a36Sopenharmony_ci * Is the task privileged and allowed to be privileged
64662306a36Sopenharmony_ci * by the onlycap rule.
64762306a36Sopenharmony_ci *
64862306a36Sopenharmony_ci * Returns true if the task is allowed to be privileged, false if it's not.
64962306a36Sopenharmony_ci */
65062306a36Sopenharmony_cibool smack_privileged_cred(int cap, const struct cred *cred)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct task_smack *tsp = smack_cred(cred);
65362306a36Sopenharmony_ci	struct smack_known *skp = tsp->smk_task;
65462306a36Sopenharmony_ci	struct smack_known_list_elem *sklep;
65562306a36Sopenharmony_ci	int rc;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	rc = cap_capable(cred, &init_user_ns, cap, CAP_OPT_NONE);
65862306a36Sopenharmony_ci	if (rc)
65962306a36Sopenharmony_ci		return false;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	rcu_read_lock();
66262306a36Sopenharmony_ci	if (list_empty(&smack_onlycap_list)) {
66362306a36Sopenharmony_ci		rcu_read_unlock();
66462306a36Sopenharmony_ci		return true;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	list_for_each_entry_rcu(sklep, &smack_onlycap_list, list) {
66862306a36Sopenharmony_ci		if (sklep->smk_label == skp) {
66962306a36Sopenharmony_ci			rcu_read_unlock();
67062306a36Sopenharmony_ci			return true;
67162306a36Sopenharmony_ci		}
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci	rcu_read_unlock();
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	return false;
67662306a36Sopenharmony_ci}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci/**
67962306a36Sopenharmony_ci * smack_privileged - are all privilege requirements met
68062306a36Sopenharmony_ci * @cap: The requested capability
68162306a36Sopenharmony_ci *
68262306a36Sopenharmony_ci * Is the task privileged and allowed to be privileged
68362306a36Sopenharmony_ci * by the onlycap rule.
68462306a36Sopenharmony_ci *
68562306a36Sopenharmony_ci * Returns true if the task is allowed to be privileged, false if it's not.
68662306a36Sopenharmony_ci */
68762306a36Sopenharmony_cibool smack_privileged(int cap)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	/*
69062306a36Sopenharmony_ci	 * All kernel tasks are privileged
69162306a36Sopenharmony_ci	 */
69262306a36Sopenharmony_ci	if (unlikely(current->flags & PF_KTHREAD))
69362306a36Sopenharmony_ci		return true;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	return smack_privileged_cred(cap, current_cred());
69662306a36Sopenharmony_ci}
697