xref: /linux/security/ipe/policy.c (revision 8203ca380913af8d807f82089ec623e117955c85)
154a88cd2SDeven Bowers // SPDX-License-Identifier: GPL-2.0
254a88cd2SDeven Bowers /*
354a88cd2SDeven Bowers  * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
454a88cd2SDeven Bowers  */
554a88cd2SDeven Bowers 
654a88cd2SDeven Bowers #include <linux/errno.h>
754a88cd2SDeven Bowers #include <linux/verification.h>
854a88cd2SDeven Bowers 
954a88cd2SDeven Bowers #include "ipe.h"
102261306fSDeven Bowers #include "eval.h"
112261306fSDeven Bowers #include "fs.h"
1254a88cd2SDeven Bowers #include "policy.h"
1354a88cd2SDeven Bowers #include "policy_parser.h"
14f44554b5SDeven Bowers #include "audit.h"
1554a88cd2SDeven Bowers 
162261306fSDeven Bowers /* lock for synchronizing writers across ipe policy */
172261306fSDeven Bowers DEFINE_MUTEX(ipe_policy_lock);
182261306fSDeven Bowers 
192261306fSDeven Bowers /**
202261306fSDeven Bowers  * ver_to_u64() - Convert an internal ipe_policy_version to a u64.
212261306fSDeven Bowers  * @p: Policy to extract the version from.
222261306fSDeven Bowers  *
232261306fSDeven Bowers  * Bits (LSB is index 0):
242261306fSDeven Bowers  *	[48,32] -> Major
252261306fSDeven Bowers  *	[32,16] -> Minor
262261306fSDeven Bowers  *	[16, 0] -> Revision
272261306fSDeven Bowers  *
282261306fSDeven Bowers  * Return: u64 version of the embedded version structure.
292261306fSDeven Bowers  */
ver_to_u64(const struct ipe_policy * const p)302261306fSDeven Bowers static inline u64 ver_to_u64(const struct ipe_policy *const p)
312261306fSDeven Bowers {
322261306fSDeven Bowers 	u64 r;
332261306fSDeven Bowers 
342261306fSDeven Bowers 	r = (((u64)p->parsed->version.major) << 32)
352261306fSDeven Bowers 	  | (((u64)p->parsed->version.minor) << 16)
362261306fSDeven Bowers 	  | ((u64)(p->parsed->version.rev));
372261306fSDeven Bowers 
382261306fSDeven Bowers 	return r;
392261306fSDeven Bowers }
402261306fSDeven Bowers 
4154a88cd2SDeven Bowers /**
4254a88cd2SDeven Bowers  * ipe_free_policy() - Deallocate a given IPE policy.
4354a88cd2SDeven Bowers  * @p: Supplies the policy to free.
4454a88cd2SDeven Bowers  *
4554a88cd2SDeven Bowers  * Safe to call on IS_ERR/NULL.
4654a88cd2SDeven Bowers  */
ipe_free_policy(struct ipe_policy * p)4754a88cd2SDeven Bowers void ipe_free_policy(struct ipe_policy *p)
4854a88cd2SDeven Bowers {
4954a88cd2SDeven Bowers 	if (IS_ERR_OR_NULL(p))
5054a88cd2SDeven Bowers 		return;
5154a88cd2SDeven Bowers 
522261306fSDeven Bowers 	ipe_del_policyfs_node(p);
5354a88cd2SDeven Bowers 	ipe_free_parsed_policy(p->parsed);
5454a88cd2SDeven Bowers 	/*
5554a88cd2SDeven Bowers 	 * p->text is allocated only when p->pkcs7 is not NULL
5654a88cd2SDeven Bowers 	 * otherwise it points to the plaintext data inside the pkcs7
5754a88cd2SDeven Bowers 	 */
5854a88cd2SDeven Bowers 	if (!p->pkcs7)
5954a88cd2SDeven Bowers 		kfree(p->text);
6054a88cd2SDeven Bowers 	kfree(p->pkcs7);
6154a88cd2SDeven Bowers 	kfree(p);
6254a88cd2SDeven Bowers }
6354a88cd2SDeven Bowers 
set_pkcs7_data(void * ctx,const void * data,size_t len,size_t asn1hdrlen __always_unused)6454a88cd2SDeven Bowers static int set_pkcs7_data(void *ctx, const void *data, size_t len,
6554a88cd2SDeven Bowers 			  size_t asn1hdrlen __always_unused)
6654a88cd2SDeven Bowers {
6754a88cd2SDeven Bowers 	struct ipe_policy *p = ctx;
6854a88cd2SDeven Bowers 
6954a88cd2SDeven Bowers 	p->text = (const char *)data;
7054a88cd2SDeven Bowers 	p->textlen = len;
7154a88cd2SDeven Bowers 
7254a88cd2SDeven Bowers 	return 0;
7354a88cd2SDeven Bowers }
7454a88cd2SDeven Bowers 
7554a88cd2SDeven Bowers /**
762261306fSDeven Bowers  * ipe_update_policy() - parse a new policy and replace old with it.
772261306fSDeven Bowers  * @root: Supplies a pointer to the securityfs inode saved the policy.
782261306fSDeven Bowers  * @text: Supplies a pointer to the plain text policy.
792261306fSDeven Bowers  * @textlen: Supplies the length of @text.
802261306fSDeven Bowers  * @pkcs7: Supplies a pointer to a buffer containing a pkcs7 message.
812261306fSDeven Bowers  * @pkcs7len: Supplies the length of @pkcs7len.
822261306fSDeven Bowers  *
832261306fSDeven Bowers  * @text/@textlen is mutually exclusive with @pkcs7/@pkcs7len - see
842261306fSDeven Bowers  * ipe_new_policy.
852261306fSDeven Bowers  *
862261306fSDeven Bowers  * Context: Requires root->i_rwsem to be held.
872261306fSDeven Bowers  * Return: %0 on success. If an error occurs, the function will return
882261306fSDeven Bowers  * the -errno.
892261306fSDeven Bowers  */
ipe_update_policy(struct inode * root,const char * text,size_t textlen,const char * pkcs7,size_t pkcs7len)902261306fSDeven Bowers int ipe_update_policy(struct inode *root, const char *text, size_t textlen,
912261306fSDeven Bowers 		      const char *pkcs7, size_t pkcs7len)
922261306fSDeven Bowers {
932261306fSDeven Bowers 	struct ipe_policy *old, *ap, *new = NULL;
942261306fSDeven Bowers 	int rc = 0;
952261306fSDeven Bowers 
962261306fSDeven Bowers 	old = (struct ipe_policy *)root->i_private;
972261306fSDeven Bowers 	if (!old)
982261306fSDeven Bowers 		return -ENOENT;
992261306fSDeven Bowers 
1002261306fSDeven Bowers 	new = ipe_new_policy(text, textlen, pkcs7, pkcs7len);
1012261306fSDeven Bowers 	if (IS_ERR(new))
1022261306fSDeven Bowers 		return PTR_ERR(new);
1032261306fSDeven Bowers 
1042261306fSDeven Bowers 	if (strcmp(new->parsed->name, old->parsed->name)) {
1052261306fSDeven Bowers 		rc = -EINVAL;
1062261306fSDeven Bowers 		goto err;
1072261306fSDeven Bowers 	}
1082261306fSDeven Bowers 
1095ceecb30SLuca Boccassi 	if (ver_to_u64(old) >= ver_to_u64(new)) {
11057994189SLuca Boccassi 		rc = -ESTALE;
1112261306fSDeven Bowers 		goto err;
1122261306fSDeven Bowers 	}
1132261306fSDeven Bowers 
1142261306fSDeven Bowers 	root->i_private = new;
1152261306fSDeven Bowers 	swap(new->policyfs, old->policyfs);
116f44554b5SDeven Bowers 	ipe_audit_policy_load(new);
1172261306fSDeven Bowers 
1182261306fSDeven Bowers 	mutex_lock(&ipe_policy_lock);
1192261306fSDeven Bowers 	ap = rcu_dereference_protected(ipe_active_policy,
1202261306fSDeven Bowers 				       lockdep_is_held(&ipe_policy_lock));
1212261306fSDeven Bowers 	if (old == ap) {
1222261306fSDeven Bowers 		rcu_assign_pointer(ipe_active_policy, new);
1232261306fSDeven Bowers 		mutex_unlock(&ipe_policy_lock);
124f44554b5SDeven Bowers 		ipe_audit_policy_activation(old, new);
1252261306fSDeven Bowers 	} else {
1262261306fSDeven Bowers 		mutex_unlock(&ipe_policy_lock);
1272261306fSDeven Bowers 	}
1282261306fSDeven Bowers 	synchronize_rcu();
1292261306fSDeven Bowers 	ipe_free_policy(old);
1302261306fSDeven Bowers 
1312261306fSDeven Bowers 	return 0;
1322261306fSDeven Bowers err:
1332261306fSDeven Bowers 	ipe_free_policy(new);
1342261306fSDeven Bowers 	return rc;
1352261306fSDeven Bowers }
1362261306fSDeven Bowers 
1372261306fSDeven Bowers /**
13854a88cd2SDeven Bowers  * ipe_new_policy() - Allocate and parse an ipe_policy structure.
13954a88cd2SDeven Bowers  *
14054a88cd2SDeven Bowers  * @text: Supplies a pointer to the plain-text policy to parse.
14154a88cd2SDeven Bowers  * @textlen: Supplies the length of @text.
14254a88cd2SDeven Bowers  * @pkcs7: Supplies a pointer to a pkcs7-signed IPE policy.
14354a88cd2SDeven Bowers  * @pkcs7len: Supplies the length of @pkcs7.
14454a88cd2SDeven Bowers  *
14554a88cd2SDeven Bowers  * @text/@textlen Should be NULL/0 if @pkcs7/@pkcs7len is set.
14654a88cd2SDeven Bowers  *
14754a88cd2SDeven Bowers  * Return:
14854a88cd2SDeven Bowers  * * a pointer to the ipe_policy structure	- Success
14954a88cd2SDeven Bowers  * * %-EBADMSG					- Policy is invalid
15054a88cd2SDeven Bowers  * * %-ENOMEM					- Out of memory (OOM)
15154a88cd2SDeven Bowers  * * %-ERANGE					- Policy version number overflow
15254a88cd2SDeven Bowers  * * %-EINVAL					- Policy version parsing error
15354a88cd2SDeven Bowers  */
ipe_new_policy(const char * text,size_t textlen,const char * pkcs7,size_t pkcs7len)15454a88cd2SDeven Bowers struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
15554a88cd2SDeven Bowers 				  const char *pkcs7, size_t pkcs7len)
15654a88cd2SDeven Bowers {
15754a88cd2SDeven Bowers 	struct ipe_policy *new = NULL;
15854a88cd2SDeven Bowers 	int rc = 0;
15954a88cd2SDeven Bowers 
16054a88cd2SDeven Bowers 	new = kzalloc(sizeof(*new), GFP_KERNEL);
16154a88cd2SDeven Bowers 	if (!new)
16254a88cd2SDeven Bowers 		return ERR_PTR(-ENOMEM);
16354a88cd2SDeven Bowers 
16454a88cd2SDeven Bowers 	if (!text) {
16554a88cd2SDeven Bowers 		new->pkcs7len = pkcs7len;
16654a88cd2SDeven Bowers 		new->pkcs7 = kmemdup(pkcs7, pkcs7len, GFP_KERNEL);
16754a88cd2SDeven Bowers 		if (!new->pkcs7) {
16854a88cd2SDeven Bowers 			rc = -ENOMEM;
16954a88cd2SDeven Bowers 			goto err;
17054a88cd2SDeven Bowers 		}
17154a88cd2SDeven Bowers 
17202e2f9aaSLuca Boccassi 		rc = verify_pkcs7_signature(NULL, 0, new->pkcs7, pkcs7len,
17302e2f9aaSLuca Boccassi #ifdef CONFIG_IPE_POLICY_SIG_SECONDARY_KEYRING
17402e2f9aaSLuca Boccassi 					    VERIFY_USE_SECONDARY_KEYRING,
17502e2f9aaSLuca Boccassi #else
17602e2f9aaSLuca Boccassi 					    NULL,
17702e2f9aaSLuca Boccassi #endif
17854a88cd2SDeven Bowers 					    VERIFYING_UNSPECIFIED_SIGNATURE,
17954a88cd2SDeven Bowers 					    set_pkcs7_data, new);
18002e2f9aaSLuca Boccassi #ifdef CONFIG_IPE_POLICY_SIG_PLATFORM_KEYRING
181*f40998a8SLuca Boccassi 		if (rc == -ENOKEY || rc == -EKEYREJECTED)
18202e2f9aaSLuca Boccassi 			rc = verify_pkcs7_signature(NULL, 0, new->pkcs7, pkcs7len,
18302e2f9aaSLuca Boccassi 						    VERIFY_USE_PLATFORM_KEYRING,
18402e2f9aaSLuca Boccassi 						    VERIFYING_UNSPECIFIED_SIGNATURE,
18502e2f9aaSLuca Boccassi 						    set_pkcs7_data, new);
18602e2f9aaSLuca Boccassi #endif
18754a88cd2SDeven Bowers 		if (rc)
18854a88cd2SDeven Bowers 			goto err;
18954a88cd2SDeven Bowers 	} else {
19054a88cd2SDeven Bowers 		new->textlen = textlen;
19154a88cd2SDeven Bowers 		new->text = kstrdup(text, GFP_KERNEL);
19254a88cd2SDeven Bowers 		if (!new->text) {
19354a88cd2SDeven Bowers 			rc = -ENOMEM;
19454a88cd2SDeven Bowers 			goto err;
19554a88cd2SDeven Bowers 		}
19654a88cd2SDeven Bowers 	}
19754a88cd2SDeven Bowers 
19854a88cd2SDeven Bowers 	rc = ipe_parse_policy(new);
19954a88cd2SDeven Bowers 	if (rc)
20054a88cd2SDeven Bowers 		goto err;
20154a88cd2SDeven Bowers 
20254a88cd2SDeven Bowers 	return new;
20354a88cd2SDeven Bowers err:
20454a88cd2SDeven Bowers 	ipe_free_policy(new);
20554a88cd2SDeven Bowers 	return ERR_PTR(rc);
20654a88cd2SDeven Bowers }
2072261306fSDeven Bowers 
2082261306fSDeven Bowers /**
2092261306fSDeven Bowers  * ipe_set_active_pol() - Make @p the active policy.
2102261306fSDeven Bowers  * @p: Supplies a pointer to the policy to make active.
2112261306fSDeven Bowers  *
2122261306fSDeven Bowers  * Context: Requires root->i_rwsem, which i_private has the policy, to be held.
2132261306fSDeven Bowers  * Return:
2142261306fSDeven Bowers  * * %0	- Success
2152261306fSDeven Bowers  * * %-EINVAL	- New active policy version is invalid
2162261306fSDeven Bowers  */
ipe_set_active_pol(const struct ipe_policy * p)2172261306fSDeven Bowers int ipe_set_active_pol(const struct ipe_policy *p)
2182261306fSDeven Bowers {
2192261306fSDeven Bowers 	struct ipe_policy *ap = NULL;
2202261306fSDeven Bowers 
2212261306fSDeven Bowers 	mutex_lock(&ipe_policy_lock);
2222261306fSDeven Bowers 
2232261306fSDeven Bowers 	ap = rcu_dereference_protected(ipe_active_policy,
2242261306fSDeven Bowers 				       lockdep_is_held(&ipe_policy_lock));
2252261306fSDeven Bowers 	if (ap == p) {
2262261306fSDeven Bowers 		mutex_unlock(&ipe_policy_lock);
2272261306fSDeven Bowers 		return 0;
2282261306fSDeven Bowers 	}
2292261306fSDeven Bowers 	if (ap && ver_to_u64(ap) > ver_to_u64(p)) {
2302261306fSDeven Bowers 		mutex_unlock(&ipe_policy_lock);
2312261306fSDeven Bowers 		return -EINVAL;
2322261306fSDeven Bowers 	}
2332261306fSDeven Bowers 
2342261306fSDeven Bowers 	rcu_assign_pointer(ipe_active_policy, p);
235f44554b5SDeven Bowers 	ipe_audit_policy_activation(ap, p);
2362261306fSDeven Bowers 	mutex_unlock(&ipe_policy_lock);
2372261306fSDeven Bowers 
2382261306fSDeven Bowers 	return 0;
2392261306fSDeven Bowers }
240