diff options
Diffstat (limited to 'security/integrity/ima/ima_policy.c')
-rw-r--r-- | security/integrity/ima/ima_policy.c | 107 |
1 files changed, 79 insertions, 28 deletions
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 8643a93c5963..aef8c0a923ab 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c | |||
@@ -246,6 +246,9 @@ static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry, | |||
246 | { | 246 | { |
247 | int result; | 247 | int result; |
248 | 248 | ||
249 | if (entry->lsm[lsm_rule].rule) | ||
250 | return -EINVAL; | ||
251 | |||
249 | entry->lsm[lsm_rule].type = audit_type; | 252 | entry->lsm[lsm_rule].type = audit_type; |
250 | result = security_filter_rule_init(entry->lsm[lsm_rule].type, | 253 | result = security_filter_rule_init(entry->lsm[lsm_rule].type, |
251 | Audit_equal, args, | 254 | Audit_equal, args, |
@@ -253,6 +256,13 @@ static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry, | |||
253 | return result; | 256 | return result; |
254 | } | 257 | } |
255 | 258 | ||
259 | static void ima_log_string(struct audit_buffer *ab, char *key, char *value) | ||
260 | { | ||
261 | audit_log_format(ab, "%s=", key); | ||
262 | audit_log_untrustedstring(ab, value); | ||
263 | audit_log_format(ab, " "); | ||
264 | } | ||
265 | |||
256 | static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) | 266 | static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) |
257 | { | 267 | { |
258 | struct audit_buffer *ab; | 268 | struct audit_buffer *ab; |
@@ -261,28 +271,41 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) | |||
261 | 271 | ||
262 | ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); | 272 | ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); |
263 | 273 | ||
264 | entry->action = -1; | 274 | entry->uid = -1; |
265 | while ((p = strsep(&rule, " \n")) != NULL) { | 275 | entry->action = UNKNOWN; |
276 | while ((p = strsep(&rule, " \t")) != NULL) { | ||
266 | substring_t args[MAX_OPT_ARGS]; | 277 | substring_t args[MAX_OPT_ARGS]; |
267 | int token; | 278 | int token; |
268 | unsigned long lnum; | 279 | unsigned long lnum; |
269 | 280 | ||
270 | if (result < 0) | 281 | if (result < 0) |
271 | break; | 282 | break; |
272 | if (!*p) | 283 | if ((*p == '\0') || (*p == ' ') || (*p == '\t')) |
273 | continue; | 284 | continue; |
274 | token = match_token(p, policy_tokens, args); | 285 | token = match_token(p, policy_tokens, args); |
275 | switch (token) { | 286 | switch (token) { |
276 | case Opt_measure: | 287 | case Opt_measure: |
277 | audit_log_format(ab, "%s ", "measure"); | 288 | ima_log_string(ab, "action", "measure"); |
289 | |||
290 | if (entry->action != UNKNOWN) | ||
291 | result = -EINVAL; | ||
292 | |||
278 | entry->action = MEASURE; | 293 | entry->action = MEASURE; |
279 | break; | 294 | break; |
280 | case Opt_dont_measure: | 295 | case Opt_dont_measure: |
281 | audit_log_format(ab, "%s ", "dont_measure"); | 296 | ima_log_string(ab, "action", "dont_measure"); |
297 | |||
298 | if (entry->action != UNKNOWN) | ||
299 | result = -EINVAL; | ||
300 | |||
282 | entry->action = DONT_MEASURE; | 301 | entry->action = DONT_MEASURE; |
283 | break; | 302 | break; |
284 | case Opt_func: | 303 | case Opt_func: |
285 | audit_log_format(ab, "func=%s ", args[0].from); | 304 | ima_log_string(ab, "func", args[0].from); |
305 | |||
306 | if (entry->func) | ||
307 | result = -EINVAL; | ||
308 | |||
286 | if (strcmp(args[0].from, "FILE_CHECK") == 0) | 309 | if (strcmp(args[0].from, "FILE_CHECK") == 0) |
287 | entry->func = FILE_CHECK; | 310 | entry->func = FILE_CHECK; |
288 | /* PATH_CHECK is for backwards compat */ | 311 | /* PATH_CHECK is for backwards compat */ |
@@ -298,7 +321,11 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) | |||
298 | entry->flags |= IMA_FUNC; | 321 | entry->flags |= IMA_FUNC; |
299 | break; | 322 | break; |
300 | case Opt_mask: | 323 | case Opt_mask: |
301 | audit_log_format(ab, "mask=%s ", args[0].from); | 324 | ima_log_string(ab, "mask", args[0].from); |
325 | |||
326 | if (entry->mask) | ||
327 | result = -EINVAL; | ||
328 | |||
302 | if ((strcmp(args[0].from, "MAY_EXEC")) == 0) | 329 | if ((strcmp(args[0].from, "MAY_EXEC")) == 0) |
303 | entry->mask = MAY_EXEC; | 330 | entry->mask = MAY_EXEC; |
304 | else if (strcmp(args[0].from, "MAY_WRITE") == 0) | 331 | else if (strcmp(args[0].from, "MAY_WRITE") == 0) |
@@ -313,14 +340,26 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) | |||
313 | entry->flags |= IMA_MASK; | 340 | entry->flags |= IMA_MASK; |
314 | break; | 341 | break; |
315 | case Opt_fsmagic: | 342 | case Opt_fsmagic: |
316 | audit_log_format(ab, "fsmagic=%s ", args[0].from); | 343 | ima_log_string(ab, "fsmagic", args[0].from); |
344 | |||
345 | if (entry->fsmagic) { | ||
346 | result = -EINVAL; | ||
347 | break; | ||
348 | } | ||
349 | |||
317 | result = strict_strtoul(args[0].from, 16, | 350 | result = strict_strtoul(args[0].from, 16, |
318 | &entry->fsmagic); | 351 | &entry->fsmagic); |
319 | if (!result) | 352 | if (!result) |
320 | entry->flags |= IMA_FSMAGIC; | 353 | entry->flags |= IMA_FSMAGIC; |
321 | break; | 354 | break; |
322 | case Opt_uid: | 355 | case Opt_uid: |
323 | audit_log_format(ab, "uid=%s ", args[0].from); | 356 | ima_log_string(ab, "uid", args[0].from); |
357 | |||
358 | if (entry->uid != -1) { | ||
359 | result = -EINVAL; | ||
360 | break; | ||
361 | } | ||
362 | |||
324 | result = strict_strtoul(args[0].from, 10, &lnum); | 363 | result = strict_strtoul(args[0].from, 10, &lnum); |
325 | if (!result) { | 364 | if (!result) { |
326 | entry->uid = (uid_t) lnum; | 365 | entry->uid = (uid_t) lnum; |
@@ -331,50 +370,51 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) | |||
331 | } | 370 | } |
332 | break; | 371 | break; |
333 | case Opt_obj_user: | 372 | case Opt_obj_user: |
334 | audit_log_format(ab, "obj_user=%s ", args[0].from); | 373 | ima_log_string(ab, "obj_user", args[0].from); |
335 | result = ima_lsm_rule_init(entry, args[0].from, | 374 | result = ima_lsm_rule_init(entry, args[0].from, |
336 | LSM_OBJ_USER, | 375 | LSM_OBJ_USER, |
337 | AUDIT_OBJ_USER); | 376 | AUDIT_OBJ_USER); |
338 | break; | 377 | break; |
339 | case Opt_obj_role: | 378 | case Opt_obj_role: |
340 | audit_log_format(ab, "obj_role=%s ", args[0].from); | 379 | ima_log_string(ab, "obj_role", args[0].from); |
341 | result = ima_lsm_rule_init(entry, args[0].from, | 380 | result = ima_lsm_rule_init(entry, args[0].from, |
342 | LSM_OBJ_ROLE, | 381 | LSM_OBJ_ROLE, |
343 | AUDIT_OBJ_ROLE); | 382 | AUDIT_OBJ_ROLE); |
344 | break; | 383 | break; |
345 | case Opt_obj_type: | 384 | case Opt_obj_type: |
346 | audit_log_format(ab, "obj_type=%s ", args[0].from); | 385 | ima_log_string(ab, "obj_type", args[0].from); |
347 | result = ima_lsm_rule_init(entry, args[0].from, | 386 | result = ima_lsm_rule_init(entry, args[0].from, |
348 | LSM_OBJ_TYPE, | 387 | LSM_OBJ_TYPE, |
349 | AUDIT_OBJ_TYPE); | 388 | AUDIT_OBJ_TYPE); |
350 | break; | 389 | break; |
351 | case Opt_subj_user: | 390 | case Opt_subj_user: |
352 | audit_log_format(ab, "subj_user=%s ", args[0].from); | 391 | ima_log_string(ab, "subj_user", args[0].from); |
353 | result = ima_lsm_rule_init(entry, args[0].from, | 392 | result = ima_lsm_rule_init(entry, args[0].from, |
354 | LSM_SUBJ_USER, | 393 | LSM_SUBJ_USER, |
355 | AUDIT_SUBJ_USER); | 394 | AUDIT_SUBJ_USER); |
356 | break; | 395 | break; |
357 | case Opt_subj_role: | 396 | case Opt_subj_role: |
358 | audit_log_format(ab, "subj_role=%s ", args[0].from); | 397 | ima_log_string(ab, "subj_role", args[0].from); |
359 | result = ima_lsm_rule_init(entry, args[0].from, | 398 | result = ima_lsm_rule_init(entry, args[0].from, |
360 | LSM_SUBJ_ROLE, | 399 | LSM_SUBJ_ROLE, |
361 | AUDIT_SUBJ_ROLE); | 400 | AUDIT_SUBJ_ROLE); |
362 | break; | 401 | break; |
363 | case Opt_subj_type: | 402 | case Opt_subj_type: |
364 | audit_log_format(ab, "subj_type=%s ", args[0].from); | 403 | ima_log_string(ab, "subj_type", args[0].from); |
365 | result = ima_lsm_rule_init(entry, args[0].from, | 404 | result = ima_lsm_rule_init(entry, args[0].from, |
366 | LSM_SUBJ_TYPE, | 405 | LSM_SUBJ_TYPE, |
367 | AUDIT_SUBJ_TYPE); | 406 | AUDIT_SUBJ_TYPE); |
368 | break; | 407 | break; |
369 | case Opt_err: | 408 | case Opt_err: |
370 | audit_log_format(ab, "UNKNOWN=%s ", p); | 409 | ima_log_string(ab, "UNKNOWN", p); |
410 | result = -EINVAL; | ||
371 | break; | 411 | break; |
372 | } | 412 | } |
373 | } | 413 | } |
374 | if (entry->action == UNKNOWN) | 414 | if (!result && (entry->action == UNKNOWN)) |
375 | result = -EINVAL; | 415 | result = -EINVAL; |
376 | 416 | ||
377 | audit_log_format(ab, "res=%d", !result ? 0 : 1); | 417 | audit_log_format(ab, "res=%d", !!result); |
378 | audit_log_end(ab); | 418 | audit_log_end(ab); |
379 | return result; | 419 | return result; |
380 | } | 420 | } |
@@ -384,13 +424,14 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) | |||
384 | * @rule - ima measurement policy rule | 424 | * @rule - ima measurement policy rule |
385 | * | 425 | * |
386 | * Uses a mutex to protect the policy list from multiple concurrent writers. | 426 | * Uses a mutex to protect the policy list from multiple concurrent writers. |
387 | * Returns 0 on success, an error code on failure. | 427 | * Returns the length of the rule parsed, an error code on failure |
388 | */ | 428 | */ |
389 | int ima_parse_add_rule(char *rule) | 429 | ssize_t ima_parse_add_rule(char *rule) |
390 | { | 430 | { |
391 | const char *op = "update_policy"; | 431 | const char *op = "update_policy"; |
432 | char *p; | ||
392 | struct ima_measure_rule_entry *entry; | 433 | struct ima_measure_rule_entry *entry; |
393 | int result = 0; | 434 | ssize_t result, len; |
394 | int audit_info = 0; | 435 | int audit_info = 0; |
395 | 436 | ||
396 | /* Prevent installed policy from changing */ | 437 | /* Prevent installed policy from changing */ |
@@ -410,18 +451,28 @@ int ima_parse_add_rule(char *rule) | |||
410 | 451 | ||
411 | INIT_LIST_HEAD(&entry->list); | 452 | INIT_LIST_HEAD(&entry->list); |
412 | 453 | ||
413 | result = ima_parse_rule(rule, entry); | 454 | p = strsep(&rule, "\n"); |
414 | if (!result) { | 455 | len = strlen(p) + 1; |
415 | mutex_lock(&ima_measure_mutex); | 456 | |
416 | list_add_tail(&entry->list, &measure_policy_rules); | 457 | if (*p == '#') { |
417 | mutex_unlock(&ima_measure_mutex); | 458 | kfree(entry); |
418 | } else { | 459 | return len; |
460 | } | ||
461 | |||
462 | result = ima_parse_rule(p, entry); | ||
463 | if (result) { | ||
419 | kfree(entry); | 464 | kfree(entry); |
420 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, | 465 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, |
421 | NULL, op, "invalid policy", result, | 466 | NULL, op, "invalid policy", result, |
422 | audit_info); | 467 | audit_info); |
468 | return result; | ||
423 | } | 469 | } |
424 | return result; | 470 | |
471 | mutex_lock(&ima_measure_mutex); | ||
472 | list_add_tail(&entry->list, &measure_policy_rules); | ||
473 | mutex_unlock(&ima_measure_mutex); | ||
474 | |||
475 | return len; | ||
425 | } | 476 | } |
426 | 477 | ||
427 | /* ima_delete_rules called to cleanup invalid policy */ | 478 | /* ima_delete_rules called to cleanup invalid policy */ |