summaryrefslogtreecommitdiffstats
path: root/security/apparmor/secid.c
blob: 47dc08fc583e9ac486909c0bf3f31dcef5600370 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// SPDX-License-Identifier: GPL-2.0-only
/*
 * AppArmor security module
 *
 * This file contains AppArmor security identifier (secid) manipulation fns
 *
 * Copyright 2009-2017 Canonical Ltd.
 *
 * AppArmor allocates a unique secid for every label used. If a label
 * is replaced it receives the secid of the label it is replacing.
 */

#include <linux/errno.h>
#include <linux/err.h>
#include <linux/gfp.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/xarray.h>

#include "include/cred.h"
#include "include/lib.h"
#include "include/secid.h"
#include "include/label.h"
#include "include/policy_ns.h"

/*
 * secids - do not pin labels with a refcount. They rely on the label
 * properly updating/freeing them
 */
#define AA_FIRST_SECID 2

static DEFINE_XARRAY_FLAGS(aa_secids, XA_FLAGS_LOCK_IRQ | XA_FLAGS_TRACK_FREE);

int apparmor_display_secid_mode;

/*
 * TODO: allow policy to reserve a secid range?
 * TODO: add secid pinning
 * TODO: use secid_update in label replace
 */

/*
 * see label for inverse aa_label_to_secid
 */
struct aa_label *aa_secid_to_label(u32 secid)
{
	return xa_load(&aa_secids, secid);
}

static int apparmor_label_to_secctx(struct aa_label *label, char **secdata,
				    u32 *seclen)
{
	/* TODO: cache secctx and ref count so we don't have to recreate */
	int flags = FLAG_VIEW_SUBNS | FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT;
	int len;

	AA_BUG(!seclen);

	if (!label)
		return -EINVAL;

	if (apparmor_display_secid_mode)
		flags |= FLAG_SHOW_MODE;

	if (secdata)
		len = aa_label_asxprint(secdata, root_ns, label,
					flags, GFP_ATOMIC);
	else
		len = aa_label_snxprint(NULL, 0, root_ns, label, flags);

	if (len < 0)
		return -ENOMEM;

	*seclen = len;

	return 0;
}

int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
{
	struct aa_label *label = aa_secid_to_label(secid);

	return apparmor_label_to_secctx(label, secdata, seclen);
}

int apparmor_lsmprop_to_secctx(struct lsm_prop *prop, char **secdata,
			       u32 *seclen)
{
	struct aa_label *label;

	label = prop->apparmor.label;

	return apparmor_label_to_secctx(label, secdata, seclen);
}

int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
{
	struct aa_label *label;

	label = aa_label_strn_parse(&root_ns->unconfined->label, secdata,
				    seclen, GFP_KERNEL, false, false);
	if (IS_ERR(label))
		return PTR_ERR(label);
	*secid = label->secid;

	return 0;
}

void apparmor_release_secctx(char *secdata, u32 seclen)
{
	kfree(secdata);
}

/**
 * aa_alloc_secid - allocate a new secid for a profile
 * @label: the label to allocate a secid for
 * @gfp: memory allocation flags
 *
 * Returns: 0 with @label->secid initialized
 *          <0 returns error with @label->secid set to AA_SECID_INVALID
 */
int aa_alloc_secid(struct aa_label *label, gfp_t gfp)
{
	unsigned long flags;
	int ret;

	xa_lock_irqsave(&aa_secids, flags);
	ret = __xa_alloc(&aa_secids, &label->secid, label,
			XA_LIMIT(AA_FIRST_SECID, INT_MAX), gfp);
	xa_unlock_irqrestore(&aa_secids, flags);

	if (ret < 0) {
		label->secid = AA_SECID_INVALID;
		return ret;
	}

	return 0;
}

/**
 * aa_free_secid - free a secid
 * @secid: secid to free
 */
void aa_free_secid(u32 secid)
{
	unsigned long flags;

	xa_lock_irqsave(&aa_secids, flags);
	__xa_erase(&aa_secids, secid);
	xa_unlock_irqrestore(&aa_secids, flags);
}