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