File Index
Files and directories
Sign in
FILE

policy.c

File preview with syntax highlighting.

linux-6.19.14/security/ipe/policy.c C
  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 */
 17DEFINE_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 */
 30static 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 */
 47void 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
 64static 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 */
 93int 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;
135err:
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 */
159struct 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;
208err:
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 */
222int 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}
~/ Read-only file preview.