Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/security/ipe/policy.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
4
*/
5
6
#include <linux/errno.h>
7
#include <linux/verification.h>
8
9
#include "ipe.h"
10
#include "eval.h"
11
#include "fs.h"
12
#include "policy.h"
13
#include "policy_parser.h"
14
#include "audit.h"
15
16
/* lock for synchronizing writers across ipe policy */
17
DEFINE_MUTEX(ipe_policy_lock);
18
19
/**
20
* ver_to_u64() - Convert an internal ipe_policy_version to a u64.
21
* @p: Policy to extract the version from.
22
*
23
* Bits (LSB is index 0):
24
* [48,32] -> Major
25
* [32,16] -> Minor
26
* [16, 0] -> Revision
27
*
28
* Return: u64 version of the embedded version structure.
29
*/
30
static inline u64 ver_to_u64(const struct ipe_policy *const p)
31
{
32
u64 r;
33
34
r = (((u64)p->parsed->version.major) << 32)
35
| (((u64)p->parsed->version.minor) << 16)
36
| ((u64)(p->parsed->version.rev));
37
38
return r;
39
}
40
41
/**
42
* ipe_free_policy() - Deallocate a given IPE policy.
43
* @p: Supplies the policy to free.
44
*
45
* Safe to call on IS_ERR/NULL.
46
*/
47
void ipe_free_policy(struct ipe_policy *p)
48
{
49
if (IS_ERR_OR_NULL(p))
50
return;
51
52
ipe_del_policyfs_node(p);
53
ipe_free_parsed_policy(p->parsed);
54
/*
55
* p->text is allocated only when p->pkcs7 is not NULL
56
* otherwise it points to the plaintext data inside the pkcs7
57
*/
58
if (!p->pkcs7)
59
kfree(p->text);
60
kfree(p->pkcs7);
61
kfree(p);
62
}
63
64
static int set_pkcs7_data(void *ctx, const void *data, size_t len,
65
size_t asn1hdrlen __always_unused)
66
{
67
struct ipe_policy *p = ctx;
68
69
p->text = (const char *)data;
70
p->textlen = len;
71
72
return 0;
73
}
74
75
/**
76
* ipe_update_policy() - parse a new policy and replace old with it.
77
* @root: Supplies a pointer to the securityfs inode saved the policy.
78
* @text: Supplies a pointer to the plain text policy.
79
* @textlen: Supplies the length of @text.
80
* @pkcs7: Supplies a pointer to a buffer containing a pkcs7 message.
81
* @pkcs7len: Supplies the length of @pkcs7len.
82
*
83
* @text/@textlen is mutually exclusive with @pkcs7/@pkcs7len - see
84
* ipe_new_policy.
85
*
86
* Context: Requires root->i_rwsem to be held.
87
* Return:
88
* * %0 - Success
89
* * %-ENOENT - Policy was deleted while updating
90
* * %-EINVAL - Policy name mismatch
91
* * %-ESTALE - Policy version too old
92
*/
93
int ipe_update_policy(struct inode *root, const char *text, size_t textlen,
94
const char *pkcs7, size_t pkcs7len)
95
{
96
struct ipe_policy *old, *ap, *new = NULL;
97
int rc = 0;
98
99
old = (struct ipe_policy *)root->i_private;
100
if (!old)
101
return -ENOENT;
102
103
new = ipe_new_policy(text, textlen, pkcs7, pkcs7len);
104
if (IS_ERR(new))
105
return PTR_ERR(new);
106
107
if (strcmp(new->parsed->name, old->parsed->name)) {
108
rc = -EINVAL;
109
goto err;
110
}
111
112
if (ver_to_u64(old) >= ver_to_u64(new)) {
113
rc = -ESTALE;
114
goto err;
115
}
116
117
root->i_private = new;
118
swap(new->policyfs, old->policyfs);
119
ipe_audit_policy_load(new);
120
121
mutex_lock(&ipe_policy_lock);
122
ap = rcu_dereference_protected(ipe_active_policy,
123
lockdep_is_held(&ipe_policy_lock));
124
if (old == ap) {
125
rcu_assign_pointer(ipe_active_policy, new);
126
mutex_unlock(&ipe_policy_lock);
127
ipe_audit_policy_activation(old, new);
128
} else {
129
mutex_unlock(&ipe_policy_lock);
130
}
131
synchronize_rcu();
132
ipe_free_policy(old);
133
134
return 0;
135
err:
136
ipe_free_policy(new);
137
return rc;
138
}
139
140
/**
141
* ipe_new_policy() - Allocate and parse an ipe_policy structure.
142
*
143
* @text: Supplies a pointer to the plain-text policy to parse.
144
* @textlen: Supplies the length of @text.
145
* @pkcs7: Supplies a pointer to a pkcs7-signed IPE policy.
146
* @pkcs7len: Supplies the length of @pkcs7.
147
*
148
* @text/@textlen Should be NULL/0 if @pkcs7/@pkcs7len is set.
149
*
150
* Return:
151
* * a pointer to the ipe_policy structure - Success
152
* * %-EBADMSG - Policy is invalid
153
* * %-ENOMEM - Out of memory (OOM)
154
* * %-ERANGE - Policy version number overflow
155
* * %-EINVAL - Policy version parsing error
156
* * %-ENOKEY - Policy signing key not found
157
* * %-EKEYREJECTED - Policy signature verification failed
158
*/
159
struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
160
const char *pkcs7, size_t pkcs7len)
161
{
162
struct ipe_policy *new = NULL;
163
int rc = 0;
164
165
new = kzalloc(sizeof(*new), GFP_KERNEL);
166
if (!new)
167
return ERR_PTR(-ENOMEM);
168
169
if (!text) {
170
new->pkcs7len = pkcs7len;
171
new->pkcs7 = kmemdup(pkcs7, pkcs7len, GFP_KERNEL);
172
if (!new->pkcs7) {
173
rc = -ENOMEM;
174
goto err;
175
}
176
177
rc = verify_pkcs7_signature(NULL, 0, new->pkcs7, pkcs7len,
178
#ifdef CONFIG_IPE_POLICY_SIG_SECONDARY_KEYRING
179
VERIFY_USE_SECONDARY_KEYRING,
180
#else
181
NULL,
182
#endif
183
VERIFYING_UNSPECIFIED_SIGNATURE,
184
set_pkcs7_data, new);
185
#ifdef CONFIG_IPE_POLICY_SIG_PLATFORM_KEYRING
186
if (rc == -ENOKEY || rc == -EKEYREJECTED)
187
rc = verify_pkcs7_signature(NULL, 0, new->pkcs7, pkcs7len,
188
VERIFY_USE_PLATFORM_KEYRING,
189
VERIFYING_UNSPECIFIED_SIGNATURE,
190
set_pkcs7_data, new);
191
#endif
192
if (rc)
193
goto err;
194
} else {
195
new->textlen = textlen;
196
new->text = kstrdup(text, GFP_KERNEL);
197
if (!new->text) {
198
rc = -ENOMEM;
199
goto err;
200
}
201
}
202
203
rc = ipe_parse_policy(new);
204
if (rc)
205
goto err;
206
207
return new;
208
err:
209
ipe_free_policy(new);
210
return ERR_PTR(rc);
211
}
212
213
/**
214
* ipe_set_active_pol() - Make @p the active policy.
215
* @p: Supplies a pointer to the policy to make active.
216
*
217
* Context: Requires root->i_rwsem, which i_private has the policy, to be held.
218
* Return:
219
* * %0 - Success
220
* * %-EINVAL - New active policy version is invalid
221
*/
222
int ipe_set_active_pol(const struct ipe_policy *p)
223
{
224
struct ipe_policy *ap = NULL;
225
226
mutex_lock(&ipe_policy_lock);
227
228
ap = rcu_dereference_protected(ipe_active_policy,
229
lockdep_is_held(&ipe_policy_lock));
230
if (ap == p) {
231
mutex_unlock(&ipe_policy_lock);
232
return 0;
233
}
234
if (ap && ver_to_u64(ap) > ver_to_u64(p)) {
235
mutex_unlock(&ipe_policy_lock);
236
return -EINVAL;
237
}
238
239
rcu_assign_pointer(ipe_active_policy, p);
240
ipe_audit_policy_activation(ap, p);
241
mutex_unlock(&ipe_policy_lock);
242
243
return 0;
244
}
245
246