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 * Authors:
662306a36Sopenharmony_ci * 	Casey Schaufler <casey@schaufler-ca.com>
762306a36Sopenharmony_ci * 	Ahmed S. Darwish <darwish.07@gmail.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Special thanks to the authors of selinuxfs.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *	Karl MacMillan <kmacmillan@tresys.com>
1262306a36Sopenharmony_ci *	James Morris <jmorris@redhat.com>
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/vmalloc.h>
1762306a36Sopenharmony_ci#include <linux/security.h>
1862306a36Sopenharmony_ci#include <linux/mutex.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <net/net_namespace.h>
2162306a36Sopenharmony_ci#include <net/cipso_ipv4.h>
2262306a36Sopenharmony_ci#include <linux/seq_file.h>
2362306a36Sopenharmony_ci#include <linux/ctype.h>
2462306a36Sopenharmony_ci#include <linux/audit.h>
2562306a36Sopenharmony_ci#include <linux/magic.h>
2662306a36Sopenharmony_ci#include <linux/mount.h>
2762306a36Sopenharmony_ci#include <linux/fs_context.h>
2862306a36Sopenharmony_ci#include "smack.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define BEBITS	(sizeof(__be32) * 8)
3162306a36Sopenharmony_ci/*
3262306a36Sopenharmony_ci * smackfs pseudo filesystem.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cienum smk_inos {
3662306a36Sopenharmony_ci	SMK_ROOT_INO	= 2,
3762306a36Sopenharmony_ci	SMK_LOAD	= 3,	/* load policy */
3862306a36Sopenharmony_ci	SMK_CIPSO	= 4,	/* load label -> CIPSO mapping */
3962306a36Sopenharmony_ci	SMK_DOI		= 5,	/* CIPSO DOI */
4062306a36Sopenharmony_ci	SMK_DIRECT	= 6,	/* CIPSO level indicating direct label */
4162306a36Sopenharmony_ci	SMK_AMBIENT	= 7,	/* internet ambient label */
4262306a36Sopenharmony_ci	SMK_NET4ADDR	= 8,	/* single label hosts */
4362306a36Sopenharmony_ci	SMK_ONLYCAP	= 9,	/* the only "capable" label */
4462306a36Sopenharmony_ci	SMK_LOGGING	= 10,	/* logging */
4562306a36Sopenharmony_ci	SMK_LOAD_SELF	= 11,	/* task specific rules */
4662306a36Sopenharmony_ci	SMK_ACCESSES	= 12,	/* access policy */
4762306a36Sopenharmony_ci	SMK_MAPPED	= 13,	/* CIPSO level indicating mapped label */
4862306a36Sopenharmony_ci	SMK_LOAD2	= 14,	/* load policy with long labels */
4962306a36Sopenharmony_ci	SMK_LOAD_SELF2	= 15,	/* load task specific rules with long labels */
5062306a36Sopenharmony_ci	SMK_ACCESS2	= 16,	/* make an access check with long labels */
5162306a36Sopenharmony_ci	SMK_CIPSO2	= 17,	/* load long label -> CIPSO mapping */
5262306a36Sopenharmony_ci	SMK_REVOKE_SUBJ	= 18,	/* set rules with subject label to '-' */
5362306a36Sopenharmony_ci	SMK_CHANGE_RULE	= 19,	/* change or add rules (long labels) */
5462306a36Sopenharmony_ci	SMK_SYSLOG	= 20,	/* change syslog label) */
5562306a36Sopenharmony_ci	SMK_PTRACE	= 21,	/* set ptrace rule */
5662306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SMACK_BRINGUP
5762306a36Sopenharmony_ci	SMK_UNCONFINED	= 22,	/* define an unconfined label */
5862306a36Sopenharmony_ci#endif
5962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
6062306a36Sopenharmony_ci	SMK_NET6ADDR	= 23,	/* single label IPv6 hosts */
6162306a36Sopenharmony_ci#endif /* CONFIG_IPV6 */
6262306a36Sopenharmony_ci	SMK_RELABEL_SELF = 24, /* relabel possible without CAP_MAC_ADMIN */
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/*
6662306a36Sopenharmony_ci * List locks
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_cistatic DEFINE_MUTEX(smack_cipso_lock);
6962306a36Sopenharmony_cistatic DEFINE_MUTEX(smack_ambient_lock);
7062306a36Sopenharmony_cistatic DEFINE_MUTEX(smk_net4addr_lock);
7162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
7262306a36Sopenharmony_cistatic DEFINE_MUTEX(smk_net6addr_lock);
7362306a36Sopenharmony_ci#endif /* CONFIG_IPV6 */
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/*
7662306a36Sopenharmony_ci * This is the "ambient" label for network traffic.
7762306a36Sopenharmony_ci * If it isn't somehow marked, use this.
7862306a36Sopenharmony_ci * It can be reset via smackfs/ambient
7962306a36Sopenharmony_ci */
8062306a36Sopenharmony_cistruct smack_known *smack_net_ambient;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/*
8362306a36Sopenharmony_ci * This is the level in a CIPSO header that indicates a
8462306a36Sopenharmony_ci * smack label is contained directly in the category set.
8562306a36Sopenharmony_ci * It can be reset via smackfs/direct
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_ciint smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/*
9062306a36Sopenharmony_ci * This is the level in a CIPSO header that indicates a
9162306a36Sopenharmony_ci * secid is contained directly in the category set.
9262306a36Sopenharmony_ci * It can be reset via smackfs/mapped
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_ciint smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SMACK_BRINGUP
9762306a36Sopenharmony_ci/*
9862306a36Sopenharmony_ci * Allow one label to be unconfined. This is for
9962306a36Sopenharmony_ci * debugging and application bring-up purposes only.
10062306a36Sopenharmony_ci * It is bad and wrong, but everyone seems to expect
10162306a36Sopenharmony_ci * to have it.
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_cistruct smack_known *smack_unconfined;
10462306a36Sopenharmony_ci#endif
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/*
10762306a36Sopenharmony_ci * If this value is set restrict syslog use to the label specified.
10862306a36Sopenharmony_ci * It can be reset via smackfs/syslog
10962306a36Sopenharmony_ci */
11062306a36Sopenharmony_cistruct smack_known *smack_syslog_label;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/*
11362306a36Sopenharmony_ci * Ptrace current rule
11462306a36Sopenharmony_ci * SMACK_PTRACE_DEFAULT    regular smack ptrace rules (/proc based)
11562306a36Sopenharmony_ci * SMACK_PTRACE_EXACT      labels must match, but can be overriden with
11662306a36Sopenharmony_ci *			   CAP_SYS_PTRACE
11762306a36Sopenharmony_ci * SMACK_PTRACE_DRACONIAN  labels must match, CAP_SYS_PTRACE has no effect
11862306a36Sopenharmony_ci */
11962306a36Sopenharmony_ciint smack_ptrace_rule = SMACK_PTRACE_DEFAULT;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/*
12262306a36Sopenharmony_ci * Certain IP addresses may be designated as single label hosts.
12362306a36Sopenharmony_ci * Packets are sent there unlabeled, but only from tasks that
12462306a36Sopenharmony_ci * can write to the specified label.
12562306a36Sopenharmony_ci */
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ciLIST_HEAD(smk_net4addr_list);
12862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
12962306a36Sopenharmony_ciLIST_HEAD(smk_net6addr_list);
13062306a36Sopenharmony_ci#endif /* CONFIG_IPV6 */
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/*
13362306a36Sopenharmony_ci * Rule lists are maintained for each label.
13462306a36Sopenharmony_ci */
13562306a36Sopenharmony_cistruct smack_parsed_rule {
13662306a36Sopenharmony_ci	struct smack_known	*smk_subject;
13762306a36Sopenharmony_ci	struct smack_known	*smk_object;
13862306a36Sopenharmony_ci	int			smk_access1;
13962306a36Sopenharmony_ci	int			smk_access2;
14062306a36Sopenharmony_ci};
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/*
14562306a36Sopenharmony_ci * Values for parsing cipso rules
14662306a36Sopenharmony_ci * SMK_DIGITLEN: Length of a digit field in a rule.
14762306a36Sopenharmony_ci * SMK_CIPSOMIN: Minimum possible cipso rule length.
14862306a36Sopenharmony_ci * SMK_CIPSOMAX: Maximum possible cipso rule length.
14962306a36Sopenharmony_ci */
15062306a36Sopenharmony_ci#define SMK_DIGITLEN 4
15162306a36Sopenharmony_ci#define SMK_CIPSOMIN (SMK_LABELLEN + 2 * SMK_DIGITLEN)
15262306a36Sopenharmony_ci#define SMK_CIPSOMAX (SMK_CIPSOMIN + SMACK_CIPSO_MAXCATNUM * SMK_DIGITLEN)
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/*
15562306a36Sopenharmony_ci * Values for parsing MAC rules
15662306a36Sopenharmony_ci * SMK_ACCESS: Maximum possible combination of access permissions
15762306a36Sopenharmony_ci * SMK_ACCESSLEN: Maximum length for a rule access field
15862306a36Sopenharmony_ci * SMK_LOADLEN: Smack rule length
15962306a36Sopenharmony_ci */
16062306a36Sopenharmony_ci#define SMK_OACCESS	"rwxa"
16162306a36Sopenharmony_ci#define SMK_ACCESS	"rwxatl"
16262306a36Sopenharmony_ci#define SMK_OACCESSLEN	(sizeof(SMK_OACCESS) - 1)
16362306a36Sopenharmony_ci#define SMK_ACCESSLEN	(sizeof(SMK_ACCESS) - 1)
16462306a36Sopenharmony_ci#define SMK_OLOADLEN	(SMK_LABELLEN + SMK_LABELLEN + SMK_OACCESSLEN)
16562306a36Sopenharmony_ci#define SMK_LOADLEN	(SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN)
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/*
16862306a36Sopenharmony_ci * Stricly for CIPSO level manipulation.
16962306a36Sopenharmony_ci * Set the category bit number in a smack label sized buffer.
17062306a36Sopenharmony_ci */
17162306a36Sopenharmony_cistatic inline void smack_catset_bit(unsigned int cat, char *catsetp)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	if (cat == 0 || cat > (SMK_CIPSOLEN * 8))
17462306a36Sopenharmony_ci		return;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	catsetp[(cat - 1) / 8] |= 0x80 >> ((cat - 1) % 8);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/**
18062306a36Sopenharmony_ci * smk_netlabel_audit_set - fill a netlbl_audit struct
18162306a36Sopenharmony_ci * @nap: structure to fill
18262306a36Sopenharmony_ci */
18362306a36Sopenharmony_cistatic void smk_netlabel_audit_set(struct netlbl_audit *nap)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct smack_known *skp = smk_of_current();
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	nap->loginuid = audit_get_loginuid(current);
18862306a36Sopenharmony_ci	nap->sessionid = audit_get_sessionid(current);
18962306a36Sopenharmony_ci	nap->secid = skp->smk_secid;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci/*
19362306a36Sopenharmony_ci * Value for parsing single label host rules
19462306a36Sopenharmony_ci * "1.2.3.4 X"
19562306a36Sopenharmony_ci */
19662306a36Sopenharmony_ci#define SMK_NETLBLADDRMIN	9
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/**
19962306a36Sopenharmony_ci * smk_set_access - add a rule to the rule list or replace an old rule
20062306a36Sopenharmony_ci * @srp: the rule to add or replace
20162306a36Sopenharmony_ci * @rule_list: the list of rules
20262306a36Sopenharmony_ci * @rule_lock: the rule list lock
20362306a36Sopenharmony_ci *
20462306a36Sopenharmony_ci * Looks through the current subject/object/access list for
20562306a36Sopenharmony_ci * the subject/object pair and replaces the access that was
20662306a36Sopenharmony_ci * there. If the pair isn't found add it with the specified
20762306a36Sopenharmony_ci * access.
20862306a36Sopenharmony_ci *
20962306a36Sopenharmony_ci * Returns 0 if nothing goes wrong or -ENOMEM if it fails
21062306a36Sopenharmony_ci * during the allocation of the new pair to add.
21162306a36Sopenharmony_ci */
21262306a36Sopenharmony_cistatic int smk_set_access(struct smack_parsed_rule *srp,
21362306a36Sopenharmony_ci				struct list_head *rule_list,
21462306a36Sopenharmony_ci				struct mutex *rule_lock)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct smack_rule *sp;
21762306a36Sopenharmony_ci	int found = 0;
21862306a36Sopenharmony_ci	int rc = 0;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	mutex_lock(rule_lock);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/*
22362306a36Sopenharmony_ci	 * Because the object label is less likely to match
22462306a36Sopenharmony_ci	 * than the subject label check it first
22562306a36Sopenharmony_ci	 */
22662306a36Sopenharmony_ci	list_for_each_entry_rcu(sp, rule_list, list) {
22762306a36Sopenharmony_ci		if (sp->smk_object == srp->smk_object &&
22862306a36Sopenharmony_ci		    sp->smk_subject == srp->smk_subject) {
22962306a36Sopenharmony_ci			found = 1;
23062306a36Sopenharmony_ci			sp->smk_access |= srp->smk_access1;
23162306a36Sopenharmony_ci			sp->smk_access &= ~srp->smk_access2;
23262306a36Sopenharmony_ci			break;
23362306a36Sopenharmony_ci		}
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (found == 0) {
23762306a36Sopenharmony_ci		sp = kmem_cache_zalloc(smack_rule_cache, GFP_KERNEL);
23862306a36Sopenharmony_ci		if (sp == NULL) {
23962306a36Sopenharmony_ci			rc = -ENOMEM;
24062306a36Sopenharmony_ci			goto out;
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		sp->smk_subject = srp->smk_subject;
24462306a36Sopenharmony_ci		sp->smk_object = srp->smk_object;
24562306a36Sopenharmony_ci		sp->smk_access = srp->smk_access1 & ~srp->smk_access2;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		list_add_rcu(&sp->list, rule_list);
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ciout:
25162306a36Sopenharmony_ci	mutex_unlock(rule_lock);
25262306a36Sopenharmony_ci	return rc;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci/**
25662306a36Sopenharmony_ci * smk_perm_from_str - parse smack accesses from a text string
25762306a36Sopenharmony_ci * @string: a text string that contains a Smack accesses code
25862306a36Sopenharmony_ci *
25962306a36Sopenharmony_ci * Returns an integer with respective bits set for specified accesses.
26062306a36Sopenharmony_ci */
26162306a36Sopenharmony_cistatic int smk_perm_from_str(const char *string)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	int perm = 0;
26462306a36Sopenharmony_ci	const char *cp;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	for (cp = string; ; cp++)
26762306a36Sopenharmony_ci		switch (*cp) {
26862306a36Sopenharmony_ci		case '-':
26962306a36Sopenharmony_ci			break;
27062306a36Sopenharmony_ci		case 'r':
27162306a36Sopenharmony_ci		case 'R':
27262306a36Sopenharmony_ci			perm |= MAY_READ;
27362306a36Sopenharmony_ci			break;
27462306a36Sopenharmony_ci		case 'w':
27562306a36Sopenharmony_ci		case 'W':
27662306a36Sopenharmony_ci			perm |= MAY_WRITE;
27762306a36Sopenharmony_ci			break;
27862306a36Sopenharmony_ci		case 'x':
27962306a36Sopenharmony_ci		case 'X':
28062306a36Sopenharmony_ci			perm |= MAY_EXEC;
28162306a36Sopenharmony_ci			break;
28262306a36Sopenharmony_ci		case 'a':
28362306a36Sopenharmony_ci		case 'A':
28462306a36Sopenharmony_ci			perm |= MAY_APPEND;
28562306a36Sopenharmony_ci			break;
28662306a36Sopenharmony_ci		case 't':
28762306a36Sopenharmony_ci		case 'T':
28862306a36Sopenharmony_ci			perm |= MAY_TRANSMUTE;
28962306a36Sopenharmony_ci			break;
29062306a36Sopenharmony_ci		case 'l':
29162306a36Sopenharmony_ci		case 'L':
29262306a36Sopenharmony_ci			perm |= MAY_LOCK;
29362306a36Sopenharmony_ci			break;
29462306a36Sopenharmony_ci		case 'b':
29562306a36Sopenharmony_ci		case 'B':
29662306a36Sopenharmony_ci			perm |= MAY_BRINGUP;
29762306a36Sopenharmony_ci			break;
29862306a36Sopenharmony_ci		default:
29962306a36Sopenharmony_ci			return perm;
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/**
30462306a36Sopenharmony_ci * smk_fill_rule - Fill Smack rule from strings
30562306a36Sopenharmony_ci * @subject: subject label string
30662306a36Sopenharmony_ci * @object: object label string
30762306a36Sopenharmony_ci * @access1: access string
30862306a36Sopenharmony_ci * @access2: string with permissions to be removed
30962306a36Sopenharmony_ci * @rule: Smack rule
31062306a36Sopenharmony_ci * @import: if non-zero, import labels
31162306a36Sopenharmony_ci * @len: label length limit
31262306a36Sopenharmony_ci *
31362306a36Sopenharmony_ci * Returns 0 on success, appropriate error code on failure.
31462306a36Sopenharmony_ci */
31562306a36Sopenharmony_cistatic int smk_fill_rule(const char *subject, const char *object,
31662306a36Sopenharmony_ci				const char *access1, const char *access2,
31762306a36Sopenharmony_ci				struct smack_parsed_rule *rule, int import,
31862306a36Sopenharmony_ci				int len)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	const char *cp;
32162306a36Sopenharmony_ci	struct smack_known *skp;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (import) {
32462306a36Sopenharmony_ci		rule->smk_subject = smk_import_entry(subject, len);
32562306a36Sopenharmony_ci		if (IS_ERR(rule->smk_subject))
32662306a36Sopenharmony_ci			return PTR_ERR(rule->smk_subject);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci		rule->smk_object = smk_import_entry(object, len);
32962306a36Sopenharmony_ci		if (IS_ERR(rule->smk_object))
33062306a36Sopenharmony_ci			return PTR_ERR(rule->smk_object);
33162306a36Sopenharmony_ci	} else {
33262306a36Sopenharmony_ci		cp = smk_parse_smack(subject, len);
33362306a36Sopenharmony_ci		if (IS_ERR(cp))
33462306a36Sopenharmony_ci			return PTR_ERR(cp);
33562306a36Sopenharmony_ci		skp = smk_find_entry(cp);
33662306a36Sopenharmony_ci		kfree(cp);
33762306a36Sopenharmony_ci		if (skp == NULL)
33862306a36Sopenharmony_ci			return -ENOENT;
33962306a36Sopenharmony_ci		rule->smk_subject = skp;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		cp = smk_parse_smack(object, len);
34262306a36Sopenharmony_ci		if (IS_ERR(cp))
34362306a36Sopenharmony_ci			return PTR_ERR(cp);
34462306a36Sopenharmony_ci		skp = smk_find_entry(cp);
34562306a36Sopenharmony_ci		kfree(cp);
34662306a36Sopenharmony_ci		if (skp == NULL)
34762306a36Sopenharmony_ci			return -ENOENT;
34862306a36Sopenharmony_ci		rule->smk_object = skp;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	rule->smk_access1 = smk_perm_from_str(access1);
35262306a36Sopenharmony_ci	if (access2)
35362306a36Sopenharmony_ci		rule->smk_access2 = smk_perm_from_str(access2);
35462306a36Sopenharmony_ci	else
35562306a36Sopenharmony_ci		rule->smk_access2 = ~rule->smk_access1;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return 0;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci/**
36162306a36Sopenharmony_ci * smk_parse_rule - parse Smack rule from load string
36262306a36Sopenharmony_ci * @data: string to be parsed whose size is SMK_LOADLEN
36362306a36Sopenharmony_ci * @rule: Smack rule
36462306a36Sopenharmony_ci * @import: if non-zero, import labels
36562306a36Sopenharmony_ci *
36662306a36Sopenharmony_ci * Returns 0 on success, -1 on errors.
36762306a36Sopenharmony_ci */
36862306a36Sopenharmony_cistatic int smk_parse_rule(const char *data, struct smack_parsed_rule *rule,
36962306a36Sopenharmony_ci				int import)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	int rc;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	rc = smk_fill_rule(data, data + SMK_LABELLEN,
37462306a36Sopenharmony_ci			   data + SMK_LABELLEN + SMK_LABELLEN, NULL, rule,
37562306a36Sopenharmony_ci			   import, SMK_LABELLEN);
37662306a36Sopenharmony_ci	return rc;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci/**
38062306a36Sopenharmony_ci * smk_parse_long_rule - parse Smack rule from rule string
38162306a36Sopenharmony_ci * @data: string to be parsed, null terminated
38262306a36Sopenharmony_ci * @rule: Will be filled with Smack parsed rule
38362306a36Sopenharmony_ci * @import: if non-zero, import labels
38462306a36Sopenharmony_ci * @tokens: number of substrings expected in data
38562306a36Sopenharmony_ci *
38662306a36Sopenharmony_ci * Returns number of processed bytes on success, -ERRNO on failure.
38762306a36Sopenharmony_ci */
38862306a36Sopenharmony_cistatic ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule,
38962306a36Sopenharmony_ci				int import, int tokens)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	ssize_t cnt = 0;
39262306a36Sopenharmony_ci	char *tok[4];
39362306a36Sopenharmony_ci	int rc;
39462306a36Sopenharmony_ci	int i;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/*
39762306a36Sopenharmony_ci	 * Parsing the rule in-place, filling all white-spaces with '\0'
39862306a36Sopenharmony_ci	 */
39962306a36Sopenharmony_ci	for (i = 0; i < tokens; ++i) {
40062306a36Sopenharmony_ci		while (isspace(data[cnt]))
40162306a36Sopenharmony_ci			data[cnt++] = '\0';
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		if (data[cnt] == '\0')
40462306a36Sopenharmony_ci			/* Unexpected end of data */
40562306a36Sopenharmony_ci			return -EINVAL;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		tok[i] = data + cnt;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		while (data[cnt] && !isspace(data[cnt]))
41062306a36Sopenharmony_ci			++cnt;
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci	while (isspace(data[cnt]))
41362306a36Sopenharmony_ci		data[cnt++] = '\0';
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	while (i < 4)
41662306a36Sopenharmony_ci		tok[i++] = NULL;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	rc = smk_fill_rule(tok[0], tok[1], tok[2], tok[3], rule, import, 0);
41962306a36Sopenharmony_ci	return rc == 0 ? cnt : rc;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci#define SMK_FIXED24_FMT	0	/* Fixed 24byte label format */
42362306a36Sopenharmony_ci#define SMK_LONG_FMT	1	/* Variable long label format */
42462306a36Sopenharmony_ci#define SMK_CHANGE_FMT	2	/* Rule modification format */
42562306a36Sopenharmony_ci/**
42662306a36Sopenharmony_ci * smk_write_rules_list - write() for any /smack rule file
42762306a36Sopenharmony_ci * @file: file pointer, not actually used
42862306a36Sopenharmony_ci * @buf: where to get the data from
42962306a36Sopenharmony_ci * @count: bytes sent
43062306a36Sopenharmony_ci * @ppos: where to start - must be 0
43162306a36Sopenharmony_ci * @rule_list: the list of rules to write to
43262306a36Sopenharmony_ci * @rule_lock: lock for the rule list
43362306a36Sopenharmony_ci * @format: /smack/load or /smack/load2 or /smack/change-rule format.
43462306a36Sopenharmony_ci *
43562306a36Sopenharmony_ci * Get one smack access rule from above.
43662306a36Sopenharmony_ci * The format for SMK_LONG_FMT is:
43762306a36Sopenharmony_ci *	"subject<whitespace>object<whitespace>access[<whitespace>...]"
43862306a36Sopenharmony_ci * The format for SMK_FIXED24_FMT is exactly:
43962306a36Sopenharmony_ci *	"subject                 object                  rwxat"
44062306a36Sopenharmony_ci * The format for SMK_CHANGE_FMT is:
44162306a36Sopenharmony_ci *	"subject<whitespace>object<whitespace>
44262306a36Sopenharmony_ci *	 acc_enable<whitespace>acc_disable[<whitespace>...]"
44362306a36Sopenharmony_ci */
44462306a36Sopenharmony_cistatic ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
44562306a36Sopenharmony_ci					size_t count, loff_t *ppos,
44662306a36Sopenharmony_ci					struct list_head *rule_list,
44762306a36Sopenharmony_ci					struct mutex *rule_lock, int format)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct smack_parsed_rule rule;
45062306a36Sopenharmony_ci	char *data;
45162306a36Sopenharmony_ci	int rc;
45262306a36Sopenharmony_ci	int trunc = 0;
45362306a36Sopenharmony_ci	int tokens;
45462306a36Sopenharmony_ci	ssize_t cnt = 0;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	/*
45762306a36Sopenharmony_ci	 * No partial writes.
45862306a36Sopenharmony_ci	 * Enough data must be present.
45962306a36Sopenharmony_ci	 */
46062306a36Sopenharmony_ci	if (*ppos != 0)
46162306a36Sopenharmony_ci		return -EINVAL;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (format == SMK_FIXED24_FMT) {
46462306a36Sopenharmony_ci		/*
46562306a36Sopenharmony_ci		 * Minor hack for backward compatibility
46662306a36Sopenharmony_ci		 */
46762306a36Sopenharmony_ci		if (count < SMK_OLOADLEN || count > SMK_LOADLEN)
46862306a36Sopenharmony_ci			return -EINVAL;
46962306a36Sopenharmony_ci	} else {
47062306a36Sopenharmony_ci		if (count >= PAGE_SIZE) {
47162306a36Sopenharmony_ci			count = PAGE_SIZE - 1;
47262306a36Sopenharmony_ci			trunc = 1;
47362306a36Sopenharmony_ci		}
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	data = memdup_user_nul(buf, count);
47762306a36Sopenharmony_ci	if (IS_ERR(data))
47862306a36Sopenharmony_ci		return PTR_ERR(data);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	/*
48162306a36Sopenharmony_ci	 * In case of parsing only part of user buf,
48262306a36Sopenharmony_ci	 * avoid having partial rule at the data buffer
48362306a36Sopenharmony_ci	 */
48462306a36Sopenharmony_ci	if (trunc) {
48562306a36Sopenharmony_ci		while (count > 0 && (data[count - 1] != '\n'))
48662306a36Sopenharmony_ci			--count;
48762306a36Sopenharmony_ci		if (count == 0) {
48862306a36Sopenharmony_ci			rc = -EINVAL;
48962306a36Sopenharmony_ci			goto out;
49062306a36Sopenharmony_ci		}
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	data[count] = '\0';
49462306a36Sopenharmony_ci	tokens = (format == SMK_CHANGE_FMT ? 4 : 3);
49562306a36Sopenharmony_ci	while (cnt < count) {
49662306a36Sopenharmony_ci		if (format == SMK_FIXED24_FMT) {
49762306a36Sopenharmony_ci			rc = smk_parse_rule(data, &rule, 1);
49862306a36Sopenharmony_ci			if (rc < 0)
49962306a36Sopenharmony_ci				goto out;
50062306a36Sopenharmony_ci			cnt = count;
50162306a36Sopenharmony_ci		} else {
50262306a36Sopenharmony_ci			rc = smk_parse_long_rule(data + cnt, &rule, 1, tokens);
50362306a36Sopenharmony_ci			if (rc < 0)
50462306a36Sopenharmony_ci				goto out;
50562306a36Sopenharmony_ci			if (rc == 0) {
50662306a36Sopenharmony_ci				rc = -EINVAL;
50762306a36Sopenharmony_ci				goto out;
50862306a36Sopenharmony_ci			}
50962306a36Sopenharmony_ci			cnt += rc;
51062306a36Sopenharmony_ci		}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		if (rule_list == NULL)
51362306a36Sopenharmony_ci			rc = smk_set_access(&rule, &rule.smk_subject->smk_rules,
51462306a36Sopenharmony_ci				&rule.smk_subject->smk_rules_lock);
51562306a36Sopenharmony_ci		else
51662306a36Sopenharmony_ci			rc = smk_set_access(&rule, rule_list, rule_lock);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci		if (rc)
51962306a36Sopenharmony_ci			goto out;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	rc = cnt;
52362306a36Sopenharmony_ciout:
52462306a36Sopenharmony_ci	kfree(data);
52562306a36Sopenharmony_ci	return rc;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci/*
52962306a36Sopenharmony_ci * Core logic for smackfs seq list operations.
53062306a36Sopenharmony_ci */
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic void *smk_seq_start(struct seq_file *s, loff_t *pos,
53362306a36Sopenharmony_ci				struct list_head *head)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	struct list_head *list;
53662306a36Sopenharmony_ci	int i = *pos;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	rcu_read_lock();
53962306a36Sopenharmony_ci	for (list = rcu_dereference(list_next_rcu(head));
54062306a36Sopenharmony_ci		list != head;
54162306a36Sopenharmony_ci		list = rcu_dereference(list_next_rcu(list))) {
54262306a36Sopenharmony_ci		if (i-- == 0)
54362306a36Sopenharmony_ci			return list;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	return NULL;
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic void *smk_seq_next(struct seq_file *s, void *v, loff_t *pos,
55062306a36Sopenharmony_ci				struct list_head *head)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	struct list_head *list = v;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	++*pos;
55562306a36Sopenharmony_ci	list = rcu_dereference(list_next_rcu(list));
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	return (list == head) ? NULL : list;
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic void smk_seq_stop(struct seq_file *s, void *v)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	rcu_read_unlock();
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	/*
56862306a36Sopenharmony_ci	 * Don't show any rules with label names too long for
56962306a36Sopenharmony_ci	 * interface file (/smack/load or /smack/load2)
57062306a36Sopenharmony_ci	 * because you should expect to be able to write
57162306a36Sopenharmony_ci	 * anything you read back.
57262306a36Sopenharmony_ci	 */
57362306a36Sopenharmony_ci	if (strlen(srp->smk_subject->smk_known) >= max ||
57462306a36Sopenharmony_ci	    strlen(srp->smk_object->smk_known) >= max)
57562306a36Sopenharmony_ci		return;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (srp->smk_access == 0)
57862306a36Sopenharmony_ci		return;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	seq_printf(s, "%s %s",
58162306a36Sopenharmony_ci		   srp->smk_subject->smk_known,
58262306a36Sopenharmony_ci		   srp->smk_object->smk_known);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	seq_putc(s, ' ');
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	if (srp->smk_access & MAY_READ)
58762306a36Sopenharmony_ci		seq_putc(s, 'r');
58862306a36Sopenharmony_ci	if (srp->smk_access & MAY_WRITE)
58962306a36Sopenharmony_ci		seq_putc(s, 'w');
59062306a36Sopenharmony_ci	if (srp->smk_access & MAY_EXEC)
59162306a36Sopenharmony_ci		seq_putc(s, 'x');
59262306a36Sopenharmony_ci	if (srp->smk_access & MAY_APPEND)
59362306a36Sopenharmony_ci		seq_putc(s, 'a');
59462306a36Sopenharmony_ci	if (srp->smk_access & MAY_TRANSMUTE)
59562306a36Sopenharmony_ci		seq_putc(s, 't');
59662306a36Sopenharmony_ci	if (srp->smk_access & MAY_LOCK)
59762306a36Sopenharmony_ci		seq_putc(s, 'l');
59862306a36Sopenharmony_ci	if (srp->smk_access & MAY_BRINGUP)
59962306a36Sopenharmony_ci		seq_putc(s, 'b');
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	seq_putc(s, '\n');
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci/*
60562306a36Sopenharmony_ci * Seq_file read operations for /smack/load
60662306a36Sopenharmony_ci */
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic void *load2_seq_start(struct seq_file *s, loff_t *pos)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	return smk_seq_start(s, pos, &smack_known_list);
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic void *load2_seq_next(struct seq_file *s, void *v, loff_t *pos)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	return smk_seq_next(s, v, pos, &smack_known_list);
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic int load_seq_show(struct seq_file *s, void *v)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	struct list_head *list = v;
62162306a36Sopenharmony_ci	struct smack_rule *srp;
62262306a36Sopenharmony_ci	struct smack_known *skp =
62362306a36Sopenharmony_ci		list_entry_rcu(list, struct smack_known, list);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	list_for_each_entry_rcu(srp, &skp->smk_rules, list)
62662306a36Sopenharmony_ci		smk_rule_show(s, srp, SMK_LABELLEN);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	return 0;
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cistatic const struct seq_operations load_seq_ops = {
63262306a36Sopenharmony_ci	.start = load2_seq_start,
63362306a36Sopenharmony_ci	.next  = load2_seq_next,
63462306a36Sopenharmony_ci	.show  = load_seq_show,
63562306a36Sopenharmony_ci	.stop  = smk_seq_stop,
63662306a36Sopenharmony_ci};
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci/**
63962306a36Sopenharmony_ci * smk_open_load - open() for /smack/load
64062306a36Sopenharmony_ci * @inode: inode structure representing file
64162306a36Sopenharmony_ci * @file: "load" file pointer
64262306a36Sopenharmony_ci *
64362306a36Sopenharmony_ci * For reading, use load_seq_* seq_file reading operations.
64462306a36Sopenharmony_ci */
64562306a36Sopenharmony_cistatic int smk_open_load(struct inode *inode, struct file *file)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	return seq_open(file, &load_seq_ops);
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci/**
65162306a36Sopenharmony_ci * smk_write_load - write() for /smack/load
65262306a36Sopenharmony_ci * @file: file pointer, not actually used
65362306a36Sopenharmony_ci * @buf: where to get the data from
65462306a36Sopenharmony_ci * @count: bytes sent
65562306a36Sopenharmony_ci * @ppos: where to start - must be 0
65662306a36Sopenharmony_ci *
65762306a36Sopenharmony_ci */
65862306a36Sopenharmony_cistatic ssize_t smk_write_load(struct file *file, const char __user *buf,
65962306a36Sopenharmony_ci			      size_t count, loff_t *ppos)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	/*
66262306a36Sopenharmony_ci	 * Must have privilege.
66362306a36Sopenharmony_ci	 * No partial writes.
66462306a36Sopenharmony_ci	 * Enough data must be present.
66562306a36Sopenharmony_ci	 */
66662306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
66762306a36Sopenharmony_ci		return -EPERM;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	return smk_write_rules_list(file, buf, count, ppos, NULL, NULL,
67062306a36Sopenharmony_ci				    SMK_FIXED24_FMT);
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic const struct file_operations smk_load_ops = {
67462306a36Sopenharmony_ci	.open           = smk_open_load,
67562306a36Sopenharmony_ci	.read		= seq_read,
67662306a36Sopenharmony_ci	.llseek         = seq_lseek,
67762306a36Sopenharmony_ci	.write		= smk_write_load,
67862306a36Sopenharmony_ci	.release        = seq_release,
67962306a36Sopenharmony_ci};
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci/**
68262306a36Sopenharmony_ci * smk_cipso_doi - initialize the CIPSO domain
68362306a36Sopenharmony_ci */
68462306a36Sopenharmony_cistatic void smk_cipso_doi(void)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	int rc;
68762306a36Sopenharmony_ci	struct cipso_v4_doi *doip;
68862306a36Sopenharmony_ci	struct netlbl_audit nai;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	smk_netlabel_audit_set(&nai);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	rc = netlbl_cfg_map_del(NULL, PF_INET, NULL, NULL, &nai);
69362306a36Sopenharmony_ci	if (rc != 0)
69462306a36Sopenharmony_ci		printk(KERN_WARNING "%s:%d remove rc = %d\n",
69562306a36Sopenharmony_ci		       __func__, __LINE__, rc);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	doip = kmalloc(sizeof(struct cipso_v4_doi), GFP_KERNEL | __GFP_NOFAIL);
69862306a36Sopenharmony_ci	doip->map.std = NULL;
69962306a36Sopenharmony_ci	doip->doi = smk_cipso_doi_value;
70062306a36Sopenharmony_ci	doip->type = CIPSO_V4_MAP_PASS;
70162306a36Sopenharmony_ci	doip->tags[0] = CIPSO_V4_TAG_RBITMAP;
70262306a36Sopenharmony_ci	for (rc = 1; rc < CIPSO_V4_TAG_MAXCNT; rc++)
70362306a36Sopenharmony_ci		doip->tags[rc] = CIPSO_V4_TAG_INVALID;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	rc = netlbl_cfg_cipsov4_add(doip, &nai);
70662306a36Sopenharmony_ci	if (rc != 0) {
70762306a36Sopenharmony_ci		printk(KERN_WARNING "%s:%d cipso add rc = %d\n",
70862306a36Sopenharmony_ci		       __func__, __LINE__, rc);
70962306a36Sopenharmony_ci		kfree(doip);
71062306a36Sopenharmony_ci		return;
71162306a36Sopenharmony_ci	}
71262306a36Sopenharmony_ci	rc = netlbl_cfg_cipsov4_map_add(doip->doi, NULL, NULL, NULL, &nai);
71362306a36Sopenharmony_ci	if (rc != 0) {
71462306a36Sopenharmony_ci		printk(KERN_WARNING "%s:%d map add rc = %d\n",
71562306a36Sopenharmony_ci		       __func__, __LINE__, rc);
71662306a36Sopenharmony_ci		netlbl_cfg_cipsov4_del(doip->doi, &nai);
71762306a36Sopenharmony_ci		return;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci/**
72262306a36Sopenharmony_ci * smk_unlbl_ambient - initialize the unlabeled domain
72362306a36Sopenharmony_ci * @oldambient: previous domain string
72462306a36Sopenharmony_ci */
72562306a36Sopenharmony_cistatic void smk_unlbl_ambient(char *oldambient)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	int rc;
72862306a36Sopenharmony_ci	struct netlbl_audit nai;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	smk_netlabel_audit_set(&nai);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (oldambient != NULL) {
73362306a36Sopenharmony_ci		rc = netlbl_cfg_map_del(oldambient, PF_INET, NULL, NULL, &nai);
73462306a36Sopenharmony_ci		if (rc != 0)
73562306a36Sopenharmony_ci			printk(KERN_WARNING "%s:%d remove rc = %d\n",
73662306a36Sopenharmony_ci			       __func__, __LINE__, rc);
73762306a36Sopenharmony_ci	}
73862306a36Sopenharmony_ci	if (smack_net_ambient == NULL)
73962306a36Sopenharmony_ci		smack_net_ambient = &smack_known_floor;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	rc = netlbl_cfg_unlbl_map_add(smack_net_ambient->smk_known, PF_INET,
74262306a36Sopenharmony_ci				      NULL, NULL, &nai);
74362306a36Sopenharmony_ci	if (rc != 0)
74462306a36Sopenharmony_ci		printk(KERN_WARNING "%s:%d add rc = %d\n",
74562306a36Sopenharmony_ci		       __func__, __LINE__, rc);
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci/*
74962306a36Sopenharmony_ci * Seq_file read operations for /smack/cipso
75062306a36Sopenharmony_ci */
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_cistatic void *cipso_seq_start(struct seq_file *s, loff_t *pos)
75362306a36Sopenharmony_ci{
75462306a36Sopenharmony_ci	return smk_seq_start(s, pos, &smack_known_list);
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	return smk_seq_next(s, v, pos, &smack_known_list);
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci/*
76362306a36Sopenharmony_ci * Print cipso labels in format:
76462306a36Sopenharmony_ci * label level[/cat[,cat]]
76562306a36Sopenharmony_ci */
76662306a36Sopenharmony_cistatic int cipso_seq_show(struct seq_file *s, void *v)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	struct list_head  *list = v;
76962306a36Sopenharmony_ci	struct smack_known *skp =
77062306a36Sopenharmony_ci		list_entry_rcu(list, struct smack_known, list);
77162306a36Sopenharmony_ci	struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat;
77262306a36Sopenharmony_ci	char sep = '/';
77362306a36Sopenharmony_ci	int i;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	/*
77662306a36Sopenharmony_ci	 * Don't show a label that could not have been set using
77762306a36Sopenharmony_ci	 * /smack/cipso. This is in support of the notion that
77862306a36Sopenharmony_ci	 * anything read from /smack/cipso ought to be writeable
77962306a36Sopenharmony_ci	 * to /smack/cipso.
78062306a36Sopenharmony_ci	 *
78162306a36Sopenharmony_ci	 * /smack/cipso2 should be used instead.
78262306a36Sopenharmony_ci	 */
78362306a36Sopenharmony_ci	if (strlen(skp->smk_known) >= SMK_LABELLEN)
78462306a36Sopenharmony_ci		return 0;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	for (i = netlbl_catmap_walk(cmp, 0); i >= 0;
78962306a36Sopenharmony_ci	     i = netlbl_catmap_walk(cmp, i + 1)) {
79062306a36Sopenharmony_ci		seq_printf(s, "%c%d", sep, i);
79162306a36Sopenharmony_ci		sep = ',';
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	seq_putc(s, '\n');
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	return 0;
79762306a36Sopenharmony_ci}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_cistatic const struct seq_operations cipso_seq_ops = {
80062306a36Sopenharmony_ci	.start = cipso_seq_start,
80162306a36Sopenharmony_ci	.next  = cipso_seq_next,
80262306a36Sopenharmony_ci	.show  = cipso_seq_show,
80362306a36Sopenharmony_ci	.stop  = smk_seq_stop,
80462306a36Sopenharmony_ci};
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci/**
80762306a36Sopenharmony_ci * smk_open_cipso - open() for /smack/cipso
80862306a36Sopenharmony_ci * @inode: inode structure representing file
80962306a36Sopenharmony_ci * @file: "cipso" file pointer
81062306a36Sopenharmony_ci *
81162306a36Sopenharmony_ci * Connect our cipso_seq_* operations with /smack/cipso
81262306a36Sopenharmony_ci * file_operations
81362306a36Sopenharmony_ci */
81462306a36Sopenharmony_cistatic int smk_open_cipso(struct inode *inode, struct file *file)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	return seq_open(file, &cipso_seq_ops);
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci/**
82062306a36Sopenharmony_ci * smk_set_cipso - do the work for write() for cipso and cipso2
82162306a36Sopenharmony_ci * @file: file pointer, not actually used
82262306a36Sopenharmony_ci * @buf: where to get the data from
82362306a36Sopenharmony_ci * @count: bytes sent
82462306a36Sopenharmony_ci * @ppos: where to start
82562306a36Sopenharmony_ci * @format: /smack/cipso or /smack/cipso2
82662306a36Sopenharmony_ci *
82762306a36Sopenharmony_ci * Accepts only one cipso rule per write call.
82862306a36Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
82962306a36Sopenharmony_ci */
83062306a36Sopenharmony_cistatic ssize_t smk_set_cipso(struct file *file, const char __user *buf,
83162306a36Sopenharmony_ci				size_t count, loff_t *ppos, int format)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	struct netlbl_lsm_catmap *old_cat, *new_cat = NULL;
83462306a36Sopenharmony_ci	struct smack_known *skp;
83562306a36Sopenharmony_ci	struct netlbl_lsm_secattr ncats;
83662306a36Sopenharmony_ci	char mapcatset[SMK_CIPSOLEN];
83762306a36Sopenharmony_ci	int maplevel;
83862306a36Sopenharmony_ci	unsigned int cat;
83962306a36Sopenharmony_ci	int catlen;
84062306a36Sopenharmony_ci	ssize_t rc = -EINVAL;
84162306a36Sopenharmony_ci	char *data = NULL;
84262306a36Sopenharmony_ci	char *rule;
84362306a36Sopenharmony_ci	int ret;
84462306a36Sopenharmony_ci	int i;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	/*
84762306a36Sopenharmony_ci	 * Must have privilege.
84862306a36Sopenharmony_ci	 * No partial writes.
84962306a36Sopenharmony_ci	 * Enough data must be present.
85062306a36Sopenharmony_ci	 */
85162306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
85262306a36Sopenharmony_ci		return -EPERM;
85362306a36Sopenharmony_ci	if (*ppos != 0)
85462306a36Sopenharmony_ci		return -EINVAL;
85562306a36Sopenharmony_ci	if (format == SMK_FIXED24_FMT &&
85662306a36Sopenharmony_ci	    (count < SMK_CIPSOMIN || count > SMK_CIPSOMAX))
85762306a36Sopenharmony_ci		return -EINVAL;
85862306a36Sopenharmony_ci	if (count > PAGE_SIZE)
85962306a36Sopenharmony_ci		return -EINVAL;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	data = memdup_user_nul(buf, count);
86262306a36Sopenharmony_ci	if (IS_ERR(data))
86362306a36Sopenharmony_ci		return PTR_ERR(data);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	rule = data;
86662306a36Sopenharmony_ci	/*
86762306a36Sopenharmony_ci	 * Only allow one writer at a time. Writes should be
86862306a36Sopenharmony_ci	 * quite rare and small in any case.
86962306a36Sopenharmony_ci	 */
87062306a36Sopenharmony_ci	mutex_lock(&smack_cipso_lock);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	skp = smk_import_entry(rule, 0);
87362306a36Sopenharmony_ci	if (IS_ERR(skp)) {
87462306a36Sopenharmony_ci		rc = PTR_ERR(skp);
87562306a36Sopenharmony_ci		goto out;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (format == SMK_FIXED24_FMT)
87962306a36Sopenharmony_ci		rule += SMK_LABELLEN;
88062306a36Sopenharmony_ci	else
88162306a36Sopenharmony_ci		rule += strlen(skp->smk_known) + 1;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	if (rule > data + count) {
88462306a36Sopenharmony_ci		rc = -EOVERFLOW;
88562306a36Sopenharmony_ci		goto out;
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	ret = sscanf(rule, "%d", &maplevel);
88962306a36Sopenharmony_ci	if (ret != 1 || maplevel < 0 || maplevel > SMACK_CIPSO_MAXLEVEL)
89062306a36Sopenharmony_ci		goto out;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	rule += SMK_DIGITLEN;
89362306a36Sopenharmony_ci	if (rule > data + count) {
89462306a36Sopenharmony_ci		rc = -EOVERFLOW;
89562306a36Sopenharmony_ci		goto out;
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	ret = sscanf(rule, "%d", &catlen);
89962306a36Sopenharmony_ci	if (ret != 1 || catlen < 0 || catlen > SMACK_CIPSO_MAXCATNUM)
90062306a36Sopenharmony_ci		goto out;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	if (format == SMK_FIXED24_FMT &&
90362306a36Sopenharmony_ci	    count != (SMK_CIPSOMIN + catlen * SMK_DIGITLEN))
90462306a36Sopenharmony_ci		goto out;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	memset(mapcatset, 0, sizeof(mapcatset));
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	for (i = 0; i < catlen; i++) {
90962306a36Sopenharmony_ci		rule += SMK_DIGITLEN;
91062306a36Sopenharmony_ci		if (rule > data + count) {
91162306a36Sopenharmony_ci			rc = -EOVERFLOW;
91262306a36Sopenharmony_ci			goto out;
91362306a36Sopenharmony_ci		}
91462306a36Sopenharmony_ci		ret = sscanf(rule, "%u", &cat);
91562306a36Sopenharmony_ci		if (ret != 1 || cat > SMACK_CIPSO_MAXCATNUM)
91662306a36Sopenharmony_ci			goto out;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci		smack_catset_bit(cat, mapcatset);
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci	ncats.flags = 0;
92162306a36Sopenharmony_ci	if (catlen == 0) {
92262306a36Sopenharmony_ci		ncats.attr.mls.cat = NULL;
92362306a36Sopenharmony_ci		ncats.attr.mls.lvl = maplevel;
92462306a36Sopenharmony_ci		new_cat = netlbl_catmap_alloc(GFP_ATOMIC);
92562306a36Sopenharmony_ci		if (new_cat)
92662306a36Sopenharmony_ci			new_cat->next = ncats.attr.mls.cat;
92762306a36Sopenharmony_ci		ncats.attr.mls.cat = new_cat;
92862306a36Sopenharmony_ci		skp->smk_netlabel.flags &= ~(1U << 3);
92962306a36Sopenharmony_ci		rc = 0;
93062306a36Sopenharmony_ci	} else {
93162306a36Sopenharmony_ci		rc = smk_netlbl_mls(maplevel, mapcatset, &ncats, SMK_CIPSOLEN);
93262306a36Sopenharmony_ci	}
93362306a36Sopenharmony_ci	if (rc >= 0) {
93462306a36Sopenharmony_ci		old_cat = skp->smk_netlabel.attr.mls.cat;
93562306a36Sopenharmony_ci		skp->smk_netlabel.attr.mls.cat = ncats.attr.mls.cat;
93662306a36Sopenharmony_ci		skp->smk_netlabel.attr.mls.lvl = ncats.attr.mls.lvl;
93762306a36Sopenharmony_ci		synchronize_rcu();
93862306a36Sopenharmony_ci		netlbl_catmap_free(old_cat);
93962306a36Sopenharmony_ci		rc = count;
94062306a36Sopenharmony_ci		/*
94162306a36Sopenharmony_ci		 * This mapping may have been cached, so clear the cache.
94262306a36Sopenharmony_ci		 */
94362306a36Sopenharmony_ci		netlbl_cache_invalidate();
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ciout:
94762306a36Sopenharmony_ci	mutex_unlock(&smack_cipso_lock);
94862306a36Sopenharmony_ci	kfree(data);
94962306a36Sopenharmony_ci	return rc;
95062306a36Sopenharmony_ci}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci/**
95362306a36Sopenharmony_ci * smk_write_cipso - write() for /smack/cipso
95462306a36Sopenharmony_ci * @file: file pointer, not actually used
95562306a36Sopenharmony_ci * @buf: where to get the data from
95662306a36Sopenharmony_ci * @count: bytes sent
95762306a36Sopenharmony_ci * @ppos: where to start
95862306a36Sopenharmony_ci *
95962306a36Sopenharmony_ci * Accepts only one cipso rule per write call.
96062306a36Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
96162306a36Sopenharmony_ci */
96262306a36Sopenharmony_cistatic ssize_t smk_write_cipso(struct file *file, const char __user *buf,
96362306a36Sopenharmony_ci			       size_t count, loff_t *ppos)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	return smk_set_cipso(file, buf, count, ppos, SMK_FIXED24_FMT);
96662306a36Sopenharmony_ci}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_cistatic const struct file_operations smk_cipso_ops = {
96962306a36Sopenharmony_ci	.open           = smk_open_cipso,
97062306a36Sopenharmony_ci	.read		= seq_read,
97162306a36Sopenharmony_ci	.llseek         = seq_lseek,
97262306a36Sopenharmony_ci	.write		= smk_write_cipso,
97362306a36Sopenharmony_ci	.release        = seq_release,
97462306a36Sopenharmony_ci};
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci/*
97762306a36Sopenharmony_ci * Seq_file read operations for /smack/cipso2
97862306a36Sopenharmony_ci */
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci/*
98162306a36Sopenharmony_ci * Print cipso labels in format:
98262306a36Sopenharmony_ci * label level[/cat[,cat]]
98362306a36Sopenharmony_ci */
98462306a36Sopenharmony_cistatic int cipso2_seq_show(struct seq_file *s, void *v)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	struct list_head  *list = v;
98762306a36Sopenharmony_ci	struct smack_known *skp =
98862306a36Sopenharmony_ci		list_entry_rcu(list, struct smack_known, list);
98962306a36Sopenharmony_ci	struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat;
99062306a36Sopenharmony_ci	char sep = '/';
99162306a36Sopenharmony_ci	int i;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	for (i = netlbl_catmap_walk(cmp, 0); i >= 0;
99662306a36Sopenharmony_ci	     i = netlbl_catmap_walk(cmp, i + 1)) {
99762306a36Sopenharmony_ci		seq_printf(s, "%c%d", sep, i);
99862306a36Sopenharmony_ci		sep = ',';
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	seq_putc(s, '\n');
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	return 0;
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_cistatic const struct seq_operations cipso2_seq_ops = {
100762306a36Sopenharmony_ci	.start = cipso_seq_start,
100862306a36Sopenharmony_ci	.next  = cipso_seq_next,
100962306a36Sopenharmony_ci	.show  = cipso2_seq_show,
101062306a36Sopenharmony_ci	.stop  = smk_seq_stop,
101162306a36Sopenharmony_ci};
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci/**
101462306a36Sopenharmony_ci * smk_open_cipso2 - open() for /smack/cipso2
101562306a36Sopenharmony_ci * @inode: inode structure representing file
101662306a36Sopenharmony_ci * @file: "cipso2" file pointer
101762306a36Sopenharmony_ci *
101862306a36Sopenharmony_ci * Connect our cipso_seq_* operations with /smack/cipso2
101962306a36Sopenharmony_ci * file_operations
102062306a36Sopenharmony_ci */
102162306a36Sopenharmony_cistatic int smk_open_cipso2(struct inode *inode, struct file *file)
102262306a36Sopenharmony_ci{
102362306a36Sopenharmony_ci	return seq_open(file, &cipso2_seq_ops);
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci/**
102762306a36Sopenharmony_ci * smk_write_cipso2 - write() for /smack/cipso2
102862306a36Sopenharmony_ci * @file: file pointer, not actually used
102962306a36Sopenharmony_ci * @buf: where to get the data from
103062306a36Sopenharmony_ci * @count: bytes sent
103162306a36Sopenharmony_ci * @ppos: where to start
103262306a36Sopenharmony_ci *
103362306a36Sopenharmony_ci * Accepts only one cipso rule per write call.
103462306a36Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
103562306a36Sopenharmony_ci */
103662306a36Sopenharmony_cistatic ssize_t smk_write_cipso2(struct file *file, const char __user *buf,
103762306a36Sopenharmony_ci			      size_t count, loff_t *ppos)
103862306a36Sopenharmony_ci{
103962306a36Sopenharmony_ci	return smk_set_cipso(file, buf, count, ppos, SMK_LONG_FMT);
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_cistatic const struct file_operations smk_cipso2_ops = {
104362306a36Sopenharmony_ci	.open           = smk_open_cipso2,
104462306a36Sopenharmony_ci	.read		= seq_read,
104562306a36Sopenharmony_ci	.llseek         = seq_lseek,
104662306a36Sopenharmony_ci	.write		= smk_write_cipso2,
104762306a36Sopenharmony_ci	.release        = seq_release,
104862306a36Sopenharmony_ci};
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci/*
105162306a36Sopenharmony_ci * Seq_file read operations for /smack/netlabel
105262306a36Sopenharmony_ci */
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_cistatic void *net4addr_seq_start(struct seq_file *s, loff_t *pos)
105562306a36Sopenharmony_ci{
105662306a36Sopenharmony_ci	return smk_seq_start(s, pos, &smk_net4addr_list);
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_cistatic void *net4addr_seq_next(struct seq_file *s, void *v, loff_t *pos)
106062306a36Sopenharmony_ci{
106162306a36Sopenharmony_ci	return smk_seq_next(s, v, pos, &smk_net4addr_list);
106262306a36Sopenharmony_ci}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci/*
106562306a36Sopenharmony_ci * Print host/label pairs
106662306a36Sopenharmony_ci */
106762306a36Sopenharmony_cistatic int net4addr_seq_show(struct seq_file *s, void *v)
106862306a36Sopenharmony_ci{
106962306a36Sopenharmony_ci	struct list_head *list = v;
107062306a36Sopenharmony_ci	struct smk_net4addr *skp =
107162306a36Sopenharmony_ci			list_entry_rcu(list, struct smk_net4addr, list);
107262306a36Sopenharmony_ci	char *kp = SMACK_CIPSO_OPTION;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	if (skp->smk_label != NULL)
107562306a36Sopenharmony_ci		kp = skp->smk_label->smk_known;
107662306a36Sopenharmony_ci	seq_printf(s, "%pI4/%d %s\n", &skp->smk_host.s_addr,
107762306a36Sopenharmony_ci			skp->smk_masks, kp);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	return 0;
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_cistatic const struct seq_operations net4addr_seq_ops = {
108362306a36Sopenharmony_ci	.start = net4addr_seq_start,
108462306a36Sopenharmony_ci	.next  = net4addr_seq_next,
108562306a36Sopenharmony_ci	.show  = net4addr_seq_show,
108662306a36Sopenharmony_ci	.stop  = smk_seq_stop,
108762306a36Sopenharmony_ci};
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci/**
109062306a36Sopenharmony_ci * smk_open_net4addr - open() for /smack/netlabel
109162306a36Sopenharmony_ci * @inode: inode structure representing file
109262306a36Sopenharmony_ci * @file: "netlabel" file pointer
109362306a36Sopenharmony_ci *
109462306a36Sopenharmony_ci * Connect our net4addr_seq_* operations with /smack/netlabel
109562306a36Sopenharmony_ci * file_operations
109662306a36Sopenharmony_ci */
109762306a36Sopenharmony_cistatic int smk_open_net4addr(struct inode *inode, struct file *file)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	return seq_open(file, &net4addr_seq_ops);
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci/**
110362306a36Sopenharmony_ci * smk_net4addr_insert
110462306a36Sopenharmony_ci * @new : netlabel to insert
110562306a36Sopenharmony_ci *
110662306a36Sopenharmony_ci * This helper insert netlabel in the smack_net4addrs list
110762306a36Sopenharmony_ci * sorted by netmask length (longest to smallest)
110862306a36Sopenharmony_ci * locked by &smk_net4addr_lock in smk_write_net4addr
110962306a36Sopenharmony_ci *
111062306a36Sopenharmony_ci */
111162306a36Sopenharmony_cistatic void smk_net4addr_insert(struct smk_net4addr *new)
111262306a36Sopenharmony_ci{
111362306a36Sopenharmony_ci	struct smk_net4addr *m;
111462306a36Sopenharmony_ci	struct smk_net4addr *m_next;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	if (list_empty(&smk_net4addr_list)) {
111762306a36Sopenharmony_ci		list_add_rcu(&new->list, &smk_net4addr_list);
111862306a36Sopenharmony_ci		return;
111962306a36Sopenharmony_ci	}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	m = list_entry_rcu(smk_net4addr_list.next,
112262306a36Sopenharmony_ci			   struct smk_net4addr, list);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	/* the comparison '>' is a bit hacky, but works */
112562306a36Sopenharmony_ci	if (new->smk_masks > m->smk_masks) {
112662306a36Sopenharmony_ci		list_add_rcu(&new->list, &smk_net4addr_list);
112762306a36Sopenharmony_ci		return;
112862306a36Sopenharmony_ci	}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	list_for_each_entry_rcu(m, &smk_net4addr_list, list) {
113162306a36Sopenharmony_ci		if (list_is_last(&m->list, &smk_net4addr_list)) {
113262306a36Sopenharmony_ci			list_add_rcu(&new->list, &m->list);
113362306a36Sopenharmony_ci			return;
113462306a36Sopenharmony_ci		}
113562306a36Sopenharmony_ci		m_next = list_entry_rcu(m->list.next,
113662306a36Sopenharmony_ci					struct smk_net4addr, list);
113762306a36Sopenharmony_ci		if (new->smk_masks > m_next->smk_masks) {
113862306a36Sopenharmony_ci			list_add_rcu(&new->list, &m->list);
113962306a36Sopenharmony_ci			return;
114062306a36Sopenharmony_ci		}
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci/**
114662306a36Sopenharmony_ci * smk_write_net4addr - write() for /smack/netlabel
114762306a36Sopenharmony_ci * @file: file pointer, not actually used
114862306a36Sopenharmony_ci * @buf: where to get the data from
114962306a36Sopenharmony_ci * @count: bytes sent
115062306a36Sopenharmony_ci * @ppos: where to start
115162306a36Sopenharmony_ci *
115262306a36Sopenharmony_ci * Accepts only one net4addr per write call.
115362306a36Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
115462306a36Sopenharmony_ci */
115562306a36Sopenharmony_cistatic ssize_t smk_write_net4addr(struct file *file, const char __user *buf,
115662306a36Sopenharmony_ci				size_t count, loff_t *ppos)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	struct smk_net4addr *snp;
115962306a36Sopenharmony_ci	struct sockaddr_in newname;
116062306a36Sopenharmony_ci	char *smack;
116162306a36Sopenharmony_ci	struct smack_known *skp = NULL;
116262306a36Sopenharmony_ci	char *data;
116362306a36Sopenharmony_ci	char *host = (char *)&newname.sin_addr.s_addr;
116462306a36Sopenharmony_ci	int rc;
116562306a36Sopenharmony_ci	struct netlbl_audit audit_info;
116662306a36Sopenharmony_ci	struct in_addr mask;
116762306a36Sopenharmony_ci	unsigned int m;
116862306a36Sopenharmony_ci	unsigned int masks;
116962306a36Sopenharmony_ci	int found;
117062306a36Sopenharmony_ci	u32 mask_bits = (1<<31);
117162306a36Sopenharmony_ci	__be32 nsa;
117262306a36Sopenharmony_ci	u32 temp_mask;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	/*
117562306a36Sopenharmony_ci	 * Must have privilege.
117662306a36Sopenharmony_ci	 * No partial writes.
117762306a36Sopenharmony_ci	 * Enough data must be present.
117862306a36Sopenharmony_ci	 * "<addr/mask, as a.b.c.d/e><space><label>"
117962306a36Sopenharmony_ci	 * "<addr, as a.b.c.d><space><label>"
118062306a36Sopenharmony_ci	 */
118162306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
118262306a36Sopenharmony_ci		return -EPERM;
118362306a36Sopenharmony_ci	if (*ppos != 0)
118462306a36Sopenharmony_ci		return -EINVAL;
118562306a36Sopenharmony_ci	if (count < SMK_NETLBLADDRMIN || count > PAGE_SIZE - 1)
118662306a36Sopenharmony_ci		return -EINVAL;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	data = memdup_user_nul(buf, count);
118962306a36Sopenharmony_ci	if (IS_ERR(data))
119062306a36Sopenharmony_ci		return PTR_ERR(data);
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	smack = kzalloc(count + 1, GFP_KERNEL);
119362306a36Sopenharmony_ci	if (smack == NULL) {
119462306a36Sopenharmony_ci		rc = -ENOMEM;
119562306a36Sopenharmony_ci		goto free_data_out;
119662306a36Sopenharmony_ci	}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%u %s",
119962306a36Sopenharmony_ci		&host[0], &host[1], &host[2], &host[3], &masks, smack);
120062306a36Sopenharmony_ci	if (rc != 6) {
120162306a36Sopenharmony_ci		rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s",
120262306a36Sopenharmony_ci			&host[0], &host[1], &host[2], &host[3], smack);
120362306a36Sopenharmony_ci		if (rc != 5) {
120462306a36Sopenharmony_ci			rc = -EINVAL;
120562306a36Sopenharmony_ci			goto free_out;
120662306a36Sopenharmony_ci		}
120762306a36Sopenharmony_ci		masks = 32;
120862306a36Sopenharmony_ci	}
120962306a36Sopenharmony_ci	if (masks > BEBITS) {
121062306a36Sopenharmony_ci		rc = -EINVAL;
121162306a36Sopenharmony_ci		goto free_out;
121262306a36Sopenharmony_ci	}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	/*
121562306a36Sopenharmony_ci	 * If smack begins with '-', it is an option, don't import it
121662306a36Sopenharmony_ci	 */
121762306a36Sopenharmony_ci	if (smack[0] != '-') {
121862306a36Sopenharmony_ci		skp = smk_import_entry(smack, 0);
121962306a36Sopenharmony_ci		if (IS_ERR(skp)) {
122062306a36Sopenharmony_ci			rc = PTR_ERR(skp);
122162306a36Sopenharmony_ci			goto free_out;
122262306a36Sopenharmony_ci		}
122362306a36Sopenharmony_ci	} else {
122462306a36Sopenharmony_ci		/*
122562306a36Sopenharmony_ci		 * Only the -CIPSO option is supported for IPv4
122662306a36Sopenharmony_ci		 */
122762306a36Sopenharmony_ci		if (strcmp(smack, SMACK_CIPSO_OPTION) != 0) {
122862306a36Sopenharmony_ci			rc = -EINVAL;
122962306a36Sopenharmony_ci			goto free_out;
123062306a36Sopenharmony_ci		}
123162306a36Sopenharmony_ci	}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	for (m = masks, temp_mask = 0; m > 0; m--) {
123462306a36Sopenharmony_ci		temp_mask |= mask_bits;
123562306a36Sopenharmony_ci		mask_bits >>= 1;
123662306a36Sopenharmony_ci	}
123762306a36Sopenharmony_ci	mask.s_addr = cpu_to_be32(temp_mask);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	newname.sin_addr.s_addr &= mask.s_addr;
124062306a36Sopenharmony_ci	/*
124162306a36Sopenharmony_ci	 * Only allow one writer at a time. Writes should be
124262306a36Sopenharmony_ci	 * quite rare and small in any case.
124362306a36Sopenharmony_ci	 */
124462306a36Sopenharmony_ci	mutex_lock(&smk_net4addr_lock);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	nsa = newname.sin_addr.s_addr;
124762306a36Sopenharmony_ci	/* try to find if the prefix is already in the list */
124862306a36Sopenharmony_ci	found = 0;
124962306a36Sopenharmony_ci	list_for_each_entry_rcu(snp, &smk_net4addr_list, list) {
125062306a36Sopenharmony_ci		if (snp->smk_host.s_addr == nsa && snp->smk_masks == masks) {
125162306a36Sopenharmony_ci			found = 1;
125262306a36Sopenharmony_ci			break;
125362306a36Sopenharmony_ci		}
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci	smk_netlabel_audit_set(&audit_info);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	if (found == 0) {
125862306a36Sopenharmony_ci		snp = kzalloc(sizeof(*snp), GFP_KERNEL);
125962306a36Sopenharmony_ci		if (snp == NULL)
126062306a36Sopenharmony_ci			rc = -ENOMEM;
126162306a36Sopenharmony_ci		else {
126262306a36Sopenharmony_ci			rc = 0;
126362306a36Sopenharmony_ci			snp->smk_host.s_addr = newname.sin_addr.s_addr;
126462306a36Sopenharmony_ci			snp->smk_mask.s_addr = mask.s_addr;
126562306a36Sopenharmony_ci			snp->smk_label = skp;
126662306a36Sopenharmony_ci			snp->smk_masks = masks;
126762306a36Sopenharmony_ci			smk_net4addr_insert(snp);
126862306a36Sopenharmony_ci		}
126962306a36Sopenharmony_ci	} else {
127062306a36Sopenharmony_ci		/*
127162306a36Sopenharmony_ci		 * Delete the unlabeled entry, only if the previous label
127262306a36Sopenharmony_ci		 * wasn't the special CIPSO option
127362306a36Sopenharmony_ci		 */
127462306a36Sopenharmony_ci		if (snp->smk_label != NULL)
127562306a36Sopenharmony_ci			rc = netlbl_cfg_unlbl_static_del(&init_net, NULL,
127662306a36Sopenharmony_ci					&snp->smk_host, &snp->smk_mask,
127762306a36Sopenharmony_ci					PF_INET, &audit_info);
127862306a36Sopenharmony_ci		else
127962306a36Sopenharmony_ci			rc = 0;
128062306a36Sopenharmony_ci		snp->smk_label = skp;
128162306a36Sopenharmony_ci	}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	/*
128462306a36Sopenharmony_ci	 * Now tell netlabel about the single label nature of
128562306a36Sopenharmony_ci	 * this host so that incoming packets get labeled.
128662306a36Sopenharmony_ci	 * but only if we didn't get the special CIPSO option
128762306a36Sopenharmony_ci	 */
128862306a36Sopenharmony_ci	if (rc == 0 && skp != NULL)
128962306a36Sopenharmony_ci		rc = netlbl_cfg_unlbl_static_add(&init_net, NULL,
129062306a36Sopenharmony_ci			&snp->smk_host, &snp->smk_mask, PF_INET,
129162306a36Sopenharmony_ci			snp->smk_label->smk_secid, &audit_info);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	if (rc == 0)
129462306a36Sopenharmony_ci		rc = count;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	mutex_unlock(&smk_net4addr_lock);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_cifree_out:
129962306a36Sopenharmony_ci	kfree(smack);
130062306a36Sopenharmony_cifree_data_out:
130162306a36Sopenharmony_ci	kfree(data);
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	return rc;
130462306a36Sopenharmony_ci}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_cistatic const struct file_operations smk_net4addr_ops = {
130762306a36Sopenharmony_ci	.open           = smk_open_net4addr,
130862306a36Sopenharmony_ci	.read		= seq_read,
130962306a36Sopenharmony_ci	.llseek         = seq_lseek,
131062306a36Sopenharmony_ci	.write		= smk_write_net4addr,
131162306a36Sopenharmony_ci	.release        = seq_release,
131262306a36Sopenharmony_ci};
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
131562306a36Sopenharmony_ci/*
131662306a36Sopenharmony_ci * Seq_file read operations for /smack/netlabel6
131762306a36Sopenharmony_ci */
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_cistatic void *net6addr_seq_start(struct seq_file *s, loff_t *pos)
132062306a36Sopenharmony_ci{
132162306a36Sopenharmony_ci	return smk_seq_start(s, pos, &smk_net6addr_list);
132262306a36Sopenharmony_ci}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_cistatic void *net6addr_seq_next(struct seq_file *s, void *v, loff_t *pos)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	return smk_seq_next(s, v, pos, &smk_net6addr_list);
132762306a36Sopenharmony_ci}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci/*
133062306a36Sopenharmony_ci * Print host/label pairs
133162306a36Sopenharmony_ci */
133262306a36Sopenharmony_cistatic int net6addr_seq_show(struct seq_file *s, void *v)
133362306a36Sopenharmony_ci{
133462306a36Sopenharmony_ci	struct list_head *list = v;
133562306a36Sopenharmony_ci	struct smk_net6addr *skp =
133662306a36Sopenharmony_ci			 list_entry(list, struct smk_net6addr, list);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	if (skp->smk_label != NULL)
133962306a36Sopenharmony_ci		seq_printf(s, "%pI6/%d %s\n", &skp->smk_host, skp->smk_masks,
134062306a36Sopenharmony_ci				skp->smk_label->smk_known);
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	return 0;
134362306a36Sopenharmony_ci}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_cistatic const struct seq_operations net6addr_seq_ops = {
134662306a36Sopenharmony_ci	.start = net6addr_seq_start,
134762306a36Sopenharmony_ci	.next  = net6addr_seq_next,
134862306a36Sopenharmony_ci	.show  = net6addr_seq_show,
134962306a36Sopenharmony_ci	.stop  = smk_seq_stop,
135062306a36Sopenharmony_ci};
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci/**
135362306a36Sopenharmony_ci * smk_open_net6addr - open() for /smack/netlabel
135462306a36Sopenharmony_ci * @inode: inode structure representing file
135562306a36Sopenharmony_ci * @file: "netlabel" file pointer
135662306a36Sopenharmony_ci *
135762306a36Sopenharmony_ci * Connect our net6addr_seq_* operations with /smack/netlabel
135862306a36Sopenharmony_ci * file_operations
135962306a36Sopenharmony_ci */
136062306a36Sopenharmony_cistatic int smk_open_net6addr(struct inode *inode, struct file *file)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	return seq_open(file, &net6addr_seq_ops);
136362306a36Sopenharmony_ci}
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci/**
136662306a36Sopenharmony_ci * smk_net6addr_insert
136762306a36Sopenharmony_ci * @new : entry to insert
136862306a36Sopenharmony_ci *
136962306a36Sopenharmony_ci * This inserts an entry in the smack_net6addrs list
137062306a36Sopenharmony_ci * sorted by netmask length (longest to smallest)
137162306a36Sopenharmony_ci * locked by &smk_net6addr_lock in smk_write_net6addr
137262306a36Sopenharmony_ci *
137362306a36Sopenharmony_ci */
137462306a36Sopenharmony_cistatic void smk_net6addr_insert(struct smk_net6addr *new)
137562306a36Sopenharmony_ci{
137662306a36Sopenharmony_ci	struct smk_net6addr *m_next;
137762306a36Sopenharmony_ci	struct smk_net6addr *m;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	if (list_empty(&smk_net6addr_list)) {
138062306a36Sopenharmony_ci		list_add_rcu(&new->list, &smk_net6addr_list);
138162306a36Sopenharmony_ci		return;
138262306a36Sopenharmony_ci	}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	m = list_entry_rcu(smk_net6addr_list.next,
138562306a36Sopenharmony_ci			   struct smk_net6addr, list);
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	if (new->smk_masks > m->smk_masks) {
138862306a36Sopenharmony_ci		list_add_rcu(&new->list, &smk_net6addr_list);
138962306a36Sopenharmony_ci		return;
139062306a36Sopenharmony_ci	}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	list_for_each_entry_rcu(m, &smk_net6addr_list, list) {
139362306a36Sopenharmony_ci		if (list_is_last(&m->list, &smk_net6addr_list)) {
139462306a36Sopenharmony_ci			list_add_rcu(&new->list, &m->list);
139562306a36Sopenharmony_ci			return;
139662306a36Sopenharmony_ci		}
139762306a36Sopenharmony_ci		m_next = list_entry_rcu(m->list.next,
139862306a36Sopenharmony_ci					struct smk_net6addr, list);
139962306a36Sopenharmony_ci		if (new->smk_masks > m_next->smk_masks) {
140062306a36Sopenharmony_ci			list_add_rcu(&new->list, &m->list);
140162306a36Sopenharmony_ci			return;
140262306a36Sopenharmony_ci		}
140362306a36Sopenharmony_ci	}
140462306a36Sopenharmony_ci}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci/**
140862306a36Sopenharmony_ci * smk_write_net6addr - write() for /smack/netlabel
140962306a36Sopenharmony_ci * @file: file pointer, not actually used
141062306a36Sopenharmony_ci * @buf: where to get the data from
141162306a36Sopenharmony_ci * @count: bytes sent
141262306a36Sopenharmony_ci * @ppos: where to start
141362306a36Sopenharmony_ci *
141462306a36Sopenharmony_ci * Accepts only one net6addr per write call.
141562306a36Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
141662306a36Sopenharmony_ci */
141762306a36Sopenharmony_cistatic ssize_t smk_write_net6addr(struct file *file, const char __user *buf,
141862306a36Sopenharmony_ci				size_t count, loff_t *ppos)
141962306a36Sopenharmony_ci{
142062306a36Sopenharmony_ci	struct smk_net6addr *snp;
142162306a36Sopenharmony_ci	struct in6_addr newname;
142262306a36Sopenharmony_ci	struct in6_addr fullmask;
142362306a36Sopenharmony_ci	struct smack_known *skp = NULL;
142462306a36Sopenharmony_ci	char *smack;
142562306a36Sopenharmony_ci	char *data;
142662306a36Sopenharmony_ci	int rc = 0;
142762306a36Sopenharmony_ci	int found = 0;
142862306a36Sopenharmony_ci	int i;
142962306a36Sopenharmony_ci	unsigned int scanned[8];
143062306a36Sopenharmony_ci	unsigned int m;
143162306a36Sopenharmony_ci	unsigned int mask = 128;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	/*
143462306a36Sopenharmony_ci	 * Must have privilege.
143562306a36Sopenharmony_ci	 * No partial writes.
143662306a36Sopenharmony_ci	 * Enough data must be present.
143762306a36Sopenharmony_ci	 * "<addr/mask, as a:b:c:d:e:f:g:h/e><space><label>"
143862306a36Sopenharmony_ci	 * "<addr, as a:b:c:d:e:f:g:h><space><label>"
143962306a36Sopenharmony_ci	 */
144062306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
144162306a36Sopenharmony_ci		return -EPERM;
144262306a36Sopenharmony_ci	if (*ppos != 0)
144362306a36Sopenharmony_ci		return -EINVAL;
144462306a36Sopenharmony_ci	if (count < SMK_NETLBLADDRMIN || count > PAGE_SIZE - 1)
144562306a36Sopenharmony_ci		return -EINVAL;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	data = memdup_user_nul(buf, count);
144862306a36Sopenharmony_ci	if (IS_ERR(data))
144962306a36Sopenharmony_ci		return PTR_ERR(data);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	smack = kzalloc(count + 1, GFP_KERNEL);
145262306a36Sopenharmony_ci	if (smack == NULL) {
145362306a36Sopenharmony_ci		rc = -ENOMEM;
145462306a36Sopenharmony_ci		goto free_data_out;
145562306a36Sopenharmony_ci	}
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	i = sscanf(data, "%x:%x:%x:%x:%x:%x:%x:%x/%u %s",
145862306a36Sopenharmony_ci			&scanned[0], &scanned[1], &scanned[2], &scanned[3],
145962306a36Sopenharmony_ci			&scanned[4], &scanned[5], &scanned[6], &scanned[7],
146062306a36Sopenharmony_ci			&mask, smack);
146162306a36Sopenharmony_ci	if (i != 10) {
146262306a36Sopenharmony_ci		i = sscanf(data, "%x:%x:%x:%x:%x:%x:%x:%x %s",
146362306a36Sopenharmony_ci				&scanned[0], &scanned[1], &scanned[2],
146462306a36Sopenharmony_ci				&scanned[3], &scanned[4], &scanned[5],
146562306a36Sopenharmony_ci				&scanned[6], &scanned[7], smack);
146662306a36Sopenharmony_ci		if (i != 9) {
146762306a36Sopenharmony_ci			rc = -EINVAL;
146862306a36Sopenharmony_ci			goto free_out;
146962306a36Sopenharmony_ci		}
147062306a36Sopenharmony_ci	}
147162306a36Sopenharmony_ci	if (mask > 128) {
147262306a36Sopenharmony_ci		rc = -EINVAL;
147362306a36Sopenharmony_ci		goto free_out;
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
147662306a36Sopenharmony_ci		if (scanned[i] > 0xffff) {
147762306a36Sopenharmony_ci			rc = -EINVAL;
147862306a36Sopenharmony_ci			goto free_out;
147962306a36Sopenharmony_ci		}
148062306a36Sopenharmony_ci		newname.s6_addr16[i] = htons(scanned[i]);
148162306a36Sopenharmony_ci	}
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	/*
148462306a36Sopenharmony_ci	 * If smack begins with '-', it is an option, don't import it
148562306a36Sopenharmony_ci	 */
148662306a36Sopenharmony_ci	if (smack[0] != '-') {
148762306a36Sopenharmony_ci		skp = smk_import_entry(smack, 0);
148862306a36Sopenharmony_ci		if (IS_ERR(skp)) {
148962306a36Sopenharmony_ci			rc = PTR_ERR(skp);
149062306a36Sopenharmony_ci			goto free_out;
149162306a36Sopenharmony_ci		}
149262306a36Sopenharmony_ci	} else {
149362306a36Sopenharmony_ci		/*
149462306a36Sopenharmony_ci		 * Only -DELETE is supported for IPv6
149562306a36Sopenharmony_ci		 */
149662306a36Sopenharmony_ci		if (strcmp(smack, SMACK_DELETE_OPTION) != 0) {
149762306a36Sopenharmony_ci			rc = -EINVAL;
149862306a36Sopenharmony_ci			goto free_out;
149962306a36Sopenharmony_ci		}
150062306a36Sopenharmony_ci	}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	for (i = 0, m = mask; i < 8; i++) {
150362306a36Sopenharmony_ci		if (m >= 16) {
150462306a36Sopenharmony_ci			fullmask.s6_addr16[i] = 0xffff;
150562306a36Sopenharmony_ci			m -= 16;
150662306a36Sopenharmony_ci		} else if (m > 0) {
150762306a36Sopenharmony_ci			fullmask.s6_addr16[i] = (1 << m) - 1;
150862306a36Sopenharmony_ci			m = 0;
150962306a36Sopenharmony_ci		} else
151062306a36Sopenharmony_ci			fullmask.s6_addr16[i] = 0;
151162306a36Sopenharmony_ci		newname.s6_addr16[i] &= fullmask.s6_addr16[i];
151262306a36Sopenharmony_ci	}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	/*
151562306a36Sopenharmony_ci	 * Only allow one writer at a time. Writes should be
151662306a36Sopenharmony_ci	 * quite rare and small in any case.
151762306a36Sopenharmony_ci	 */
151862306a36Sopenharmony_ci	mutex_lock(&smk_net6addr_lock);
151962306a36Sopenharmony_ci	/*
152062306a36Sopenharmony_ci	 * Try to find the prefix in the list
152162306a36Sopenharmony_ci	 */
152262306a36Sopenharmony_ci	list_for_each_entry_rcu(snp, &smk_net6addr_list, list) {
152362306a36Sopenharmony_ci		if (mask != snp->smk_masks)
152462306a36Sopenharmony_ci			continue;
152562306a36Sopenharmony_ci		for (found = 1, i = 0; i < 8; i++) {
152662306a36Sopenharmony_ci			if (newname.s6_addr16[i] !=
152762306a36Sopenharmony_ci			    snp->smk_host.s6_addr16[i]) {
152862306a36Sopenharmony_ci				found = 0;
152962306a36Sopenharmony_ci				break;
153062306a36Sopenharmony_ci			}
153162306a36Sopenharmony_ci		}
153262306a36Sopenharmony_ci		if (found == 1)
153362306a36Sopenharmony_ci			break;
153462306a36Sopenharmony_ci	}
153562306a36Sopenharmony_ci	if (found == 0) {
153662306a36Sopenharmony_ci		snp = kzalloc(sizeof(*snp), GFP_KERNEL);
153762306a36Sopenharmony_ci		if (snp == NULL)
153862306a36Sopenharmony_ci			rc = -ENOMEM;
153962306a36Sopenharmony_ci		else {
154062306a36Sopenharmony_ci			snp->smk_host = newname;
154162306a36Sopenharmony_ci			snp->smk_mask = fullmask;
154262306a36Sopenharmony_ci			snp->smk_masks = mask;
154362306a36Sopenharmony_ci			snp->smk_label = skp;
154462306a36Sopenharmony_ci			smk_net6addr_insert(snp);
154562306a36Sopenharmony_ci		}
154662306a36Sopenharmony_ci	} else {
154762306a36Sopenharmony_ci		snp->smk_label = skp;
154862306a36Sopenharmony_ci	}
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	if (rc == 0)
155162306a36Sopenharmony_ci		rc = count;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	mutex_unlock(&smk_net6addr_lock);
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_cifree_out:
155662306a36Sopenharmony_ci	kfree(smack);
155762306a36Sopenharmony_cifree_data_out:
155862306a36Sopenharmony_ci	kfree(data);
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	return rc;
156162306a36Sopenharmony_ci}
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_cistatic const struct file_operations smk_net6addr_ops = {
156462306a36Sopenharmony_ci	.open           = smk_open_net6addr,
156562306a36Sopenharmony_ci	.read		= seq_read,
156662306a36Sopenharmony_ci	.llseek         = seq_lseek,
156762306a36Sopenharmony_ci	.write		= smk_write_net6addr,
156862306a36Sopenharmony_ci	.release        = seq_release,
156962306a36Sopenharmony_ci};
157062306a36Sopenharmony_ci#endif /* CONFIG_IPV6 */
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci/**
157362306a36Sopenharmony_ci * smk_read_doi - read() for /smack/doi
157462306a36Sopenharmony_ci * @filp: file pointer, not actually used
157562306a36Sopenharmony_ci * @buf: where to put the result
157662306a36Sopenharmony_ci * @count: maximum to send along
157762306a36Sopenharmony_ci * @ppos: where to start
157862306a36Sopenharmony_ci *
157962306a36Sopenharmony_ci * Returns number of bytes read or error code, as appropriate
158062306a36Sopenharmony_ci */
158162306a36Sopenharmony_cistatic ssize_t smk_read_doi(struct file *filp, char __user *buf,
158262306a36Sopenharmony_ci			    size_t count, loff_t *ppos)
158362306a36Sopenharmony_ci{
158462306a36Sopenharmony_ci	char temp[80];
158562306a36Sopenharmony_ci	ssize_t rc;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	if (*ppos != 0)
158862306a36Sopenharmony_ci		return 0;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	sprintf(temp, "%d", smk_cipso_doi_value);
159162306a36Sopenharmony_ci	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	return rc;
159462306a36Sopenharmony_ci}
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci/**
159762306a36Sopenharmony_ci * smk_write_doi - write() for /smack/doi
159862306a36Sopenharmony_ci * @file: file pointer, not actually used
159962306a36Sopenharmony_ci * @buf: where to get the data from
160062306a36Sopenharmony_ci * @count: bytes sent
160162306a36Sopenharmony_ci * @ppos: where to start
160262306a36Sopenharmony_ci *
160362306a36Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
160462306a36Sopenharmony_ci */
160562306a36Sopenharmony_cistatic ssize_t smk_write_doi(struct file *file, const char __user *buf,
160662306a36Sopenharmony_ci			     size_t count, loff_t *ppos)
160762306a36Sopenharmony_ci{
160862306a36Sopenharmony_ci	char temp[80];
160962306a36Sopenharmony_ci	int i;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
161262306a36Sopenharmony_ci		return -EPERM;
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	if (count >= sizeof(temp) || count == 0)
161562306a36Sopenharmony_ci		return -EINVAL;
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	if (copy_from_user(temp, buf, count) != 0)
161862306a36Sopenharmony_ci		return -EFAULT;
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	temp[count] = '\0';
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	if (sscanf(temp, "%d", &i) != 1)
162362306a36Sopenharmony_ci		return -EINVAL;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	smk_cipso_doi_value = i;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	smk_cipso_doi();
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	return count;
163062306a36Sopenharmony_ci}
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_cistatic const struct file_operations smk_doi_ops = {
163362306a36Sopenharmony_ci	.read		= smk_read_doi,
163462306a36Sopenharmony_ci	.write		= smk_write_doi,
163562306a36Sopenharmony_ci	.llseek		= default_llseek,
163662306a36Sopenharmony_ci};
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci/**
163962306a36Sopenharmony_ci * smk_read_direct - read() for /smack/direct
164062306a36Sopenharmony_ci * @filp: file pointer, not actually used
164162306a36Sopenharmony_ci * @buf: where to put the result
164262306a36Sopenharmony_ci * @count: maximum to send along
164362306a36Sopenharmony_ci * @ppos: where to start
164462306a36Sopenharmony_ci *
164562306a36Sopenharmony_ci * Returns number of bytes read or error code, as appropriate
164662306a36Sopenharmony_ci */
164762306a36Sopenharmony_cistatic ssize_t smk_read_direct(struct file *filp, char __user *buf,
164862306a36Sopenharmony_ci			       size_t count, loff_t *ppos)
164962306a36Sopenharmony_ci{
165062306a36Sopenharmony_ci	char temp[80];
165162306a36Sopenharmony_ci	ssize_t rc;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	if (*ppos != 0)
165462306a36Sopenharmony_ci		return 0;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	sprintf(temp, "%d", smack_cipso_direct);
165762306a36Sopenharmony_ci	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	return rc;
166062306a36Sopenharmony_ci}
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci/**
166362306a36Sopenharmony_ci * smk_write_direct - write() for /smack/direct
166462306a36Sopenharmony_ci * @file: file pointer, not actually used
166562306a36Sopenharmony_ci * @buf: where to get the data from
166662306a36Sopenharmony_ci * @count: bytes sent
166762306a36Sopenharmony_ci * @ppos: where to start
166862306a36Sopenharmony_ci *
166962306a36Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
167062306a36Sopenharmony_ci */
167162306a36Sopenharmony_cistatic ssize_t smk_write_direct(struct file *file, const char __user *buf,
167262306a36Sopenharmony_ci				size_t count, loff_t *ppos)
167362306a36Sopenharmony_ci{
167462306a36Sopenharmony_ci	struct smack_known *skp;
167562306a36Sopenharmony_ci	char temp[80];
167662306a36Sopenharmony_ci	int i;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
167962306a36Sopenharmony_ci		return -EPERM;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	if (count >= sizeof(temp) || count == 0)
168262306a36Sopenharmony_ci		return -EINVAL;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	if (copy_from_user(temp, buf, count) != 0)
168562306a36Sopenharmony_ci		return -EFAULT;
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	temp[count] = '\0';
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	if (sscanf(temp, "%d", &i) != 1)
169062306a36Sopenharmony_ci		return -EINVAL;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	/*
169362306a36Sopenharmony_ci	 * Don't do anything if the value hasn't actually changed.
169462306a36Sopenharmony_ci	 * If it is changing reset the level on entries that were
169562306a36Sopenharmony_ci	 * set up to be direct when they were created.
169662306a36Sopenharmony_ci	 */
169762306a36Sopenharmony_ci	if (smack_cipso_direct != i) {
169862306a36Sopenharmony_ci		mutex_lock(&smack_known_lock);
169962306a36Sopenharmony_ci		list_for_each_entry_rcu(skp, &smack_known_list, list)
170062306a36Sopenharmony_ci			if (skp->smk_netlabel.attr.mls.lvl ==
170162306a36Sopenharmony_ci			    smack_cipso_direct)
170262306a36Sopenharmony_ci				skp->smk_netlabel.attr.mls.lvl = i;
170362306a36Sopenharmony_ci		smack_cipso_direct = i;
170462306a36Sopenharmony_ci		mutex_unlock(&smack_known_lock);
170562306a36Sopenharmony_ci	}
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	return count;
170862306a36Sopenharmony_ci}
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_cistatic const struct file_operations smk_direct_ops = {
171162306a36Sopenharmony_ci	.read		= smk_read_direct,
171262306a36Sopenharmony_ci	.write		= smk_write_direct,
171362306a36Sopenharmony_ci	.llseek		= default_llseek,
171462306a36Sopenharmony_ci};
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci/**
171762306a36Sopenharmony_ci * smk_read_mapped - read() for /smack/mapped
171862306a36Sopenharmony_ci * @filp: file pointer, not actually used
171962306a36Sopenharmony_ci * @buf: where to put the result
172062306a36Sopenharmony_ci * @count: maximum to send along
172162306a36Sopenharmony_ci * @ppos: where to start
172262306a36Sopenharmony_ci *
172362306a36Sopenharmony_ci * Returns number of bytes read or error code, as appropriate
172462306a36Sopenharmony_ci */
172562306a36Sopenharmony_cistatic ssize_t smk_read_mapped(struct file *filp, char __user *buf,
172662306a36Sopenharmony_ci			       size_t count, loff_t *ppos)
172762306a36Sopenharmony_ci{
172862306a36Sopenharmony_ci	char temp[80];
172962306a36Sopenharmony_ci	ssize_t rc;
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	if (*ppos != 0)
173262306a36Sopenharmony_ci		return 0;
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	sprintf(temp, "%d", smack_cipso_mapped);
173562306a36Sopenharmony_ci	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	return rc;
173862306a36Sopenharmony_ci}
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci/**
174162306a36Sopenharmony_ci * smk_write_mapped - write() for /smack/mapped
174262306a36Sopenharmony_ci * @file: file pointer, not actually used
174362306a36Sopenharmony_ci * @buf: where to get the data from
174462306a36Sopenharmony_ci * @count: bytes sent
174562306a36Sopenharmony_ci * @ppos: where to start
174662306a36Sopenharmony_ci *
174762306a36Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
174862306a36Sopenharmony_ci */
174962306a36Sopenharmony_cistatic ssize_t smk_write_mapped(struct file *file, const char __user *buf,
175062306a36Sopenharmony_ci				size_t count, loff_t *ppos)
175162306a36Sopenharmony_ci{
175262306a36Sopenharmony_ci	struct smack_known *skp;
175362306a36Sopenharmony_ci	char temp[80];
175462306a36Sopenharmony_ci	int i;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
175762306a36Sopenharmony_ci		return -EPERM;
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	if (count >= sizeof(temp) || count == 0)
176062306a36Sopenharmony_ci		return -EINVAL;
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	if (copy_from_user(temp, buf, count) != 0)
176362306a36Sopenharmony_ci		return -EFAULT;
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	temp[count] = '\0';
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	if (sscanf(temp, "%d", &i) != 1)
176862306a36Sopenharmony_ci		return -EINVAL;
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	/*
177162306a36Sopenharmony_ci	 * Don't do anything if the value hasn't actually changed.
177262306a36Sopenharmony_ci	 * If it is changing reset the level on entries that were
177362306a36Sopenharmony_ci	 * set up to be mapped when they were created.
177462306a36Sopenharmony_ci	 */
177562306a36Sopenharmony_ci	if (smack_cipso_mapped != i) {
177662306a36Sopenharmony_ci		mutex_lock(&smack_known_lock);
177762306a36Sopenharmony_ci		list_for_each_entry_rcu(skp, &smack_known_list, list)
177862306a36Sopenharmony_ci			if (skp->smk_netlabel.attr.mls.lvl ==
177962306a36Sopenharmony_ci			    smack_cipso_mapped)
178062306a36Sopenharmony_ci				skp->smk_netlabel.attr.mls.lvl = i;
178162306a36Sopenharmony_ci		smack_cipso_mapped = i;
178262306a36Sopenharmony_ci		mutex_unlock(&smack_known_lock);
178362306a36Sopenharmony_ci	}
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	return count;
178662306a36Sopenharmony_ci}
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_cistatic const struct file_operations smk_mapped_ops = {
178962306a36Sopenharmony_ci	.read		= smk_read_mapped,
179062306a36Sopenharmony_ci	.write		= smk_write_mapped,
179162306a36Sopenharmony_ci	.llseek		= default_llseek,
179262306a36Sopenharmony_ci};
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci/**
179562306a36Sopenharmony_ci * smk_read_ambient - read() for /smack/ambient
179662306a36Sopenharmony_ci * @filp: file pointer, not actually used
179762306a36Sopenharmony_ci * @buf: where to put the result
179862306a36Sopenharmony_ci * @cn: maximum to send along
179962306a36Sopenharmony_ci * @ppos: where to start
180062306a36Sopenharmony_ci *
180162306a36Sopenharmony_ci * Returns number of bytes read or error code, as appropriate
180262306a36Sopenharmony_ci */
180362306a36Sopenharmony_cistatic ssize_t smk_read_ambient(struct file *filp, char __user *buf,
180462306a36Sopenharmony_ci				size_t cn, loff_t *ppos)
180562306a36Sopenharmony_ci{
180662306a36Sopenharmony_ci	ssize_t rc;
180762306a36Sopenharmony_ci	int asize;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	if (*ppos != 0)
181062306a36Sopenharmony_ci		return 0;
181162306a36Sopenharmony_ci	/*
181262306a36Sopenharmony_ci	 * Being careful to avoid a problem in the case where
181362306a36Sopenharmony_ci	 * smack_net_ambient gets changed in midstream.
181462306a36Sopenharmony_ci	 */
181562306a36Sopenharmony_ci	mutex_lock(&smack_ambient_lock);
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	asize = strlen(smack_net_ambient->smk_known) + 1;
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	if (cn >= asize)
182062306a36Sopenharmony_ci		rc = simple_read_from_buffer(buf, cn, ppos,
182162306a36Sopenharmony_ci					     smack_net_ambient->smk_known,
182262306a36Sopenharmony_ci					     asize);
182362306a36Sopenharmony_ci	else
182462306a36Sopenharmony_ci		rc = -EINVAL;
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	mutex_unlock(&smack_ambient_lock);
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	return rc;
182962306a36Sopenharmony_ci}
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci/**
183262306a36Sopenharmony_ci * smk_write_ambient - write() for /smack/ambient
183362306a36Sopenharmony_ci * @file: file pointer, not actually used
183462306a36Sopenharmony_ci * @buf: where to get the data from
183562306a36Sopenharmony_ci * @count: bytes sent
183662306a36Sopenharmony_ci * @ppos: where to start
183762306a36Sopenharmony_ci *
183862306a36Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
183962306a36Sopenharmony_ci */
184062306a36Sopenharmony_cistatic ssize_t smk_write_ambient(struct file *file, const char __user *buf,
184162306a36Sopenharmony_ci				 size_t count, loff_t *ppos)
184262306a36Sopenharmony_ci{
184362306a36Sopenharmony_ci	struct smack_known *skp;
184462306a36Sopenharmony_ci	char *oldambient;
184562306a36Sopenharmony_ci	char *data;
184662306a36Sopenharmony_ci	int rc = count;
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
184962306a36Sopenharmony_ci		return -EPERM;
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	/* Enough data must be present */
185262306a36Sopenharmony_ci	if (count == 0 || count > PAGE_SIZE)
185362306a36Sopenharmony_ci		return -EINVAL;
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	data = memdup_user_nul(buf, count);
185662306a36Sopenharmony_ci	if (IS_ERR(data))
185762306a36Sopenharmony_ci		return PTR_ERR(data);
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	skp = smk_import_entry(data, count);
186062306a36Sopenharmony_ci	if (IS_ERR(skp)) {
186162306a36Sopenharmony_ci		rc = PTR_ERR(skp);
186262306a36Sopenharmony_ci		goto out;
186362306a36Sopenharmony_ci	}
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	mutex_lock(&smack_ambient_lock);
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	oldambient = smack_net_ambient->smk_known;
186862306a36Sopenharmony_ci	smack_net_ambient = skp;
186962306a36Sopenharmony_ci	smk_unlbl_ambient(oldambient);
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	mutex_unlock(&smack_ambient_lock);
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ciout:
187462306a36Sopenharmony_ci	kfree(data);
187562306a36Sopenharmony_ci	return rc;
187662306a36Sopenharmony_ci}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_cistatic const struct file_operations smk_ambient_ops = {
187962306a36Sopenharmony_ci	.read		= smk_read_ambient,
188062306a36Sopenharmony_ci	.write		= smk_write_ambient,
188162306a36Sopenharmony_ci	.llseek		= default_llseek,
188262306a36Sopenharmony_ci};
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci/*
188562306a36Sopenharmony_ci * Seq_file operations for /smack/onlycap
188662306a36Sopenharmony_ci */
188762306a36Sopenharmony_cistatic void *onlycap_seq_start(struct seq_file *s, loff_t *pos)
188862306a36Sopenharmony_ci{
188962306a36Sopenharmony_ci	return smk_seq_start(s, pos, &smack_onlycap_list);
189062306a36Sopenharmony_ci}
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_cistatic void *onlycap_seq_next(struct seq_file *s, void *v, loff_t *pos)
189362306a36Sopenharmony_ci{
189462306a36Sopenharmony_ci	return smk_seq_next(s, v, pos, &smack_onlycap_list);
189562306a36Sopenharmony_ci}
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_cistatic int onlycap_seq_show(struct seq_file *s, void *v)
189862306a36Sopenharmony_ci{
189962306a36Sopenharmony_ci	struct list_head *list = v;
190062306a36Sopenharmony_ci	struct smack_known_list_elem *sklep =
190162306a36Sopenharmony_ci		list_entry_rcu(list, struct smack_known_list_elem, list);
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	seq_puts(s, sklep->smk_label->smk_known);
190462306a36Sopenharmony_ci	seq_putc(s, ' ');
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	return 0;
190762306a36Sopenharmony_ci}
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_cistatic const struct seq_operations onlycap_seq_ops = {
191062306a36Sopenharmony_ci	.start = onlycap_seq_start,
191162306a36Sopenharmony_ci	.next  = onlycap_seq_next,
191262306a36Sopenharmony_ci	.show  = onlycap_seq_show,
191362306a36Sopenharmony_ci	.stop  = smk_seq_stop,
191462306a36Sopenharmony_ci};
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_cistatic int smk_open_onlycap(struct inode *inode, struct file *file)
191762306a36Sopenharmony_ci{
191862306a36Sopenharmony_ci	return seq_open(file, &onlycap_seq_ops);
191962306a36Sopenharmony_ci}
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci/**
192262306a36Sopenharmony_ci * smk_list_swap_rcu - swap public list with a private one in RCU-safe way
192362306a36Sopenharmony_ci * The caller must hold appropriate mutex to prevent concurrent modifications
192462306a36Sopenharmony_ci * to the public list.
192562306a36Sopenharmony_ci * Private list is assumed to be not accessible to other threads yet.
192662306a36Sopenharmony_ci *
192762306a36Sopenharmony_ci * @public: public list
192862306a36Sopenharmony_ci * @private: private list
192962306a36Sopenharmony_ci */
193062306a36Sopenharmony_cistatic void smk_list_swap_rcu(struct list_head *public,
193162306a36Sopenharmony_ci			      struct list_head *private)
193262306a36Sopenharmony_ci{
193362306a36Sopenharmony_ci	struct list_head *first, *last;
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	if (list_empty(public)) {
193662306a36Sopenharmony_ci		list_splice_init_rcu(private, public, synchronize_rcu);
193762306a36Sopenharmony_ci	} else {
193862306a36Sopenharmony_ci		/* Remember public list before replacing it */
193962306a36Sopenharmony_ci		first = public->next;
194062306a36Sopenharmony_ci		last = public->prev;
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci		/* Publish private list in place of public in RCU-safe way */
194362306a36Sopenharmony_ci		private->prev->next = public;
194462306a36Sopenharmony_ci		private->next->prev = public;
194562306a36Sopenharmony_ci		rcu_assign_pointer(public->next, private->next);
194662306a36Sopenharmony_ci		public->prev = private->prev;
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci		synchronize_rcu();
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci		/* When all readers are done with the old public list,
195162306a36Sopenharmony_ci		 * attach it in place of private */
195262306a36Sopenharmony_ci		private->next = first;
195362306a36Sopenharmony_ci		private->prev = last;
195462306a36Sopenharmony_ci		first->prev = private;
195562306a36Sopenharmony_ci		last->next = private;
195662306a36Sopenharmony_ci	}
195762306a36Sopenharmony_ci}
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci/**
196062306a36Sopenharmony_ci * smk_parse_label_list - parse list of Smack labels, separated by spaces
196162306a36Sopenharmony_ci *
196262306a36Sopenharmony_ci * @data: the string to parse
196362306a36Sopenharmony_ci * @list: destination list
196462306a36Sopenharmony_ci *
196562306a36Sopenharmony_ci * Returns zero on success or error code, as appropriate
196662306a36Sopenharmony_ci */
196762306a36Sopenharmony_cistatic int smk_parse_label_list(char *data, struct list_head *list)
196862306a36Sopenharmony_ci{
196962306a36Sopenharmony_ci	char *tok;
197062306a36Sopenharmony_ci	struct smack_known *skp;
197162306a36Sopenharmony_ci	struct smack_known_list_elem *sklep;
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci	while ((tok = strsep(&data, " ")) != NULL) {
197462306a36Sopenharmony_ci		if (!*tok)
197562306a36Sopenharmony_ci			continue;
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci		skp = smk_import_entry(tok, 0);
197862306a36Sopenharmony_ci		if (IS_ERR(skp))
197962306a36Sopenharmony_ci			return PTR_ERR(skp);
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci		sklep = kzalloc(sizeof(*sklep), GFP_KERNEL);
198262306a36Sopenharmony_ci		if (sklep == NULL)
198362306a36Sopenharmony_ci			return -ENOMEM;
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci		sklep->smk_label = skp;
198662306a36Sopenharmony_ci		list_add(&sklep->list, list);
198762306a36Sopenharmony_ci	}
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	return 0;
199062306a36Sopenharmony_ci}
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci/**
199362306a36Sopenharmony_ci * smk_destroy_label_list - destroy a list of smack_known_list_elem
199462306a36Sopenharmony_ci * @list: header pointer of the list to destroy
199562306a36Sopenharmony_ci */
199662306a36Sopenharmony_civoid smk_destroy_label_list(struct list_head *list)
199762306a36Sopenharmony_ci{
199862306a36Sopenharmony_ci	struct smack_known_list_elem *sklep;
199962306a36Sopenharmony_ci	struct smack_known_list_elem *sklep2;
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci	list_for_each_entry_safe(sklep, sklep2, list, list)
200262306a36Sopenharmony_ci		kfree(sklep);
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	INIT_LIST_HEAD(list);
200562306a36Sopenharmony_ci}
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci/**
200862306a36Sopenharmony_ci * smk_write_onlycap - write() for smackfs/onlycap
200962306a36Sopenharmony_ci * @file: file pointer, not actually used
201062306a36Sopenharmony_ci * @buf: where to get the data from
201162306a36Sopenharmony_ci * @count: bytes sent
201262306a36Sopenharmony_ci * @ppos: where to start
201362306a36Sopenharmony_ci *
201462306a36Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
201562306a36Sopenharmony_ci */
201662306a36Sopenharmony_cistatic ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
201762306a36Sopenharmony_ci				 size_t count, loff_t *ppos)
201862306a36Sopenharmony_ci{
201962306a36Sopenharmony_ci	char *data;
202062306a36Sopenharmony_ci	LIST_HEAD(list_tmp);
202162306a36Sopenharmony_ci	int rc;
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
202462306a36Sopenharmony_ci		return -EPERM;
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	if (count > PAGE_SIZE)
202762306a36Sopenharmony_ci		return -EINVAL;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	data = memdup_user_nul(buf, count);
203062306a36Sopenharmony_ci	if (IS_ERR(data))
203162306a36Sopenharmony_ci		return PTR_ERR(data);
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ci	rc = smk_parse_label_list(data, &list_tmp);
203462306a36Sopenharmony_ci	kfree(data);
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	/*
203762306a36Sopenharmony_ci	 * Clear the smack_onlycap on invalid label errors. This means
203862306a36Sopenharmony_ci	 * that we can pass a null string to unset the onlycap value.
203962306a36Sopenharmony_ci	 *
204062306a36Sopenharmony_ci	 * Importing will also reject a label beginning with '-',
204162306a36Sopenharmony_ci	 * so "-usecapabilities" will also work.
204262306a36Sopenharmony_ci	 *
204362306a36Sopenharmony_ci	 * But do so only on invalid label, not on system errors.
204462306a36Sopenharmony_ci	 * The invalid label must be first to count as clearing attempt.
204562306a36Sopenharmony_ci	 */
204662306a36Sopenharmony_ci	if (!rc || (rc == -EINVAL && list_empty(&list_tmp))) {
204762306a36Sopenharmony_ci		mutex_lock(&smack_onlycap_lock);
204862306a36Sopenharmony_ci		smk_list_swap_rcu(&smack_onlycap_list, &list_tmp);
204962306a36Sopenharmony_ci		mutex_unlock(&smack_onlycap_lock);
205062306a36Sopenharmony_ci		rc = count;
205162306a36Sopenharmony_ci	}
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	smk_destroy_label_list(&list_tmp);
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	return rc;
205662306a36Sopenharmony_ci}
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_cistatic const struct file_operations smk_onlycap_ops = {
205962306a36Sopenharmony_ci	.open		= smk_open_onlycap,
206062306a36Sopenharmony_ci	.read		= seq_read,
206162306a36Sopenharmony_ci	.write		= smk_write_onlycap,
206262306a36Sopenharmony_ci	.llseek		= seq_lseek,
206362306a36Sopenharmony_ci	.release	= seq_release,
206462306a36Sopenharmony_ci};
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SMACK_BRINGUP
206762306a36Sopenharmony_ci/**
206862306a36Sopenharmony_ci * smk_read_unconfined - read() for smackfs/unconfined
206962306a36Sopenharmony_ci * @filp: file pointer, not actually used
207062306a36Sopenharmony_ci * @buf: where to put the result
207162306a36Sopenharmony_ci * @cn: maximum to send along
207262306a36Sopenharmony_ci * @ppos: where to start
207362306a36Sopenharmony_ci *
207462306a36Sopenharmony_ci * Returns number of bytes read or error code, as appropriate
207562306a36Sopenharmony_ci */
207662306a36Sopenharmony_cistatic ssize_t smk_read_unconfined(struct file *filp, char __user *buf,
207762306a36Sopenharmony_ci					size_t cn, loff_t *ppos)
207862306a36Sopenharmony_ci{
207962306a36Sopenharmony_ci	char *smack = "";
208062306a36Sopenharmony_ci	ssize_t rc = -EINVAL;
208162306a36Sopenharmony_ci	int asize;
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci	if (*ppos != 0)
208462306a36Sopenharmony_ci		return 0;
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	if (smack_unconfined != NULL)
208762306a36Sopenharmony_ci		smack = smack_unconfined->smk_known;
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	asize = strlen(smack) + 1;
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	if (cn >= asize)
209262306a36Sopenharmony_ci		rc = simple_read_from_buffer(buf, cn, ppos, smack, asize);
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	return rc;
209562306a36Sopenharmony_ci}
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci/**
209862306a36Sopenharmony_ci * smk_write_unconfined - write() for smackfs/unconfined
209962306a36Sopenharmony_ci * @file: file pointer, not actually used
210062306a36Sopenharmony_ci * @buf: where to get the data from
210162306a36Sopenharmony_ci * @count: bytes sent
210262306a36Sopenharmony_ci * @ppos: where to start
210362306a36Sopenharmony_ci *
210462306a36Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
210562306a36Sopenharmony_ci */
210662306a36Sopenharmony_cistatic ssize_t smk_write_unconfined(struct file *file, const char __user *buf,
210762306a36Sopenharmony_ci					size_t count, loff_t *ppos)
210862306a36Sopenharmony_ci{
210962306a36Sopenharmony_ci	char *data;
211062306a36Sopenharmony_ci	struct smack_known *skp;
211162306a36Sopenharmony_ci	int rc = count;
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
211462306a36Sopenharmony_ci		return -EPERM;
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	if (count > PAGE_SIZE)
211762306a36Sopenharmony_ci		return -EINVAL;
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci	data = memdup_user_nul(buf, count);
212062306a36Sopenharmony_ci	if (IS_ERR(data))
212162306a36Sopenharmony_ci		return PTR_ERR(data);
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	/*
212462306a36Sopenharmony_ci	 * Clear the smack_unconfined on invalid label errors. This means
212562306a36Sopenharmony_ci	 * that we can pass a null string to unset the unconfined value.
212662306a36Sopenharmony_ci	 *
212762306a36Sopenharmony_ci	 * Importing will also reject a label beginning with '-',
212862306a36Sopenharmony_ci	 * so "-confine" will also work.
212962306a36Sopenharmony_ci	 *
213062306a36Sopenharmony_ci	 * But do so only on invalid label, not on system errors.
213162306a36Sopenharmony_ci	 */
213262306a36Sopenharmony_ci	skp = smk_import_entry(data, count);
213362306a36Sopenharmony_ci	if (PTR_ERR(skp) == -EINVAL)
213462306a36Sopenharmony_ci		skp = NULL;
213562306a36Sopenharmony_ci	else if (IS_ERR(skp)) {
213662306a36Sopenharmony_ci		rc = PTR_ERR(skp);
213762306a36Sopenharmony_ci		goto freeout;
213862306a36Sopenharmony_ci	}
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_ci	smack_unconfined = skp;
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_cifreeout:
214362306a36Sopenharmony_ci	kfree(data);
214462306a36Sopenharmony_ci	return rc;
214562306a36Sopenharmony_ci}
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_cistatic const struct file_operations smk_unconfined_ops = {
214862306a36Sopenharmony_ci	.read		= smk_read_unconfined,
214962306a36Sopenharmony_ci	.write		= smk_write_unconfined,
215062306a36Sopenharmony_ci	.llseek		= default_llseek,
215162306a36Sopenharmony_ci};
215262306a36Sopenharmony_ci#endif /* CONFIG_SECURITY_SMACK_BRINGUP */
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci/**
215562306a36Sopenharmony_ci * smk_read_logging - read() for /smack/logging
215662306a36Sopenharmony_ci * @filp: file pointer, not actually used
215762306a36Sopenharmony_ci * @buf: where to put the result
215862306a36Sopenharmony_ci * @count: maximum to send along
215962306a36Sopenharmony_ci * @ppos: where to start
216062306a36Sopenharmony_ci *
216162306a36Sopenharmony_ci * Returns number of bytes read or error code, as appropriate
216262306a36Sopenharmony_ci */
216362306a36Sopenharmony_cistatic ssize_t smk_read_logging(struct file *filp, char __user *buf,
216462306a36Sopenharmony_ci				size_t count, loff_t *ppos)
216562306a36Sopenharmony_ci{
216662306a36Sopenharmony_ci	char temp[32];
216762306a36Sopenharmony_ci	ssize_t rc;
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	if (*ppos != 0)
217062306a36Sopenharmony_ci		return 0;
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	sprintf(temp, "%d\n", log_policy);
217362306a36Sopenharmony_ci	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
217462306a36Sopenharmony_ci	return rc;
217562306a36Sopenharmony_ci}
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci/**
217862306a36Sopenharmony_ci * smk_write_logging - write() for /smack/logging
217962306a36Sopenharmony_ci * @file: file pointer, not actually used
218062306a36Sopenharmony_ci * @buf: where to get the data from
218162306a36Sopenharmony_ci * @count: bytes sent
218262306a36Sopenharmony_ci * @ppos: where to start
218362306a36Sopenharmony_ci *
218462306a36Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
218562306a36Sopenharmony_ci */
218662306a36Sopenharmony_cistatic ssize_t smk_write_logging(struct file *file, const char __user *buf,
218762306a36Sopenharmony_ci				size_t count, loff_t *ppos)
218862306a36Sopenharmony_ci{
218962306a36Sopenharmony_ci	char temp[32];
219062306a36Sopenharmony_ci	int i;
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
219362306a36Sopenharmony_ci		return -EPERM;
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	if (count >= sizeof(temp) || count == 0)
219662306a36Sopenharmony_ci		return -EINVAL;
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	if (copy_from_user(temp, buf, count) != 0)
219962306a36Sopenharmony_ci		return -EFAULT;
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci	temp[count] = '\0';
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci	if (sscanf(temp, "%d", &i) != 1)
220462306a36Sopenharmony_ci		return -EINVAL;
220562306a36Sopenharmony_ci	if (i < 0 || i > 3)
220662306a36Sopenharmony_ci		return -EINVAL;
220762306a36Sopenharmony_ci	log_policy = i;
220862306a36Sopenharmony_ci	return count;
220962306a36Sopenharmony_ci}
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_cistatic const struct file_operations smk_logging_ops = {
221462306a36Sopenharmony_ci	.read		= smk_read_logging,
221562306a36Sopenharmony_ci	.write		= smk_write_logging,
221662306a36Sopenharmony_ci	.llseek		= default_llseek,
221762306a36Sopenharmony_ci};
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci/*
222062306a36Sopenharmony_ci * Seq_file read operations for /smack/load-self
222162306a36Sopenharmony_ci */
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_cistatic void *load_self_seq_start(struct seq_file *s, loff_t *pos)
222462306a36Sopenharmony_ci{
222562306a36Sopenharmony_ci	struct task_smack *tsp = smack_cred(current_cred());
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	return smk_seq_start(s, pos, &tsp->smk_rules);
222862306a36Sopenharmony_ci}
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_cistatic void *load_self_seq_next(struct seq_file *s, void *v, loff_t *pos)
223162306a36Sopenharmony_ci{
223262306a36Sopenharmony_ci	struct task_smack *tsp = smack_cred(current_cred());
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_ci	return smk_seq_next(s, v, pos, &tsp->smk_rules);
223562306a36Sopenharmony_ci}
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_cistatic int load_self_seq_show(struct seq_file *s, void *v)
223862306a36Sopenharmony_ci{
223962306a36Sopenharmony_ci	struct list_head *list = v;
224062306a36Sopenharmony_ci	struct smack_rule *srp =
224162306a36Sopenharmony_ci		list_entry_rcu(list, struct smack_rule, list);
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	smk_rule_show(s, srp, SMK_LABELLEN);
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci	return 0;
224662306a36Sopenharmony_ci}
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_cistatic const struct seq_operations load_self_seq_ops = {
224962306a36Sopenharmony_ci	.start = load_self_seq_start,
225062306a36Sopenharmony_ci	.next  = load_self_seq_next,
225162306a36Sopenharmony_ci	.show  = load_self_seq_show,
225262306a36Sopenharmony_ci	.stop  = smk_seq_stop,
225362306a36Sopenharmony_ci};
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci/**
225762306a36Sopenharmony_ci * smk_open_load_self - open() for /smack/load-self2
225862306a36Sopenharmony_ci * @inode: inode structure representing file
225962306a36Sopenharmony_ci * @file: "load" file pointer
226062306a36Sopenharmony_ci *
226162306a36Sopenharmony_ci * For reading, use load_seq_* seq_file reading operations.
226262306a36Sopenharmony_ci */
226362306a36Sopenharmony_cistatic int smk_open_load_self(struct inode *inode, struct file *file)
226462306a36Sopenharmony_ci{
226562306a36Sopenharmony_ci	return seq_open(file, &load_self_seq_ops);
226662306a36Sopenharmony_ci}
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci/**
226962306a36Sopenharmony_ci * smk_write_load_self - write() for /smack/load-self
227062306a36Sopenharmony_ci * @file: file pointer, not actually used
227162306a36Sopenharmony_ci * @buf: where to get the data from
227262306a36Sopenharmony_ci * @count: bytes sent
227362306a36Sopenharmony_ci * @ppos: where to start - must be 0
227462306a36Sopenharmony_ci *
227562306a36Sopenharmony_ci */
227662306a36Sopenharmony_cistatic ssize_t smk_write_load_self(struct file *file, const char __user *buf,
227762306a36Sopenharmony_ci			      size_t count, loff_t *ppos)
227862306a36Sopenharmony_ci{
227962306a36Sopenharmony_ci	struct task_smack *tsp = smack_cred(current_cred());
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules,
228262306a36Sopenharmony_ci				    &tsp->smk_rules_lock, SMK_FIXED24_FMT);
228362306a36Sopenharmony_ci}
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_cistatic const struct file_operations smk_load_self_ops = {
228662306a36Sopenharmony_ci	.open           = smk_open_load_self,
228762306a36Sopenharmony_ci	.read		= seq_read,
228862306a36Sopenharmony_ci	.llseek         = seq_lseek,
228962306a36Sopenharmony_ci	.write		= smk_write_load_self,
229062306a36Sopenharmony_ci	.release        = seq_release,
229162306a36Sopenharmony_ci};
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci/**
229462306a36Sopenharmony_ci * smk_user_access - handle access check transaction
229562306a36Sopenharmony_ci * @file: file pointer
229662306a36Sopenharmony_ci * @buf: data from user space
229762306a36Sopenharmony_ci * @count: bytes sent
229862306a36Sopenharmony_ci * @ppos: where to start - must be 0
229962306a36Sopenharmony_ci * @format: /smack/load or /smack/load2 or /smack/change-rule format.
230062306a36Sopenharmony_ci */
230162306a36Sopenharmony_cistatic ssize_t smk_user_access(struct file *file, const char __user *buf,
230262306a36Sopenharmony_ci				size_t count, loff_t *ppos, int format)
230362306a36Sopenharmony_ci{
230462306a36Sopenharmony_ci	struct smack_parsed_rule rule;
230562306a36Sopenharmony_ci	char *data;
230662306a36Sopenharmony_ci	int res;
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci	data = simple_transaction_get(file, buf, count);
230962306a36Sopenharmony_ci	if (IS_ERR(data))
231062306a36Sopenharmony_ci		return PTR_ERR(data);
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	if (format == SMK_FIXED24_FMT) {
231362306a36Sopenharmony_ci		if (count < SMK_LOADLEN)
231462306a36Sopenharmony_ci			return -EINVAL;
231562306a36Sopenharmony_ci		res = smk_parse_rule(data, &rule, 0);
231662306a36Sopenharmony_ci	} else {
231762306a36Sopenharmony_ci		/*
231862306a36Sopenharmony_ci		 * simple_transaction_get() returns null-terminated data
231962306a36Sopenharmony_ci		 */
232062306a36Sopenharmony_ci		res = smk_parse_long_rule(data, &rule, 0, 3);
232162306a36Sopenharmony_ci	}
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	if (res >= 0)
232462306a36Sopenharmony_ci		res = smk_access(rule.smk_subject, rule.smk_object,
232562306a36Sopenharmony_ci				 rule.smk_access1, NULL);
232662306a36Sopenharmony_ci	else if (res != -ENOENT)
232762306a36Sopenharmony_ci		return res;
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci	/*
233062306a36Sopenharmony_ci	 * smk_access() can return a value > 0 in the "bringup" case.
233162306a36Sopenharmony_ci	 */
233262306a36Sopenharmony_ci	data[0] = res >= 0 ? '1' : '0';
233362306a36Sopenharmony_ci	data[1] = '\0';
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	simple_transaction_set(file, 2);
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ci	if (format == SMK_FIXED24_FMT)
233862306a36Sopenharmony_ci		return SMK_LOADLEN;
233962306a36Sopenharmony_ci	return count;
234062306a36Sopenharmony_ci}
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_ci/**
234362306a36Sopenharmony_ci * smk_write_access - handle access check transaction
234462306a36Sopenharmony_ci * @file: file pointer
234562306a36Sopenharmony_ci * @buf: data from user space
234662306a36Sopenharmony_ci * @count: bytes sent
234762306a36Sopenharmony_ci * @ppos: where to start - must be 0
234862306a36Sopenharmony_ci */
234962306a36Sopenharmony_cistatic ssize_t smk_write_access(struct file *file, const char __user *buf,
235062306a36Sopenharmony_ci				size_t count, loff_t *ppos)
235162306a36Sopenharmony_ci{
235262306a36Sopenharmony_ci	return smk_user_access(file, buf, count, ppos, SMK_FIXED24_FMT);
235362306a36Sopenharmony_ci}
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_cistatic const struct file_operations smk_access_ops = {
235662306a36Sopenharmony_ci	.write		= smk_write_access,
235762306a36Sopenharmony_ci	.read		= simple_transaction_read,
235862306a36Sopenharmony_ci	.release	= simple_transaction_release,
235962306a36Sopenharmony_ci	.llseek		= generic_file_llseek,
236062306a36Sopenharmony_ci};
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci/*
236462306a36Sopenharmony_ci * Seq_file read operations for /smack/load2
236562306a36Sopenharmony_ci */
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_cistatic int load2_seq_show(struct seq_file *s, void *v)
236862306a36Sopenharmony_ci{
236962306a36Sopenharmony_ci	struct list_head *list = v;
237062306a36Sopenharmony_ci	struct smack_rule *srp;
237162306a36Sopenharmony_ci	struct smack_known *skp =
237262306a36Sopenharmony_ci		list_entry_rcu(list, struct smack_known, list);
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci	list_for_each_entry_rcu(srp, &skp->smk_rules, list)
237562306a36Sopenharmony_ci		smk_rule_show(s, srp, SMK_LONGLABEL);
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci	return 0;
237862306a36Sopenharmony_ci}
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_cistatic const struct seq_operations load2_seq_ops = {
238162306a36Sopenharmony_ci	.start = load2_seq_start,
238262306a36Sopenharmony_ci	.next  = load2_seq_next,
238362306a36Sopenharmony_ci	.show  = load2_seq_show,
238462306a36Sopenharmony_ci	.stop  = smk_seq_stop,
238562306a36Sopenharmony_ci};
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_ci/**
238862306a36Sopenharmony_ci * smk_open_load2 - open() for /smack/load2
238962306a36Sopenharmony_ci * @inode: inode structure representing file
239062306a36Sopenharmony_ci * @file: "load2" file pointer
239162306a36Sopenharmony_ci *
239262306a36Sopenharmony_ci * For reading, use load2_seq_* seq_file reading operations.
239362306a36Sopenharmony_ci */
239462306a36Sopenharmony_cistatic int smk_open_load2(struct inode *inode, struct file *file)
239562306a36Sopenharmony_ci{
239662306a36Sopenharmony_ci	return seq_open(file, &load2_seq_ops);
239762306a36Sopenharmony_ci}
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_ci/**
240062306a36Sopenharmony_ci * smk_write_load2 - write() for /smack/load2
240162306a36Sopenharmony_ci * @file: file pointer, not actually used
240262306a36Sopenharmony_ci * @buf: where to get the data from
240362306a36Sopenharmony_ci * @count: bytes sent
240462306a36Sopenharmony_ci * @ppos: where to start - must be 0
240562306a36Sopenharmony_ci *
240662306a36Sopenharmony_ci */
240762306a36Sopenharmony_cistatic ssize_t smk_write_load2(struct file *file, const char __user *buf,
240862306a36Sopenharmony_ci				size_t count, loff_t *ppos)
240962306a36Sopenharmony_ci{
241062306a36Sopenharmony_ci	/*
241162306a36Sopenharmony_ci	 * Must have privilege.
241262306a36Sopenharmony_ci	 */
241362306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
241462306a36Sopenharmony_ci		return -EPERM;
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_ci	return smk_write_rules_list(file, buf, count, ppos, NULL, NULL,
241762306a36Sopenharmony_ci				    SMK_LONG_FMT);
241862306a36Sopenharmony_ci}
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_cistatic const struct file_operations smk_load2_ops = {
242162306a36Sopenharmony_ci	.open           = smk_open_load2,
242262306a36Sopenharmony_ci	.read		= seq_read,
242362306a36Sopenharmony_ci	.llseek         = seq_lseek,
242462306a36Sopenharmony_ci	.write		= smk_write_load2,
242562306a36Sopenharmony_ci	.release        = seq_release,
242662306a36Sopenharmony_ci};
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci/*
242962306a36Sopenharmony_ci * Seq_file read operations for /smack/load-self2
243062306a36Sopenharmony_ci */
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_cistatic void *load_self2_seq_start(struct seq_file *s, loff_t *pos)
243362306a36Sopenharmony_ci{
243462306a36Sopenharmony_ci	struct task_smack *tsp = smack_cred(current_cred());
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci	return smk_seq_start(s, pos, &tsp->smk_rules);
243762306a36Sopenharmony_ci}
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_cistatic void *load_self2_seq_next(struct seq_file *s, void *v, loff_t *pos)
244062306a36Sopenharmony_ci{
244162306a36Sopenharmony_ci	struct task_smack *tsp = smack_cred(current_cred());
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci	return smk_seq_next(s, v, pos, &tsp->smk_rules);
244462306a36Sopenharmony_ci}
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_cistatic int load_self2_seq_show(struct seq_file *s, void *v)
244762306a36Sopenharmony_ci{
244862306a36Sopenharmony_ci	struct list_head *list = v;
244962306a36Sopenharmony_ci	struct smack_rule *srp =
245062306a36Sopenharmony_ci		list_entry_rcu(list, struct smack_rule, list);
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci	smk_rule_show(s, srp, SMK_LONGLABEL);
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	return 0;
245562306a36Sopenharmony_ci}
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_cistatic const struct seq_operations load_self2_seq_ops = {
245862306a36Sopenharmony_ci	.start = load_self2_seq_start,
245962306a36Sopenharmony_ci	.next  = load_self2_seq_next,
246062306a36Sopenharmony_ci	.show  = load_self2_seq_show,
246162306a36Sopenharmony_ci	.stop  = smk_seq_stop,
246262306a36Sopenharmony_ci};
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_ci/**
246562306a36Sopenharmony_ci * smk_open_load_self2 - open() for /smack/load-self2
246662306a36Sopenharmony_ci * @inode: inode structure representing file
246762306a36Sopenharmony_ci * @file: "load" file pointer
246862306a36Sopenharmony_ci *
246962306a36Sopenharmony_ci * For reading, use load_seq_* seq_file reading operations.
247062306a36Sopenharmony_ci */
247162306a36Sopenharmony_cistatic int smk_open_load_self2(struct inode *inode, struct file *file)
247262306a36Sopenharmony_ci{
247362306a36Sopenharmony_ci	return seq_open(file, &load_self2_seq_ops);
247462306a36Sopenharmony_ci}
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci/**
247762306a36Sopenharmony_ci * smk_write_load_self2 - write() for /smack/load-self2
247862306a36Sopenharmony_ci * @file: file pointer, not actually used
247962306a36Sopenharmony_ci * @buf: where to get the data from
248062306a36Sopenharmony_ci * @count: bytes sent
248162306a36Sopenharmony_ci * @ppos: where to start - must be 0
248262306a36Sopenharmony_ci *
248362306a36Sopenharmony_ci */
248462306a36Sopenharmony_cistatic ssize_t smk_write_load_self2(struct file *file, const char __user *buf,
248562306a36Sopenharmony_ci			      size_t count, loff_t *ppos)
248662306a36Sopenharmony_ci{
248762306a36Sopenharmony_ci	struct task_smack *tsp = smack_cred(current_cred());
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci	return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules,
249062306a36Sopenharmony_ci				    &tsp->smk_rules_lock, SMK_LONG_FMT);
249162306a36Sopenharmony_ci}
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_cistatic const struct file_operations smk_load_self2_ops = {
249462306a36Sopenharmony_ci	.open           = smk_open_load_self2,
249562306a36Sopenharmony_ci	.read		= seq_read,
249662306a36Sopenharmony_ci	.llseek         = seq_lseek,
249762306a36Sopenharmony_ci	.write		= smk_write_load_self2,
249862306a36Sopenharmony_ci	.release        = seq_release,
249962306a36Sopenharmony_ci};
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci/**
250262306a36Sopenharmony_ci * smk_write_access2 - handle access check transaction
250362306a36Sopenharmony_ci * @file: file pointer
250462306a36Sopenharmony_ci * @buf: data from user space
250562306a36Sopenharmony_ci * @count: bytes sent
250662306a36Sopenharmony_ci * @ppos: where to start - must be 0
250762306a36Sopenharmony_ci */
250862306a36Sopenharmony_cistatic ssize_t smk_write_access2(struct file *file, const char __user *buf,
250962306a36Sopenharmony_ci					size_t count, loff_t *ppos)
251062306a36Sopenharmony_ci{
251162306a36Sopenharmony_ci	return smk_user_access(file, buf, count, ppos, SMK_LONG_FMT);
251262306a36Sopenharmony_ci}
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_cistatic const struct file_operations smk_access2_ops = {
251562306a36Sopenharmony_ci	.write		= smk_write_access2,
251662306a36Sopenharmony_ci	.read		= simple_transaction_read,
251762306a36Sopenharmony_ci	.release	= simple_transaction_release,
251862306a36Sopenharmony_ci	.llseek		= generic_file_llseek,
251962306a36Sopenharmony_ci};
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_ci/**
252262306a36Sopenharmony_ci * smk_write_revoke_subj - write() for /smack/revoke-subject
252362306a36Sopenharmony_ci * @file: file pointer
252462306a36Sopenharmony_ci * @buf: data from user space
252562306a36Sopenharmony_ci * @count: bytes sent
252662306a36Sopenharmony_ci * @ppos: where to start - must be 0
252762306a36Sopenharmony_ci */
252862306a36Sopenharmony_cistatic ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf,
252962306a36Sopenharmony_ci				size_t count, loff_t *ppos)
253062306a36Sopenharmony_ci{
253162306a36Sopenharmony_ci	char *data;
253262306a36Sopenharmony_ci	const char *cp;
253362306a36Sopenharmony_ci	struct smack_known *skp;
253462306a36Sopenharmony_ci	struct smack_rule *sp;
253562306a36Sopenharmony_ci	struct list_head *rule_list;
253662306a36Sopenharmony_ci	struct mutex *rule_lock;
253762306a36Sopenharmony_ci	int rc = count;
253862306a36Sopenharmony_ci
253962306a36Sopenharmony_ci	if (*ppos != 0)
254062306a36Sopenharmony_ci		return -EINVAL;
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
254362306a36Sopenharmony_ci		return -EPERM;
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_ci	if (count == 0 || count > SMK_LONGLABEL)
254662306a36Sopenharmony_ci		return -EINVAL;
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci	data = memdup_user(buf, count);
254962306a36Sopenharmony_ci	if (IS_ERR(data))
255062306a36Sopenharmony_ci		return PTR_ERR(data);
255162306a36Sopenharmony_ci
255262306a36Sopenharmony_ci	cp = smk_parse_smack(data, count);
255362306a36Sopenharmony_ci	if (IS_ERR(cp)) {
255462306a36Sopenharmony_ci		rc = PTR_ERR(cp);
255562306a36Sopenharmony_ci		goto out_data;
255662306a36Sopenharmony_ci	}
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci	skp = smk_find_entry(cp);
255962306a36Sopenharmony_ci	if (skp == NULL)
256062306a36Sopenharmony_ci		goto out_cp;
256162306a36Sopenharmony_ci
256262306a36Sopenharmony_ci	rule_list = &skp->smk_rules;
256362306a36Sopenharmony_ci	rule_lock = &skp->smk_rules_lock;
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	mutex_lock(rule_lock);
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_ci	list_for_each_entry_rcu(sp, rule_list, list)
256862306a36Sopenharmony_ci		sp->smk_access = 0;
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci	mutex_unlock(rule_lock);
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ciout_cp:
257362306a36Sopenharmony_ci	kfree(cp);
257462306a36Sopenharmony_ciout_data:
257562306a36Sopenharmony_ci	kfree(data);
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	return rc;
257862306a36Sopenharmony_ci}
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_cistatic const struct file_operations smk_revoke_subj_ops = {
258162306a36Sopenharmony_ci	.write		= smk_write_revoke_subj,
258262306a36Sopenharmony_ci	.read		= simple_transaction_read,
258362306a36Sopenharmony_ci	.release	= simple_transaction_release,
258462306a36Sopenharmony_ci	.llseek		= generic_file_llseek,
258562306a36Sopenharmony_ci};
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci/**
258862306a36Sopenharmony_ci * smk_init_sysfs - initialize /sys/fs/smackfs
258962306a36Sopenharmony_ci *
259062306a36Sopenharmony_ci */
259162306a36Sopenharmony_cistatic int smk_init_sysfs(void)
259262306a36Sopenharmony_ci{
259362306a36Sopenharmony_ci	return sysfs_create_mount_point(fs_kobj, "smackfs");
259462306a36Sopenharmony_ci}
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci/**
259762306a36Sopenharmony_ci * smk_write_change_rule - write() for /smack/change-rule
259862306a36Sopenharmony_ci * @file: file pointer
259962306a36Sopenharmony_ci * @buf: data from user space
260062306a36Sopenharmony_ci * @count: bytes sent
260162306a36Sopenharmony_ci * @ppos: where to start - must be 0
260262306a36Sopenharmony_ci */
260362306a36Sopenharmony_cistatic ssize_t smk_write_change_rule(struct file *file, const char __user *buf,
260462306a36Sopenharmony_ci				size_t count, loff_t *ppos)
260562306a36Sopenharmony_ci{
260662306a36Sopenharmony_ci	/*
260762306a36Sopenharmony_ci	 * Must have privilege.
260862306a36Sopenharmony_ci	 */
260962306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
261062306a36Sopenharmony_ci		return -EPERM;
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	return smk_write_rules_list(file, buf, count, ppos, NULL, NULL,
261362306a36Sopenharmony_ci				    SMK_CHANGE_FMT);
261462306a36Sopenharmony_ci}
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_cistatic const struct file_operations smk_change_rule_ops = {
261762306a36Sopenharmony_ci	.write		= smk_write_change_rule,
261862306a36Sopenharmony_ci	.read		= simple_transaction_read,
261962306a36Sopenharmony_ci	.release	= simple_transaction_release,
262062306a36Sopenharmony_ci	.llseek		= generic_file_llseek,
262162306a36Sopenharmony_ci};
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_ci/**
262462306a36Sopenharmony_ci * smk_read_syslog - read() for smackfs/syslog
262562306a36Sopenharmony_ci * @filp: file pointer, not actually used
262662306a36Sopenharmony_ci * @buf: where to put the result
262762306a36Sopenharmony_ci * @cn: maximum to send along
262862306a36Sopenharmony_ci * @ppos: where to start
262962306a36Sopenharmony_ci *
263062306a36Sopenharmony_ci * Returns number of bytes read or error code, as appropriate
263162306a36Sopenharmony_ci */
263262306a36Sopenharmony_cistatic ssize_t smk_read_syslog(struct file *filp, char __user *buf,
263362306a36Sopenharmony_ci				size_t cn, loff_t *ppos)
263462306a36Sopenharmony_ci{
263562306a36Sopenharmony_ci	struct smack_known *skp;
263662306a36Sopenharmony_ci	ssize_t rc = -EINVAL;
263762306a36Sopenharmony_ci	int asize;
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci	if (*ppos != 0)
264062306a36Sopenharmony_ci		return 0;
264162306a36Sopenharmony_ci
264262306a36Sopenharmony_ci	if (smack_syslog_label == NULL)
264362306a36Sopenharmony_ci		skp = &smack_known_star;
264462306a36Sopenharmony_ci	else
264562306a36Sopenharmony_ci		skp = smack_syslog_label;
264662306a36Sopenharmony_ci
264762306a36Sopenharmony_ci	asize = strlen(skp->smk_known) + 1;
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci	if (cn >= asize)
265062306a36Sopenharmony_ci		rc = simple_read_from_buffer(buf, cn, ppos, skp->smk_known,
265162306a36Sopenharmony_ci						asize);
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_ci	return rc;
265462306a36Sopenharmony_ci}
265562306a36Sopenharmony_ci
265662306a36Sopenharmony_ci/**
265762306a36Sopenharmony_ci * smk_write_syslog - write() for smackfs/syslog
265862306a36Sopenharmony_ci * @file: file pointer, not actually used
265962306a36Sopenharmony_ci * @buf: where to get the data from
266062306a36Sopenharmony_ci * @count: bytes sent
266162306a36Sopenharmony_ci * @ppos: where to start
266262306a36Sopenharmony_ci *
266362306a36Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
266462306a36Sopenharmony_ci */
266562306a36Sopenharmony_cistatic ssize_t smk_write_syslog(struct file *file, const char __user *buf,
266662306a36Sopenharmony_ci				size_t count, loff_t *ppos)
266762306a36Sopenharmony_ci{
266862306a36Sopenharmony_ci	char *data;
266962306a36Sopenharmony_ci	struct smack_known *skp;
267062306a36Sopenharmony_ci	int rc = count;
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
267362306a36Sopenharmony_ci		return -EPERM;
267462306a36Sopenharmony_ci
267562306a36Sopenharmony_ci	/* Enough data must be present */
267662306a36Sopenharmony_ci	if (count == 0 || count > PAGE_SIZE)
267762306a36Sopenharmony_ci		return -EINVAL;
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci	data = memdup_user_nul(buf, count);
268062306a36Sopenharmony_ci	if (IS_ERR(data))
268162306a36Sopenharmony_ci		return PTR_ERR(data);
268262306a36Sopenharmony_ci
268362306a36Sopenharmony_ci	skp = smk_import_entry(data, count);
268462306a36Sopenharmony_ci	if (IS_ERR(skp))
268562306a36Sopenharmony_ci		rc = PTR_ERR(skp);
268662306a36Sopenharmony_ci	else
268762306a36Sopenharmony_ci		smack_syslog_label = skp;
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci	kfree(data);
269062306a36Sopenharmony_ci	return rc;
269162306a36Sopenharmony_ci}
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_cistatic const struct file_operations smk_syslog_ops = {
269462306a36Sopenharmony_ci	.read		= smk_read_syslog,
269562306a36Sopenharmony_ci	.write		= smk_write_syslog,
269662306a36Sopenharmony_ci	.llseek		= default_llseek,
269762306a36Sopenharmony_ci};
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_ci/*
270062306a36Sopenharmony_ci * Seq_file read operations for /smack/relabel-self
270162306a36Sopenharmony_ci */
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_cistatic void *relabel_self_seq_start(struct seq_file *s, loff_t *pos)
270462306a36Sopenharmony_ci{
270562306a36Sopenharmony_ci	struct task_smack *tsp = smack_cred(current_cred());
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci	return smk_seq_start(s, pos, &tsp->smk_relabel);
270862306a36Sopenharmony_ci}
270962306a36Sopenharmony_ci
271062306a36Sopenharmony_cistatic void *relabel_self_seq_next(struct seq_file *s, void *v, loff_t *pos)
271162306a36Sopenharmony_ci{
271262306a36Sopenharmony_ci	struct task_smack *tsp = smack_cred(current_cred());
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_ci	return smk_seq_next(s, v, pos, &tsp->smk_relabel);
271562306a36Sopenharmony_ci}
271662306a36Sopenharmony_ci
271762306a36Sopenharmony_cistatic int relabel_self_seq_show(struct seq_file *s, void *v)
271862306a36Sopenharmony_ci{
271962306a36Sopenharmony_ci	struct list_head *list = v;
272062306a36Sopenharmony_ci	struct smack_known_list_elem *sklep =
272162306a36Sopenharmony_ci		list_entry(list, struct smack_known_list_elem, list);
272262306a36Sopenharmony_ci
272362306a36Sopenharmony_ci	seq_puts(s, sklep->smk_label->smk_known);
272462306a36Sopenharmony_ci	seq_putc(s, ' ');
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci	return 0;
272762306a36Sopenharmony_ci}
272862306a36Sopenharmony_ci
272962306a36Sopenharmony_cistatic const struct seq_operations relabel_self_seq_ops = {
273062306a36Sopenharmony_ci	.start = relabel_self_seq_start,
273162306a36Sopenharmony_ci	.next  = relabel_self_seq_next,
273262306a36Sopenharmony_ci	.show  = relabel_self_seq_show,
273362306a36Sopenharmony_ci	.stop  = smk_seq_stop,
273462306a36Sopenharmony_ci};
273562306a36Sopenharmony_ci
273662306a36Sopenharmony_ci/**
273762306a36Sopenharmony_ci * smk_open_relabel_self - open() for /smack/relabel-self
273862306a36Sopenharmony_ci * @inode: inode structure representing file
273962306a36Sopenharmony_ci * @file: "relabel-self" file pointer
274062306a36Sopenharmony_ci *
274162306a36Sopenharmony_ci * Connect our relabel_self_seq_* operations with /smack/relabel-self
274262306a36Sopenharmony_ci * file_operations
274362306a36Sopenharmony_ci */
274462306a36Sopenharmony_cistatic int smk_open_relabel_self(struct inode *inode, struct file *file)
274562306a36Sopenharmony_ci{
274662306a36Sopenharmony_ci	return seq_open(file, &relabel_self_seq_ops);
274762306a36Sopenharmony_ci}
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci/**
275062306a36Sopenharmony_ci * smk_write_relabel_self - write() for /smack/relabel-self
275162306a36Sopenharmony_ci * @file: file pointer, not actually used
275262306a36Sopenharmony_ci * @buf: where to get the data from
275362306a36Sopenharmony_ci * @count: bytes sent
275462306a36Sopenharmony_ci * @ppos: where to start - must be 0
275562306a36Sopenharmony_ci *
275662306a36Sopenharmony_ci */
275762306a36Sopenharmony_cistatic ssize_t smk_write_relabel_self(struct file *file, const char __user *buf,
275862306a36Sopenharmony_ci				size_t count, loff_t *ppos)
275962306a36Sopenharmony_ci{
276062306a36Sopenharmony_ci	char *data;
276162306a36Sopenharmony_ci	int rc;
276262306a36Sopenharmony_ci	LIST_HEAD(list_tmp);
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci	/*
276562306a36Sopenharmony_ci	 * Must have privilege.
276662306a36Sopenharmony_ci	 */
276762306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
276862306a36Sopenharmony_ci		return -EPERM;
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci	/*
277162306a36Sopenharmony_ci	 * No partial write.
277262306a36Sopenharmony_ci	 * Enough data must be present.
277362306a36Sopenharmony_ci	 */
277462306a36Sopenharmony_ci	if (*ppos != 0)
277562306a36Sopenharmony_ci		return -EINVAL;
277662306a36Sopenharmony_ci	if (count == 0 || count > PAGE_SIZE)
277762306a36Sopenharmony_ci		return -EINVAL;
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_ci	data = memdup_user_nul(buf, count);
278062306a36Sopenharmony_ci	if (IS_ERR(data))
278162306a36Sopenharmony_ci		return PTR_ERR(data);
278262306a36Sopenharmony_ci
278362306a36Sopenharmony_ci	rc = smk_parse_label_list(data, &list_tmp);
278462306a36Sopenharmony_ci	kfree(data);
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci	if (!rc || (rc == -EINVAL && list_empty(&list_tmp))) {
278762306a36Sopenharmony_ci		struct cred *new;
278862306a36Sopenharmony_ci		struct task_smack *tsp;
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci		new = prepare_creds();
279162306a36Sopenharmony_ci		if (!new) {
279262306a36Sopenharmony_ci			rc = -ENOMEM;
279362306a36Sopenharmony_ci			goto out;
279462306a36Sopenharmony_ci		}
279562306a36Sopenharmony_ci		tsp = smack_cred(new);
279662306a36Sopenharmony_ci		smk_destroy_label_list(&tsp->smk_relabel);
279762306a36Sopenharmony_ci		list_splice(&list_tmp, &tsp->smk_relabel);
279862306a36Sopenharmony_ci		commit_creds(new);
279962306a36Sopenharmony_ci		return count;
280062306a36Sopenharmony_ci	}
280162306a36Sopenharmony_ciout:
280262306a36Sopenharmony_ci	smk_destroy_label_list(&list_tmp);
280362306a36Sopenharmony_ci	return rc;
280462306a36Sopenharmony_ci}
280562306a36Sopenharmony_ci
280662306a36Sopenharmony_cistatic const struct file_operations smk_relabel_self_ops = {
280762306a36Sopenharmony_ci	.open		= smk_open_relabel_self,
280862306a36Sopenharmony_ci	.read		= seq_read,
280962306a36Sopenharmony_ci	.llseek		= seq_lseek,
281062306a36Sopenharmony_ci	.write		= smk_write_relabel_self,
281162306a36Sopenharmony_ci	.release	= seq_release,
281262306a36Sopenharmony_ci};
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci/**
281562306a36Sopenharmony_ci * smk_read_ptrace - read() for /smack/ptrace
281662306a36Sopenharmony_ci * @filp: file pointer, not actually used
281762306a36Sopenharmony_ci * @buf: where to put the result
281862306a36Sopenharmony_ci * @count: maximum to send along
281962306a36Sopenharmony_ci * @ppos: where to start
282062306a36Sopenharmony_ci *
282162306a36Sopenharmony_ci * Returns number of bytes read or error code, as appropriate
282262306a36Sopenharmony_ci */
282362306a36Sopenharmony_cistatic ssize_t smk_read_ptrace(struct file *filp, char __user *buf,
282462306a36Sopenharmony_ci			       size_t count, loff_t *ppos)
282562306a36Sopenharmony_ci{
282662306a36Sopenharmony_ci	char temp[32];
282762306a36Sopenharmony_ci	ssize_t rc;
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci	if (*ppos != 0)
283062306a36Sopenharmony_ci		return 0;
283162306a36Sopenharmony_ci
283262306a36Sopenharmony_ci	sprintf(temp, "%d\n", smack_ptrace_rule);
283362306a36Sopenharmony_ci	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
283462306a36Sopenharmony_ci	return rc;
283562306a36Sopenharmony_ci}
283662306a36Sopenharmony_ci
283762306a36Sopenharmony_ci/**
283862306a36Sopenharmony_ci * smk_write_ptrace - write() for /smack/ptrace
283962306a36Sopenharmony_ci * @file: file pointer
284062306a36Sopenharmony_ci * @buf: data from user space
284162306a36Sopenharmony_ci * @count: bytes sent
284262306a36Sopenharmony_ci * @ppos: where to start - must be 0
284362306a36Sopenharmony_ci */
284462306a36Sopenharmony_cistatic ssize_t smk_write_ptrace(struct file *file, const char __user *buf,
284562306a36Sopenharmony_ci				size_t count, loff_t *ppos)
284662306a36Sopenharmony_ci{
284762306a36Sopenharmony_ci	char temp[32];
284862306a36Sopenharmony_ci	int i;
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_ci	if (!smack_privileged(CAP_MAC_ADMIN))
285162306a36Sopenharmony_ci		return -EPERM;
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci	if (*ppos != 0 || count >= sizeof(temp) || count == 0)
285462306a36Sopenharmony_ci		return -EINVAL;
285562306a36Sopenharmony_ci
285662306a36Sopenharmony_ci	if (copy_from_user(temp, buf, count) != 0)
285762306a36Sopenharmony_ci		return -EFAULT;
285862306a36Sopenharmony_ci
285962306a36Sopenharmony_ci	temp[count] = '\0';
286062306a36Sopenharmony_ci
286162306a36Sopenharmony_ci	if (sscanf(temp, "%d", &i) != 1)
286262306a36Sopenharmony_ci		return -EINVAL;
286362306a36Sopenharmony_ci	if (i < SMACK_PTRACE_DEFAULT || i > SMACK_PTRACE_MAX)
286462306a36Sopenharmony_ci		return -EINVAL;
286562306a36Sopenharmony_ci	smack_ptrace_rule = i;
286662306a36Sopenharmony_ci
286762306a36Sopenharmony_ci	return count;
286862306a36Sopenharmony_ci}
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_cistatic const struct file_operations smk_ptrace_ops = {
287162306a36Sopenharmony_ci	.write		= smk_write_ptrace,
287262306a36Sopenharmony_ci	.read		= smk_read_ptrace,
287362306a36Sopenharmony_ci	.llseek		= default_llseek,
287462306a36Sopenharmony_ci};
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_ci/**
287762306a36Sopenharmony_ci * smk_fill_super - fill the smackfs superblock
287862306a36Sopenharmony_ci * @sb: the empty superblock
287962306a36Sopenharmony_ci * @fc: unused
288062306a36Sopenharmony_ci *
288162306a36Sopenharmony_ci * Fill in the well known entries for the smack filesystem
288262306a36Sopenharmony_ci *
288362306a36Sopenharmony_ci * Returns 0 on success, an error code on failure
288462306a36Sopenharmony_ci */
288562306a36Sopenharmony_cistatic int smk_fill_super(struct super_block *sb, struct fs_context *fc)
288662306a36Sopenharmony_ci{
288762306a36Sopenharmony_ci	int rc;
288862306a36Sopenharmony_ci
288962306a36Sopenharmony_ci	static const struct tree_descr smack_files[] = {
289062306a36Sopenharmony_ci		[SMK_LOAD] = {
289162306a36Sopenharmony_ci			"load", &smk_load_ops, S_IRUGO|S_IWUSR},
289262306a36Sopenharmony_ci		[SMK_CIPSO] = {
289362306a36Sopenharmony_ci			"cipso", &smk_cipso_ops, S_IRUGO|S_IWUSR},
289462306a36Sopenharmony_ci		[SMK_DOI] = {
289562306a36Sopenharmony_ci			"doi", &smk_doi_ops, S_IRUGO|S_IWUSR},
289662306a36Sopenharmony_ci		[SMK_DIRECT] = {
289762306a36Sopenharmony_ci			"direct", &smk_direct_ops, S_IRUGO|S_IWUSR},
289862306a36Sopenharmony_ci		[SMK_AMBIENT] = {
289962306a36Sopenharmony_ci			"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
290062306a36Sopenharmony_ci		[SMK_NET4ADDR] = {
290162306a36Sopenharmony_ci			"netlabel", &smk_net4addr_ops, S_IRUGO|S_IWUSR},
290262306a36Sopenharmony_ci		[SMK_ONLYCAP] = {
290362306a36Sopenharmony_ci			"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
290462306a36Sopenharmony_ci		[SMK_LOGGING] = {
290562306a36Sopenharmony_ci			"logging", &smk_logging_ops, S_IRUGO|S_IWUSR},
290662306a36Sopenharmony_ci		[SMK_LOAD_SELF] = {
290762306a36Sopenharmony_ci			"load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO},
290862306a36Sopenharmony_ci		[SMK_ACCESSES] = {
290962306a36Sopenharmony_ci			"access", &smk_access_ops, S_IRUGO|S_IWUGO},
291062306a36Sopenharmony_ci		[SMK_MAPPED] = {
291162306a36Sopenharmony_ci			"mapped", &smk_mapped_ops, S_IRUGO|S_IWUSR},
291262306a36Sopenharmony_ci		[SMK_LOAD2] = {
291362306a36Sopenharmony_ci			"load2", &smk_load2_ops, S_IRUGO|S_IWUSR},
291462306a36Sopenharmony_ci		[SMK_LOAD_SELF2] = {
291562306a36Sopenharmony_ci			"load-self2", &smk_load_self2_ops, S_IRUGO|S_IWUGO},
291662306a36Sopenharmony_ci		[SMK_ACCESS2] = {
291762306a36Sopenharmony_ci			"access2", &smk_access2_ops, S_IRUGO|S_IWUGO},
291862306a36Sopenharmony_ci		[SMK_CIPSO2] = {
291962306a36Sopenharmony_ci			"cipso2", &smk_cipso2_ops, S_IRUGO|S_IWUSR},
292062306a36Sopenharmony_ci		[SMK_REVOKE_SUBJ] = {
292162306a36Sopenharmony_ci			"revoke-subject", &smk_revoke_subj_ops,
292262306a36Sopenharmony_ci			S_IRUGO|S_IWUSR},
292362306a36Sopenharmony_ci		[SMK_CHANGE_RULE] = {
292462306a36Sopenharmony_ci			"change-rule", &smk_change_rule_ops, S_IRUGO|S_IWUSR},
292562306a36Sopenharmony_ci		[SMK_SYSLOG] = {
292662306a36Sopenharmony_ci			"syslog", &smk_syslog_ops, S_IRUGO|S_IWUSR},
292762306a36Sopenharmony_ci		[SMK_PTRACE] = {
292862306a36Sopenharmony_ci			"ptrace", &smk_ptrace_ops, S_IRUGO|S_IWUSR},
292962306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SMACK_BRINGUP
293062306a36Sopenharmony_ci		[SMK_UNCONFINED] = {
293162306a36Sopenharmony_ci			"unconfined", &smk_unconfined_ops, S_IRUGO|S_IWUSR},
293262306a36Sopenharmony_ci#endif
293362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
293462306a36Sopenharmony_ci		[SMK_NET6ADDR] = {
293562306a36Sopenharmony_ci			"ipv6host", &smk_net6addr_ops, S_IRUGO|S_IWUSR},
293662306a36Sopenharmony_ci#endif /* CONFIG_IPV6 */
293762306a36Sopenharmony_ci		[SMK_RELABEL_SELF] = {
293862306a36Sopenharmony_ci			"relabel-self", &smk_relabel_self_ops,
293962306a36Sopenharmony_ci				S_IRUGO|S_IWUGO},
294062306a36Sopenharmony_ci		/* last one */
294162306a36Sopenharmony_ci			{""}
294262306a36Sopenharmony_ci	};
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci	rc = simple_fill_super(sb, SMACK_MAGIC, smack_files);
294562306a36Sopenharmony_ci	if (rc != 0) {
294662306a36Sopenharmony_ci		printk(KERN_ERR "%s failed %d while creating inodes\n",
294762306a36Sopenharmony_ci			__func__, rc);
294862306a36Sopenharmony_ci		return rc;
294962306a36Sopenharmony_ci	}
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_ci	return 0;
295262306a36Sopenharmony_ci}
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci/**
295562306a36Sopenharmony_ci * smk_get_tree - get the smackfs superblock
295662306a36Sopenharmony_ci * @fc: The mount context, including any options
295762306a36Sopenharmony_ci *
295862306a36Sopenharmony_ci * Just passes everything along.
295962306a36Sopenharmony_ci *
296062306a36Sopenharmony_ci * Returns what the lower level code does.
296162306a36Sopenharmony_ci */
296262306a36Sopenharmony_cistatic int smk_get_tree(struct fs_context *fc)
296362306a36Sopenharmony_ci{
296462306a36Sopenharmony_ci	return get_tree_single(fc, smk_fill_super);
296562306a36Sopenharmony_ci}
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_cistatic const struct fs_context_operations smk_context_ops = {
296862306a36Sopenharmony_ci	.get_tree	= smk_get_tree,
296962306a36Sopenharmony_ci};
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_ci/**
297262306a36Sopenharmony_ci * smk_init_fs_context - Initialise a filesystem context for smackfs
297362306a36Sopenharmony_ci * @fc: The blank mount context
297462306a36Sopenharmony_ci */
297562306a36Sopenharmony_cistatic int smk_init_fs_context(struct fs_context *fc)
297662306a36Sopenharmony_ci{
297762306a36Sopenharmony_ci	fc->ops = &smk_context_ops;
297862306a36Sopenharmony_ci	return 0;
297962306a36Sopenharmony_ci}
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_cistatic struct file_system_type smk_fs_type = {
298262306a36Sopenharmony_ci	.name		= "smackfs",
298362306a36Sopenharmony_ci	.init_fs_context = smk_init_fs_context,
298462306a36Sopenharmony_ci	.kill_sb	= kill_litter_super,
298562306a36Sopenharmony_ci};
298662306a36Sopenharmony_ci
298762306a36Sopenharmony_cistatic struct vfsmount *smackfs_mount;
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_ci/**
299062306a36Sopenharmony_ci * init_smk_fs - get the smackfs superblock
299162306a36Sopenharmony_ci *
299262306a36Sopenharmony_ci * register the smackfs
299362306a36Sopenharmony_ci *
299462306a36Sopenharmony_ci * Do not register smackfs if Smack wasn't enabled
299562306a36Sopenharmony_ci * on boot. We can not put this method normally under the
299662306a36Sopenharmony_ci * smack_init() code path since the security subsystem get
299762306a36Sopenharmony_ci * initialized before the vfs caches.
299862306a36Sopenharmony_ci *
299962306a36Sopenharmony_ci * Returns true if we were not chosen on boot or if
300062306a36Sopenharmony_ci * we were chosen and filesystem registration succeeded.
300162306a36Sopenharmony_ci */
300262306a36Sopenharmony_cistatic int __init init_smk_fs(void)
300362306a36Sopenharmony_ci{
300462306a36Sopenharmony_ci	int err;
300562306a36Sopenharmony_ci	int rc;
300662306a36Sopenharmony_ci
300762306a36Sopenharmony_ci	if (smack_enabled == 0)
300862306a36Sopenharmony_ci		return 0;
300962306a36Sopenharmony_ci
301062306a36Sopenharmony_ci	err = smk_init_sysfs();
301162306a36Sopenharmony_ci	if (err)
301262306a36Sopenharmony_ci		printk(KERN_ERR "smackfs: sysfs mountpoint problem.\n");
301362306a36Sopenharmony_ci
301462306a36Sopenharmony_ci	err = register_filesystem(&smk_fs_type);
301562306a36Sopenharmony_ci	if (!err) {
301662306a36Sopenharmony_ci		smackfs_mount = kern_mount(&smk_fs_type);
301762306a36Sopenharmony_ci		if (IS_ERR(smackfs_mount)) {
301862306a36Sopenharmony_ci			printk(KERN_ERR "smackfs:  could not mount!\n");
301962306a36Sopenharmony_ci			err = PTR_ERR(smackfs_mount);
302062306a36Sopenharmony_ci			smackfs_mount = NULL;
302162306a36Sopenharmony_ci		}
302262306a36Sopenharmony_ci	}
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci	smk_cipso_doi();
302562306a36Sopenharmony_ci	smk_unlbl_ambient(NULL);
302662306a36Sopenharmony_ci
302762306a36Sopenharmony_ci	rc = smack_populate_secattr(&smack_known_floor);
302862306a36Sopenharmony_ci	if (err == 0 && rc < 0)
302962306a36Sopenharmony_ci		err = rc;
303062306a36Sopenharmony_ci	rc = smack_populate_secattr(&smack_known_hat);
303162306a36Sopenharmony_ci	if (err == 0 && rc < 0)
303262306a36Sopenharmony_ci		err = rc;
303362306a36Sopenharmony_ci	rc = smack_populate_secattr(&smack_known_huh);
303462306a36Sopenharmony_ci	if (err == 0 && rc < 0)
303562306a36Sopenharmony_ci		err = rc;
303662306a36Sopenharmony_ci	rc = smack_populate_secattr(&smack_known_star);
303762306a36Sopenharmony_ci	if (err == 0 && rc < 0)
303862306a36Sopenharmony_ci		err = rc;
303962306a36Sopenharmony_ci	rc = smack_populate_secattr(&smack_known_web);
304062306a36Sopenharmony_ci	if (err == 0 && rc < 0)
304162306a36Sopenharmony_ci		err = rc;
304262306a36Sopenharmony_ci
304362306a36Sopenharmony_ci	return err;
304462306a36Sopenharmony_ci}
304562306a36Sopenharmony_ci
304662306a36Sopenharmony_ci__initcall(init_smk_fs);
3047