aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorRafal Krypa <r.krypa@samsung.com>2013-08-09 05:47:07 -0400
committerCasey Schaufler <casey@schaufler-ca.com>2013-08-12 14:51:40 -0400
commit10289b0f738e8b301969f2288c4942455f1b1e59 (patch)
tree511a13ddeaa36bcdc3b966ade00e104d42edc78c /security
parent677264e8fb73ea35a508700e19ce76c527576d1c (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.c167
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 */
375static int smk_parse_long_rule(const char *data, struct smack_parsed_rule *rule, 375static 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)
414free_out_a: 402 tok[i++] = NULL;
415 kfree(access1); 403
416free_out_o: 404 if (smk_fill_rule(tok[0], tok[1], tok[2], tok[3], rule, import, 0))
417 kfree(object); 405 return -1;
418free_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;
512out: 516out:
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,