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