diff options
author | Rafal Krypa <r.krypa@samsung.com> | 2013-08-09 05:47:07 -0400 |
---|---|---|
committer | Casey Schaufler <casey@schaufler-ca.com> | 2013-08-12 14:51:40 -0400 |
commit | 10289b0f738e8b301969f2288c4942455f1b1e59 (patch) | |
tree | 511a13ddeaa36bcdc3b966ade00e104d42edc78c /security | |
parent | 677264e8fb73ea35a508700e19ce76c527576d1c (diff) |
Smack: parse multiple rules per write to load2, up to PAGE_SIZE-1 bytes
Smack interface for loading rules has always parsed only single rule from
data written to it. This requires user program to call one write() per
each rule it wants to load.
This change makes it possible to write multiple rules, separated by new
line character. Smack will load at most PAGE_SIZE-1 characters and properly
return number of processed bytes. In case when user buffer is larger, it
will be additionally truncated. All characters after last \n will not get
parsed to avoid partial rule near input buffer boundary.
Signed-off-by: Rafal Krypa <r.krypa@samsung.com>
Diffstat (limited to 'security')
-rw-r--r-- | security/smack/smackfs.c | 167 |
1 files changed, 82 insertions, 85 deletions
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index a07e93f00a0f..80f4b4a45725 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c | |||
@@ -368,56 +368,43 @@ static int smk_parse_rule(const char *data, struct smack_parsed_rule *rule, | |||
368 | * @data: string to be parsed, null terminated | 368 | * @data: string to be parsed, null terminated |
369 | * @rule: Will be filled with Smack parsed rule | 369 | * @rule: Will be filled with Smack parsed rule |
370 | * @import: if non-zero, import labels | 370 | * @import: if non-zero, import labels |
371 | * @change: if non-zero, data is from /smack/change-rule | 371 | * @tokens: numer of substrings expected in data |
372 | * | 372 | * |
373 | * Returns 0 on success, -1 on failure | 373 | * Returns number of processed bytes on success, -1 on failure. |
374 | */ | 374 | */ |
375 | static int smk_parse_long_rule(const char *data, struct smack_parsed_rule *rule, | 375 | static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule, |
376 | int import, int change) | 376 | int import, int tokens) |
377 | { | 377 | { |
378 | char *subject; | 378 | ssize_t cnt = 0; |
379 | char *object; | 379 | char *tok[4]; |
380 | char *access1; | 380 | int i; |
381 | char *access2; | ||
382 | int datalen; | ||
383 | int rc = -1; | ||
384 | 381 | ||
385 | /* This is inefficient */ | 382 | /* |
386 | datalen = strlen(data); | 383 | * Parsing the rule in-place, filling all white-spaces with '\0' |
384 | */ | ||
385 | for (i = 0; i < tokens; ++i) { | ||
386 | while (isspace(data[cnt])) | ||
387 | data[cnt++] = '\0'; | ||
387 | 388 | ||
388 | /* Our first element can be 64 + \0 with no spaces */ | 389 | if (data[cnt] == '\0') |
389 | subject = kzalloc(datalen + 1, GFP_KERNEL); | 390 | /* Unexpected end of data */ |
390 | if (subject == NULL) | 391 | return -1; |
391 | return -1; | 392 | |
392 | object = kzalloc(datalen, GFP_KERNEL); | 393 | tok[i] = data + cnt; |
393 | if (object == NULL) | 394 | |
394 | goto free_out_s; | 395 | while (data[cnt] && !isspace(data[cnt])) |
395 | access1 = kzalloc(datalen, GFP_KERNEL); | 396 | ++cnt; |
396 | if (access1 == NULL) | ||
397 | goto free_out_o; | ||
398 | access2 = kzalloc(datalen, GFP_KERNEL); | ||
399 | if (access2 == NULL) | ||
400 | goto free_out_a; | ||
401 | |||
402 | if (change) { | ||
403 | if (sscanf(data, "%s %s %s %s", | ||
404 | subject, object, access1, access2) == 4) | ||
405 | rc = smk_fill_rule(subject, object, access1, access2, | ||
406 | rule, import, 0); | ||
407 | } else { | ||
408 | if (sscanf(data, "%s %s %s", subject, object, access1) == 3) | ||
409 | rc = smk_fill_rule(subject, object, access1, NULL, | ||
410 | rule, import, 0); | ||
411 | } | 397 | } |
398 | while (isspace(data[cnt])) | ||
399 | data[cnt++] = '\0'; | ||
412 | 400 | ||
413 | kfree(access2); | 401 | while (i < 4) |
414 | free_out_a: | 402 | tok[i++] = NULL; |
415 | kfree(access1); | 403 | |
416 | free_out_o: | 404 | if (smk_fill_rule(tok[0], tok[1], tok[2], tok[3], rule, import, 0)) |
417 | kfree(object); | 405 | return -1; |
418 | free_out_s: | 406 | |
419 | kfree(subject); | 407 | return cnt; |
420 | return rc; | ||
421 | } | 408 | } |
422 | 409 | ||
423 | #define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */ | 410 | #define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */ |
@@ -449,9 +436,10 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, | |||
449 | { | 436 | { |
450 | struct smack_parsed_rule rule; | 437 | struct smack_parsed_rule rule; |
451 | char *data; | 438 | char *data; |
452 | int datalen; | 439 | int rc; |
453 | int rc = -EINVAL; | 440 | int trunc = 0; |
454 | int load = 0; | 441 | int tokens; |
442 | ssize_t cnt = 0; | ||
455 | 443 | ||
456 | /* | 444 | /* |
457 | * No partial writes. | 445 | * No partial writes. |
@@ -466,11 +454,14 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, | |||
466 | */ | 454 | */ |
467 | if (count != SMK_OLOADLEN && count != SMK_LOADLEN) | 455 | if (count != SMK_OLOADLEN && count != SMK_LOADLEN) |
468 | return -EINVAL; | 456 | return -EINVAL; |
469 | datalen = SMK_LOADLEN; | 457 | } else { |
470 | } else | 458 | if (count >= PAGE_SIZE) { |
471 | datalen = count + 1; | 459 | count = PAGE_SIZE - 1; |
460 | trunc = 1; | ||
461 | } | ||
462 | } | ||
472 | 463 | ||
473 | data = kzalloc(datalen, GFP_KERNEL); | 464 | data = kmalloc(count + 1, GFP_KERNEL); |
474 | if (data == NULL) | 465 | if (data == NULL) |
475 | return -ENOMEM; | 466 | return -ENOMEM; |
476 | 467 | ||
@@ -479,36 +470,49 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, | |||
479 | goto out; | 470 | goto out; |
480 | } | 471 | } |
481 | 472 | ||
482 | if (format == SMK_LONG_FMT) { | 473 | /* |
483 | /* | 474 | * In case of parsing only part of user buf, |
484 | * Be sure the data string is terminated. | 475 | * avoid having partial rule at the data buffer |
485 | */ | 476 | */ |
486 | data[count] = '\0'; | 477 | if (trunc) { |
487 | if (smk_parse_long_rule(data, &rule, 1, 0)) | 478 | while (count > 0 && (data[count - 1] != '\n')) |
488 | goto out; | 479 | --count; |
489 | } else if (format == SMK_CHANGE_FMT) { | 480 | if (count == 0) { |
490 | data[count] = '\0'; | 481 | rc = -EINVAL; |
491 | if (smk_parse_long_rule(data, &rule, 1, 1)) | ||
492 | goto out; | ||
493 | } else { | ||
494 | /* | ||
495 | * More on the minor hack for backward compatibility | ||
496 | */ | ||
497 | if (count == (SMK_OLOADLEN)) | ||
498 | data[SMK_OLOADLEN] = '-'; | ||
499 | if (smk_parse_rule(data, &rule, 1)) | ||
500 | goto out; | 482 | goto out; |
483 | } | ||
501 | } | 484 | } |
502 | 485 | ||
503 | if (rule_list == NULL) { | 486 | data[count] = '\0'; |
504 | load = 1; | 487 | tokens = (format == SMK_CHANGE_FMT ? 4 : 3); |
505 | rule_list = &rule.smk_subject->smk_rules; | 488 | while (cnt < count) { |
506 | rule_lock = &rule.smk_subject->smk_rules_lock; | 489 | if (format == SMK_FIXED24_FMT) { |
490 | rc = smk_parse_rule(data, &rule, 1); | ||
491 | if (rc != 0) { | ||
492 | rc = -EINVAL; | ||
493 | goto out; | ||
494 | } | ||
495 | cnt = count; | ||
496 | } else { | ||
497 | rc = smk_parse_long_rule(data + cnt, &rule, 1, tokens); | ||
498 | if (rc <= 0) { | ||
499 | rc = -EINVAL; | ||
500 | goto out; | ||
501 | } | ||
502 | cnt += rc; | ||
503 | } | ||
504 | |||
505 | if (rule_list == NULL) | ||
506 | rc = smk_set_access(&rule, &rule.smk_subject->smk_rules, | ||
507 | &rule.smk_subject->smk_rules_lock, 1); | ||
508 | else | ||
509 | rc = smk_set_access(&rule, rule_list, rule_lock, 0); | ||
510 | |||
511 | if (rc) | ||
512 | goto out; | ||
507 | } | 513 | } |
508 | 514 | ||
509 | rc = smk_set_access(&rule, rule_list, rule_lock, load); | 515 | rc = cnt; |
510 | if (rc == 0) | ||
511 | rc = count; | ||
512 | out: | 516 | out: |
513 | kfree(data); | 517 | kfree(data); |
514 | return rc; | 518 | return rc; |
@@ -1829,7 +1833,6 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, | |||
1829 | { | 1833 | { |
1830 | struct smack_parsed_rule rule; | 1834 | struct smack_parsed_rule rule; |
1831 | char *data; | 1835 | char *data; |
1832 | char *cod; | ||
1833 | int res; | 1836 | int res; |
1834 | 1837 | ||
1835 | data = simple_transaction_get(file, buf, count); | 1838 | data = simple_transaction_get(file, buf, count); |
@@ -1842,18 +1845,12 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, | |||
1842 | res = smk_parse_rule(data, &rule, 0); | 1845 | res = smk_parse_rule(data, &rule, 0); |
1843 | } else { | 1846 | } else { |
1844 | /* | 1847 | /* |
1845 | * Copy the data to make sure the string is terminated. | 1848 | * simple_transaction_get() returns null-terminated data |
1846 | */ | 1849 | */ |
1847 | cod = kzalloc(count + 1, GFP_KERNEL); | 1850 | res = smk_parse_long_rule(data, &rule, 0, 3); |
1848 | if (cod == NULL) | ||
1849 | return -ENOMEM; | ||
1850 | memcpy(cod, data, count); | ||
1851 | cod[count] = '\0'; | ||
1852 | res = smk_parse_long_rule(cod, &rule, 0, 0); | ||
1853 | kfree(cod); | ||
1854 | } | 1851 | } |
1855 | 1852 | ||
1856 | if (res) | 1853 | if (res < 0) |
1857 | return -EINVAL; | 1854 | return -EINVAL; |
1858 | 1855 | ||
1859 | res = smk_access(rule.smk_subject, rule.smk_object, | 1856 | res = smk_access(rule.smk_subject, rule.smk_object, |