]> Git Repo - linux.git/blob - security/ipe/policy.c
Linux 6.14-rc3
[linux.git] / security / ipe / policy.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 */
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: %0 on success. If an error occurs, the function will return
88  * the -errno.
89  */
90 int ipe_update_policy(struct inode *root, const char *text, size_t textlen,
91                       const char *pkcs7, size_t pkcs7len)
92 {
93         struct ipe_policy *old, *ap, *new = NULL;
94         int rc = 0;
95
96         old = (struct ipe_policy *)root->i_private;
97         if (!old)
98                 return -ENOENT;
99
100         new = ipe_new_policy(text, textlen, pkcs7, pkcs7len);
101         if (IS_ERR(new))
102                 return PTR_ERR(new);
103
104         if (strcmp(new->parsed->name, old->parsed->name)) {
105                 rc = -EINVAL;
106                 goto err;
107         }
108
109         if (ver_to_u64(old) >= ver_to_u64(new)) {
110                 rc = -ESTALE;
111                 goto err;
112         }
113
114         root->i_private = new;
115         swap(new->policyfs, old->policyfs);
116         ipe_audit_policy_load(new);
117
118         mutex_lock(&ipe_policy_lock);
119         ap = rcu_dereference_protected(ipe_active_policy,
120                                        lockdep_is_held(&ipe_policy_lock));
121         if (old == ap) {
122                 rcu_assign_pointer(ipe_active_policy, new);
123                 mutex_unlock(&ipe_policy_lock);
124                 ipe_audit_policy_activation(old, new);
125         } else {
126                 mutex_unlock(&ipe_policy_lock);
127         }
128         synchronize_rcu();
129         ipe_free_policy(old);
130
131         return 0;
132 err:
133         ipe_free_policy(new);
134         return rc;
135 }
136
137 /**
138  * ipe_new_policy() - Allocate and parse an ipe_policy structure.
139  *
140  * @text: Supplies a pointer to the plain-text policy to parse.
141  * @textlen: Supplies the length of @text.
142  * @pkcs7: Supplies a pointer to a pkcs7-signed IPE policy.
143  * @pkcs7len: Supplies the length of @pkcs7.
144  *
145  * @text/@textlen Should be NULL/0 if @pkcs7/@pkcs7len is set.
146  *
147  * Return:
148  * * a pointer to the ipe_policy structure      - Success
149  * * %-EBADMSG                                  - Policy is invalid
150  * * %-ENOMEM                                   - Out of memory (OOM)
151  * * %-ERANGE                                   - Policy version number overflow
152  * * %-EINVAL                                   - Policy version parsing error
153  */
154 struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
155                                   const char *pkcs7, size_t pkcs7len)
156 {
157         struct ipe_policy *new = NULL;
158         int rc = 0;
159
160         new = kzalloc(sizeof(*new), GFP_KERNEL);
161         if (!new)
162                 return ERR_PTR(-ENOMEM);
163
164         if (!text) {
165                 new->pkcs7len = pkcs7len;
166                 new->pkcs7 = kmemdup(pkcs7, pkcs7len, GFP_KERNEL);
167                 if (!new->pkcs7) {
168                         rc = -ENOMEM;
169                         goto err;
170                 }
171
172                 rc = verify_pkcs7_signature(NULL, 0, new->pkcs7, pkcs7len,
173 #ifdef CONFIG_IPE_POLICY_SIG_SECONDARY_KEYRING
174                                             VERIFY_USE_SECONDARY_KEYRING,
175 #else
176                                             NULL,
177 #endif
178                                             VERIFYING_UNSPECIFIED_SIGNATURE,
179                                             set_pkcs7_data, new);
180 #ifdef CONFIG_IPE_POLICY_SIG_PLATFORM_KEYRING
181                 if (rc == -ENOKEY || rc == -EKEYREJECTED)
182                         rc = verify_pkcs7_signature(NULL, 0, new->pkcs7, pkcs7len,
183                                                     VERIFY_USE_PLATFORM_KEYRING,
184                                                     VERIFYING_UNSPECIFIED_SIGNATURE,
185                                                     set_pkcs7_data, new);
186 #endif
187                 if (rc)
188                         goto err;
189         } else {
190                 new->textlen = textlen;
191                 new->text = kstrdup(text, GFP_KERNEL);
192                 if (!new->text) {
193                         rc = -ENOMEM;
194                         goto err;
195                 }
196         }
197
198         rc = ipe_parse_policy(new);
199         if (rc)
200                 goto err;
201
202         return new;
203 err:
204         ipe_free_policy(new);
205         return ERR_PTR(rc);
206 }
207
208 /**
209  * ipe_set_active_pol() - Make @p the active policy.
210  * @p: Supplies a pointer to the policy to make active.
211  *
212  * Context: Requires root->i_rwsem, which i_private has the policy, to be held.
213  * Return:
214  * * %0 - Success
215  * * %-EINVAL   - New active policy version is invalid
216  */
217 int ipe_set_active_pol(const struct ipe_policy *p)
218 {
219         struct ipe_policy *ap = NULL;
220
221         mutex_lock(&ipe_policy_lock);
222
223         ap = rcu_dereference_protected(ipe_active_policy,
224                                        lockdep_is_held(&ipe_policy_lock));
225         if (ap == p) {
226                 mutex_unlock(&ipe_policy_lock);
227                 return 0;
228         }
229         if (ap && ver_to_u64(ap) > ver_to_u64(p)) {
230                 mutex_unlock(&ipe_policy_lock);
231                 return -EINVAL;
232         }
233
234         rcu_assign_pointer(ipe_active_policy, p);
235         ipe_audit_policy_activation(ap, p);
236         mutex_unlock(&ipe_policy_lock);
237
238         return 0;
239 }
This page took 0.039478 seconds and 4 git commands to generate.