diff options
Diffstat (limited to 'security/integrity/ima/ima_policy.c')
-rw-r--r-- | security/integrity/ima/ima_policy.c | 293 |
1 files changed, 290 insertions, 3 deletions
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 7c3d1ffb1472..bd453603e2c3 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/audit.h> | 15 | #include <linux/audit.h> |
16 | #include <linux/security.h> | 16 | #include <linux/security.h> |
17 | #include <linux/magic.h> | 17 | #include <linux/magic.h> |
18 | #include <linux/parser.h> | ||
18 | 19 | ||
19 | #include "ima.h" | 20 | #include "ima.h" |
20 | 21 | ||
@@ -24,7 +25,12 @@ | |||
24 | #define IMA_FSMAGIC 0x0004 | 25 | #define IMA_FSMAGIC 0x0004 |
25 | #define IMA_UID 0x0008 | 26 | #define IMA_UID 0x0008 |
26 | 27 | ||
27 | enum ima_action { DONT_MEASURE, MEASURE }; | 28 | enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE }; |
29 | |||
30 | #define MAX_LSM_RULES 6 | ||
31 | enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, | ||
32 | LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE | ||
33 | }; | ||
28 | 34 | ||
29 | struct ima_measure_rule_entry { | 35 | struct ima_measure_rule_entry { |
30 | struct list_head list; | 36 | struct list_head list; |
@@ -34,8 +40,15 @@ struct ima_measure_rule_entry { | |||
34 | int mask; | 40 | int mask; |
35 | unsigned long fsmagic; | 41 | unsigned long fsmagic; |
36 | uid_t uid; | 42 | uid_t uid; |
43 | struct { | ||
44 | void *rule; /* LSM file metadata specific */ | ||
45 | int type; /* audit type */ | ||
46 | } lsm[MAX_LSM_RULES]; | ||
37 | }; | 47 | }; |
38 | 48 | ||
49 | /* Without LSM specific knowledge, the default policy can only be | ||
50 | * written in terms of .action, .func, .mask, .fsmagic, and .uid | ||
51 | */ | ||
39 | static struct ima_measure_rule_entry default_rules[] = { | 52 | static struct ima_measure_rule_entry default_rules[] = { |
40 | {.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC, | 53 | {.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC, |
41 | .flags = IMA_FSMAGIC}, | 54 | .flags = IMA_FSMAGIC}, |
@@ -54,8 +67,11 @@ static struct ima_measure_rule_entry default_rules[] = { | |||
54 | }; | 67 | }; |
55 | 68 | ||
56 | static LIST_HEAD(measure_default_rules); | 69 | static LIST_HEAD(measure_default_rules); |
70 | static LIST_HEAD(measure_policy_rules); | ||
57 | static struct list_head *ima_measure; | 71 | static struct list_head *ima_measure; |
58 | 72 | ||
73 | static DEFINE_MUTEX(ima_measure_mutex); | ||
74 | |||
59 | /** | 75 | /** |
60 | * ima_match_rules - determine whether an inode matches the measure rule. | 76 | * ima_match_rules - determine whether an inode matches the measure rule. |
61 | * @rule: a pointer to a rule | 77 | * @rule: a pointer to a rule |
@@ -69,6 +85,7 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule, | |||
69 | struct inode *inode, enum ima_hooks func, int mask) | 85 | struct inode *inode, enum ima_hooks func, int mask) |
70 | { | 86 | { |
71 | struct task_struct *tsk = current; | 87 | struct task_struct *tsk = current; |
88 | int i; | ||
72 | 89 | ||
73 | if ((rule->flags & IMA_FUNC) && rule->func != func) | 90 | if ((rule->flags & IMA_FUNC) && rule->func != func) |
74 | return false; | 91 | return false; |
@@ -79,6 +96,39 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule, | |||
79 | return false; | 96 | return false; |
80 | if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid) | 97 | if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid) |
81 | return false; | 98 | return false; |
99 | for (i = 0; i < MAX_LSM_RULES; i++) { | ||
100 | int rc; | ||
101 | u32 osid, sid; | ||
102 | |||
103 | if (!rule->lsm[i].rule) | ||
104 | continue; | ||
105 | |||
106 | switch (i) { | ||
107 | case LSM_OBJ_USER: | ||
108 | case LSM_OBJ_ROLE: | ||
109 | case LSM_OBJ_TYPE: | ||
110 | security_inode_getsecid(inode, &osid); | ||
111 | rc = security_filter_rule_match(osid, | ||
112 | rule->lsm[i].type, | ||
113 | AUDIT_EQUAL, | ||
114 | rule->lsm[i].rule, | ||
115 | NULL); | ||
116 | break; | ||
117 | case LSM_SUBJ_USER: | ||
118 | case LSM_SUBJ_ROLE: | ||
119 | case LSM_SUBJ_TYPE: | ||
120 | security_task_getsecid(tsk, &sid); | ||
121 | rc = security_filter_rule_match(sid, | ||
122 | rule->lsm[i].type, | ||
123 | AUDIT_EQUAL, | ||
124 | rule->lsm[i].rule, | ||
125 | NULL); | ||
126 | default: | ||
127 | break; | ||
128 | } | ||
129 | if (!rc) | ||
130 | return false; | ||
131 | } | ||
82 | return true; | 132 | return true; |
83 | } | 133 | } |
84 | 134 | ||
@@ -112,9 +162,8 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask) | |||
112 | /** | 162 | /** |
113 | * ima_init_policy - initialize the default measure rules. | 163 | * ima_init_policy - initialize the default measure rules. |
114 | * | 164 | * |
115 | * (Could use the default_rules directly, but in policy patch | ||
116 | * ima_measure points to either the measure_default_rules or the | 165 | * ima_measure points to either the measure_default_rules or the |
117 | * the new measure_policy_rules.) | 166 | * the new measure_policy_rules. |
118 | */ | 167 | */ |
119 | void ima_init_policy(void) | 168 | void ima_init_policy(void) |
120 | { | 169 | { |
@@ -124,3 +173,241 @@ void ima_init_policy(void) | |||
124 | list_add_tail(&default_rules[i].list, &measure_default_rules); | 173 | list_add_tail(&default_rules[i].list, &measure_default_rules); |
125 | ima_measure = &measure_default_rules; | 174 | ima_measure = &measure_default_rules; |
126 | } | 175 | } |
176 | |||
177 | /** | ||
178 | * ima_update_policy - update default_rules with new measure rules | ||
179 | * | ||
180 | * Called on file .release to update the default rules with a complete new | ||
181 | * policy. Once updated, the policy is locked, no additional rules can be | ||
182 | * added to the policy. | ||
183 | */ | ||
184 | void ima_update_policy(void) | ||
185 | { | ||
186 | const char *op = "policy_update"; | ||
187 | const char *cause = "already exists"; | ||
188 | int result = 1; | ||
189 | int audit_info = 0; | ||
190 | |||
191 | if (ima_measure == &measure_default_rules) { | ||
192 | ima_measure = &measure_policy_rules; | ||
193 | cause = "complete"; | ||
194 | result = 0; | ||
195 | } | ||
196 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, | ||
197 | NULL, op, cause, result, audit_info); | ||
198 | } | ||
199 | |||
200 | enum { | ||
201 | Opt_err = -1, | ||
202 | Opt_measure = 1, Opt_dont_measure, | ||
203 | Opt_obj_user, Opt_obj_role, Opt_obj_type, | ||
204 | Opt_subj_user, Opt_subj_role, Opt_subj_type, | ||
205 | Opt_func, Opt_mask, Opt_fsmagic, Opt_uid | ||
206 | }; | ||
207 | |||
208 | static match_table_t policy_tokens = { | ||
209 | {Opt_measure, "measure"}, | ||
210 | {Opt_dont_measure, "dont_measure"}, | ||
211 | {Opt_obj_user, "obj_user=%s"}, | ||
212 | {Opt_obj_role, "obj_role=%s"}, | ||
213 | {Opt_obj_type, "obj_type=%s"}, | ||
214 | {Opt_subj_user, "subj_user=%s"}, | ||
215 | {Opt_subj_role, "subj_role=%s"}, | ||
216 | {Opt_subj_type, "subj_type=%s"}, | ||
217 | {Opt_func, "func=%s"}, | ||
218 | {Opt_mask, "mask=%s"}, | ||
219 | {Opt_fsmagic, "fsmagic=%s"}, | ||
220 | {Opt_uid, "uid=%s"}, | ||
221 | {Opt_err, NULL} | ||
222 | }; | ||
223 | |||
224 | static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry, | ||
225 | char *args, int lsm_rule, int audit_type) | ||
226 | { | ||
227 | int result; | ||
228 | |||
229 | entry->lsm[lsm_rule].type = audit_type; | ||
230 | result = security_filter_rule_init(entry->lsm[lsm_rule].type, | ||
231 | AUDIT_EQUAL, args, | ||
232 | &entry->lsm[lsm_rule].rule); | ||
233 | return result; | ||
234 | } | ||
235 | |||
236 | static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) | ||
237 | { | ||
238 | struct audit_buffer *ab; | ||
239 | char *p; | ||
240 | int result = 0; | ||
241 | |||
242 | ab = audit_log_start(current->audit_context, GFP_KERNEL, | ||
243 | AUDIT_INTEGRITY_STATUS); | ||
244 | |||
245 | entry->action = -1; | ||
246 | while ((p = strsep(&rule, " \n")) != NULL) { | ||
247 | substring_t args[MAX_OPT_ARGS]; | ||
248 | int token; | ||
249 | unsigned long lnum; | ||
250 | |||
251 | if (result < 0) | ||
252 | break; | ||
253 | if (!*p) | ||
254 | continue; | ||
255 | token = match_token(p, policy_tokens, args); | ||
256 | switch (token) { | ||
257 | case Opt_measure: | ||
258 | audit_log_format(ab, "%s ", "measure"); | ||
259 | entry->action = MEASURE; | ||
260 | break; | ||
261 | case Opt_dont_measure: | ||
262 | audit_log_format(ab, "%s ", "dont_measure"); | ||
263 | entry->action = DONT_MEASURE; | ||
264 | break; | ||
265 | case Opt_func: | ||
266 | audit_log_format(ab, "func=%s ", args[0].from); | ||
267 | if (strcmp(args[0].from, "PATH_CHECK") == 0) | ||
268 | entry->func = PATH_CHECK; | ||
269 | else if (strcmp(args[0].from, "FILE_MMAP") == 0) | ||
270 | entry->func = FILE_MMAP; | ||
271 | else if (strcmp(args[0].from, "BPRM_CHECK") == 0) | ||
272 | entry->func = BPRM_CHECK; | ||
273 | else | ||
274 | result = -EINVAL; | ||
275 | if (!result) | ||
276 | entry->flags |= IMA_FUNC; | ||
277 | break; | ||
278 | case Opt_mask: | ||
279 | audit_log_format(ab, "mask=%s ", args[0].from); | ||
280 | if ((strcmp(args[0].from, "MAY_EXEC")) == 0) | ||
281 | entry->mask = MAY_EXEC; | ||
282 | else if (strcmp(args[0].from, "MAY_WRITE") == 0) | ||
283 | entry->mask = MAY_WRITE; | ||
284 | else if (strcmp(args[0].from, "MAY_READ") == 0) | ||
285 | entry->mask = MAY_READ; | ||
286 | else if (strcmp(args[0].from, "MAY_APPEND") == 0) | ||
287 | entry->mask = MAY_APPEND; | ||
288 | else | ||
289 | result = -EINVAL; | ||
290 | if (!result) | ||
291 | entry->flags |= IMA_MASK; | ||
292 | break; | ||
293 | case Opt_fsmagic: | ||
294 | audit_log_format(ab, "fsmagic=%s ", args[0].from); | ||
295 | result = strict_strtoul(args[0].from, 16, | ||
296 | &entry->fsmagic); | ||
297 | if (!result) | ||
298 | entry->flags |= IMA_FSMAGIC; | ||
299 | break; | ||
300 | case Opt_uid: | ||
301 | audit_log_format(ab, "uid=%s ", args[0].from); | ||
302 | result = strict_strtoul(args[0].from, 10, &lnum); | ||
303 | if (!result) { | ||
304 | entry->uid = (uid_t) lnum; | ||
305 | if (entry->uid != lnum) | ||
306 | result = -EINVAL; | ||
307 | else | ||
308 | entry->flags |= IMA_UID; | ||
309 | } | ||
310 | break; | ||
311 | case Opt_obj_user: | ||
312 | audit_log_format(ab, "obj_user=%s ", args[0].from); | ||
313 | result = ima_lsm_rule_init(entry, args[0].from, | ||
314 | LSM_OBJ_USER, | ||
315 | AUDIT_OBJ_USER); | ||
316 | break; | ||
317 | case Opt_obj_role: | ||
318 | audit_log_format(ab, "obj_role=%s ", args[0].from); | ||
319 | result = ima_lsm_rule_init(entry, args[0].from, | ||
320 | LSM_OBJ_ROLE, | ||
321 | AUDIT_OBJ_ROLE); | ||
322 | break; | ||
323 | case Opt_obj_type: | ||
324 | audit_log_format(ab, "obj_type=%s ", args[0].from); | ||
325 | result = ima_lsm_rule_init(entry, args[0].from, | ||
326 | LSM_OBJ_TYPE, | ||
327 | AUDIT_OBJ_TYPE); | ||
328 | break; | ||
329 | case Opt_subj_user: | ||
330 | audit_log_format(ab, "subj_user=%s ", args[0].from); | ||
331 | result = ima_lsm_rule_init(entry, args[0].from, | ||
332 | LSM_SUBJ_USER, | ||
333 | AUDIT_SUBJ_USER); | ||
334 | break; | ||
335 | case Opt_subj_role: | ||
336 | audit_log_format(ab, "subj_role=%s ", args[0].from); | ||
337 | result = ima_lsm_rule_init(entry, args[0].from, | ||
338 | LSM_SUBJ_ROLE, | ||
339 | AUDIT_SUBJ_ROLE); | ||
340 | break; | ||
341 | case Opt_subj_type: | ||
342 | audit_log_format(ab, "subj_type=%s ", args[0].from); | ||
343 | result = ima_lsm_rule_init(entry, args[0].from, | ||
344 | LSM_SUBJ_TYPE, | ||
345 | AUDIT_SUBJ_TYPE); | ||
346 | break; | ||
347 | case Opt_err: | ||
348 | printk(KERN_INFO "%s: unknown token: %s\n", | ||
349 | __FUNCTION__, p); | ||
350 | break; | ||
351 | } | ||
352 | } | ||
353 | if (entry->action == UNKNOWN) | ||
354 | result = -EINVAL; | ||
355 | |||
356 | audit_log_format(ab, "res=%d", result); | ||
357 | audit_log_end(ab); | ||
358 | return result; | ||
359 | } | ||
360 | |||
361 | /** | ||
362 | * ima_parse_add_rule - add a rule to measure_policy_rules | ||
363 | * @rule - ima measurement policy rule | ||
364 | * | ||
365 | * Uses a mutex to protect the policy list from multiple concurrent writers. | ||
366 | * Returns 0 on success, an error code on failure. | ||
367 | */ | ||
368 | int ima_parse_add_rule(char *rule) | ||
369 | { | ||
370 | const char *op = "add_rule"; | ||
371 | struct ima_measure_rule_entry *entry; | ||
372 | int result = 0; | ||
373 | int audit_info = 0; | ||
374 | |||
375 | /* Prevent installed policy from changing */ | ||
376 | if (ima_measure != &measure_default_rules) { | ||
377 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, | ||
378 | NULL, op, "already exists", | ||
379 | -EACCES, audit_info); | ||
380 | return -EACCES; | ||
381 | } | ||
382 | |||
383 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); | ||
384 | if (!entry) { | ||
385 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, | ||
386 | NULL, op, "-ENOMEM", -ENOMEM, audit_info); | ||
387 | return -ENOMEM; | ||
388 | } | ||
389 | |||
390 | INIT_LIST_HEAD(&entry->list); | ||
391 | |||
392 | result = ima_parse_rule(rule, entry); | ||
393 | if (!result) { | ||
394 | mutex_lock(&ima_measure_mutex); | ||
395 | list_add_tail(&entry->list, &measure_policy_rules); | ||
396 | mutex_unlock(&ima_measure_mutex); | ||
397 | } else | ||
398 | kfree(entry); | ||
399 | return result; | ||
400 | } | ||
401 | |||
402 | /* ima_delete_rules called to cleanup invalid policy */ | ||
403 | void ima_delete_rules() | ||
404 | { | ||
405 | struct ima_measure_rule_entry *entry, *tmp; | ||
406 | |||
407 | mutex_lock(&ima_measure_mutex); | ||
408 | list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) { | ||
409 | list_del(&entry->list); | ||
410 | kfree(entry); | ||
411 | } | ||
412 | mutex_unlock(&ima_measure_mutex); | ||
413 | } | ||