]>
Commit | Line | Data |
---|---|---|
3323eec9 MZ |
1 | /* |
2 | * Copyright (C) 2008 IBM Corporation | |
3 | * Author: Mimi Zohar <zohar@us.ibm.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, version 2 of the License. | |
8 | * | |
9 | * ima_policy.c | |
2bb930ab | 10 | * - initialize default measure policy rules |
3323eec9 MZ |
11 | * |
12 | */ | |
13 | #include <linux/module.h> | |
14 | #include <linux/list.h> | |
cf222217 | 15 | #include <linux/fs.h> |
3323eec9 MZ |
16 | #include <linux/security.h> |
17 | #include <linux/magic.h> | |
4af4662f | 18 | #include <linux/parser.h> |
5a0e3ad6 | 19 | #include <linux/slab.h> |
38d859f9 | 20 | #include <linux/rculist.h> |
85865c1f | 21 | #include <linux/genhd.h> |
80eae209 | 22 | #include <linux/seq_file.h> |
3323eec9 MZ |
23 | |
24 | #include "ima.h" | |
25 | ||
26 | /* flags definitions */ | |
2bb930ab DK |
27 | #define IMA_FUNC 0x0001 |
28 | #define IMA_MASK 0x0002 | |
3323eec9 MZ |
29 | #define IMA_FSMAGIC 0x0004 |
30 | #define IMA_UID 0x0008 | |
07f6a794 | 31 | #define IMA_FOWNER 0x0010 |
85865c1f | 32 | #define IMA_FSUUID 0x0020 |
4351c294 | 33 | #define IMA_INMASK 0x0040 |
139069ef | 34 | #define IMA_EUID 0x0080 |
0260643c | 35 | #define IMA_PCR 0x0100 |
3323eec9 | 36 | |
45e2472e DK |
37 | #define UNKNOWN 0 |
38 | #define MEASURE 0x0001 /* same as IMA_MEASURE */ | |
39 | #define DONT_MEASURE 0x0002 | |
40 | #define APPRAISE 0x0004 /* same as IMA_APPRAISE */ | |
41 | #define DONT_APPRAISE 0x0008 | |
e7c568e0 | 42 | #define AUDIT 0x0040 |
4af4662f | 43 | |
0260643c ER |
44 | #define INVALID_PCR(a) (((a) < 0) || \ |
45 | (a) >= (FIELD_SIZEOF(struct integrity_iint_cache, measured_pcrs) * 8)) | |
46 | ||
a756024e | 47 | int ima_policy_flag; |
6ad6afa1 | 48 | static int temp_ima_appraise; |
a756024e | 49 | |
4af4662f MZ |
50 | #define MAX_LSM_RULES 6 |
51 | enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, | |
52 | LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE | |
53 | }; | |
3323eec9 | 54 | |
24fd03c8 MZ |
55 | enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB }; |
56 | ||
07f6a794 | 57 | struct ima_rule_entry { |
3323eec9 | 58 | struct list_head list; |
2fe5d6de | 59 | int action; |
3323eec9 MZ |
60 | unsigned int flags; |
61 | enum ima_hooks func; | |
62 | int mask; | |
63 | unsigned long fsmagic; | |
787d8c53 | 64 | uuid_t fsuuid; |
8b94eea4 | 65 | kuid_t uid; |
88265322 | 66 | kuid_t fowner; |
3dd0c8d0 MK |
67 | bool (*uid_op)(kuid_t, kuid_t); /* Handlers for operators */ |
68 | bool (*fowner_op)(kuid_t, kuid_t); /* uid_eq(), uid_gt(), uid_lt() */ | |
0260643c | 69 | int pcr; |
4af4662f MZ |
70 | struct { |
71 | void *rule; /* LSM file metadata specific */ | |
7163a993 | 72 | void *args_p; /* audit value */ |
4af4662f MZ |
73 | int type; /* audit type */ |
74 | } lsm[MAX_LSM_RULES]; | |
3323eec9 MZ |
75 | }; |
76 | ||
5789ba3b EP |
77 | /* |
78 | * Without LSM specific knowledge, the default policy can only be | |
07f6a794 | 79 | * written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner |
4af4662f | 80 | */ |
5789ba3b EP |
81 | |
82 | /* | |
83 | * The minimum rule set to allow for full TCB coverage. Measures all files | |
84 | * opened or mmap for exec and everything read by root. Dangerous because | |
85 | * normal users can easily run the machine out of memory simply building | |
86 | * and running executables. | |
87 | */ | |
bad4417b | 88 | static struct ima_rule_entry dont_measure_rules[] __ro_after_init = { |
2bb930ab DK |
89 | {.action = DONT_MEASURE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC}, |
90 | {.action = DONT_MEASURE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC}, | |
91 | {.action = DONT_MEASURE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC}, | |
92 | {.action = DONT_MEASURE, .fsmagic = TMPFS_MAGIC, .flags = IMA_FSMAGIC}, | |
93 | {.action = DONT_MEASURE, .fsmagic = DEVPTS_SUPER_MAGIC, .flags = IMA_FSMAGIC}, | |
94 | {.action = DONT_MEASURE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC}, | |
95 | {.action = DONT_MEASURE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC}, | |
96 | {.action = DONT_MEASURE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC}, | |
6438de9f RS |
97 | {.action = DONT_MEASURE, .fsmagic = CGROUP_SUPER_MAGIC, |
98 | .flags = IMA_FSMAGIC}, | |
82e3bb4d LA |
99 | {.action = DONT_MEASURE, .fsmagic = CGROUP2_SUPER_MAGIC, |
100 | .flags = IMA_FSMAGIC}, | |
24fd03c8 MZ |
101 | {.action = DONT_MEASURE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC} |
102 | }; | |
103 | ||
bad4417b | 104 | static struct ima_rule_entry original_measurement_rules[] __ro_after_init = { |
24fd03c8 MZ |
105 | {.action = MEASURE, .func = MMAP_CHECK, .mask = MAY_EXEC, |
106 | .flags = IMA_FUNC | IMA_MASK}, | |
107 | {.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC, | |
108 | .flags = IMA_FUNC | IMA_MASK}, | |
109 | {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, | |
3dd0c8d0 MK |
110 | .uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq, |
111 | .flags = IMA_FUNC | IMA_MASK | IMA_UID}, | |
24fd03c8 MZ |
112 | {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, |
113 | {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, | |
114 | }; | |
115 | ||
bad4417b | 116 | static struct ima_rule_entry default_measurement_rules[] __ro_after_init = { |
2bb930ab | 117 | {.action = MEASURE, .func = MMAP_CHECK, .mask = MAY_EXEC, |
3323eec9 | 118 | .flags = IMA_FUNC | IMA_MASK}, |
2bb930ab | 119 | {.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC, |
3323eec9 | 120 | .flags = IMA_FUNC | IMA_MASK}, |
24fd03c8 | 121 | {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, |
3dd0c8d0 MK |
122 | .uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq, |
123 | .flags = IMA_FUNC | IMA_INMASK | IMA_EUID}, | |
24fd03c8 | 124 | {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, |
3dd0c8d0 MK |
125 | .uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq, |
126 | .flags = IMA_FUNC | IMA_INMASK | IMA_UID}, | |
2bb930ab | 127 | {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, |
5a9196d7 | 128 | {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, |
19f8a847 | 129 | {.action = MEASURE, .func = POLICY_CHECK, .flags = IMA_FUNC}, |
3323eec9 MZ |
130 | }; |
131 | ||
bad4417b | 132 | static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { |
2bb930ab DK |
133 | {.action = DONT_APPRAISE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC}, |
134 | {.action = DONT_APPRAISE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC}, | |
135 | {.action = DONT_APPRAISE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC}, | |
136 | {.action = DONT_APPRAISE, .fsmagic = TMPFS_MAGIC, .flags = IMA_FSMAGIC}, | |
137 | {.action = DONT_APPRAISE, .fsmagic = RAMFS_MAGIC, .flags = IMA_FSMAGIC}, | |
138 | {.action = DONT_APPRAISE, .fsmagic = DEVPTS_SUPER_MAGIC, .flags = IMA_FSMAGIC}, | |
139 | {.action = DONT_APPRAISE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC}, | |
140 | {.action = DONT_APPRAISE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC}, | |
141 | {.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC}, | |
cd025f7f | 142 | {.action = DONT_APPRAISE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC}, |
2bb930ab | 143 | {.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC}, |
82e3bb4d | 144 | {.action = DONT_APPRAISE, .fsmagic = CGROUP2_SUPER_MAGIC, .flags = IMA_FSMAGIC}, |
95ee08fa MZ |
145 | #ifdef CONFIG_IMA_WRITE_POLICY |
146 | {.action = APPRAISE, .func = POLICY_CHECK, | |
147 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | |
148 | #endif | |
c57782c1 | 149 | #ifndef CONFIG_IMA_APPRAISE_SIGNED_INIT |
3dd0c8d0 MK |
150 | {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &uid_eq, |
151 | .flags = IMA_FOWNER}, | |
c57782c1 DK |
152 | #else |
153 | /* force signature */ | |
3dd0c8d0 | 154 | {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &uid_eq, |
c57782c1 DK |
155 | .flags = IMA_FOWNER | IMA_DIGSIG_REQUIRED}, |
156 | #endif | |
07f6a794 MZ |
157 | }; |
158 | ||
503ceaef MZ |
159 | static struct ima_rule_entry secure_boot_rules[] __ro_after_init = { |
160 | {.action = APPRAISE, .func = MODULE_CHECK, | |
161 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | |
162 | {.action = APPRAISE, .func = FIRMWARE_CHECK, | |
163 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | |
164 | {.action = APPRAISE, .func = KEXEC_KERNEL_CHECK, | |
165 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | |
166 | {.action = APPRAISE, .func = POLICY_CHECK, | |
167 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | |
168 | }; | |
169 | ||
07f6a794 MZ |
170 | static LIST_HEAD(ima_default_rules); |
171 | static LIST_HEAD(ima_policy_rules); | |
38d859f9 | 172 | static LIST_HEAD(ima_temp_rules); |
07f6a794 | 173 | static struct list_head *ima_rules; |
3323eec9 | 174 | |
24fd03c8 | 175 | static int ima_policy __initdata; |
38d859f9 | 176 | |
07f6a794 | 177 | static int __init default_measure_policy_setup(char *str) |
5789ba3b | 178 | { |
24fd03c8 MZ |
179 | if (ima_policy) |
180 | return 1; | |
181 | ||
182 | ima_policy = ORIGINAL_TCB; | |
5789ba3b EP |
183 | return 1; |
184 | } | |
07f6a794 MZ |
185 | __setup("ima_tcb", default_measure_policy_setup); |
186 | ||
33ce9549 | 187 | static bool ima_use_appraise_tcb __initdata; |
503ceaef | 188 | static bool ima_use_secure_boot __initdata; |
24fd03c8 MZ |
189 | static int __init policy_setup(char *str) |
190 | { | |
33ce9549 | 191 | char *p; |
24fd03c8 | 192 | |
33ce9549 MZ |
193 | while ((p = strsep(&str, " |\n")) != NULL) { |
194 | if (*p == ' ') | |
195 | continue; | |
196 | if ((strcmp(p, "tcb") == 0) && !ima_policy) | |
197 | ima_policy = DEFAULT_TCB; | |
198 | else if (strcmp(p, "appraise_tcb") == 0) | |
199 | ima_use_appraise_tcb = 1; | |
503ceaef MZ |
200 | else if (strcmp(p, "secure_boot") == 0) |
201 | ima_use_secure_boot = 1; | |
33ce9549 | 202 | } |
24fd03c8 MZ |
203 | |
204 | return 1; | |
205 | } | |
206 | __setup("ima_policy=", policy_setup); | |
207 | ||
07f6a794 MZ |
208 | static int __init default_appraise_policy_setup(char *str) |
209 | { | |
210 | ima_use_appraise_tcb = 1; | |
211 | return 1; | |
212 | } | |
213 | __setup("ima_appraise_tcb", default_appraise_policy_setup); | |
5789ba3b | 214 | |
2bb930ab | 215 | /* |
38d859f9 PM |
216 | * The LSM policy can be reloaded, leaving the IMA LSM based rules referring |
217 | * to the old, stale LSM policy. Update the IMA LSM based rules to reflect | |
218 | * the reloaded LSM policy. We assume the rules still exist; and BUG_ON() if | |
219 | * they don't. | |
7163a993 MZ |
220 | */ |
221 | static void ima_lsm_update_rules(void) | |
222 | { | |
38d859f9 | 223 | struct ima_rule_entry *entry; |
7163a993 MZ |
224 | int result; |
225 | int i; | |
226 | ||
38d859f9 | 227 | list_for_each_entry(entry, &ima_policy_rules, list) { |
7163a993 MZ |
228 | for (i = 0; i < MAX_LSM_RULES; i++) { |
229 | if (!entry->lsm[i].rule) | |
230 | continue; | |
231 | result = security_filter_rule_init(entry->lsm[i].type, | |
232 | Audit_equal, | |
233 | entry->lsm[i].args_p, | |
234 | &entry->lsm[i].rule); | |
235 | BUG_ON(!entry->lsm[i].rule); | |
236 | } | |
237 | } | |
7163a993 MZ |
238 | } |
239 | ||
3323eec9 MZ |
240 | /** |
241 | * ima_match_rules - determine whether an inode matches the measure rule. | |
242 | * @rule: a pointer to a rule | |
243 | * @inode: a pointer to an inode | |
244 | * @func: LIM hook identifier | |
245 | * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) | |
246 | * | |
247 | * Returns true on rule match, false on failure. | |
248 | */ | |
4ad87a3d MZ |
249 | static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, |
250 | enum ima_hooks func, int mask) | |
3323eec9 MZ |
251 | { |
252 | struct task_struct *tsk = current; | |
3db59dd9 | 253 | const struct cred *cred = current_cred(); |
4af4662f | 254 | int i; |
3323eec9 | 255 | |
09b1148e DK |
256 | if ((rule->flags & IMA_FUNC) && |
257 | (rule->func != func && func != POST_SETATTR)) | |
3323eec9 | 258 | return false; |
09b1148e DK |
259 | if ((rule->flags & IMA_MASK) && |
260 | (rule->mask != mask && func != POST_SETATTR)) | |
3323eec9 | 261 | return false; |
4351c294 MZ |
262 | if ((rule->flags & IMA_INMASK) && |
263 | (!(rule->mask & mask) && func != POST_SETATTR)) | |
264 | return false; | |
3323eec9 MZ |
265 | if ((rule->flags & IMA_FSMAGIC) |
266 | && rule->fsmagic != inode->i_sb->s_magic) | |
267 | return false; | |
85865c1f | 268 | if ((rule->flags & IMA_FSUUID) && |
85787090 | 269 | !uuid_equal(&rule->fsuuid, &inode->i_sb->s_uuid)) |
85865c1f | 270 | return false; |
3dd0c8d0 | 271 | if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid)) |
3323eec9 | 272 | return false; |
139069ef MZ |
273 | if (rule->flags & IMA_EUID) { |
274 | if (has_capability_noaudit(current, CAP_SETUID)) { | |
3dd0c8d0 MK |
275 | if (!rule->uid_op(cred->euid, rule->uid) |
276 | && !rule->uid_op(cred->suid, rule->uid) | |
277 | && !rule->uid_op(cred->uid, rule->uid)) | |
139069ef | 278 | return false; |
3dd0c8d0 | 279 | } else if (!rule->uid_op(cred->euid, rule->uid)) |
139069ef MZ |
280 | return false; |
281 | } | |
282 | ||
3dd0c8d0 MK |
283 | if ((rule->flags & IMA_FOWNER) && |
284 | !rule->fowner_op(inode->i_uid, rule->fowner)) | |
07f6a794 | 285 | return false; |
4af4662f | 286 | for (i = 0; i < MAX_LSM_RULES; i++) { |
53fc0e22 | 287 | int rc = 0; |
4af4662f | 288 | u32 osid, sid; |
7163a993 | 289 | int retried = 0; |
4af4662f MZ |
290 | |
291 | if (!rule->lsm[i].rule) | |
292 | continue; | |
7163a993 | 293 | retry: |
4af4662f MZ |
294 | switch (i) { |
295 | case LSM_OBJ_USER: | |
296 | case LSM_OBJ_ROLE: | |
297 | case LSM_OBJ_TYPE: | |
298 | security_inode_getsecid(inode, &osid); | |
299 | rc = security_filter_rule_match(osid, | |
300 | rule->lsm[i].type, | |
53fc0e22 | 301 | Audit_equal, |
4af4662f MZ |
302 | rule->lsm[i].rule, |
303 | NULL); | |
304 | break; | |
305 | case LSM_SUBJ_USER: | |
306 | case LSM_SUBJ_ROLE: | |
307 | case LSM_SUBJ_TYPE: | |
308 | security_task_getsecid(tsk, &sid); | |
309 | rc = security_filter_rule_match(sid, | |
310 | rule->lsm[i].type, | |
53fc0e22 | 311 | Audit_equal, |
4af4662f MZ |
312 | rule->lsm[i].rule, |
313 | NULL); | |
314 | default: | |
315 | break; | |
316 | } | |
7163a993 MZ |
317 | if ((rc < 0) && (!retried)) { |
318 | retried = 1; | |
319 | ima_lsm_update_rules(); | |
320 | goto retry; | |
2bb930ab | 321 | } |
4af4662f MZ |
322 | if (!rc) |
323 | return false; | |
324 | } | |
3323eec9 MZ |
325 | return true; |
326 | } | |
327 | ||
d79d72e0 MZ |
328 | /* |
329 | * In addition to knowing that we need to appraise the file in general, | |
5a73fcfa | 330 | * we need to differentiate between calling hooks, for hook specific rules. |
d79d72e0 | 331 | */ |
4ad87a3d | 332 | static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func) |
d79d72e0 | 333 | { |
5a73fcfa MZ |
334 | if (!(rule->flags & IMA_FUNC)) |
335 | return IMA_FILE_APPRAISE; | |
336 | ||
2bb930ab | 337 | switch (func) { |
d79d72e0 MZ |
338 | case MMAP_CHECK: |
339 | return IMA_MMAP_APPRAISE; | |
340 | case BPRM_CHECK: | |
341 | return IMA_BPRM_APPRAISE; | |
d79d72e0 | 342 | case FILE_CHECK: |
c6af8efe | 343 | case POST_SETATTR: |
d79d72e0 | 344 | return IMA_FILE_APPRAISE; |
c6af8efe MZ |
345 | case MODULE_CHECK ... MAX_CHECK - 1: |
346 | default: | |
347 | return IMA_READ_APPRAISE; | |
d79d72e0 MZ |
348 | } |
349 | } | |
350 | ||
3323eec9 MZ |
351 | /** |
352 | * ima_match_policy - decision based on LSM and other conditions | |
353 | * @inode: pointer to an inode for which the policy decision is being made | |
354 | * @func: IMA hook identifier | |
355 | * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) | |
725de7fa | 356 | * @pcr: set the pcr to extend |
3323eec9 MZ |
357 | * |
358 | * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type) | |
359 | * conditions. | |
360 | * | |
38d859f9 PM |
361 | * Since the IMA policy may be updated multiple times we need to lock the |
362 | * list when walking it. Reads are many orders of magnitude more numerous | |
363 | * than writes so ima_match_policy() is classical RCU candidate. | |
3323eec9 | 364 | */ |
2fe5d6de | 365 | int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, |
725de7fa | 366 | int flags, int *pcr) |
3323eec9 | 367 | { |
07f6a794 | 368 | struct ima_rule_entry *entry; |
2fe5d6de | 369 | int action = 0, actmask = flags | (flags << 1); |
3323eec9 | 370 | |
38d859f9 PM |
371 | rcu_read_lock(); |
372 | list_for_each_entry_rcu(entry, ima_rules, list) { | |
3323eec9 | 373 | |
2fe5d6de MZ |
374 | if (!(entry->action & actmask)) |
375 | continue; | |
376 | ||
377 | if (!ima_match_rules(entry, inode, func, mask)) | |
378 | continue; | |
3323eec9 | 379 | |
0e5a247c DK |
380 | action |= entry->flags & IMA_ACTION_FLAGS; |
381 | ||
45e2472e | 382 | action |= entry->action & IMA_DO_MASK; |
d79d72e0 | 383 | if (entry->action & IMA_APPRAISE) |
5a73fcfa | 384 | action |= get_subaction(entry, func); |
d79d72e0 | 385 | |
45e2472e DK |
386 | if (entry->action & IMA_DO_MASK) |
387 | actmask &= ~(entry->action | entry->action << 1); | |
388 | else | |
389 | actmask &= ~(entry->action | entry->action >> 1); | |
3323eec9 | 390 | |
725de7fa ER |
391 | if ((pcr) && (entry->flags & IMA_PCR)) |
392 | *pcr = entry->pcr; | |
393 | ||
2fe5d6de MZ |
394 | if (!actmask) |
395 | break; | |
3323eec9 | 396 | } |
38d859f9 | 397 | rcu_read_unlock(); |
2fe5d6de MZ |
398 | |
399 | return action; | |
3323eec9 MZ |
400 | } |
401 | ||
a756024e RS |
402 | /* |
403 | * Initialize the ima_policy_flag variable based on the currently | |
404 | * loaded policy. Based on this flag, the decision to short circuit | |
405 | * out of a function or not call the function in the first place | |
406 | * can be made earlier. | |
407 | */ | |
408 | void ima_update_policy_flag(void) | |
409 | { | |
410 | struct ima_rule_entry *entry; | |
411 | ||
a756024e RS |
412 | list_for_each_entry(entry, ima_rules, list) { |
413 | if (entry->action & IMA_DO_MASK) | |
414 | ima_policy_flag |= entry->action; | |
415 | } | |
416 | ||
6ad6afa1 | 417 | ima_appraise |= temp_ima_appraise; |
a756024e RS |
418 | if (!ima_appraise) |
419 | ima_policy_flag &= ~IMA_APPRAISE; | |
420 | } | |
421 | ||
3323eec9 MZ |
422 | /** |
423 | * ima_init_policy - initialize the default measure rules. | |
424 | * | |
07f6a794 MZ |
425 | * ima_rules points to either the ima_default_rules or the |
426 | * the new ima_policy_rules. | |
3323eec9 | 427 | */ |
932995f0 | 428 | void __init ima_init_policy(void) |
3323eec9 | 429 | { |
503ceaef | 430 | int i, measure_entries, appraise_entries, secure_boot_entries; |
5789ba3b | 431 | |
24fd03c8 MZ |
432 | /* if !ima_policy set entries = 0 so we load NO default rules */ |
433 | measure_entries = ima_policy ? ARRAY_SIZE(dont_measure_rules) : 0; | |
07f6a794 MZ |
434 | appraise_entries = ima_use_appraise_tcb ? |
435 | ARRAY_SIZE(default_appraise_rules) : 0; | |
503ceaef MZ |
436 | secure_boot_entries = ima_use_secure_boot ? |
437 | ARRAY_SIZE(secure_boot_rules) : 0; | |
2bb930ab | 438 | |
5577857f | 439 | for (i = 0; i < measure_entries; i++) |
24fd03c8 MZ |
440 | list_add_tail(&dont_measure_rules[i].list, &ima_default_rules); |
441 | ||
442 | switch (ima_policy) { | |
443 | case ORIGINAL_TCB: | |
444 | for (i = 0; i < ARRAY_SIZE(original_measurement_rules); i++) | |
445 | list_add_tail(&original_measurement_rules[i].list, | |
446 | &ima_default_rules); | |
447 | break; | |
448 | case DEFAULT_TCB: | |
449 | for (i = 0; i < ARRAY_SIZE(default_measurement_rules); i++) | |
450 | list_add_tail(&default_measurement_rules[i].list, | |
451 | &ima_default_rules); | |
452 | default: | |
453 | break; | |
454 | } | |
5577857f | 455 | |
503ceaef MZ |
456 | /* |
457 | * Insert the appraise rules requiring file signatures, prior to | |
458 | * any other appraise rules. | |
459 | */ | |
460 | for (i = 0; i < secure_boot_entries; i++) | |
461 | list_add_tail(&secure_boot_rules[i].list, | |
462 | &ima_default_rules); | |
463 | ||
5577857f DC |
464 | for (i = 0; i < appraise_entries; i++) { |
465 | list_add_tail(&default_appraise_rules[i].list, | |
466 | &ima_default_rules); | |
95ee08fa MZ |
467 | if (default_appraise_rules[i].func == POLICY_CHECK) |
468 | temp_ima_appraise |= IMA_APPRAISE_POLICY; | |
07f6a794 MZ |
469 | } |
470 | ||
471 | ima_rules = &ima_default_rules; | |
95ee08fa | 472 | ima_update_policy_flag(); |
3323eec9 | 473 | } |
4af4662f | 474 | |
0112721d | 475 | /* Make sure we have a valid policy, at least containing some rules. */ |
c75d8e96 | 476 | int ima_check_policy(void) |
0112721d SL |
477 | { |
478 | if (list_empty(&ima_temp_rules)) | |
479 | return -EINVAL; | |
480 | return 0; | |
481 | } | |
482 | ||
4af4662f MZ |
483 | /** |
484 | * ima_update_policy - update default_rules with new measure rules | |
485 | * | |
486 | * Called on file .release to update the default rules with a complete new | |
38d859f9 PM |
487 | * policy. What we do here is to splice ima_policy_rules and ima_temp_rules so |
488 | * they make a queue. The policy may be updated multiple times and this is the | |
489 | * RCU updater. | |
490 | * | |
491 | * Policy rules are never deleted so ima_policy_flag gets zeroed only once when | |
492 | * we switch from the default policy to user defined. | |
4af4662f MZ |
493 | */ |
494 | void ima_update_policy(void) | |
495 | { | |
38d859f9 PM |
496 | struct list_head *first, *last, *policy; |
497 | ||
498 | /* append current policy with the new rules */ | |
499 | first = (&ima_temp_rules)->next; | |
500 | last = (&ima_temp_rules)->prev; | |
501 | policy = &ima_policy_rules; | |
502 | ||
503 | synchronize_rcu(); | |
504 | ||
505 | last->next = policy; | |
506 | rcu_assign_pointer(list_next_rcu(policy->prev), first); | |
507 | first->prev = policy->prev; | |
508 | policy->prev = last; | |
509 | ||
510 | /* prepare for the next policy rules addition */ | |
511 | INIT_LIST_HEAD(&ima_temp_rules); | |
512 | ||
513 | if (ima_rules != policy) { | |
514 | ima_policy_flag = 0; | |
515 | ima_rules = policy; | |
516 | } | |
0716abbb | 517 | ima_update_policy_flag(); |
4af4662f MZ |
518 | } |
519 | ||
520 | enum { | |
521 | Opt_err = -1, | |
522 | Opt_measure = 1, Opt_dont_measure, | |
07f6a794 | 523 | Opt_appraise, Opt_dont_appraise, |
e7c568e0 | 524 | Opt_audit, |
4af4662f MZ |
525 | Opt_obj_user, Opt_obj_role, Opt_obj_type, |
526 | Opt_subj_user, Opt_subj_role, Opt_subj_type, | |
139069ef | 527 | Opt_func, Opt_mask, Opt_fsmagic, |
3dd0c8d0 MK |
528 | Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq, |
529 | Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt, | |
530 | Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt, | |
0260643c ER |
531 | Opt_appraise_type, Opt_permit_directio, |
532 | Opt_pcr | |
4af4662f MZ |
533 | }; |
534 | ||
535 | static match_table_t policy_tokens = { | |
536 | {Opt_measure, "measure"}, | |
537 | {Opt_dont_measure, "dont_measure"}, | |
07f6a794 MZ |
538 | {Opt_appraise, "appraise"}, |
539 | {Opt_dont_appraise, "dont_appraise"}, | |
e7c568e0 | 540 | {Opt_audit, "audit"}, |
4af4662f MZ |
541 | {Opt_obj_user, "obj_user=%s"}, |
542 | {Opt_obj_role, "obj_role=%s"}, | |
543 | {Opt_obj_type, "obj_type=%s"}, | |
544 | {Opt_subj_user, "subj_user=%s"}, | |
545 | {Opt_subj_role, "subj_role=%s"}, | |
546 | {Opt_subj_type, "subj_type=%s"}, | |
547 | {Opt_func, "func=%s"}, | |
548 | {Opt_mask, "mask=%s"}, | |
549 | {Opt_fsmagic, "fsmagic=%s"}, | |
85865c1f | 550 | {Opt_fsuuid, "fsuuid=%s"}, |
3dd0c8d0 MK |
551 | {Opt_uid_eq, "uid=%s"}, |
552 | {Opt_euid_eq, "euid=%s"}, | |
553 | {Opt_fowner_eq, "fowner=%s"}, | |
554 | {Opt_uid_gt, "uid>%s"}, | |
555 | {Opt_euid_gt, "euid>%s"}, | |
556 | {Opt_fowner_gt, "fowner>%s"}, | |
557 | {Opt_uid_lt, "uid<%s"}, | |
558 | {Opt_euid_lt, "euid<%s"}, | |
559 | {Opt_fowner_lt, "fowner<%s"}, | |
0e5a247c | 560 | {Opt_appraise_type, "appraise_type=%s"}, |
f9b2a735 | 561 | {Opt_permit_directio, "permit_directio"}, |
0260643c | 562 | {Opt_pcr, "pcr=%s"}, |
4af4662f MZ |
563 | {Opt_err, NULL} |
564 | }; | |
565 | ||
07f6a794 | 566 | static int ima_lsm_rule_init(struct ima_rule_entry *entry, |
7163a993 | 567 | substring_t *args, int lsm_rule, int audit_type) |
4af4662f MZ |
568 | { |
569 | int result; | |
570 | ||
7b62e162 EP |
571 | if (entry->lsm[lsm_rule].rule) |
572 | return -EINVAL; | |
573 | ||
7163a993 MZ |
574 | entry->lsm[lsm_rule].args_p = match_strdup(args); |
575 | if (!entry->lsm[lsm_rule].args_p) | |
576 | return -ENOMEM; | |
577 | ||
4af4662f MZ |
578 | entry->lsm[lsm_rule].type = audit_type; |
579 | result = security_filter_rule_init(entry->lsm[lsm_rule].type, | |
7163a993 MZ |
580 | Audit_equal, |
581 | entry->lsm[lsm_rule].args_p, | |
4af4662f | 582 | &entry->lsm[lsm_rule].rule); |
7163a993 MZ |
583 | if (!entry->lsm[lsm_rule].rule) { |
584 | kfree(entry->lsm[lsm_rule].args_p); | |
867c2026 | 585 | return -EINVAL; |
7163a993 MZ |
586 | } |
587 | ||
4af4662f MZ |
588 | return result; |
589 | } | |
590 | ||
3dd0c8d0 MK |
591 | static void ima_log_string_op(struct audit_buffer *ab, char *key, char *value, |
592 | bool (*rule_operator)(kuid_t, kuid_t)) | |
2f1506cd | 593 | { |
3dd0c8d0 MK |
594 | if (rule_operator == &uid_gt) |
595 | audit_log_format(ab, "%s>", key); | |
596 | else if (rule_operator == &uid_lt) | |
597 | audit_log_format(ab, "%s<", key); | |
598 | else | |
599 | audit_log_format(ab, "%s=", key); | |
2f1506cd EP |
600 | audit_log_untrustedstring(ab, value); |
601 | audit_log_format(ab, " "); | |
602 | } | |
3dd0c8d0 MK |
603 | static void ima_log_string(struct audit_buffer *ab, char *key, char *value) |
604 | { | |
605 | ima_log_string_op(ab, key, value, NULL); | |
606 | } | |
2f1506cd | 607 | |
07f6a794 | 608 | static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) |
4af4662f MZ |
609 | { |
610 | struct audit_buffer *ab; | |
4351c294 | 611 | char *from; |
4af4662f | 612 | char *p; |
3dd0c8d0 | 613 | bool uid_token; |
4af4662f MZ |
614 | int result = 0; |
615 | ||
523979ad | 616 | ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); |
4af4662f | 617 | |
8b94eea4 | 618 | entry->uid = INVALID_UID; |
88265322 | 619 | entry->fowner = INVALID_UID; |
3dd0c8d0 MK |
620 | entry->uid_op = &uid_eq; |
621 | entry->fowner_op = &uid_eq; | |
b9035b1f | 622 | entry->action = UNKNOWN; |
28ef4002 | 623 | while ((p = strsep(&rule, " \t")) != NULL) { |
4af4662f MZ |
624 | substring_t args[MAX_OPT_ARGS]; |
625 | int token; | |
626 | unsigned long lnum; | |
627 | ||
628 | if (result < 0) | |
629 | break; | |
28ef4002 EP |
630 | if ((*p == '\0') || (*p == ' ') || (*p == '\t')) |
631 | continue; | |
4af4662f MZ |
632 | token = match_token(p, policy_tokens, args); |
633 | switch (token) { | |
634 | case Opt_measure: | |
2f1506cd | 635 | ima_log_string(ab, "action", "measure"); |
7b62e162 EP |
636 | |
637 | if (entry->action != UNKNOWN) | |
638 | result = -EINVAL; | |
639 | ||
4af4662f MZ |
640 | entry->action = MEASURE; |
641 | break; | |
642 | case Opt_dont_measure: | |
2f1506cd | 643 | ima_log_string(ab, "action", "dont_measure"); |
7b62e162 EP |
644 | |
645 | if (entry->action != UNKNOWN) | |
646 | result = -EINVAL; | |
647 | ||
4af4662f MZ |
648 | entry->action = DONT_MEASURE; |
649 | break; | |
07f6a794 MZ |
650 | case Opt_appraise: |
651 | ima_log_string(ab, "action", "appraise"); | |
652 | ||
653 | if (entry->action != UNKNOWN) | |
654 | result = -EINVAL; | |
655 | ||
656 | entry->action = APPRAISE; | |
657 | break; | |
658 | case Opt_dont_appraise: | |
659 | ima_log_string(ab, "action", "dont_appraise"); | |
660 | ||
661 | if (entry->action != UNKNOWN) | |
662 | result = -EINVAL; | |
663 | ||
664 | entry->action = DONT_APPRAISE; | |
665 | break; | |
e7c568e0 PM |
666 | case Opt_audit: |
667 | ima_log_string(ab, "action", "audit"); | |
668 | ||
669 | if (entry->action != UNKNOWN) | |
670 | result = -EINVAL; | |
671 | ||
672 | entry->action = AUDIT; | |
673 | break; | |
4af4662f | 674 | case Opt_func: |
2f1506cd | 675 | ima_log_string(ab, "func", args[0].from); |
7b62e162 EP |
676 | |
677 | if (entry->func) | |
07f6a794 | 678 | result = -EINVAL; |
7b62e162 | 679 | |
1e93d005 MZ |
680 | if (strcmp(args[0].from, "FILE_CHECK") == 0) |
681 | entry->func = FILE_CHECK; | |
682 | /* PATH_CHECK is for backwards compat */ | |
683 | else if (strcmp(args[0].from, "PATH_CHECK") == 0) | |
684 | entry->func = FILE_CHECK; | |
fdf90729 MZ |
685 | else if (strcmp(args[0].from, "MODULE_CHECK") == 0) |
686 | entry->func = MODULE_CHECK; | |
5a9196d7 MZ |
687 | else if (strcmp(args[0].from, "FIRMWARE_CHECK") == 0) |
688 | entry->func = FIRMWARE_CHECK; | |
16cac49f MZ |
689 | else if ((strcmp(args[0].from, "FILE_MMAP") == 0) |
690 | || (strcmp(args[0].from, "MMAP_CHECK") == 0)) | |
691 | entry->func = MMAP_CHECK; | |
4af4662f MZ |
692 | else if (strcmp(args[0].from, "BPRM_CHECK") == 0) |
693 | entry->func = BPRM_CHECK; | |
d9ddf077 MZ |
694 | else if (strcmp(args[0].from, "KEXEC_KERNEL_CHECK") == |
695 | 0) | |
696 | entry->func = KEXEC_KERNEL_CHECK; | |
697 | else if (strcmp(args[0].from, "KEXEC_INITRAMFS_CHECK") | |
698 | == 0) | |
699 | entry->func = KEXEC_INITRAMFS_CHECK; | |
19f8a847 MZ |
700 | else if (strcmp(args[0].from, "POLICY_CHECK") == 0) |
701 | entry->func = POLICY_CHECK; | |
4af4662f MZ |
702 | else |
703 | result = -EINVAL; | |
704 | if (!result) | |
705 | entry->flags |= IMA_FUNC; | |
706 | break; | |
707 | case Opt_mask: | |
2f1506cd | 708 | ima_log_string(ab, "mask", args[0].from); |
7b62e162 EP |
709 | |
710 | if (entry->mask) | |
711 | result = -EINVAL; | |
712 | ||
4351c294 MZ |
713 | from = args[0].from; |
714 | if (*from == '^') | |
715 | from++; | |
716 | ||
717 | if ((strcmp(from, "MAY_EXEC")) == 0) | |
4af4662f | 718 | entry->mask = MAY_EXEC; |
4351c294 | 719 | else if (strcmp(from, "MAY_WRITE") == 0) |
4af4662f | 720 | entry->mask = MAY_WRITE; |
4351c294 | 721 | else if (strcmp(from, "MAY_READ") == 0) |
4af4662f | 722 | entry->mask = MAY_READ; |
4351c294 | 723 | else if (strcmp(from, "MAY_APPEND") == 0) |
4af4662f MZ |
724 | entry->mask = MAY_APPEND; |
725 | else | |
726 | result = -EINVAL; | |
727 | if (!result) | |
4351c294 MZ |
728 | entry->flags |= (*args[0].from == '^') |
729 | ? IMA_INMASK : IMA_MASK; | |
4af4662f MZ |
730 | break; |
731 | case Opt_fsmagic: | |
2f1506cd | 732 | ima_log_string(ab, "fsmagic", args[0].from); |
7b62e162 EP |
733 | |
734 | if (entry->fsmagic) { | |
735 | result = -EINVAL; | |
736 | break; | |
737 | } | |
738 | ||
2bb930ab | 739 | result = kstrtoul(args[0].from, 16, &entry->fsmagic); |
4af4662f MZ |
740 | if (!result) |
741 | entry->flags |= IMA_FSMAGIC; | |
742 | break; | |
85865c1f DK |
743 | case Opt_fsuuid: |
744 | ima_log_string(ab, "fsuuid", args[0].from); | |
745 | ||
787d8c53 | 746 | if (uuid_is_null(&entry->fsuuid)) { |
85865c1f DK |
747 | result = -EINVAL; |
748 | break; | |
749 | } | |
750 | ||
787d8c53 | 751 | result = uuid_parse(args[0].from, &entry->fsuuid); |
446d64e3 MZ |
752 | if (!result) |
753 | entry->flags |= IMA_FSUUID; | |
85865c1f | 754 | break; |
3dd0c8d0 MK |
755 | case Opt_uid_gt: |
756 | case Opt_euid_gt: | |
757 | entry->uid_op = &uid_gt; | |
758 | case Opt_uid_lt: | |
759 | case Opt_euid_lt: | |
760 | if ((token == Opt_uid_lt) || (token == Opt_euid_lt)) | |
761 | entry->uid_op = &uid_lt; | |
762 | case Opt_uid_eq: | |
763 | case Opt_euid_eq: | |
764 | uid_token = (token == Opt_uid_eq) || | |
765 | (token == Opt_uid_gt) || | |
766 | (token == Opt_uid_lt); | |
767 | ||
768 | ima_log_string_op(ab, uid_token ? "uid" : "euid", | |
769 | args[0].from, entry->uid_op); | |
7b62e162 | 770 | |
8b94eea4 | 771 | if (uid_valid(entry->uid)) { |
7b62e162 EP |
772 | result = -EINVAL; |
773 | break; | |
774 | } | |
775 | ||
29707b20 | 776 | result = kstrtoul(args[0].from, 10, &lnum); |
4af4662f | 777 | if (!result) { |
139069ef MZ |
778 | entry->uid = make_kuid(current_user_ns(), |
779 | (uid_t) lnum); | |
780 | if (!uid_valid(entry->uid) || | |
781 | (uid_t)lnum != lnum) | |
4af4662f MZ |
782 | result = -EINVAL; |
783 | else | |
3dd0c8d0 | 784 | entry->flags |= uid_token |
139069ef | 785 | ? IMA_UID : IMA_EUID; |
4af4662f MZ |
786 | } |
787 | break; | |
3dd0c8d0 MK |
788 | case Opt_fowner_gt: |
789 | entry->fowner_op = &uid_gt; | |
790 | case Opt_fowner_lt: | |
791 | if (token == Opt_fowner_lt) | |
792 | entry->fowner_op = &uid_lt; | |
793 | case Opt_fowner_eq: | |
794 | ima_log_string_op(ab, "fowner", args[0].from, | |
795 | entry->fowner_op); | |
07f6a794 | 796 | |
88265322 | 797 | if (uid_valid(entry->fowner)) { |
07f6a794 MZ |
798 | result = -EINVAL; |
799 | break; | |
800 | } | |
801 | ||
29707b20 | 802 | result = kstrtoul(args[0].from, 10, &lnum); |
07f6a794 | 803 | if (!result) { |
88265322 LT |
804 | entry->fowner = make_kuid(current_user_ns(), (uid_t)lnum); |
805 | if (!uid_valid(entry->fowner) || (((uid_t)lnum) != lnum)) | |
07f6a794 MZ |
806 | result = -EINVAL; |
807 | else | |
808 | entry->flags |= IMA_FOWNER; | |
809 | } | |
810 | break; | |
4af4662f | 811 | case Opt_obj_user: |
2f1506cd | 812 | ima_log_string(ab, "obj_user", args[0].from); |
7163a993 | 813 | result = ima_lsm_rule_init(entry, args, |
4af4662f MZ |
814 | LSM_OBJ_USER, |
815 | AUDIT_OBJ_USER); | |
816 | break; | |
817 | case Opt_obj_role: | |
2f1506cd | 818 | ima_log_string(ab, "obj_role", args[0].from); |
7163a993 | 819 | result = ima_lsm_rule_init(entry, args, |
4af4662f MZ |
820 | LSM_OBJ_ROLE, |
821 | AUDIT_OBJ_ROLE); | |
822 | break; | |
823 | case Opt_obj_type: | |
2f1506cd | 824 | ima_log_string(ab, "obj_type", args[0].from); |
7163a993 | 825 | result = ima_lsm_rule_init(entry, args, |
4af4662f MZ |
826 | LSM_OBJ_TYPE, |
827 | AUDIT_OBJ_TYPE); | |
828 | break; | |
829 | case Opt_subj_user: | |
2f1506cd | 830 | ima_log_string(ab, "subj_user", args[0].from); |
7163a993 | 831 | result = ima_lsm_rule_init(entry, args, |
4af4662f MZ |
832 | LSM_SUBJ_USER, |
833 | AUDIT_SUBJ_USER); | |
834 | break; | |
835 | case Opt_subj_role: | |
2f1506cd | 836 | ima_log_string(ab, "subj_role", args[0].from); |
7163a993 | 837 | result = ima_lsm_rule_init(entry, args, |
4af4662f MZ |
838 | LSM_SUBJ_ROLE, |
839 | AUDIT_SUBJ_ROLE); | |
840 | break; | |
841 | case Opt_subj_type: | |
2f1506cd | 842 | ima_log_string(ab, "subj_type", args[0].from); |
7163a993 | 843 | result = ima_lsm_rule_init(entry, args, |
4af4662f MZ |
844 | LSM_SUBJ_TYPE, |
845 | AUDIT_SUBJ_TYPE); | |
846 | break; | |
0e5a247c DK |
847 | case Opt_appraise_type: |
848 | if (entry->action != APPRAISE) { | |
849 | result = -EINVAL; | |
850 | break; | |
851 | } | |
852 | ||
853 | ima_log_string(ab, "appraise_type", args[0].from); | |
854 | if ((strcmp(args[0].from, "imasig")) == 0) | |
855 | entry->flags |= IMA_DIGSIG_REQUIRED; | |
856 | else | |
857 | result = -EINVAL; | |
858 | break; | |
f9b2a735 MZ |
859 | case Opt_permit_directio: |
860 | entry->flags |= IMA_PERMIT_DIRECTIO; | |
0260643c ER |
861 | break; |
862 | case Opt_pcr: | |
863 | if (entry->action != MEASURE) { | |
864 | result = -EINVAL; | |
865 | break; | |
866 | } | |
867 | ima_log_string(ab, "pcr", args[0].from); | |
868 | ||
869 | result = kstrtoint(args[0].from, 10, &entry->pcr); | |
870 | if (result || INVALID_PCR(entry->pcr)) | |
871 | result = -EINVAL; | |
872 | else | |
873 | entry->flags |= IMA_PCR; | |
874 | ||
f9b2a735 | 875 | break; |
4af4662f | 876 | case Opt_err: |
2f1506cd | 877 | ima_log_string(ab, "UNKNOWN", p); |
e9d393bf | 878 | result = -EINVAL; |
4af4662f MZ |
879 | break; |
880 | } | |
881 | } | |
7b62e162 | 882 | if (!result && (entry->action == UNKNOWN)) |
4af4662f | 883 | result = -EINVAL; |
a7f2a366 | 884 | else if (entry->func == MODULE_CHECK) |
6ad6afa1 | 885 | temp_ima_appraise |= IMA_APPRAISE_MODULES; |
5a9196d7 | 886 | else if (entry->func == FIRMWARE_CHECK) |
6ad6afa1 | 887 | temp_ima_appraise |= IMA_APPRAISE_FIRMWARE; |
19f8a847 MZ |
888 | else if (entry->func == POLICY_CHECK) |
889 | temp_ima_appraise |= IMA_APPRAISE_POLICY; | |
b0d5de4d | 890 | audit_log_format(ab, "res=%d", !result); |
4af4662f MZ |
891 | audit_log_end(ab); |
892 | return result; | |
893 | } | |
894 | ||
895 | /** | |
07f6a794 | 896 | * ima_parse_add_rule - add a rule to ima_policy_rules |
4af4662f MZ |
897 | * @rule - ima measurement policy rule |
898 | * | |
38d859f9 | 899 | * Avoid locking by allowing just one writer at a time in ima_write_policy() |
6ccd0456 | 900 | * Returns the length of the rule parsed, an error code on failure |
4af4662f | 901 | */ |
6ccd0456 | 902 | ssize_t ima_parse_add_rule(char *rule) |
4af4662f | 903 | { |
52a13284 | 904 | static const char op[] = "update_policy"; |
6ccd0456 | 905 | char *p; |
07f6a794 | 906 | struct ima_rule_entry *entry; |
6ccd0456 | 907 | ssize_t result, len; |
4af4662f MZ |
908 | int audit_info = 0; |
909 | ||
272a6e90 DK |
910 | p = strsep(&rule, "\n"); |
911 | len = strlen(p) + 1; | |
7178784f | 912 | p += strspn(p, " \t"); |
272a6e90 | 913 | |
7178784f | 914 | if (*p == '#' || *p == '\0') |
272a6e90 DK |
915 | return len; |
916 | ||
4af4662f MZ |
917 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); |
918 | if (!entry) { | |
919 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, | |
920 | NULL, op, "-ENOMEM", -ENOMEM, audit_info); | |
921 | return -ENOMEM; | |
922 | } | |
923 | ||
924 | INIT_LIST_HEAD(&entry->list); | |
925 | ||
6ccd0456 | 926 | result = ima_parse_rule(p, entry); |
7233e3ee | 927 | if (result) { |
4af4662f | 928 | kfree(entry); |
523979ad | 929 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, |
7e9001f6 | 930 | NULL, op, "invalid-policy", result, |
523979ad | 931 | audit_info); |
7233e3ee | 932 | return result; |
523979ad | 933 | } |
7233e3ee | 934 | |
38d859f9 | 935 | list_add_tail(&entry->list, &ima_temp_rules); |
7233e3ee EP |
936 | |
937 | return len; | |
4af4662f MZ |
938 | } |
939 | ||
38d859f9 PM |
940 | /** |
941 | * ima_delete_rules() called to cleanup invalid in-flight policy. | |
942 | * We don't need locking as we operate on the temp list, which is | |
943 | * different from the active one. There is also only one user of | |
944 | * ima_delete_rules() at a time. | |
945 | */ | |
64c61d80 | 946 | void ima_delete_rules(void) |
4af4662f | 947 | { |
07f6a794 | 948 | struct ima_rule_entry *entry, *tmp; |
7163a993 | 949 | int i; |
4af4662f | 950 | |
6ad6afa1 | 951 | temp_ima_appraise = 0; |
38d859f9 | 952 | list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) { |
7163a993 MZ |
953 | for (i = 0; i < MAX_LSM_RULES; i++) |
954 | kfree(entry->lsm[i].args_p); | |
955 | ||
4af4662f MZ |
956 | list_del(&entry->list); |
957 | kfree(entry); | |
958 | } | |
4af4662f | 959 | } |
80eae209 PM |
960 | |
961 | #ifdef CONFIG_IMA_READ_POLICY | |
962 | enum { | |
963 | mask_exec = 0, mask_write, mask_read, mask_append | |
964 | }; | |
965 | ||
bb543e39 | 966 | static const char *const mask_tokens[] = { |
80eae209 PM |
967 | "MAY_EXEC", |
968 | "MAY_WRITE", | |
969 | "MAY_READ", | |
970 | "MAY_APPEND" | |
971 | }; | |
972 | ||
2663218b | 973 | #define __ima_hook_stringify(str) (#str), |
80eae209 | 974 | |
bb543e39 | 975 | static const char *const func_tokens[] = { |
2663218b | 976 | __ima_hooks(__ima_hook_stringify) |
80eae209 PM |
977 | }; |
978 | ||
979 | void *ima_policy_start(struct seq_file *m, loff_t *pos) | |
980 | { | |
981 | loff_t l = *pos; | |
982 | struct ima_rule_entry *entry; | |
983 | ||
984 | rcu_read_lock(); | |
985 | list_for_each_entry_rcu(entry, ima_rules, list) { | |
986 | if (!l--) { | |
987 | rcu_read_unlock(); | |
988 | return entry; | |
989 | } | |
990 | } | |
991 | rcu_read_unlock(); | |
992 | return NULL; | |
993 | } | |
994 | ||
995 | void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos) | |
996 | { | |
997 | struct ima_rule_entry *entry = v; | |
998 | ||
999 | rcu_read_lock(); | |
1000 | entry = list_entry_rcu(entry->list.next, struct ima_rule_entry, list); | |
1001 | rcu_read_unlock(); | |
1002 | (*pos)++; | |
1003 | ||
1004 | return (&entry->list == ima_rules) ? NULL : entry; | |
1005 | } | |
1006 | ||
1007 | void ima_policy_stop(struct seq_file *m, void *v) | |
1008 | { | |
1009 | } | |
1010 | ||
1011 | #define pt(token) policy_tokens[token + Opt_err].pattern | |
1012 | #define mt(token) mask_tokens[token] | |
80eae209 | 1013 | |
b5269ab3 MZ |
1014 | /* |
1015 | * policy_func_show - display the ima_hooks policy rule | |
1016 | */ | |
1017 | static void policy_func_show(struct seq_file *m, enum ima_hooks func) | |
1018 | { | |
2663218b TJB |
1019 | if (func > 0 && func < MAX_CHECK) |
1020 | seq_printf(m, "func=%s ", func_tokens[func]); | |
1021 | else | |
1022 | seq_printf(m, "func=%d ", func); | |
b5269ab3 MZ |
1023 | } |
1024 | ||
80eae209 PM |
1025 | int ima_policy_show(struct seq_file *m, void *v) |
1026 | { | |
1027 | struct ima_rule_entry *entry = v; | |
b8b57278 | 1028 | int i; |
80eae209 PM |
1029 | char tbuf[64] = {0,}; |
1030 | ||
1031 | rcu_read_lock(); | |
1032 | ||
1033 | if (entry->action & MEASURE) | |
1034 | seq_puts(m, pt(Opt_measure)); | |
1035 | if (entry->action & DONT_MEASURE) | |
1036 | seq_puts(m, pt(Opt_dont_measure)); | |
1037 | if (entry->action & APPRAISE) | |
1038 | seq_puts(m, pt(Opt_appraise)); | |
1039 | if (entry->action & DONT_APPRAISE) | |
1040 | seq_puts(m, pt(Opt_dont_appraise)); | |
1041 | if (entry->action & AUDIT) | |
1042 | seq_puts(m, pt(Opt_audit)); | |
1043 | ||
1044 | seq_puts(m, " "); | |
1045 | ||
b5269ab3 MZ |
1046 | if (entry->flags & IMA_FUNC) |
1047 | policy_func_show(m, entry->func); | |
80eae209 PM |
1048 | |
1049 | if (entry->flags & IMA_MASK) { | |
1050 | if (entry->mask & MAY_EXEC) | |
1051 | seq_printf(m, pt(Opt_mask), mt(mask_exec)); | |
1052 | if (entry->mask & MAY_WRITE) | |
1053 | seq_printf(m, pt(Opt_mask), mt(mask_write)); | |
1054 | if (entry->mask & MAY_READ) | |
1055 | seq_printf(m, pt(Opt_mask), mt(mask_read)); | |
1056 | if (entry->mask & MAY_APPEND) | |
1057 | seq_printf(m, pt(Opt_mask), mt(mask_append)); | |
1058 | seq_puts(m, " "); | |
1059 | } | |
1060 | ||
1061 | if (entry->flags & IMA_FSMAGIC) { | |
1062 | snprintf(tbuf, sizeof(tbuf), "0x%lx", entry->fsmagic); | |
1063 | seq_printf(m, pt(Opt_fsmagic), tbuf); | |
1064 | seq_puts(m, " "); | |
1065 | } | |
1066 | ||
0260643c ER |
1067 | if (entry->flags & IMA_PCR) { |
1068 | snprintf(tbuf, sizeof(tbuf), "%d", entry->pcr); | |
1069 | seq_printf(m, pt(Opt_pcr), tbuf); | |
1070 | seq_puts(m, " "); | |
1071 | } | |
1072 | ||
80eae209 | 1073 | if (entry->flags & IMA_FSUUID) { |
787d8c53 | 1074 | seq_printf(m, "fsuuid=%pU", &entry->fsuuid); |
80eae209 PM |
1075 | seq_puts(m, " "); |
1076 | } | |
1077 | ||
1078 | if (entry->flags & IMA_UID) { | |
1079 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid)); | |
3dd0c8d0 MK |
1080 | if (entry->uid_op == &uid_gt) |
1081 | seq_printf(m, pt(Opt_uid_gt), tbuf); | |
1082 | else if (entry->uid_op == &uid_lt) | |
1083 | seq_printf(m, pt(Opt_uid_lt), tbuf); | |
1084 | else | |
1085 | seq_printf(m, pt(Opt_uid_eq), tbuf); | |
80eae209 PM |
1086 | seq_puts(m, " "); |
1087 | } | |
1088 | ||
1089 | if (entry->flags & IMA_EUID) { | |
1090 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid)); | |
3dd0c8d0 MK |
1091 | if (entry->uid_op == &uid_gt) |
1092 | seq_printf(m, pt(Opt_euid_gt), tbuf); | |
1093 | else if (entry->uid_op == &uid_lt) | |
1094 | seq_printf(m, pt(Opt_euid_lt), tbuf); | |
1095 | else | |
1096 | seq_printf(m, pt(Opt_euid_eq), tbuf); | |
80eae209 PM |
1097 | seq_puts(m, " "); |
1098 | } | |
1099 | ||
1100 | if (entry->flags & IMA_FOWNER) { | |
1101 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner)); | |
3dd0c8d0 MK |
1102 | if (entry->fowner_op == &uid_gt) |
1103 | seq_printf(m, pt(Opt_fowner_gt), tbuf); | |
1104 | else if (entry->fowner_op == &uid_lt) | |
1105 | seq_printf(m, pt(Opt_fowner_lt), tbuf); | |
1106 | else | |
1107 | seq_printf(m, pt(Opt_fowner_eq), tbuf); | |
80eae209 PM |
1108 | seq_puts(m, " "); |
1109 | } | |
1110 | ||
1111 | for (i = 0; i < MAX_LSM_RULES; i++) { | |
1112 | if (entry->lsm[i].rule) { | |
1113 | switch (i) { | |
1114 | case LSM_OBJ_USER: | |
1115 | seq_printf(m, pt(Opt_obj_user), | |
1116 | (char *)entry->lsm[i].args_p); | |
1117 | break; | |
1118 | case LSM_OBJ_ROLE: | |
1119 | seq_printf(m, pt(Opt_obj_role), | |
1120 | (char *)entry->lsm[i].args_p); | |
1121 | break; | |
1122 | case LSM_OBJ_TYPE: | |
1123 | seq_printf(m, pt(Opt_obj_type), | |
1124 | (char *)entry->lsm[i].args_p); | |
1125 | break; | |
1126 | case LSM_SUBJ_USER: | |
1127 | seq_printf(m, pt(Opt_subj_user), | |
1128 | (char *)entry->lsm[i].args_p); | |
1129 | break; | |
1130 | case LSM_SUBJ_ROLE: | |
1131 | seq_printf(m, pt(Opt_subj_role), | |
1132 | (char *)entry->lsm[i].args_p); | |
1133 | break; | |
1134 | case LSM_SUBJ_TYPE: | |
1135 | seq_printf(m, pt(Opt_subj_type), | |
1136 | (char *)entry->lsm[i].args_p); | |
1137 | break; | |
1138 | } | |
1139 | } | |
1140 | } | |
1141 | if (entry->flags & IMA_DIGSIG_REQUIRED) | |
1142 | seq_puts(m, "appraise_type=imasig "); | |
1143 | if (entry->flags & IMA_PERMIT_DIRECTIO) | |
1144 | seq_puts(m, "permit_directio "); | |
1145 | rcu_read_unlock(); | |
1146 | seq_puts(m, "\n"); | |
1147 | return 0; | |
1148 | } | |
1149 | #endif /* CONFIG_IMA_READ_POLICY */ |