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 */
ver_to_u64(const struct ipe_policy * const p)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 */
ipe_free_policy(struct ipe_policy * p)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
set_pkcs7_data(void * ctx,const void * data,size_t len,size_t asn1hdrlen __always_unused)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 */
ipe_update_policy(struct inode * root,const char * text,size_t textlen,const char * pkcs7,size_t pkcs7len)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 */
ipe_new_policy(const char * text,size_t textlen,const char * pkcs7,size_t pkcs7len)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 */
ipe_set_active_pol(const struct ipe_policy * p)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