xref: /linux/security/ipe/policy.c (revision 2261306f4a3cea362fc40285e750a801dc0cfbe3)
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 
15 /* lock for synchronizing writers across ipe policy */
16 DEFINE_MUTEX(ipe_policy_lock);
17 
18 /**
19  * ver_to_u64() - Convert an internal ipe_policy_version to a u64.
20  * @p: Policy to extract the version from.
21  *
22  * Bits (LSB is index 0):
23  *	[48,32] -> Major
24  *	[32,16] -> Minor
25  *	[16, 0] -> Revision
26  *
27  * Return: u64 version of the embedded version structure.
28  */
29 static inline u64 ver_to_u64(const struct ipe_policy *const p)
30 {
31 	u64 r;
32 
33 	r = (((u64)p->parsed->version.major) << 32)
34 	  | (((u64)p->parsed->version.minor) << 16)
35 	  | ((u64)(p->parsed->version.rev));
36 
37 	return r;
38 }
39 
40 /**
41  * ipe_free_policy() - Deallocate a given IPE policy.
42  * @p: Supplies the policy to free.
43  *
44  * Safe to call on IS_ERR/NULL.
45  */
46 void ipe_free_policy(struct ipe_policy *p)
47 {
48 	if (IS_ERR_OR_NULL(p))
49 		return;
50 
51 	ipe_del_policyfs_node(p);
52 	ipe_free_parsed_policy(p->parsed);
53 	/*
54 	 * p->text is allocated only when p->pkcs7 is not NULL
55 	 * otherwise it points to the plaintext data inside the pkcs7
56 	 */
57 	if (!p->pkcs7)
58 		kfree(p->text);
59 	kfree(p->pkcs7);
60 	kfree(p);
61 }
62 
63 static int set_pkcs7_data(void *ctx, const void *data, size_t len,
64 			  size_t asn1hdrlen __always_unused)
65 {
66 	struct ipe_policy *p = ctx;
67 
68 	p->text = (const char *)data;
69 	p->textlen = len;
70 
71 	return 0;
72 }
73 
74 /**
75  * ipe_update_policy() - parse a new policy and replace old with it.
76  * @root: Supplies a pointer to the securityfs inode saved the policy.
77  * @text: Supplies a pointer to the plain text policy.
78  * @textlen: Supplies the length of @text.
79  * @pkcs7: Supplies a pointer to a buffer containing a pkcs7 message.
80  * @pkcs7len: Supplies the length of @pkcs7len.
81  *
82  * @text/@textlen is mutually exclusive with @pkcs7/@pkcs7len - see
83  * ipe_new_policy.
84  *
85  * Context: Requires root->i_rwsem to be held.
86  * Return: %0 on success. If an error occurs, the function will return
87  * the -errno.
88  */
89 int ipe_update_policy(struct inode *root, const char *text, size_t textlen,
90 		      const char *pkcs7, size_t pkcs7len)
91 {
92 	struct ipe_policy *old, *ap, *new = NULL;
93 	int rc = 0;
94 
95 	old = (struct ipe_policy *)root->i_private;
96 	if (!old)
97 		return -ENOENT;
98 
99 	new = ipe_new_policy(text, textlen, pkcs7, pkcs7len);
100 	if (IS_ERR(new))
101 		return PTR_ERR(new);
102 
103 	if (strcmp(new->parsed->name, old->parsed->name)) {
104 		rc = -EINVAL;
105 		goto err;
106 	}
107 
108 	if (ver_to_u64(old) > ver_to_u64(new)) {
109 		rc = -EINVAL;
110 		goto err;
111 	}
112 
113 	root->i_private = new;
114 	swap(new->policyfs, old->policyfs);
115 
116 	mutex_lock(&ipe_policy_lock);
117 	ap = rcu_dereference_protected(ipe_active_policy,
118 				       lockdep_is_held(&ipe_policy_lock));
119 	if (old == ap) {
120 		rcu_assign_pointer(ipe_active_policy, new);
121 		mutex_unlock(&ipe_policy_lock);
122 	} else {
123 		mutex_unlock(&ipe_policy_lock);
124 	}
125 	synchronize_rcu();
126 	ipe_free_policy(old);
127 
128 	return 0;
129 err:
130 	ipe_free_policy(new);
131 	return rc;
132 }
133 
134 /**
135  * ipe_new_policy() - Allocate and parse an ipe_policy structure.
136  *
137  * @text: Supplies a pointer to the plain-text policy to parse.
138  * @textlen: Supplies the length of @text.
139  * @pkcs7: Supplies a pointer to a pkcs7-signed IPE policy.
140  * @pkcs7len: Supplies the length of @pkcs7.
141  *
142  * @text/@textlen Should be NULL/0 if @pkcs7/@pkcs7len is set.
143  *
144  * Return:
145  * * a pointer to the ipe_policy structure	- Success
146  * * %-EBADMSG					- Policy is invalid
147  * * %-ENOMEM					- Out of memory (OOM)
148  * * %-ERANGE					- Policy version number overflow
149  * * %-EINVAL					- Policy version parsing error
150  */
151 struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
152 				  const char *pkcs7, size_t pkcs7len)
153 {
154 	struct ipe_policy *new = NULL;
155 	int rc = 0;
156 
157 	new = kzalloc(sizeof(*new), GFP_KERNEL);
158 	if (!new)
159 		return ERR_PTR(-ENOMEM);
160 
161 	if (!text) {
162 		new->pkcs7len = pkcs7len;
163 		new->pkcs7 = kmemdup(pkcs7, pkcs7len, GFP_KERNEL);
164 		if (!new->pkcs7) {
165 			rc = -ENOMEM;
166 			goto err;
167 		}
168 
169 		rc = verify_pkcs7_signature(NULL, 0, new->pkcs7, pkcs7len, NULL,
170 					    VERIFYING_UNSPECIFIED_SIGNATURE,
171 					    set_pkcs7_data, new);
172 		if (rc)
173 			goto err;
174 	} else {
175 		new->textlen = textlen;
176 		new->text = kstrdup(text, GFP_KERNEL);
177 		if (!new->text) {
178 			rc = -ENOMEM;
179 			goto err;
180 		}
181 	}
182 
183 	rc = ipe_parse_policy(new);
184 	if (rc)
185 		goto err;
186 
187 	return new;
188 err:
189 	ipe_free_policy(new);
190 	return ERR_PTR(rc);
191 }
192 
193 /**
194  * ipe_set_active_pol() - Make @p the active policy.
195  * @p: Supplies a pointer to the policy to make active.
196  *
197  * Context: Requires root->i_rwsem, which i_private has the policy, to be held.
198  * Return:
199  * * %0	- Success
200  * * %-EINVAL	- New active policy version is invalid
201  */
202 int ipe_set_active_pol(const struct ipe_policy *p)
203 {
204 	struct ipe_policy *ap = NULL;
205 
206 	mutex_lock(&ipe_policy_lock);
207 
208 	ap = rcu_dereference_protected(ipe_active_policy,
209 				       lockdep_is_held(&ipe_policy_lock));
210 	if (ap == p) {
211 		mutex_unlock(&ipe_policy_lock);
212 		return 0;
213 	}
214 	if (ap && ver_to_u64(ap) > ver_to_u64(p)) {
215 		mutex_unlock(&ipe_policy_lock);
216 		return -EINVAL;
217 	}
218 
219 	rcu_assign_pointer(ipe_active_policy, p);
220 	mutex_unlock(&ipe_policy_lock);
221 
222 	return 0;
223 }
224