]>
Commit | Line | Data |
---|---|---|
b886d83c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
3323eec9 MZ |
2 | /* |
3 | * Copyright (C) 2008 IBM Corporation | |
4 | * Author: Mimi Zohar <zohar@us.ibm.com> | |
5 | * | |
3323eec9 | 6 | * ima_policy.c |
2bb930ab | 7 | * - initialize default measure policy rules |
3323eec9 | 8 | */ |
3878d505 | 9 | |
876979c9 | 10 | #include <linux/init.h> |
3323eec9 | 11 | #include <linux/list.h> |
b89999d0 | 12 | #include <linux/kernel_read_file.h> |
cf222217 | 13 | #include <linux/fs.h> |
3323eec9 MZ |
14 | #include <linux/security.h> |
15 | #include <linux/magic.h> | |
4af4662f | 16 | #include <linux/parser.h> |
5a0e3ad6 | 17 | #include <linux/slab.h> |
38d859f9 | 18 | #include <linux/rculist.h> |
85865c1f | 19 | #include <linux/genhd.h> |
80eae209 | 20 | #include <linux/seq_file.h> |
61917062 | 21 | #include <linux/ima.h> |
3323eec9 MZ |
22 | |
23 | #include "ima.h" | |
24 | ||
25 | /* flags definitions */ | |
2bb930ab DK |
26 | #define IMA_FUNC 0x0001 |
27 | #define IMA_MASK 0x0002 | |
3323eec9 MZ |
28 | #define IMA_FSMAGIC 0x0004 |
29 | #define IMA_UID 0x0008 | |
07f6a794 | 30 | #define IMA_FOWNER 0x0010 |
85865c1f | 31 | #define IMA_FSUUID 0x0020 |
4351c294 | 32 | #define IMA_INMASK 0x0040 |
139069ef | 33 | #define IMA_EUID 0x0080 |
0260643c | 34 | #define IMA_PCR 0x0100 |
f1b08bbc | 35 | #define IMA_FSNAME 0x0200 |
2b60c0ec | 36 | #define IMA_KEYRINGS 0x0400 |
47d76a48 | 37 | #define IMA_LABEL 0x0800 |
1624dc00 | 38 | #define IMA_VALIDATE_ALGOS 0x1000 |
3323eec9 | 39 | |
45e2472e DK |
40 | #define UNKNOWN 0 |
41 | #define MEASURE 0x0001 /* same as IMA_MEASURE */ | |
42 | #define DONT_MEASURE 0x0002 | |
43 | #define APPRAISE 0x0004 /* same as IMA_APPRAISE */ | |
44 | #define DONT_APPRAISE 0x0008 | |
e7c568e0 | 45 | #define AUDIT 0x0040 |
da1b0029 MZ |
46 | #define HASH 0x0100 |
47 | #define DONT_HASH 0x0200 | |
4af4662f | 48 | |
0260643c | 49 | #define INVALID_PCR(a) (((a) < 0) || \ |
c593642c | 50 | (a) >= (sizeof_field(struct integrity_iint_cache, measured_pcrs) * 8)) |
0260643c | 51 | |
a756024e | 52 | int ima_policy_flag; |
6ad6afa1 | 53 | static int temp_ima_appraise; |
ef96837b | 54 | static int build_ima_appraise __ro_after_init; |
a756024e | 55 | |
4f2946aa TS |
56 | atomic_t ima_setxattr_allowed_hash_algorithms; |
57 | ||
4af4662f MZ |
58 | #define MAX_LSM_RULES 6 |
59 | enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, | |
60 | LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE | |
61 | }; | |
3323eec9 | 62 | |
24fd03c8 MZ |
63 | enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB }; |
64 | ||
c52657d9 NJ |
65 | enum policy_rule_list { IMA_DEFAULT_POLICY = 1, IMA_CUSTOM_POLICY }; |
66 | ||
176377d9 TH |
67 | struct ima_rule_opt_list { |
68 | size_t count; | |
69 | char *items[]; | |
70 | }; | |
71 | ||
07f6a794 | 72 | struct ima_rule_entry { |
3323eec9 | 73 | struct list_head list; |
2fe5d6de | 74 | int action; |
3323eec9 MZ |
75 | unsigned int flags; |
76 | enum ima_hooks func; | |
77 | int mask; | |
78 | unsigned long fsmagic; | |
787d8c53 | 79 | uuid_t fsuuid; |
8b94eea4 | 80 | kuid_t uid; |
88265322 | 81 | kuid_t fowner; |
3dd0c8d0 MK |
82 | bool (*uid_op)(kuid_t, kuid_t); /* Handlers for operators */ |
83 | bool (*fowner_op)(kuid_t, kuid_t); /* uid_eq(), uid_gt(), uid_lt() */ | |
0260643c | 84 | int pcr; |
1624dc00 | 85 | unsigned int allowed_algos; /* bitfield of allowed hash algorithms */ |
4af4662f | 86 | struct { |
f17b27a2 | 87 | void *rules[LSMBLOB_ENTRIES]; /* LSM file metadata specific */ |
aa0c0227 | 88 | char *args_p; /* audit value */ |
4af4662f MZ |
89 | int type; /* audit type */ |
90 | } lsm[MAX_LSM_RULES]; | |
f1b08bbc | 91 | char *fsname; |
176377d9 | 92 | struct ima_rule_opt_list *keyrings; /* Measure keys added to these keyrings */ |
47d76a48 | 93 | struct ima_rule_opt_list *label; /* Measure data grouped under this label */ |
19453ce0 | 94 | struct ima_template_desc *template; |
3323eec9 MZ |
95 | }; |
96 | ||
f17b27a2 CS |
97 | /** |
98 | * ima_lsm_isset - Is a rule set for any of the active security modules | |
99 | * @rules: The set of IMA rules to check. | |
100 | * | |
101 | * If a rule is set for any LSM return true, otherwise return false. | |
102 | */ | |
103 | static inline bool ima_lsm_isset(void *rules[]) | |
104 | { | |
105 | int i; | |
106 | ||
107 | for (i = 0; i < LSMBLOB_ENTRIES; i++) | |
108 | if (rules[i]) | |
109 | return true; | |
110 | return false; | |
111 | } | |
112 | ||
1624dc00 TS |
113 | /* |
114 | * sanity check in case the kernels gains more hash algorithms that can | |
115 | * fit in an unsigned int | |
116 | */ | |
117 | static_assert( | |
118 | 8 * sizeof(unsigned int) >= HASH_ALGO__LAST, | |
119 | "The bitfield allowed_algos in ima_rule_entry is too small to contain all the supported hash algorithms, consider using a bigger type"); | |
120 | ||
5789ba3b EP |
121 | /* |
122 | * Without LSM specific knowledge, the default policy can only be | |
07f6a794 | 123 | * written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner |
4af4662f | 124 | */ |
5789ba3b EP |
125 | |
126 | /* | |
127 | * The minimum rule set to allow for full TCB coverage. Measures all files | |
128 | * opened or mmap for exec and everything read by root. Dangerous because | |
129 | * normal users can easily run the machine out of memory simply building | |
130 | * and running executables. | |
131 | */ | |
bad4417b | 132 | static struct ima_rule_entry dont_measure_rules[] __ro_after_init = { |
2bb930ab DK |
133 | {.action = DONT_MEASURE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC}, |
134 | {.action = DONT_MEASURE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC}, | |
135 | {.action = DONT_MEASURE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC}, | |
136 | {.action = DONT_MEASURE, .fsmagic = TMPFS_MAGIC, .flags = IMA_FSMAGIC}, | |
137 | {.action = DONT_MEASURE, .fsmagic = DEVPTS_SUPER_MAGIC, .flags = IMA_FSMAGIC}, | |
138 | {.action = DONT_MEASURE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC}, | |
139 | {.action = DONT_MEASURE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC}, | |
140 | {.action = DONT_MEASURE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC}, | |
1c070b18 | 141 | {.action = DONT_MEASURE, .fsmagic = SMACK_MAGIC, .flags = IMA_FSMAGIC}, |
6438de9f RS |
142 | {.action = DONT_MEASURE, .fsmagic = CGROUP_SUPER_MAGIC, |
143 | .flags = IMA_FSMAGIC}, | |
82e3bb4d LA |
144 | {.action = DONT_MEASURE, .fsmagic = CGROUP2_SUPER_MAGIC, |
145 | .flags = IMA_FSMAGIC}, | |
060190fb MZ |
146 | {.action = DONT_MEASURE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC}, |
147 | {.action = DONT_MEASURE, .fsmagic = EFIVARFS_MAGIC, .flags = IMA_FSMAGIC} | |
24fd03c8 MZ |
148 | }; |
149 | ||
bad4417b | 150 | static struct ima_rule_entry original_measurement_rules[] __ro_after_init = { |
24fd03c8 MZ |
151 | {.action = MEASURE, .func = MMAP_CHECK, .mask = MAY_EXEC, |
152 | .flags = IMA_FUNC | IMA_MASK}, | |
153 | {.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC, | |
154 | .flags = IMA_FUNC | IMA_MASK}, | |
155 | {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, | |
3dd0c8d0 MK |
156 | .uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq, |
157 | .flags = IMA_FUNC | IMA_MASK | IMA_UID}, | |
24fd03c8 MZ |
158 | {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, |
159 | {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, | |
160 | }; | |
161 | ||
bad4417b | 162 | static struct ima_rule_entry default_measurement_rules[] __ro_after_init = { |
2bb930ab | 163 | {.action = MEASURE, .func = MMAP_CHECK, .mask = MAY_EXEC, |
3323eec9 | 164 | .flags = IMA_FUNC | IMA_MASK}, |
2bb930ab | 165 | {.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC, |
3323eec9 | 166 | .flags = IMA_FUNC | IMA_MASK}, |
24fd03c8 | 167 | {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, |
3dd0c8d0 MK |
168 | .uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq, |
169 | .flags = IMA_FUNC | IMA_INMASK | IMA_EUID}, | |
24fd03c8 | 170 | {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, |
3dd0c8d0 MK |
171 | .uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq, |
172 | .flags = IMA_FUNC | IMA_INMASK | IMA_UID}, | |
2bb930ab | 173 | {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, |
5a9196d7 | 174 | {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, |
19f8a847 | 175 | {.action = MEASURE, .func = POLICY_CHECK, .flags = IMA_FUNC}, |
3323eec9 MZ |
176 | }; |
177 | ||
bad4417b | 178 | static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { |
2bb930ab DK |
179 | {.action = DONT_APPRAISE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC}, |
180 | {.action = DONT_APPRAISE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC}, | |
181 | {.action = DONT_APPRAISE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC}, | |
182 | {.action = DONT_APPRAISE, .fsmagic = TMPFS_MAGIC, .flags = IMA_FSMAGIC}, | |
183 | {.action = DONT_APPRAISE, .fsmagic = RAMFS_MAGIC, .flags = IMA_FSMAGIC}, | |
184 | {.action = DONT_APPRAISE, .fsmagic = DEVPTS_SUPER_MAGIC, .flags = IMA_FSMAGIC}, | |
185 | {.action = DONT_APPRAISE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC}, | |
186 | {.action = DONT_APPRAISE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC}, | |
187 | {.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC}, | |
1c070b18 | 188 | {.action = DONT_APPRAISE, .fsmagic = SMACK_MAGIC, .flags = IMA_FSMAGIC}, |
cd025f7f | 189 | {.action = DONT_APPRAISE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC}, |
060190fb | 190 | {.action = DONT_APPRAISE, .fsmagic = EFIVARFS_MAGIC, .flags = IMA_FSMAGIC}, |
2bb930ab | 191 | {.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC}, |
82e3bb4d | 192 | {.action = DONT_APPRAISE, .fsmagic = CGROUP2_SUPER_MAGIC, .flags = IMA_FSMAGIC}, |
95ee08fa MZ |
193 | #ifdef CONFIG_IMA_WRITE_POLICY |
194 | {.action = APPRAISE, .func = POLICY_CHECK, | |
195 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | |
196 | #endif | |
c57782c1 | 197 | #ifndef CONFIG_IMA_APPRAISE_SIGNED_INIT |
3dd0c8d0 MK |
198 | {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &uid_eq, |
199 | .flags = IMA_FOWNER}, | |
c57782c1 DK |
200 | #else |
201 | /* force signature */ | |
3dd0c8d0 | 202 | {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &uid_eq, |
c57782c1 DK |
203 | .flags = IMA_FOWNER | IMA_DIGSIG_REQUIRED}, |
204 | #endif | |
07f6a794 MZ |
205 | }; |
206 | ||
ef96837b MZ |
207 | static struct ima_rule_entry build_appraise_rules[] __ro_after_init = { |
208 | #ifdef CONFIG_IMA_APPRAISE_REQUIRE_MODULE_SIGS | |
209 | {.action = APPRAISE, .func = MODULE_CHECK, | |
210 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | |
211 | #endif | |
212 | #ifdef CONFIG_IMA_APPRAISE_REQUIRE_FIRMWARE_SIGS | |
213 | {.action = APPRAISE, .func = FIRMWARE_CHECK, | |
214 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | |
215 | #endif | |
216 | #ifdef CONFIG_IMA_APPRAISE_REQUIRE_KEXEC_SIGS | |
217 | {.action = APPRAISE, .func = KEXEC_KERNEL_CHECK, | |
218 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | |
219 | #endif | |
220 | #ifdef CONFIG_IMA_APPRAISE_REQUIRE_POLICY_SIGS | |
221 | {.action = APPRAISE, .func = POLICY_CHECK, | |
222 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | |
223 | #endif | |
224 | }; | |
225 | ||
503ceaef MZ |
226 | static struct ima_rule_entry secure_boot_rules[] __ro_after_init = { |
227 | {.action = APPRAISE, .func = MODULE_CHECK, | |
228 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | |
229 | {.action = APPRAISE, .func = FIRMWARE_CHECK, | |
230 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | |
231 | {.action = APPRAISE, .func = KEXEC_KERNEL_CHECK, | |
232 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | |
233 | {.action = APPRAISE, .func = POLICY_CHECK, | |
234 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | |
235 | }; | |
236 | ||
03cee168 LR |
237 | static struct ima_rule_entry critical_data_rules[] __ro_after_init = { |
238 | {.action = MEASURE, .func = CRITICAL_DATA, .flags = IMA_FUNC}, | |
239 | }; | |
240 | ||
61917062 | 241 | /* An array of architecture specific rules */ |
68f25290 | 242 | static struct ima_rule_entry *arch_policy_entry __ro_after_init; |
61917062 | 243 | |
07f6a794 MZ |
244 | static LIST_HEAD(ima_default_rules); |
245 | static LIST_HEAD(ima_policy_rules); | |
38d859f9 | 246 | static LIST_HEAD(ima_temp_rules); |
067a436b | 247 | static struct list_head *ima_rules = &ima_default_rules; |
3323eec9 | 248 | |
24fd03c8 | 249 | static int ima_policy __initdata; |
38d859f9 | 250 | |
07f6a794 | 251 | static int __init default_measure_policy_setup(char *str) |
5789ba3b | 252 | { |
24fd03c8 MZ |
253 | if (ima_policy) |
254 | return 1; | |
255 | ||
256 | ima_policy = ORIGINAL_TCB; | |
5789ba3b EP |
257 | return 1; |
258 | } | |
07f6a794 MZ |
259 | __setup("ima_tcb", default_measure_policy_setup); |
260 | ||
33ce9549 | 261 | static bool ima_use_appraise_tcb __initdata; |
503ceaef | 262 | static bool ima_use_secure_boot __initdata; |
03cee168 | 263 | static bool ima_use_critical_data __initdata; |
9e67028e | 264 | static bool ima_fail_unverifiable_sigs __ro_after_init; |
24fd03c8 MZ |
265 | static int __init policy_setup(char *str) |
266 | { | |
33ce9549 | 267 | char *p; |
24fd03c8 | 268 | |
33ce9549 MZ |
269 | while ((p = strsep(&str, " |\n")) != NULL) { |
270 | if (*p == ' ') | |
271 | continue; | |
272 | if ((strcmp(p, "tcb") == 0) && !ima_policy) | |
273 | ima_policy = DEFAULT_TCB; | |
274 | else if (strcmp(p, "appraise_tcb") == 0) | |
39adb925 | 275 | ima_use_appraise_tcb = true; |
503ceaef | 276 | else if (strcmp(p, "secure_boot") == 0) |
39adb925 | 277 | ima_use_secure_boot = true; |
03cee168 LR |
278 | else if (strcmp(p, "critical_data") == 0) |
279 | ima_use_critical_data = true; | |
9e67028e MZ |
280 | else if (strcmp(p, "fail_securely") == 0) |
281 | ima_fail_unverifiable_sigs = true; | |
7fe2bb7e BM |
282 | else |
283 | pr_err("policy \"%s\" not found", p); | |
33ce9549 | 284 | } |
24fd03c8 MZ |
285 | |
286 | return 1; | |
287 | } | |
288 | __setup("ima_policy=", policy_setup); | |
289 | ||
07f6a794 MZ |
290 | static int __init default_appraise_policy_setup(char *str) |
291 | { | |
39adb925 | 292 | ima_use_appraise_tcb = true; |
07f6a794 MZ |
293 | return 1; |
294 | } | |
295 | __setup("ima_appraise_tcb", default_appraise_policy_setup); | |
5789ba3b | 296 | |
176377d9 TH |
297 | static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src) |
298 | { | |
299 | struct ima_rule_opt_list *opt_list; | |
300 | size_t count = 0; | |
301 | char *src_copy; | |
302 | char *cur, *next; | |
303 | size_t i; | |
304 | ||
305 | src_copy = match_strdup(src); | |
306 | if (!src_copy) | |
307 | return ERR_PTR(-ENOMEM); | |
308 | ||
309 | next = src_copy; | |
310 | while ((cur = strsep(&next, "|"))) { | |
311 | /* Don't accept an empty list item */ | |
312 | if (!(*cur)) { | |
313 | kfree(src_copy); | |
314 | return ERR_PTR(-EINVAL); | |
315 | } | |
316 | count++; | |
317 | } | |
318 | ||
319 | /* Don't accept an empty list */ | |
320 | if (!count) { | |
321 | kfree(src_copy); | |
322 | return ERR_PTR(-EINVAL); | |
323 | } | |
324 | ||
325 | opt_list = kzalloc(struct_size(opt_list, items, count), GFP_KERNEL); | |
326 | if (!opt_list) { | |
327 | kfree(src_copy); | |
328 | return ERR_PTR(-ENOMEM); | |
329 | } | |
330 | ||
331 | /* | |
332 | * strsep() has already replaced all instances of '|' with '\0', | |
333 | * leaving a byte sequence of NUL-terminated strings. Reference each | |
334 | * string with the array of items. | |
335 | * | |
336 | * IMPORTANT: Ownership of the allocated buffer is transferred from | |
337 | * src_copy to the first element in the items array. To free the | |
338 | * buffer, kfree() must only be called on the first element of the | |
339 | * array. | |
340 | */ | |
341 | for (i = 0, cur = src_copy; i < count; i++) { | |
342 | opt_list->items[i] = cur; | |
343 | cur = strchr(cur, '\0') + 1; | |
344 | } | |
345 | opt_list->count = count; | |
346 | ||
347 | return opt_list; | |
348 | } | |
349 | ||
350 | static void ima_free_rule_opt_list(struct ima_rule_opt_list *opt_list) | |
351 | { | |
352 | if (!opt_list) | |
353 | return; | |
354 | ||
355 | if (opt_list->count) { | |
356 | kfree(opt_list->items[0]); | |
357 | opt_list->count = 0; | |
358 | } | |
359 | ||
360 | kfree(opt_list); | |
361 | } | |
362 | ||
b1694245 JK |
363 | static void ima_lsm_free_rule(struct ima_rule_entry *entry) |
364 | { | |
365 | int i; | |
f17b27a2 | 366 | int r; |
b1694245 JK |
367 | |
368 | for (i = 0; i < MAX_LSM_RULES; i++) { | |
f17b27a2 CS |
369 | for (r = 0; r < LSMBLOB_ENTRIES; r++) |
370 | ima_filter_rule_free(entry->lsm[i].rules); | |
b1694245 JK |
371 | kfree(entry->lsm[i].args_p); |
372 | } | |
465aee77 TH |
373 | } |
374 | ||
375 | static void ima_free_rule(struct ima_rule_entry *entry) | |
376 | { | |
377 | if (!entry) | |
378 | return; | |
379 | ||
380 | /* | |
381 | * entry->template->fields may be allocated in ima_parse_rule() but that | |
382 | * reference is owned by the corresponding ima_template_desc element in | |
383 | * the defined_templates list and cannot be freed here | |
384 | */ | |
385 | kfree(entry->fsname); | |
176377d9 | 386 | ima_free_rule_opt_list(entry->keyrings); |
465aee77 | 387 | ima_lsm_free_rule(entry); |
b1694245 JK |
388 | kfree(entry); |
389 | } | |
390 | ||
391 | static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry) | |
392 | { | |
393 | struct ima_rule_entry *nentry; | |
483ec26e | 394 | int i; |
b1694245 | 395 | |
b1694245 JK |
396 | /* |
397 | * Immutable elements are copied over as pointers and data; only | |
398 | * lsm rules can change | |
399 | */ | |
f60c826d AD |
400 | nentry = kmemdup(entry, sizeof(*nentry), GFP_KERNEL); |
401 | if (!nentry) | |
402 | return NULL; | |
403 | ||
c593642c | 404 | memset(nentry->lsm, 0, sizeof_field(struct ima_rule_entry, lsm)); |
b1694245 JK |
405 | |
406 | for (i = 0; i < MAX_LSM_RULES; i++) { | |
483ec26e | 407 | if (!entry->lsm[i].args_p) |
b1694245 JK |
408 | continue; |
409 | ||
410 | nentry->lsm[i].type = entry->lsm[i].type; | |
39e5993d TH |
411 | nentry->lsm[i].args_p = entry->lsm[i].args_p; |
412 | /* | |
413 | * Remove the reference from entry so that the associated | |
414 | * memory will not be freed during a later call to | |
415 | * ima_lsm_free_rule(entry). | |
416 | */ | |
417 | entry->lsm[i].args_p = NULL; | |
b1694245 | 418 | |
b8867eed TH |
419 | ima_filter_rule_init(nentry->lsm[i].type, Audit_equal, |
420 | nentry->lsm[i].args_p, | |
f17b27a2 CS |
421 | &nentry->lsm[i].rules[0]); |
422 | if (!ima_lsm_isset(nentry->lsm[i].rules)) | |
483ec26e | 423 | pr_warn("rule for LSM \'%s\' is undefined\n", |
aa0c0227 | 424 | nentry->lsm[i].args_p); |
b1694245 JK |
425 | } |
426 | return nentry; | |
b1694245 JK |
427 | } |
428 | ||
429 | static int ima_lsm_update_rule(struct ima_rule_entry *entry) | |
430 | { | |
431 | struct ima_rule_entry *nentry; | |
432 | ||
433 | nentry = ima_lsm_copy_rule(entry); | |
434 | if (!nentry) | |
435 | return -ENOMEM; | |
436 | ||
437 | list_replace_rcu(&entry->list, &nentry->list); | |
438 | synchronize_rcu(); | |
465aee77 TH |
439 | /* |
440 | * ima_lsm_copy_rule() shallow copied all references, except for the | |
441 | * LSM references, from entry to nentry so we only want to free the LSM | |
442 | * references and the entry itself. All other memory refrences will now | |
443 | * be owned by nentry. | |
444 | */ | |
b1694245 | 445 | ima_lsm_free_rule(entry); |
465aee77 | 446 | kfree(entry); |
b1694245 JK |
447 | |
448 | return 0; | |
449 | } | |
450 | ||
db2045f5 TH |
451 | static bool ima_rule_contains_lsm_cond(struct ima_rule_entry *entry) |
452 | { | |
453 | int i; | |
454 | ||
455 | for (i = 0; i < MAX_LSM_RULES; i++) | |
456 | if (entry->lsm[i].args_p) | |
457 | return true; | |
458 | ||
459 | return false; | |
460 | } | |
461 | ||
2bb930ab | 462 | /* |
38d859f9 PM |
463 | * The LSM policy can be reloaded, leaving the IMA LSM based rules referring |
464 | * to the old, stale LSM policy. Update the IMA LSM based rules to reflect | |
b1694245 | 465 | * the reloaded LSM policy. |
7163a993 MZ |
466 | */ |
467 | static void ima_lsm_update_rules(void) | |
468 | { | |
b1694245 | 469 | struct ima_rule_entry *entry, *e; |
592b24cb | 470 | int result; |
7163a993 | 471 | |
b1694245 | 472 | list_for_each_entry_safe(entry, e, &ima_policy_rules, list) { |
592b24cb | 473 | if (!ima_rule_contains_lsm_cond(entry)) |
b1694245 JK |
474 | continue; |
475 | ||
476 | result = ima_lsm_update_rule(entry); | |
477 | if (result) { | |
483ec26e | 478 | pr_err("lsm rule update error %d\n", result); |
b1694245 | 479 | return; |
7163a993 MZ |
480 | } |
481 | } | |
7163a993 MZ |
482 | } |
483 | ||
b1694245 JK |
484 | int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event, |
485 | void *lsm_data) | |
486 | { | |
487 | if (event != LSM_POLICY_CHANGE) | |
488 | return NOTIFY_DONE; | |
489 | ||
490 | ima_lsm_update_rules(); | |
491 | return NOTIFY_OK; | |
492 | } | |
493 | ||
3323eec9 | 494 | /** |
2b4a2474 | 495 | * ima_match_rule_data - determine whether func_data matches the policy rule |
e9085e0a | 496 | * @rule: a pointer to a rule |
2b4a2474 | 497 | * @func_data: data to match against the measure rule data |
e9085e0a LR |
498 | * @cred: a pointer to a credentials structure for user validation |
499 | * | |
2b4a2474 | 500 | * Returns true if func_data matches one in the rule, false otherwise. |
e9085e0a | 501 | */ |
2b4a2474 TS |
502 | static bool ima_match_rule_data(struct ima_rule_entry *rule, |
503 | const char *func_data, | |
504 | const struct cred *cred) | |
e9085e0a | 505 | { |
2b4a2474 | 506 | const struct ima_rule_opt_list *opt_list = NULL; |
e9085e0a | 507 | bool matched = false; |
176377d9 | 508 | size_t i; |
e9085e0a LR |
509 | |
510 | if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid)) | |
511 | return false; | |
512 | ||
2b4a2474 TS |
513 | switch (rule->func) { |
514 | case KEY_CHECK: | |
515 | if (!rule->keyrings) | |
516 | return true; | |
517 | ||
518 | opt_list = rule->keyrings; | |
519 | break; | |
c4e43aa2 | 520 | case CRITICAL_DATA: |
47d76a48 TS |
521 | if (!rule->label) |
522 | return true; | |
523 | ||
524 | opt_list = rule->label; | |
525 | break; | |
2b4a2474 TS |
526 | default: |
527 | return false; | |
528 | } | |
e9085e0a | 529 | |
2b4a2474 | 530 | if (!func_data) |
e9085e0a LR |
531 | return false; |
532 | ||
2b4a2474 TS |
533 | for (i = 0; i < opt_list->count; i++) { |
534 | if (!strcmp(opt_list->items[i], func_data)) { | |
e9085e0a LR |
535 | matched = true; |
536 | break; | |
537 | } | |
538 | } | |
539 | ||
e9085e0a LR |
540 | return matched; |
541 | } | |
542 | ||
3323eec9 | 543 | /** |
483ec26e | 544 | * ima_match_rules - determine whether an inode matches the policy rule. |
3323eec9 | 545 | * @rule: a pointer to a rule |
a2d2329e | 546 | * @mnt_userns: user namespace of the mount the inode was found from |
3323eec9 | 547 | * @inode: a pointer to an inode |
d906c10d MG |
548 | * @cred: a pointer to a credentials structure for user validation |
549 | * @secid: the secid of the task to be validated | |
3323eec9 MZ |
550 | * @func: LIM hook identifier |
551 | * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) | |
2b4a2474 | 552 | * @func_data: func specific data, may be NULL |
3323eec9 MZ |
553 | * |
554 | * Returns true on rule match, false on failure. | |
555 | */ | |
a2d2329e CB |
556 | static bool ima_match_rules(struct ima_rule_entry *rule, |
557 | struct user_namespace *mnt_userns, | |
558 | struct inode *inode, const struct cred *cred, | |
559 | u32 secid, enum ima_hooks func, int mask, | |
2b4a2474 | 560 | const char *func_data) |
3323eec9 | 561 | { |
4af4662f | 562 | int i; |
3323eec9 | 563 | |
09b1148e DK |
564 | if ((rule->flags & IMA_FUNC) && |
565 | (rule->func != func && func != POST_SETATTR)) | |
3323eec9 | 566 | return false; |
c4e43aa2 TS |
567 | |
568 | switch (func) { | |
569 | case KEY_CHECK: | |
570 | case CRITICAL_DATA: | |
571 | return ((rule->func == func) && | |
572 | ima_match_rule_data(rule, func_data, cred)); | |
573 | default: | |
574 | break; | |
575 | } | |
576 | ||
09b1148e DK |
577 | if ((rule->flags & IMA_MASK) && |
578 | (rule->mask != mask && func != POST_SETATTR)) | |
3323eec9 | 579 | return false; |
4351c294 MZ |
580 | if ((rule->flags & IMA_INMASK) && |
581 | (!(rule->mask & mask) && func != POST_SETATTR)) | |
582 | return false; | |
3323eec9 MZ |
583 | if ((rule->flags & IMA_FSMAGIC) |
584 | && rule->fsmagic != inode->i_sb->s_magic) | |
585 | return false; | |
f1b08bbc MZ |
586 | if ((rule->flags & IMA_FSNAME) |
587 | && strcmp(rule->fsname, inode->i_sb->s_type->name)) | |
588 | return false; | |
85865c1f | 589 | if ((rule->flags & IMA_FSUUID) && |
85787090 | 590 | !uuid_equal(&rule->fsuuid, &inode->i_sb->s_uuid)) |
85865c1f | 591 | return false; |
3dd0c8d0 | 592 | if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid)) |
3323eec9 | 593 | return false; |
139069ef MZ |
594 | if (rule->flags & IMA_EUID) { |
595 | if (has_capability_noaudit(current, CAP_SETUID)) { | |
3dd0c8d0 MK |
596 | if (!rule->uid_op(cred->euid, rule->uid) |
597 | && !rule->uid_op(cred->suid, rule->uid) | |
598 | && !rule->uid_op(cred->uid, rule->uid)) | |
139069ef | 599 | return false; |
3dd0c8d0 | 600 | } else if (!rule->uid_op(cred->euid, rule->uid)) |
139069ef MZ |
601 | return false; |
602 | } | |
603 | ||
3dd0c8d0 | 604 | if ((rule->flags & IMA_FOWNER) && |
a2d2329e | 605 | !rule->fowner_op(i_uid_into_mnt(mnt_userns, inode), rule->fowner)) |
07f6a794 | 606 | return false; |
4af4662f | 607 | for (i = 0; i < MAX_LSM_RULES; i++) { |
53fc0e22 | 608 | int rc = 0; |
d906c10d | 609 | u32 osid; |
4af4662f | 610 | |
f17b27a2 | 611 | if (!ima_lsm_isset(rule->lsm[i].rules)) { |
483ec26e JK |
612 | if (!rule->lsm[i].args_p) |
613 | continue; | |
614 | else | |
615 | return false; | |
616 | } | |
4af4662f MZ |
617 | switch (i) { |
618 | case LSM_OBJ_USER: | |
619 | case LSM_OBJ_ROLE: | |
620 | case LSM_OBJ_TYPE: | |
621 | security_inode_getsecid(inode, &osid); | |
b8867eed TH |
622 | rc = ima_filter_rule_match(osid, rule->lsm[i].type, |
623 | Audit_equal, | |
f17b27a2 | 624 | rule->lsm[i].rules); |
4af4662f MZ |
625 | break; |
626 | case LSM_SUBJ_USER: | |
627 | case LSM_SUBJ_ROLE: | |
628 | case LSM_SUBJ_TYPE: | |
b8867eed TH |
629 | rc = ima_filter_rule_match(secid, rule->lsm[i].type, |
630 | Audit_equal, | |
f17b27a2 | 631 | rule->lsm[i].rules); |
28073eb0 | 632 | break; |
4af4662f MZ |
633 | default: |
634 | break; | |
635 | } | |
636 | if (!rc) | |
637 | return false; | |
638 | } | |
3323eec9 MZ |
639 | return true; |
640 | } | |
641 | ||
d79d72e0 MZ |
642 | /* |
643 | * In addition to knowing that we need to appraise the file in general, | |
5a73fcfa | 644 | * we need to differentiate between calling hooks, for hook specific rules. |
d79d72e0 | 645 | */ |
4ad87a3d | 646 | static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func) |
d79d72e0 | 647 | { |
5a73fcfa MZ |
648 | if (!(rule->flags & IMA_FUNC)) |
649 | return IMA_FILE_APPRAISE; | |
650 | ||
2bb930ab | 651 | switch (func) { |
d79d72e0 MZ |
652 | case MMAP_CHECK: |
653 | return IMA_MMAP_APPRAISE; | |
654 | case BPRM_CHECK: | |
655 | return IMA_BPRM_APPRAISE; | |
d906c10d MG |
656 | case CREDS_CHECK: |
657 | return IMA_CREDS_APPRAISE; | |
d79d72e0 | 658 | case FILE_CHECK: |
c6af8efe | 659 | case POST_SETATTR: |
d79d72e0 | 660 | return IMA_FILE_APPRAISE; |
c6af8efe MZ |
661 | case MODULE_CHECK ... MAX_CHECK - 1: |
662 | default: | |
663 | return IMA_READ_APPRAISE; | |
d79d72e0 MZ |
664 | } |
665 | } | |
666 | ||
3323eec9 MZ |
667 | /** |
668 | * ima_match_policy - decision based on LSM and other conditions | |
a2d2329e | 669 | * @mnt_userns: user namespace of the mount the inode was found from |
3323eec9 | 670 | * @inode: pointer to an inode for which the policy decision is being made |
d906c10d MG |
671 | * @cred: pointer to a credentials structure for which the policy decision is |
672 | * being made | |
673 | * @secid: LSM secid of the task to be validated | |
3323eec9 MZ |
674 | * @func: IMA hook identifier |
675 | * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) | |
725de7fa | 676 | * @pcr: set the pcr to extend |
19453ce0 | 677 | * @template_desc: the template that should be used for this rule |
2b4a2474 | 678 | * @func_data: func specific data, may be NULL |
1624dc00 | 679 | * @allowed_algos: allowlist of hash algorithms for the IMA xattr |
3323eec9 MZ |
680 | * |
681 | * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type) | |
682 | * conditions. | |
683 | * | |
38d859f9 PM |
684 | * Since the IMA policy may be updated multiple times we need to lock the |
685 | * list when walking it. Reads are many orders of magnitude more numerous | |
686 | * than writes so ima_match_policy() is classical RCU candidate. | |
3323eec9 | 687 | */ |
a2d2329e CB |
688 | int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode, |
689 | const struct cred *cred, u32 secid, enum ima_hooks func, | |
690 | int mask, int flags, int *pcr, | |
e9085e0a | 691 | struct ima_template_desc **template_desc, |
1624dc00 | 692 | const char *func_data, unsigned int *allowed_algos) |
3323eec9 | 693 | { |
07f6a794 | 694 | struct ima_rule_entry *entry; |
2fe5d6de | 695 | int action = 0, actmask = flags | (flags << 1); |
3323eec9 | 696 | |
dea87d08 | 697 | if (template_desc && !*template_desc) |
b36f281f MZ |
698 | *template_desc = ima_template_desc_current(); |
699 | ||
38d859f9 PM |
700 | rcu_read_lock(); |
701 | list_for_each_entry_rcu(entry, ima_rules, list) { | |
3323eec9 | 702 | |
2fe5d6de MZ |
703 | if (!(entry->action & actmask)) |
704 | continue; | |
705 | ||
a2d2329e | 706 | if (!ima_match_rules(entry, mnt_userns, inode, cred, secid, |
7d6beb71 | 707 | func, mask, func_data)) |
2fe5d6de | 708 | continue; |
3323eec9 | 709 | |
0e5a247c DK |
710 | action |= entry->flags & IMA_ACTION_FLAGS; |
711 | ||
45e2472e | 712 | action |= entry->action & IMA_DO_MASK; |
da1b0029 | 713 | if (entry->action & IMA_APPRAISE) { |
5a73fcfa | 714 | action |= get_subaction(entry, func); |
a9a4935d | 715 | action &= ~IMA_HASH; |
9e67028e MZ |
716 | if (ima_fail_unverifiable_sigs) |
717 | action |= IMA_FAIL_UNVERIFIABLE_SIGS; | |
d79d72e0 | 718 | |
1624dc00 TS |
719 | if (allowed_algos && |
720 | entry->flags & IMA_VALIDATE_ALGOS) | |
721 | *allowed_algos = entry->allowed_algos; | |
722 | } | |
b36f281f | 723 | |
45e2472e DK |
724 | if (entry->action & IMA_DO_MASK) |
725 | actmask &= ~(entry->action | entry->action << 1); | |
726 | else | |
727 | actmask &= ~(entry->action | entry->action >> 1); | |
3323eec9 | 728 | |
725de7fa ER |
729 | if ((pcr) && (entry->flags & IMA_PCR)) |
730 | *pcr = entry->pcr; | |
731 | ||
19453ce0 MG |
732 | if (template_desc && entry->template) |
733 | *template_desc = entry->template; | |
19453ce0 | 734 | |
2fe5d6de MZ |
735 | if (!actmask) |
736 | break; | |
3323eec9 | 737 | } |
38d859f9 | 738 | rcu_read_unlock(); |
2fe5d6de MZ |
739 | |
740 | return action; | |
3323eec9 MZ |
741 | } |
742 | ||
4f2946aa TS |
743 | /** |
744 | * ima_update_policy_flags() - Update global IMA variables | |
745 | * | |
746 | * Update ima_policy_flag and ima_setxattr_allowed_hash_algorithms | |
747 | * based on the currently loaded policy. | |
748 | * | |
749 | * With ima_policy_flag, the decision to short circuit out of a function | |
750 | * or not call the function in the first place can be made earlier. | |
751 | * | |
752 | * With ima_setxattr_allowed_hash_algorithms, the policy can restrict the | |
753 | * set of hash algorithms accepted when updating the security.ima xattr of | |
754 | * a file. | |
755 | * | |
756 | * Context: called after a policy update and at system initialization. | |
a756024e | 757 | */ |
4f2946aa | 758 | void ima_update_policy_flags(void) |
a756024e RS |
759 | { |
760 | struct ima_rule_entry *entry; | |
4f2946aa | 761 | int new_policy_flag = 0; |
a756024e | 762 | |
4f2946aa | 763 | rcu_read_lock(); |
a756024e | 764 | list_for_each_entry(entry, ima_rules, list) { |
4f2946aa TS |
765 | /* |
766 | * SETXATTR_CHECK rules do not implement a full policy check | |
767 | * because rule checking would probably have an important | |
768 | * performance impact on setxattr(). As a consequence, only one | |
769 | * SETXATTR_CHECK can be active at a given time. | |
770 | * Because we want to preserve that property, we set out to use | |
771 | * atomic_cmpxchg. Either: | |
772 | * - the atomic was non-zero: a setxattr hash policy is | |
773 | * already enforced, we do nothing | |
774 | * - the atomic was zero: no setxattr policy was set, enable | |
775 | * the setxattr hash policy | |
776 | */ | |
777 | if (entry->func == SETXATTR_CHECK) { | |
778 | atomic_cmpxchg(&ima_setxattr_allowed_hash_algorithms, | |
779 | 0, entry->allowed_algos); | |
780 | /* SETXATTR_CHECK doesn't impact ima_policy_flag */ | |
781 | continue; | |
782 | } | |
783 | ||
a756024e | 784 | if (entry->action & IMA_DO_MASK) |
4f2946aa | 785 | new_policy_flag |= entry->action; |
a756024e | 786 | } |
4f2946aa | 787 | rcu_read_unlock(); |
a756024e | 788 | |
ef96837b | 789 | ima_appraise |= (build_ima_appraise | temp_ima_appraise); |
a756024e | 790 | if (!ima_appraise) |
4f2946aa TS |
791 | new_policy_flag &= ~IMA_APPRAISE; |
792 | ||
793 | ima_policy_flag = new_policy_flag; | |
a756024e RS |
794 | } |
795 | ||
6f0911a6 MZ |
796 | static int ima_appraise_flag(enum ima_hooks func) |
797 | { | |
798 | if (func == MODULE_CHECK) | |
799 | return IMA_APPRAISE_MODULES; | |
800 | else if (func == FIRMWARE_CHECK) | |
801 | return IMA_APPRAISE_FIRMWARE; | |
802 | else if (func == POLICY_CHECK) | |
803 | return IMA_APPRAISE_POLICY; | |
16c267aa MZ |
804 | else if (func == KEXEC_KERNEL_CHECK) |
805 | return IMA_APPRAISE_KEXEC; | |
6f0911a6 MZ |
806 | return 0; |
807 | } | |
808 | ||
c52657d9 NJ |
809 | static void add_rules(struct ima_rule_entry *entries, int count, |
810 | enum policy_rule_list policy_rule) | |
811 | { | |
812 | int i = 0; | |
813 | ||
814 | for (i = 0; i < count; i++) { | |
815 | struct ima_rule_entry *entry; | |
816 | ||
817 | if (policy_rule & IMA_DEFAULT_POLICY) | |
818 | list_add_tail(&entries[i].list, &ima_default_rules); | |
819 | ||
820 | if (policy_rule & IMA_CUSTOM_POLICY) { | |
821 | entry = kmemdup(&entries[i], sizeof(*entry), | |
822 | GFP_KERNEL); | |
823 | if (!entry) | |
824 | continue; | |
825 | ||
826 | list_add_tail(&entry->list, &ima_policy_rules); | |
827 | } | |
b59fda44 KS |
828 | if (entries[i].action == APPRAISE) { |
829 | if (entries != build_appraise_rules) | |
830 | temp_ima_appraise |= | |
831 | ima_appraise_flag(entries[i].func); | |
832 | else | |
833 | build_ima_appraise |= | |
834 | ima_appraise_flag(entries[i].func); | |
835 | } | |
c52657d9 NJ |
836 | } |
837 | } | |
838 | ||
61917062 NJ |
839 | static int ima_parse_rule(char *rule, struct ima_rule_entry *entry); |
840 | ||
841 | static int __init ima_init_arch_policy(void) | |
842 | { | |
843 | const char * const *arch_rules; | |
844 | const char * const *rules; | |
845 | int arch_entries = 0; | |
846 | int i = 0; | |
847 | ||
848 | arch_rules = arch_get_ima_policy(); | |
849 | if (!arch_rules) | |
850 | return arch_entries; | |
851 | ||
852 | /* Get number of rules */ | |
853 | for (rules = arch_rules; *rules != NULL; rules++) | |
854 | arch_entries++; | |
855 | ||
856 | arch_policy_entry = kcalloc(arch_entries + 1, | |
857 | sizeof(*arch_policy_entry), GFP_KERNEL); | |
858 | if (!arch_policy_entry) | |
859 | return 0; | |
860 | ||
861 | /* Convert each policy string rules to struct ima_rule_entry format */ | |
862 | for (rules = arch_rules, i = 0; *rules != NULL; rules++) { | |
863 | char rule[255]; | |
864 | int result; | |
865 | ||
866 | result = strlcpy(rule, *rules, sizeof(rule)); | |
867 | ||
868 | INIT_LIST_HEAD(&arch_policy_entry[i].list); | |
869 | result = ima_parse_rule(rule, &arch_policy_entry[i]); | |
870 | if (result) { | |
871 | pr_warn("Skipping unknown architecture policy rule: %s\n", | |
872 | rule); | |
873 | memset(&arch_policy_entry[i], 0, | |
874 | sizeof(*arch_policy_entry)); | |
875 | continue; | |
876 | } | |
877 | i++; | |
878 | } | |
879 | return i; | |
880 | } | |
881 | ||
3323eec9 MZ |
882 | /** |
883 | * ima_init_policy - initialize the default measure rules. | |
884 | * | |
07f6a794 MZ |
885 | * ima_rules points to either the ima_default_rules or the |
886 | * the new ima_policy_rules. | |
3323eec9 | 887 | */ |
932995f0 | 888 | void __init ima_init_policy(void) |
3323eec9 | 889 | { |
61917062 | 890 | int build_appraise_entries, arch_entries; |
2bb930ab | 891 | |
c52657d9 NJ |
892 | /* if !ima_policy, we load NO default rules */ |
893 | if (ima_policy) | |
894 | add_rules(dont_measure_rules, ARRAY_SIZE(dont_measure_rules), | |
895 | IMA_DEFAULT_POLICY); | |
24fd03c8 MZ |
896 | |
897 | switch (ima_policy) { | |
898 | case ORIGINAL_TCB: | |
c52657d9 NJ |
899 | add_rules(original_measurement_rules, |
900 | ARRAY_SIZE(original_measurement_rules), | |
901 | IMA_DEFAULT_POLICY); | |
24fd03c8 MZ |
902 | break; |
903 | case DEFAULT_TCB: | |
c52657d9 NJ |
904 | add_rules(default_measurement_rules, |
905 | ARRAY_SIZE(default_measurement_rules), | |
906 | IMA_DEFAULT_POLICY); | |
28073eb0 | 907 | break; |
24fd03c8 MZ |
908 | default: |
909 | break; | |
910 | } | |
5577857f | 911 | |
61917062 NJ |
912 | /* |
913 | * Based on runtime secure boot flags, insert arch specific measurement | |
914 | * and appraise rules requiring file signatures for both the initial | |
915 | * and custom policies, prior to other appraise rules. | |
916 | * (Highest priority) | |
917 | */ | |
918 | arch_entries = ima_init_arch_policy(); | |
919 | if (!arch_entries) | |
920 | pr_info("No architecture policies found\n"); | |
921 | else | |
922 | add_rules(arch_policy_entry, arch_entries, | |
923 | IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY); | |
924 | ||
503ceaef | 925 | /* |
ef96837b | 926 | * Insert the builtin "secure_boot" policy rules requiring file |
61917062 | 927 | * signatures, prior to other appraise rules. |
503ceaef | 928 | */ |
c52657d9 NJ |
929 | if (ima_use_secure_boot) |
930 | add_rules(secure_boot_rules, ARRAY_SIZE(secure_boot_rules), | |
931 | IMA_DEFAULT_POLICY); | |
503ceaef | 932 | |
ef96837b MZ |
933 | /* |
934 | * Insert the build time appraise rules requiring file signatures | |
935 | * for both the initial and custom policies, prior to other appraise | |
c52657d9 NJ |
936 | * rules. As the secure boot rules includes all of the build time |
937 | * rules, include either one or the other set of rules, but not both. | |
ef96837b | 938 | */ |
c52657d9 NJ |
939 | build_appraise_entries = ARRAY_SIZE(build_appraise_rules); |
940 | if (build_appraise_entries) { | |
941 | if (ima_use_secure_boot) | |
942 | add_rules(build_appraise_rules, build_appraise_entries, | |
943 | IMA_CUSTOM_POLICY); | |
944 | else | |
945 | add_rules(build_appraise_rules, build_appraise_entries, | |
946 | IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY); | |
ef96837b MZ |
947 | } |
948 | ||
c52657d9 NJ |
949 | if (ima_use_appraise_tcb) |
950 | add_rules(default_appraise_rules, | |
951 | ARRAY_SIZE(default_appraise_rules), | |
952 | IMA_DEFAULT_POLICY); | |
07f6a794 | 953 | |
03cee168 LR |
954 | if (ima_use_critical_data) |
955 | add_rules(critical_data_rules, | |
956 | ARRAY_SIZE(critical_data_rules), | |
957 | IMA_DEFAULT_POLICY); | |
958 | ||
4f2946aa TS |
959 | atomic_set(&ima_setxattr_allowed_hash_algorithms, 0); |
960 | ||
961 | ima_update_policy_flags(); | |
3323eec9 | 962 | } |
4af4662f | 963 | |
0112721d | 964 | /* Make sure we have a valid policy, at least containing some rules. */ |
c75d8e96 | 965 | int ima_check_policy(void) |
0112721d SL |
966 | { |
967 | if (list_empty(&ima_temp_rules)) | |
968 | return -EINVAL; | |
969 | return 0; | |
970 | } | |
971 | ||
4af4662f MZ |
972 | /** |
973 | * ima_update_policy - update default_rules with new measure rules | |
974 | * | |
975 | * Called on file .release to update the default rules with a complete new | |
38d859f9 PM |
976 | * policy. What we do here is to splice ima_policy_rules and ima_temp_rules so |
977 | * they make a queue. The policy may be updated multiple times and this is the | |
978 | * RCU updater. | |
979 | * | |
980 | * Policy rules are never deleted so ima_policy_flag gets zeroed only once when | |
981 | * we switch from the default policy to user defined. | |
4af4662f MZ |
982 | */ |
983 | void ima_update_policy(void) | |
984 | { | |
53b626f9 | 985 | struct list_head *policy = &ima_policy_rules; |
38d859f9 | 986 | |
53b626f9 | 987 | list_splice_tail_init_rcu(&ima_temp_rules, policy, synchronize_rcu); |
38d859f9 PM |
988 | |
989 | if (ima_rules != policy) { | |
990 | ima_policy_flag = 0; | |
991 | ima_rules = policy; | |
61917062 NJ |
992 | |
993 | /* | |
994 | * IMA architecture specific policy rules are specified | |
995 | * as strings and converted to an array of ima_entry_rules | |
996 | * on boot. After loading a custom policy, free the | |
997 | * architecture specific rules stored as an array. | |
998 | */ | |
999 | kfree(arch_policy_entry); | |
38d859f9 | 1000 | } |
4f2946aa | 1001 | ima_update_policy_flags(); |
450d0fd5 LR |
1002 | |
1003 | /* Custom IMA policy has been loaded */ | |
1004 | ima_process_queued_keys(); | |
4af4662f MZ |
1005 | } |
1006 | ||
1a9430db | 1007 | /* Keep the enumeration in sync with the policy_tokens! */ |
4af4662f | 1008 | enum { |
1a9430db | 1009 | Opt_measure, Opt_dont_measure, |
07f6a794 | 1010 | Opt_appraise, Opt_dont_appraise, |
da1b0029 | 1011 | Opt_audit, Opt_hash, Opt_dont_hash, |
4af4662f MZ |
1012 | Opt_obj_user, Opt_obj_role, Opt_obj_type, |
1013 | Opt_subj_user, Opt_subj_role, Opt_subj_type, | |
f1b08bbc | 1014 | Opt_func, Opt_mask, Opt_fsmagic, Opt_fsname, |
3dd0c8d0 MK |
1015 | Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq, |
1016 | Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt, | |
1017 | Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt, | |
583a80ae | 1018 | Opt_appraise_type, Opt_appraise_flag, Opt_appraise_algos, |
2b60c0ec | 1019 | Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings, |
47d76a48 | 1020 | Opt_label, Opt_err |
4af4662f MZ |
1021 | }; |
1022 | ||
1a9430db | 1023 | static const match_table_t policy_tokens = { |
4af4662f MZ |
1024 | {Opt_measure, "measure"}, |
1025 | {Opt_dont_measure, "dont_measure"}, | |
07f6a794 MZ |
1026 | {Opt_appraise, "appraise"}, |
1027 | {Opt_dont_appraise, "dont_appraise"}, | |
e7c568e0 | 1028 | {Opt_audit, "audit"}, |
da1b0029 MZ |
1029 | {Opt_hash, "hash"}, |
1030 | {Opt_dont_hash, "dont_hash"}, | |
4af4662f MZ |
1031 | {Opt_obj_user, "obj_user=%s"}, |
1032 | {Opt_obj_role, "obj_role=%s"}, | |
1033 | {Opt_obj_type, "obj_type=%s"}, | |
1034 | {Opt_subj_user, "subj_user=%s"}, | |
1035 | {Opt_subj_role, "subj_role=%s"}, | |
1036 | {Opt_subj_type, "subj_type=%s"}, | |
1037 | {Opt_func, "func=%s"}, | |
1038 | {Opt_mask, "mask=%s"}, | |
1039 | {Opt_fsmagic, "fsmagic=%s"}, | |
f1b08bbc | 1040 | {Opt_fsname, "fsname=%s"}, |
85865c1f | 1041 | {Opt_fsuuid, "fsuuid=%s"}, |
3dd0c8d0 MK |
1042 | {Opt_uid_eq, "uid=%s"}, |
1043 | {Opt_euid_eq, "euid=%s"}, | |
1044 | {Opt_fowner_eq, "fowner=%s"}, | |
1045 | {Opt_uid_gt, "uid>%s"}, | |
1046 | {Opt_euid_gt, "euid>%s"}, | |
1047 | {Opt_fowner_gt, "fowner>%s"}, | |
1048 | {Opt_uid_lt, "uid<%s"}, | |
1049 | {Opt_euid_lt, "euid<%s"}, | |
1050 | {Opt_fowner_lt, "fowner<%s"}, | |
0e5a247c | 1051 | {Opt_appraise_type, "appraise_type=%s"}, |
273df864 | 1052 | {Opt_appraise_flag, "appraise_flag=%s"}, |
583a80ae | 1053 | {Opt_appraise_algos, "appraise_algos=%s"}, |
f9b2a735 | 1054 | {Opt_permit_directio, "permit_directio"}, |
0260643c | 1055 | {Opt_pcr, "pcr=%s"}, |
19453ce0 | 1056 | {Opt_template, "template=%s"}, |
2b60c0ec | 1057 | {Opt_keyrings, "keyrings=%s"}, |
47d76a48 | 1058 | {Opt_label, "label=%s"}, |
4af4662f MZ |
1059 | {Opt_err, NULL} |
1060 | }; | |
1061 | ||
07f6a794 | 1062 | static int ima_lsm_rule_init(struct ima_rule_entry *entry, |
7163a993 | 1063 | substring_t *args, int lsm_rule, int audit_type) |
4af4662f MZ |
1064 | { |
1065 | int result; | |
1066 | ||
f17b27a2 | 1067 | if (ima_lsm_isset(entry->lsm[lsm_rule].rules)) |
7b62e162 EP |
1068 | return -EINVAL; |
1069 | ||
7163a993 MZ |
1070 | entry->lsm[lsm_rule].args_p = match_strdup(args); |
1071 | if (!entry->lsm[lsm_rule].args_p) | |
1072 | return -ENOMEM; | |
1073 | ||
4af4662f | 1074 | entry->lsm[lsm_rule].type = audit_type; |
b8867eed TH |
1075 | result = ima_filter_rule_init(entry->lsm[lsm_rule].type, Audit_equal, |
1076 | entry->lsm[lsm_rule].args_p, | |
f17b27a2 CS |
1077 | &entry->lsm[lsm_rule].rules[0]); |
1078 | if (!ima_lsm_isset(entry->lsm[lsm_rule].rules)) { | |
483ec26e | 1079 | pr_warn("rule for LSM \'%s\' is undefined\n", |
aa0c0227 | 1080 | entry->lsm[lsm_rule].args_p); |
483ec26e JK |
1081 | |
1082 | if (ima_rules == &ima_default_rules) { | |
1083 | kfree(entry->lsm[lsm_rule].args_p); | |
2bdd737c | 1084 | entry->lsm[lsm_rule].args_p = NULL; |
483ec26e JK |
1085 | result = -EINVAL; |
1086 | } else | |
1087 | result = 0; | |
7163a993 MZ |
1088 | } |
1089 | ||
4af4662f MZ |
1090 | return result; |
1091 | } | |
1092 | ||
3dd0c8d0 MK |
1093 | static void ima_log_string_op(struct audit_buffer *ab, char *key, char *value, |
1094 | bool (*rule_operator)(kuid_t, kuid_t)) | |
2f1506cd | 1095 | { |
2afd020a SB |
1096 | if (!ab) |
1097 | return; | |
1098 | ||
3dd0c8d0 MK |
1099 | if (rule_operator == &uid_gt) |
1100 | audit_log_format(ab, "%s>", key); | |
1101 | else if (rule_operator == &uid_lt) | |
1102 | audit_log_format(ab, "%s<", key); | |
1103 | else | |
1104 | audit_log_format(ab, "%s=", key); | |
3d2859d5 | 1105 | audit_log_format(ab, "%s ", value); |
2f1506cd | 1106 | } |
3dd0c8d0 MK |
1107 | static void ima_log_string(struct audit_buffer *ab, char *key, char *value) |
1108 | { | |
1109 | ima_log_string_op(ab, key, value, NULL); | |
1110 | } | |
2f1506cd | 1111 | |
3878d505 TJB |
1112 | /* |
1113 | * Validating the appended signature included in the measurement list requires | |
1114 | * the file hash calculated without the appended signature (i.e., the 'd-modsig' | |
1115 | * field). Therefore, notify the user if they have the 'modsig' field but not | |
1116 | * the 'd-modsig' field in the template. | |
1117 | */ | |
1118 | static void check_template_modsig(const struct ima_template_desc *template) | |
1119 | { | |
1120 | #define MSG "template with 'modsig' field also needs 'd-modsig' field\n" | |
1121 | bool has_modsig, has_dmodsig; | |
1122 | static bool checked; | |
1123 | int i; | |
1124 | ||
1125 | /* We only need to notify the user once. */ | |
1126 | if (checked) | |
1127 | return; | |
1128 | ||
1129 | has_modsig = has_dmodsig = false; | |
1130 | for (i = 0; i < template->num_fields; i++) { | |
1131 | if (!strcmp(template->fields[i]->field_id, "modsig")) | |
1132 | has_modsig = true; | |
1133 | else if (!strcmp(template->fields[i]->field_id, "d-modsig")) | |
1134 | has_dmodsig = true; | |
1135 | } | |
1136 | ||
1137 | if (has_modsig && !has_dmodsig) | |
1138 | pr_notice(MSG); | |
1139 | ||
1140 | checked = true; | |
1141 | #undef MSG | |
1142 | } | |
1143 | ||
71218343 TH |
1144 | static bool ima_validate_rule(struct ima_rule_entry *entry) |
1145 | { | |
30031b0e | 1146 | /* Ensure that the action is set and is compatible with the flags */ |
71218343 TH |
1147 | if (entry->action == UNKNOWN) |
1148 | return false; | |
1149 | ||
30031b0e TH |
1150 | if (entry->action != MEASURE && entry->flags & IMA_PCR) |
1151 | return false; | |
1152 | ||
1153 | if (entry->action != APPRAISE && | |
583a80ae TS |
1154 | entry->flags & (IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED | |
1155 | IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS)) | |
30031b0e TH |
1156 | return false; |
1157 | ||
1158 | /* | |
1159 | * The IMA_FUNC bit must be set if and only if there's a valid hook | |
1160 | * function specified, and vice versa. Enforcing this property allows | |
1161 | * for the NONE case below to validate a rule without an explicit hook | |
1162 | * function. | |
1163 | */ | |
1164 | if (((entry->flags & IMA_FUNC) && entry->func == NONE) || | |
1165 | (!(entry->flags & IMA_FUNC) && entry->func != NONE)) | |
1166 | return false; | |
1167 | ||
71218343 TH |
1168 | /* |
1169 | * Ensure that the hook function is compatible with the other | |
1170 | * components of the rule | |
1171 | */ | |
1172 | switch (entry->func) { | |
1173 | case NONE: | |
1174 | case FILE_CHECK: | |
1175 | case MMAP_CHECK: | |
1176 | case BPRM_CHECK: | |
1177 | case CREDS_CHECK: | |
1178 | case POST_SETATTR: | |
71218343 | 1179 | case FIRMWARE_CHECK: |
30031b0e TH |
1180 | case POLICY_CHECK: |
1181 | if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC | | |
1182 | IMA_UID | IMA_FOWNER | IMA_FSUUID | | |
1183 | IMA_INMASK | IMA_EUID | IMA_PCR | | |
1184 | IMA_FSNAME | IMA_DIGSIG_REQUIRED | | |
583a80ae | 1185 | IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS)) |
30031b0e TH |
1186 | return false; |
1187 | ||
1188 | break; | |
1189 | case MODULE_CHECK: | |
71218343 TH |
1190 | case KEXEC_KERNEL_CHECK: |
1191 | case KEXEC_INITRAMFS_CHECK: | |
30031b0e TH |
1192 | if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC | |
1193 | IMA_UID | IMA_FOWNER | IMA_FSUUID | | |
1194 | IMA_INMASK | IMA_EUID | IMA_PCR | | |
1195 | IMA_FSNAME | IMA_DIGSIG_REQUIRED | | |
1196 | IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED | | |
583a80ae | 1197 | IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS)) |
30031b0e TH |
1198 | return false; |
1199 | ||
71218343 TH |
1200 | break; |
1201 | case KEXEC_CMDLINE: | |
db2045f5 TH |
1202 | if (entry->action & ~(MEASURE | DONT_MEASURE)) |
1203 | return false; | |
1204 | ||
4834177e TH |
1205 | if (entry->flags & ~(IMA_FUNC | IMA_FSMAGIC | IMA_UID | |
1206 | IMA_FOWNER | IMA_FSUUID | IMA_EUID | | |
1207 | IMA_PCR | IMA_FSNAME)) | |
db2045f5 TH |
1208 | return false; |
1209 | ||
1210 | break; | |
71218343 TH |
1211 | case KEY_CHECK: |
1212 | if (entry->action & ~(MEASURE | DONT_MEASURE)) | |
1213 | return false; | |
1214 | ||
eb624fe2 TH |
1215 | if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_PCR | |
1216 | IMA_KEYRINGS)) | |
1217 | return false; | |
1218 | ||
1219 | if (ima_rule_contains_lsm_cond(entry)) | |
1220 | return false; | |
1221 | ||
c4e43aa2 TS |
1222 | break; |
1223 | case CRITICAL_DATA: | |
1224 | if (entry->action & ~(MEASURE | DONT_MEASURE)) | |
1225 | return false; | |
1226 | ||
47d76a48 TS |
1227 | if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_PCR | |
1228 | IMA_LABEL)) | |
c4e43aa2 TS |
1229 | return false; |
1230 | ||
1231 | if (ima_rule_contains_lsm_cond(entry)) | |
1232 | return false; | |
1233 | ||
4f2946aa TS |
1234 | break; |
1235 | case SETXATTR_CHECK: | |
1236 | /* any action other than APPRAISE is unsupported */ | |
1237 | if (entry->action != APPRAISE) | |
1238 | return false; | |
1239 | ||
1240 | /* SETXATTR_CHECK requires an appraise_algos parameter */ | |
1241 | if (!(entry->flags & IMA_VALIDATE_ALGOS)) | |
1242 | return false; | |
1243 | ||
1244 | /* | |
1245 | * full policies are not supported, they would have too | |
1246 | * much of a performance impact | |
1247 | */ | |
1248 | if (entry->flags & ~(IMA_FUNC | IMA_VALIDATE_ALGOS)) | |
1249 | return false; | |
1250 | ||
71218343 TH |
1251 | break; |
1252 | default: | |
1253 | return false; | |
1254 | } | |
1255 | ||
5f3e9265 TH |
1256 | /* Ensure that combinations of flags are compatible with each other */ |
1257 | if (entry->flags & IMA_CHECK_BLACKLIST && | |
1258 | !(entry->flags & IMA_MODSIG_ALLOWED)) | |
1259 | return false; | |
1260 | ||
71218343 TH |
1261 | return true; |
1262 | } | |
1263 | ||
583a80ae TS |
1264 | static unsigned int ima_parse_appraise_algos(char *arg) |
1265 | { | |
1266 | unsigned int res = 0; | |
1267 | int idx; | |
1268 | char *token; | |
1269 | ||
1270 | while ((token = strsep(&arg, ",")) != NULL) { | |
1271 | idx = match_string(hash_algo_name, HASH_ALGO__LAST, token); | |
1272 | ||
1273 | if (idx < 0) { | |
1274 | pr_err("unknown hash algorithm \"%s\"", | |
1275 | token); | |
1276 | return 0; | |
1277 | } | |
1278 | ||
8ecd39cb TS |
1279 | if (!crypto_has_alg(hash_algo_name[idx], 0, 0)) { |
1280 | pr_err("unavailable hash algorithm \"%s\", check your kernel configuration", | |
1281 | token); | |
1282 | return 0; | |
1283 | } | |
1284 | ||
583a80ae TS |
1285 | /* Add the hash algorithm to the 'allowed' bitfield */ |
1286 | res |= (1U << idx); | |
1287 | } | |
1288 | ||
1289 | return res; | |
1290 | } | |
1291 | ||
07f6a794 | 1292 | static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) |
4af4662f MZ |
1293 | { |
1294 | struct audit_buffer *ab; | |
4351c294 | 1295 | char *from; |
4af4662f | 1296 | char *p; |
3dd0c8d0 | 1297 | bool uid_token; |
19453ce0 | 1298 | struct ima_template_desc *template_desc; |
4af4662f MZ |
1299 | int result = 0; |
1300 | ||
dba31ee7 SB |
1301 | ab = integrity_audit_log_start(audit_context(), GFP_KERNEL, |
1302 | AUDIT_INTEGRITY_POLICY_RULE); | |
4af4662f | 1303 | |
8b94eea4 | 1304 | entry->uid = INVALID_UID; |
88265322 | 1305 | entry->fowner = INVALID_UID; |
3dd0c8d0 MK |
1306 | entry->uid_op = &uid_eq; |
1307 | entry->fowner_op = &uid_eq; | |
b9035b1f | 1308 | entry->action = UNKNOWN; |
28ef4002 | 1309 | while ((p = strsep(&rule, " \t")) != NULL) { |
4af4662f MZ |
1310 | substring_t args[MAX_OPT_ARGS]; |
1311 | int token; | |
1312 | unsigned long lnum; | |
1313 | ||
1314 | if (result < 0) | |
1315 | break; | |
28ef4002 EP |
1316 | if ((*p == '\0') || (*p == ' ') || (*p == '\t')) |
1317 | continue; | |
4af4662f MZ |
1318 | token = match_token(p, policy_tokens, args); |
1319 | switch (token) { | |
1320 | case Opt_measure: | |
2f1506cd | 1321 | ima_log_string(ab, "action", "measure"); |
7b62e162 EP |
1322 | |
1323 | if (entry->action != UNKNOWN) | |
1324 | result = -EINVAL; | |
1325 | ||
4af4662f MZ |
1326 | entry->action = MEASURE; |
1327 | break; | |
1328 | case Opt_dont_measure: | |
2f1506cd | 1329 | ima_log_string(ab, "action", "dont_measure"); |
7b62e162 EP |
1330 | |
1331 | if (entry->action != UNKNOWN) | |
1332 | result = -EINVAL; | |
1333 | ||
4af4662f MZ |
1334 | entry->action = DONT_MEASURE; |
1335 | break; | |
07f6a794 MZ |
1336 | case Opt_appraise: |
1337 | ima_log_string(ab, "action", "appraise"); | |
1338 | ||
1339 | if (entry->action != UNKNOWN) | |
1340 | result = -EINVAL; | |
1341 | ||
1342 | entry->action = APPRAISE; | |
1343 | break; | |
1344 | case Opt_dont_appraise: | |
1345 | ima_log_string(ab, "action", "dont_appraise"); | |
1346 | ||
1347 | if (entry->action != UNKNOWN) | |
1348 | result = -EINVAL; | |
1349 | ||
1350 | entry->action = DONT_APPRAISE; | |
1351 | break; | |
e7c568e0 PM |
1352 | case Opt_audit: |
1353 | ima_log_string(ab, "action", "audit"); | |
1354 | ||
1355 | if (entry->action != UNKNOWN) | |
1356 | result = -EINVAL; | |
1357 | ||
1358 | entry->action = AUDIT; | |
1359 | break; | |
da1b0029 MZ |
1360 | case Opt_hash: |
1361 | ima_log_string(ab, "action", "hash"); | |
1362 | ||
1363 | if (entry->action != UNKNOWN) | |
1364 | result = -EINVAL; | |
1365 | ||
1366 | entry->action = HASH; | |
1367 | break; | |
1368 | case Opt_dont_hash: | |
1369 | ima_log_string(ab, "action", "dont_hash"); | |
1370 | ||
1371 | if (entry->action != UNKNOWN) | |
1372 | result = -EINVAL; | |
1373 | ||
1374 | entry->action = DONT_HASH; | |
1375 | break; | |
4af4662f | 1376 | case Opt_func: |
2f1506cd | 1377 | ima_log_string(ab, "func", args[0].from); |
7b62e162 EP |
1378 | |
1379 | if (entry->func) | |
07f6a794 | 1380 | result = -EINVAL; |
7b62e162 | 1381 | |
1e93d005 MZ |
1382 | if (strcmp(args[0].from, "FILE_CHECK") == 0) |
1383 | entry->func = FILE_CHECK; | |
1384 | /* PATH_CHECK is for backwards compat */ | |
1385 | else if (strcmp(args[0].from, "PATH_CHECK") == 0) | |
1386 | entry->func = FILE_CHECK; | |
fdf90729 MZ |
1387 | else if (strcmp(args[0].from, "MODULE_CHECK") == 0) |
1388 | entry->func = MODULE_CHECK; | |
5a9196d7 MZ |
1389 | else if (strcmp(args[0].from, "FIRMWARE_CHECK") == 0) |
1390 | entry->func = FIRMWARE_CHECK; | |
16cac49f MZ |
1391 | else if ((strcmp(args[0].from, "FILE_MMAP") == 0) |
1392 | || (strcmp(args[0].from, "MMAP_CHECK") == 0)) | |
1393 | entry->func = MMAP_CHECK; | |
4af4662f MZ |
1394 | else if (strcmp(args[0].from, "BPRM_CHECK") == 0) |
1395 | entry->func = BPRM_CHECK; | |
d906c10d MG |
1396 | else if (strcmp(args[0].from, "CREDS_CHECK") == 0) |
1397 | entry->func = CREDS_CHECK; | |
d9ddf077 MZ |
1398 | else if (strcmp(args[0].from, "KEXEC_KERNEL_CHECK") == |
1399 | 0) | |
1400 | entry->func = KEXEC_KERNEL_CHECK; | |
1401 | else if (strcmp(args[0].from, "KEXEC_INITRAMFS_CHECK") | |
1402 | == 0) | |
1403 | entry->func = KEXEC_INITRAMFS_CHECK; | |
19f8a847 MZ |
1404 | else if (strcmp(args[0].from, "POLICY_CHECK") == 0) |
1405 | entry->func = POLICY_CHECK; | |
b0935123 PS |
1406 | else if (strcmp(args[0].from, "KEXEC_CMDLINE") == 0) |
1407 | entry->func = KEXEC_CMDLINE; | |
48ce1ddc TH |
1408 | else if (IS_ENABLED(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) && |
1409 | strcmp(args[0].from, "KEY_CHECK") == 0) | |
5808611c | 1410 | entry->func = KEY_CHECK; |
c4e43aa2 TS |
1411 | else if (strcmp(args[0].from, "CRITICAL_DATA") == 0) |
1412 | entry->func = CRITICAL_DATA; | |
4f2946aa TS |
1413 | else if (strcmp(args[0].from, "SETXATTR_CHECK") == 0) |
1414 | entry->func = SETXATTR_CHECK; | |
4af4662f MZ |
1415 | else |
1416 | result = -EINVAL; | |
1417 | if (!result) | |
1418 | entry->flags |= IMA_FUNC; | |
1419 | break; | |
1420 | case Opt_mask: | |
2f1506cd | 1421 | ima_log_string(ab, "mask", args[0].from); |
7b62e162 EP |
1422 | |
1423 | if (entry->mask) | |
1424 | result = -EINVAL; | |
1425 | ||
4351c294 MZ |
1426 | from = args[0].from; |
1427 | if (*from == '^') | |
1428 | from++; | |
1429 | ||
1430 | if ((strcmp(from, "MAY_EXEC")) == 0) | |
4af4662f | 1431 | entry->mask = MAY_EXEC; |
4351c294 | 1432 | else if (strcmp(from, "MAY_WRITE") == 0) |
4af4662f | 1433 | entry->mask = MAY_WRITE; |
4351c294 | 1434 | else if (strcmp(from, "MAY_READ") == 0) |
4af4662f | 1435 | entry->mask = MAY_READ; |
4351c294 | 1436 | else if (strcmp(from, "MAY_APPEND") == 0) |
4af4662f MZ |
1437 | entry->mask = MAY_APPEND; |
1438 | else | |
1439 | result = -EINVAL; | |
1440 | if (!result) | |
4351c294 MZ |
1441 | entry->flags |= (*args[0].from == '^') |
1442 | ? IMA_INMASK : IMA_MASK; | |
4af4662f MZ |
1443 | break; |
1444 | case Opt_fsmagic: | |
2f1506cd | 1445 | ima_log_string(ab, "fsmagic", args[0].from); |
7b62e162 EP |
1446 | |
1447 | if (entry->fsmagic) { | |
1448 | result = -EINVAL; | |
1449 | break; | |
1450 | } | |
1451 | ||
2bb930ab | 1452 | result = kstrtoul(args[0].from, 16, &entry->fsmagic); |
4af4662f MZ |
1453 | if (!result) |
1454 | entry->flags |= IMA_FSMAGIC; | |
1455 | break; | |
f1b08bbc MZ |
1456 | case Opt_fsname: |
1457 | ima_log_string(ab, "fsname", args[0].from); | |
1458 | ||
1459 | entry->fsname = kstrdup(args[0].from, GFP_KERNEL); | |
1460 | if (!entry->fsname) { | |
1461 | result = -ENOMEM; | |
1462 | break; | |
1463 | } | |
1464 | result = 0; | |
1465 | entry->flags |= IMA_FSNAME; | |
1466 | break; | |
2b60c0ec LR |
1467 | case Opt_keyrings: |
1468 | ima_log_string(ab, "keyrings", args[0].from); | |
1469 | ||
48ce1ddc TH |
1470 | if (!IS_ENABLED(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) || |
1471 | entry->keyrings) { | |
2b60c0ec LR |
1472 | result = -EINVAL; |
1473 | break; | |
1474 | } | |
5c7bac9f | 1475 | |
176377d9 TH |
1476 | entry->keyrings = ima_alloc_rule_opt_list(args); |
1477 | if (IS_ERR(entry->keyrings)) { | |
1478 | result = PTR_ERR(entry->keyrings); | |
1479 | entry->keyrings = NULL; | |
2b60c0ec LR |
1480 | break; |
1481 | } | |
176377d9 | 1482 | |
2b60c0ec LR |
1483 | entry->flags |= IMA_KEYRINGS; |
1484 | break; | |
47d76a48 TS |
1485 | case Opt_label: |
1486 | ima_log_string(ab, "label", args[0].from); | |
1487 | ||
1488 | if (entry->label) { | |
1489 | result = -EINVAL; | |
1490 | break; | |
1491 | } | |
1492 | ||
1493 | entry->label = ima_alloc_rule_opt_list(args); | |
1494 | if (IS_ERR(entry->label)) { | |
1495 | result = PTR_ERR(entry->label); | |
1496 | entry->label = NULL; | |
1497 | break; | |
1498 | } | |
1499 | ||
1500 | entry->flags |= IMA_LABEL; | |
1501 | break; | |
85865c1f DK |
1502 | case Opt_fsuuid: |
1503 | ima_log_string(ab, "fsuuid", args[0].from); | |
1504 | ||
36447456 | 1505 | if (!uuid_is_null(&entry->fsuuid)) { |
85865c1f DK |
1506 | result = -EINVAL; |
1507 | break; | |
1508 | } | |
1509 | ||
787d8c53 | 1510 | result = uuid_parse(args[0].from, &entry->fsuuid); |
446d64e3 MZ |
1511 | if (!result) |
1512 | entry->flags |= IMA_FSUUID; | |
85865c1f | 1513 | break; |
3dd0c8d0 MK |
1514 | case Opt_uid_gt: |
1515 | case Opt_euid_gt: | |
1516 | entry->uid_op = &uid_gt; | |
df561f66 | 1517 | fallthrough; |
3dd0c8d0 MK |
1518 | case Opt_uid_lt: |
1519 | case Opt_euid_lt: | |
1520 | if ((token == Opt_uid_lt) || (token == Opt_euid_lt)) | |
1521 | entry->uid_op = &uid_lt; | |
df561f66 | 1522 | fallthrough; |
3dd0c8d0 MK |
1523 | case Opt_uid_eq: |
1524 | case Opt_euid_eq: | |
1525 | uid_token = (token == Opt_uid_eq) || | |
1526 | (token == Opt_uid_gt) || | |
1527 | (token == Opt_uid_lt); | |
1528 | ||
1529 | ima_log_string_op(ab, uid_token ? "uid" : "euid", | |
1530 | args[0].from, entry->uid_op); | |
7b62e162 | 1531 | |
8b94eea4 | 1532 | if (uid_valid(entry->uid)) { |
7b62e162 EP |
1533 | result = -EINVAL; |
1534 | break; | |
1535 | } | |
1536 | ||
29707b20 | 1537 | result = kstrtoul(args[0].from, 10, &lnum); |
4af4662f | 1538 | if (!result) { |
139069ef MZ |
1539 | entry->uid = make_kuid(current_user_ns(), |
1540 | (uid_t) lnum); | |
1541 | if (!uid_valid(entry->uid) || | |
1542 | (uid_t)lnum != lnum) | |
4af4662f MZ |
1543 | result = -EINVAL; |
1544 | else | |
3dd0c8d0 | 1545 | entry->flags |= uid_token |
139069ef | 1546 | ? IMA_UID : IMA_EUID; |
4af4662f MZ |
1547 | } |
1548 | break; | |
3dd0c8d0 MK |
1549 | case Opt_fowner_gt: |
1550 | entry->fowner_op = &uid_gt; | |
df561f66 | 1551 | fallthrough; |
3dd0c8d0 MK |
1552 | case Opt_fowner_lt: |
1553 | if (token == Opt_fowner_lt) | |
1554 | entry->fowner_op = &uid_lt; | |
df561f66 | 1555 | fallthrough; |
3dd0c8d0 MK |
1556 | case Opt_fowner_eq: |
1557 | ima_log_string_op(ab, "fowner", args[0].from, | |
1558 | entry->fowner_op); | |
07f6a794 | 1559 | |
88265322 | 1560 | if (uid_valid(entry->fowner)) { |
07f6a794 MZ |
1561 | result = -EINVAL; |
1562 | break; | |
1563 | } | |
1564 | ||
29707b20 | 1565 | result = kstrtoul(args[0].from, 10, &lnum); |
07f6a794 | 1566 | if (!result) { |
88265322 LT |
1567 | entry->fowner = make_kuid(current_user_ns(), (uid_t)lnum); |
1568 | if (!uid_valid(entry->fowner) || (((uid_t)lnum) != lnum)) | |
07f6a794 MZ |
1569 | result = -EINVAL; |
1570 | else | |
1571 | entry->flags |= IMA_FOWNER; | |
1572 | } | |
1573 | break; | |
4af4662f | 1574 | case Opt_obj_user: |
2f1506cd | 1575 | ima_log_string(ab, "obj_user", args[0].from); |
7163a993 | 1576 | result = ima_lsm_rule_init(entry, args, |
4af4662f MZ |
1577 | LSM_OBJ_USER, |
1578 | AUDIT_OBJ_USER); | |
1579 | break; | |
1580 | case Opt_obj_role: | |
2f1506cd | 1581 | ima_log_string(ab, "obj_role", args[0].from); |
7163a993 | 1582 | result = ima_lsm_rule_init(entry, args, |
4af4662f MZ |
1583 | LSM_OBJ_ROLE, |
1584 | AUDIT_OBJ_ROLE); | |
1585 | break; | |
1586 | case Opt_obj_type: | |
2f1506cd | 1587 | ima_log_string(ab, "obj_type", args[0].from); |
7163a993 | 1588 | result = ima_lsm_rule_init(entry, args, |
4af4662f MZ |
1589 | LSM_OBJ_TYPE, |
1590 | AUDIT_OBJ_TYPE); | |
1591 | break; | |
1592 | case Opt_subj_user: | |
2f1506cd | 1593 | ima_log_string(ab, "subj_user", args[0].from); |
7163a993 | 1594 | result = ima_lsm_rule_init(entry, args, |
4af4662f MZ |
1595 | LSM_SUBJ_USER, |
1596 | AUDIT_SUBJ_USER); | |
1597 | break; | |
1598 | case Opt_subj_role: | |
2f1506cd | 1599 | ima_log_string(ab, "subj_role", args[0].from); |
7163a993 | 1600 | result = ima_lsm_rule_init(entry, args, |
4af4662f MZ |
1601 | LSM_SUBJ_ROLE, |
1602 | AUDIT_SUBJ_ROLE); | |
1603 | break; | |
1604 | case Opt_subj_type: | |
2f1506cd | 1605 | ima_log_string(ab, "subj_type", args[0].from); |
7163a993 | 1606 | result = ima_lsm_rule_init(entry, args, |
4af4662f MZ |
1607 | LSM_SUBJ_TYPE, |
1608 | AUDIT_SUBJ_TYPE); | |
1609 | break; | |
0e5a247c | 1610 | case Opt_appraise_type: |
0e5a247c DK |
1611 | ima_log_string(ab, "appraise_type", args[0].from); |
1612 | if ((strcmp(args[0].from, "imasig")) == 0) | |
1613 | entry->flags |= IMA_DIGSIG_REQUIRED; | |
30031b0e | 1614 | else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) && |
9044d627 TJB |
1615 | strcmp(args[0].from, "imasig|modsig") == 0) |
1616 | entry->flags |= IMA_DIGSIG_REQUIRED | | |
1617 | IMA_MODSIG_ALLOWED; | |
0e5a247c DK |
1618 | else |
1619 | result = -EINVAL; | |
1620 | break; | |
273df864 NJ |
1621 | case Opt_appraise_flag: |
1622 | ima_log_string(ab, "appraise_flag", args[0].from); | |
5f3e9265 TH |
1623 | if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) && |
1624 | strstr(args[0].from, "blacklist")) | |
273df864 | 1625 | entry->flags |= IMA_CHECK_BLACKLIST; |
5f3e9265 TH |
1626 | else |
1627 | result = -EINVAL; | |
273df864 | 1628 | break; |
583a80ae TS |
1629 | case Opt_appraise_algos: |
1630 | ima_log_string(ab, "appraise_algos", args[0].from); | |
1631 | ||
1632 | if (entry->allowed_algos) { | |
1633 | result = -EINVAL; | |
1634 | break; | |
1635 | } | |
1636 | ||
1637 | entry->allowed_algos = | |
1638 | ima_parse_appraise_algos(args[0].from); | |
1639 | /* invalid or empty list of algorithms */ | |
1640 | if (!entry->allowed_algos) { | |
1641 | result = -EINVAL; | |
1642 | break; | |
1643 | } | |
1644 | ||
1645 | entry->flags |= IMA_VALIDATE_ALGOS; | |
1646 | ||
1647 | break; | |
f9b2a735 MZ |
1648 | case Opt_permit_directio: |
1649 | entry->flags |= IMA_PERMIT_DIRECTIO; | |
0260643c ER |
1650 | break; |
1651 | case Opt_pcr: | |
0260643c ER |
1652 | ima_log_string(ab, "pcr", args[0].from); |
1653 | ||
1654 | result = kstrtoint(args[0].from, 10, &entry->pcr); | |
1655 | if (result || INVALID_PCR(entry->pcr)) | |
1656 | result = -EINVAL; | |
1657 | else | |
1658 | entry->flags |= IMA_PCR; | |
1659 | ||
19453ce0 MG |
1660 | break; |
1661 | case Opt_template: | |
1662 | ima_log_string(ab, "template", args[0].from); | |
1663 | if (entry->action != MEASURE) { | |
1664 | result = -EINVAL; | |
1665 | break; | |
1666 | } | |
1667 | template_desc = lookup_template_desc(args[0].from); | |
1668 | if (!template_desc || entry->template) { | |
1669 | result = -EINVAL; | |
1670 | break; | |
1671 | } | |
1672 | ||
1673 | /* | |
1674 | * template_desc_init_fields() does nothing if | |
1675 | * the template is already initialised, so | |
1676 | * it's safe to do this unconditionally | |
1677 | */ | |
1678 | template_desc_init_fields(template_desc->fmt, | |
1679 | &(template_desc->fields), | |
1680 | &(template_desc->num_fields)); | |
1681 | entry->template = template_desc; | |
f9b2a735 | 1682 | break; |
4af4662f | 1683 | case Opt_err: |
2f1506cd | 1684 | ima_log_string(ab, "UNKNOWN", p); |
e9d393bf | 1685 | result = -EINVAL; |
4af4662f MZ |
1686 | break; |
1687 | } | |
1688 | } | |
71218343 | 1689 | if (!result && !ima_validate_rule(entry)) |
4af4662f | 1690 | result = -EINVAL; |
6f0911a6 MZ |
1691 | else if (entry->action == APPRAISE) |
1692 | temp_ima_appraise |= ima_appraise_flag(entry->func); | |
1693 | ||
3878d505 TJB |
1694 | if (!result && entry->flags & IMA_MODSIG_ALLOWED) { |
1695 | template_desc = entry->template ? entry->template : | |
1696 | ima_template_desc_current(); | |
1697 | check_template_modsig(template_desc); | |
1698 | } | |
1699 | ||
b0d5de4d | 1700 | audit_log_format(ab, "res=%d", !result); |
4af4662f MZ |
1701 | audit_log_end(ab); |
1702 | return result; | |
1703 | } | |
1704 | ||
1705 | /** | |
07f6a794 | 1706 | * ima_parse_add_rule - add a rule to ima_policy_rules |
4af4662f MZ |
1707 | * @rule - ima measurement policy rule |
1708 | * | |
38d859f9 | 1709 | * Avoid locking by allowing just one writer at a time in ima_write_policy() |
6ccd0456 | 1710 | * Returns the length of the rule parsed, an error code on failure |
4af4662f | 1711 | */ |
6ccd0456 | 1712 | ssize_t ima_parse_add_rule(char *rule) |
4af4662f | 1713 | { |
52a13284 | 1714 | static const char op[] = "update_policy"; |
6ccd0456 | 1715 | char *p; |
07f6a794 | 1716 | struct ima_rule_entry *entry; |
6ccd0456 | 1717 | ssize_t result, len; |
4af4662f MZ |
1718 | int audit_info = 0; |
1719 | ||
272a6e90 DK |
1720 | p = strsep(&rule, "\n"); |
1721 | len = strlen(p) + 1; | |
7178784f | 1722 | p += strspn(p, " \t"); |
272a6e90 | 1723 | |
7178784f | 1724 | if (*p == '#' || *p == '\0') |
272a6e90 DK |
1725 | return len; |
1726 | ||
4af4662f MZ |
1727 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); |
1728 | if (!entry) { | |
1729 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, | |
1730 | NULL, op, "-ENOMEM", -ENOMEM, audit_info); | |
1731 | return -ENOMEM; | |
1732 | } | |
1733 | ||
1734 | INIT_LIST_HEAD(&entry->list); | |
1735 | ||
6ccd0456 | 1736 | result = ima_parse_rule(p, entry); |
7233e3ee | 1737 | if (result) { |
2bdd737c | 1738 | ima_free_rule(entry); |
523979ad | 1739 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, |
7e9001f6 | 1740 | NULL, op, "invalid-policy", result, |
523979ad | 1741 | audit_info); |
7233e3ee | 1742 | return result; |
523979ad | 1743 | } |
7233e3ee | 1744 | |
38d859f9 | 1745 | list_add_tail(&entry->list, &ima_temp_rules); |
7233e3ee EP |
1746 | |
1747 | return len; | |
4af4662f MZ |
1748 | } |
1749 | ||
38d859f9 PM |
1750 | /** |
1751 | * ima_delete_rules() called to cleanup invalid in-flight policy. | |
1752 | * We don't need locking as we operate on the temp list, which is | |
1753 | * different from the active one. There is also only one user of | |
1754 | * ima_delete_rules() at a time. | |
1755 | */ | |
64c61d80 | 1756 | void ima_delete_rules(void) |
4af4662f | 1757 | { |
07f6a794 | 1758 | struct ima_rule_entry *entry, *tmp; |
4af4662f | 1759 | |
6ad6afa1 | 1760 | temp_ima_appraise = 0; |
38d859f9 | 1761 | list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) { |
4af4662f | 1762 | list_del(&entry->list); |
465aee77 | 1763 | ima_free_rule(entry); |
4af4662f | 1764 | } |
4af4662f | 1765 | } |
80eae209 | 1766 | |
34e980bb | 1767 | #define __ima_hook_stringify(func, str) (#func), |
39b07096 TJB |
1768 | |
1769 | const char *const func_tokens[] = { | |
1770 | __ima_hooks(__ima_hook_stringify) | |
1771 | }; | |
1772 | ||
80eae209 PM |
1773 | #ifdef CONFIG_IMA_READ_POLICY |
1774 | enum { | |
1775 | mask_exec = 0, mask_write, mask_read, mask_append | |
1776 | }; | |
1777 | ||
bb543e39 | 1778 | static const char *const mask_tokens[] = { |
8cdc23a3 RS |
1779 | "^MAY_EXEC", |
1780 | "^MAY_WRITE", | |
1781 | "^MAY_READ", | |
1782 | "^MAY_APPEND" | |
80eae209 PM |
1783 | }; |
1784 | ||
80eae209 PM |
1785 | void *ima_policy_start(struct seq_file *m, loff_t *pos) |
1786 | { | |
1787 | loff_t l = *pos; | |
1788 | struct ima_rule_entry *entry; | |
1789 | ||
1790 | rcu_read_lock(); | |
1791 | list_for_each_entry_rcu(entry, ima_rules, list) { | |
1792 | if (!l--) { | |
1793 | rcu_read_unlock(); | |
1794 | return entry; | |
1795 | } | |
1796 | } | |
1797 | rcu_read_unlock(); | |
1798 | return NULL; | |
1799 | } | |
1800 | ||
1801 | void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos) | |
1802 | { | |
1803 | struct ima_rule_entry *entry = v; | |
1804 | ||
1805 | rcu_read_lock(); | |
1806 | entry = list_entry_rcu(entry->list.next, struct ima_rule_entry, list); | |
1807 | rcu_read_unlock(); | |
1808 | (*pos)++; | |
1809 | ||
1810 | return (&entry->list == ima_rules) ? NULL : entry; | |
1811 | } | |
1812 | ||
1813 | void ima_policy_stop(struct seq_file *m, void *v) | |
1814 | { | |
1815 | } | |
1816 | ||
1a9430db | 1817 | #define pt(token) policy_tokens[token].pattern |
80eae209 | 1818 | #define mt(token) mask_tokens[token] |
80eae209 | 1819 | |
b5269ab3 MZ |
1820 | /* |
1821 | * policy_func_show - display the ima_hooks policy rule | |
1822 | */ | |
1823 | static void policy_func_show(struct seq_file *m, enum ima_hooks func) | |
1824 | { | |
2663218b TJB |
1825 | if (func > 0 && func < MAX_CHECK) |
1826 | seq_printf(m, "func=%s ", func_tokens[func]); | |
1827 | else | |
1828 | seq_printf(m, "func=%d ", func); | |
b5269ab3 MZ |
1829 | } |
1830 | ||
176377d9 TH |
1831 | static void ima_show_rule_opt_list(struct seq_file *m, |
1832 | const struct ima_rule_opt_list *opt_list) | |
1833 | { | |
1834 | size_t i; | |
1835 | ||
1836 | for (i = 0; i < opt_list->count; i++) | |
1837 | seq_printf(m, "%s%s", i ? "|" : "", opt_list->items[i]); | |
1838 | } | |
1839 | ||
583a80ae TS |
1840 | static void ima_policy_show_appraise_algos(struct seq_file *m, |
1841 | unsigned int allowed_hashes) | |
1842 | { | |
1843 | int idx, list_size = 0; | |
1844 | ||
1845 | for (idx = 0; idx < HASH_ALGO__LAST; idx++) { | |
1846 | if (!(allowed_hashes & (1U << idx))) | |
1847 | continue; | |
1848 | ||
1849 | /* only add commas if the list contains multiple entries */ | |
1850 | if (list_size++) | |
1851 | seq_puts(m, ","); | |
1852 | ||
1853 | seq_puts(m, hash_algo_name[idx]); | |
1854 | } | |
1855 | } | |
1856 | ||
80eae209 PM |
1857 | int ima_policy_show(struct seq_file *m, void *v) |
1858 | { | |
1859 | struct ima_rule_entry *entry = v; | |
b8b57278 | 1860 | int i; |
80eae209 | 1861 | char tbuf[64] = {0,}; |
8cdc23a3 | 1862 | int offset = 0; |
80eae209 PM |
1863 | |
1864 | rcu_read_lock(); | |
1865 | ||
1866 | if (entry->action & MEASURE) | |
1867 | seq_puts(m, pt(Opt_measure)); | |
1868 | if (entry->action & DONT_MEASURE) | |
1869 | seq_puts(m, pt(Opt_dont_measure)); | |
1870 | if (entry->action & APPRAISE) | |
1871 | seq_puts(m, pt(Opt_appraise)); | |
1872 | if (entry->action & DONT_APPRAISE) | |
1873 | seq_puts(m, pt(Opt_dont_appraise)); | |
1874 | if (entry->action & AUDIT) | |
1875 | seq_puts(m, pt(Opt_audit)); | |
da1b0029 MZ |
1876 | if (entry->action & HASH) |
1877 | seq_puts(m, pt(Opt_hash)); | |
1878 | if (entry->action & DONT_HASH) | |
1879 | seq_puts(m, pt(Opt_dont_hash)); | |
80eae209 PM |
1880 | |
1881 | seq_puts(m, " "); | |
1882 | ||
b5269ab3 MZ |
1883 | if (entry->flags & IMA_FUNC) |
1884 | policy_func_show(m, entry->func); | |
80eae209 | 1885 | |
8cdc23a3 RS |
1886 | if ((entry->flags & IMA_MASK) || (entry->flags & IMA_INMASK)) { |
1887 | if (entry->flags & IMA_MASK) | |
1888 | offset = 1; | |
80eae209 | 1889 | if (entry->mask & MAY_EXEC) |
8cdc23a3 | 1890 | seq_printf(m, pt(Opt_mask), mt(mask_exec) + offset); |
80eae209 | 1891 | if (entry->mask & MAY_WRITE) |
8cdc23a3 | 1892 | seq_printf(m, pt(Opt_mask), mt(mask_write) + offset); |
80eae209 | 1893 | if (entry->mask & MAY_READ) |
8cdc23a3 | 1894 | seq_printf(m, pt(Opt_mask), mt(mask_read) + offset); |
80eae209 | 1895 | if (entry->mask & MAY_APPEND) |
8cdc23a3 | 1896 | seq_printf(m, pt(Opt_mask), mt(mask_append) + offset); |
80eae209 PM |
1897 | seq_puts(m, " "); |
1898 | } | |
1899 | ||
1900 | if (entry->flags & IMA_FSMAGIC) { | |
1901 | snprintf(tbuf, sizeof(tbuf), "0x%lx", entry->fsmagic); | |
1902 | seq_printf(m, pt(Opt_fsmagic), tbuf); | |
1903 | seq_puts(m, " "); | |
1904 | } | |
1905 | ||
f1b08bbc MZ |
1906 | if (entry->flags & IMA_FSNAME) { |
1907 | snprintf(tbuf, sizeof(tbuf), "%s", entry->fsname); | |
1908 | seq_printf(m, pt(Opt_fsname), tbuf); | |
1909 | seq_puts(m, " "); | |
1910 | } | |
1911 | ||
2b60c0ec | 1912 | if (entry->flags & IMA_KEYRINGS) { |
176377d9 TH |
1913 | seq_puts(m, "keyrings="); |
1914 | ima_show_rule_opt_list(m, entry->keyrings); | |
2b60c0ec LR |
1915 | seq_puts(m, " "); |
1916 | } | |
1917 | ||
47d76a48 TS |
1918 | if (entry->flags & IMA_LABEL) { |
1919 | seq_puts(m, "label="); | |
1920 | ima_show_rule_opt_list(m, entry->label); | |
1921 | seq_puts(m, " "); | |
1922 | } | |
1923 | ||
0260643c ER |
1924 | if (entry->flags & IMA_PCR) { |
1925 | snprintf(tbuf, sizeof(tbuf), "%d", entry->pcr); | |
1926 | seq_printf(m, pt(Opt_pcr), tbuf); | |
1927 | seq_puts(m, " "); | |
1928 | } | |
1929 | ||
80eae209 | 1930 | if (entry->flags & IMA_FSUUID) { |
787d8c53 | 1931 | seq_printf(m, "fsuuid=%pU", &entry->fsuuid); |
80eae209 PM |
1932 | seq_puts(m, " "); |
1933 | } | |
1934 | ||
1935 | if (entry->flags & IMA_UID) { | |
1936 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid)); | |
3dd0c8d0 MK |
1937 | if (entry->uid_op == &uid_gt) |
1938 | seq_printf(m, pt(Opt_uid_gt), tbuf); | |
1939 | else if (entry->uid_op == &uid_lt) | |
1940 | seq_printf(m, pt(Opt_uid_lt), tbuf); | |
1941 | else | |
1942 | seq_printf(m, pt(Opt_uid_eq), tbuf); | |
80eae209 PM |
1943 | seq_puts(m, " "); |
1944 | } | |
1945 | ||
1946 | if (entry->flags & IMA_EUID) { | |
1947 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid)); | |
3dd0c8d0 MK |
1948 | if (entry->uid_op == &uid_gt) |
1949 | seq_printf(m, pt(Opt_euid_gt), tbuf); | |
1950 | else if (entry->uid_op == &uid_lt) | |
1951 | seq_printf(m, pt(Opt_euid_lt), tbuf); | |
1952 | else | |
1953 | seq_printf(m, pt(Opt_euid_eq), tbuf); | |
80eae209 PM |
1954 | seq_puts(m, " "); |
1955 | } | |
1956 | ||
1957 | if (entry->flags & IMA_FOWNER) { | |
1958 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner)); | |
3dd0c8d0 MK |
1959 | if (entry->fowner_op == &uid_gt) |
1960 | seq_printf(m, pt(Opt_fowner_gt), tbuf); | |
1961 | else if (entry->fowner_op == &uid_lt) | |
1962 | seq_printf(m, pt(Opt_fowner_lt), tbuf); | |
1963 | else | |
1964 | seq_printf(m, pt(Opt_fowner_eq), tbuf); | |
80eae209 PM |
1965 | seq_puts(m, " "); |
1966 | } | |
1967 | ||
583a80ae TS |
1968 | if (entry->flags & IMA_VALIDATE_ALGOS) { |
1969 | seq_puts(m, "appraise_algos="); | |
1970 | ima_policy_show_appraise_algos(m, entry->allowed_algos); | |
1971 | seq_puts(m, " "); | |
1972 | } | |
1973 | ||
80eae209 | 1974 | for (i = 0; i < MAX_LSM_RULES; i++) { |
f17b27a2 | 1975 | if (ima_lsm_isset(entry->lsm[i].rules)) { |
80eae209 PM |
1976 | switch (i) { |
1977 | case LSM_OBJ_USER: | |
1978 | seq_printf(m, pt(Opt_obj_user), | |
aa0c0227 | 1979 | entry->lsm[i].args_p); |
80eae209 PM |
1980 | break; |
1981 | case LSM_OBJ_ROLE: | |
1982 | seq_printf(m, pt(Opt_obj_role), | |
aa0c0227 | 1983 | entry->lsm[i].args_p); |
80eae209 PM |
1984 | break; |
1985 | case LSM_OBJ_TYPE: | |
1986 | seq_printf(m, pt(Opt_obj_type), | |
aa0c0227 | 1987 | entry->lsm[i].args_p); |
80eae209 PM |
1988 | break; |
1989 | case LSM_SUBJ_USER: | |
1990 | seq_printf(m, pt(Opt_subj_user), | |
aa0c0227 | 1991 | entry->lsm[i].args_p); |
80eae209 PM |
1992 | break; |
1993 | case LSM_SUBJ_ROLE: | |
1994 | seq_printf(m, pt(Opt_subj_role), | |
aa0c0227 | 1995 | entry->lsm[i].args_p); |
80eae209 PM |
1996 | break; |
1997 | case LSM_SUBJ_TYPE: | |
1998 | seq_printf(m, pt(Opt_subj_type), | |
aa0c0227 | 1999 | entry->lsm[i].args_p); |
80eae209 PM |
2000 | break; |
2001 | } | |
5350ceb0 | 2002 | seq_puts(m, " "); |
80eae209 PM |
2003 | } |
2004 | } | |
19453ce0 MG |
2005 | if (entry->template) |
2006 | seq_printf(m, "template=%s ", entry->template->name); | |
9044d627 TJB |
2007 | if (entry->flags & IMA_DIGSIG_REQUIRED) { |
2008 | if (entry->flags & IMA_MODSIG_ALLOWED) | |
2009 | seq_puts(m, "appraise_type=imasig|modsig "); | |
2010 | else | |
2011 | seq_puts(m, "appraise_type=imasig "); | |
2012 | } | |
273df864 NJ |
2013 | if (entry->flags & IMA_CHECK_BLACKLIST) |
2014 | seq_puts(m, "appraise_flag=check_blacklist "); | |
80eae209 PM |
2015 | if (entry->flags & IMA_PERMIT_DIRECTIO) |
2016 | seq_puts(m, "permit_directio "); | |
2017 | rcu_read_unlock(); | |
2018 | seq_puts(m, "\n"); | |
2019 | return 0; | |
2020 | } | |
2021 | #endif /* CONFIG_IMA_READ_POLICY */ | |
29d3c1c8 MG |
2022 | |
2023 | #if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING) | |
2024 | /* | |
2025 | * ima_appraise_signature: whether IMA will appraise a given function using | |
2026 | * an IMA digital signature. This is restricted to cases where the kernel | |
2027 | * has a set of built-in trusted keys in order to avoid an attacker simply | |
2028 | * loading additional keys. | |
2029 | */ | |
2030 | bool ima_appraise_signature(enum kernel_read_file_id id) | |
2031 | { | |
2032 | struct ima_rule_entry *entry; | |
2033 | bool found = false; | |
2034 | enum ima_hooks func; | |
2035 | ||
2036 | if (id >= READING_MAX_ID) | |
2037 | return false; | |
2038 | ||
2039 | func = read_idmap[id] ?: FILE_CHECK; | |
2040 | ||
2041 | rcu_read_lock(); | |
2042 | list_for_each_entry_rcu(entry, ima_rules, list) { | |
2043 | if (entry->action != APPRAISE) | |
2044 | continue; | |
2045 | ||
2046 | /* | |
2047 | * A generic entry will match, but otherwise require that it | |
2048 | * match the func we're looking for | |
2049 | */ | |
2050 | if (entry->func && entry->func != func) | |
2051 | continue; | |
2052 | ||
2053 | /* | |
2054 | * We require this to be a digital signature, not a raw IMA | |
2055 | * hash. | |
2056 | */ | |
2057 | if (entry->flags & IMA_DIGSIG_REQUIRED) | |
2058 | found = true; | |
2059 | ||
2060 | /* | |
2061 | * We've found a rule that matches, so break now even if it | |
2062 | * didn't require a digital signature - a later rule that does | |
2063 | * won't override it, so would be a false positive. | |
2064 | */ | |
2065 | break; | |
2066 | } | |
2067 | ||
2068 | rcu_read_unlock(); | |
2069 | return found; | |
2070 | } | |
2071 | #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */ |