aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorMatthew Garrett <mjg59@google.com>2018-02-08 15:37:19 -0500
committerJohn Johansen <john.johansen@canonical.com>2018-02-09 14:30:02 -0500
commit8e51f9087f4024d20f70f4d9831e1f45d8088331 (patch)
treea466a6dfa9ffd57b9919b89931bc18fbddb1517e /security
parenta0781209cb894e5115bb00c269b1d94c4b632d6a (diff)
apparmor: Add support for attaching profiles via xattr, presence and value
Make it possible to tie Apparmor profiles to the presence of one or more extended attributes, and optionally their values. An example usecase for this is to automatically transition to a more privileged Apparmor profile if an executable has a valid IMA signature, which can then be appraised by the IMA subsystem. Signed-off-by: Matthew Garrett <mjg59@google.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
Diffstat (limited to 'security')
-rw-r--r--security/apparmor/domain.c152
-rw-r--r--security/apparmor/include/policy.h6
-rw-r--r--security/apparmor/policy.c8
-rw-r--r--security/apparmor/policy_unpack.c85
4 files changed, 217 insertions, 34 deletions
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 9d1936519cfd..6bcafe8d226d 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -19,6 +19,7 @@
19#include <linux/syscalls.h> 19#include <linux/syscalls.h>
20#include <linux/tracehook.h> 20#include <linux/tracehook.h>
21#include <linux/personality.h> 21#include <linux/personality.h>
22#include <linux/xattr.h>
22 23
23#include "include/audit.h" 24#include "include/audit.h"
24#include "include/apparmorfs.h" 25#include "include/apparmorfs.h"
@@ -302,7 +303,56 @@ static int change_profile_perms(struct aa_profile *profile,
302} 303}
303 304
304/** 305/**
306 * aa_xattrs_match - check whether a file matches the xattrs defined in profile
307 * @bprm: binprm struct for the process to validate
308 * @profile: profile to match against (NOT NULL)
309 *
310 * Returns: number of extended attributes that matched, or < 0 on error
311 */
312static int aa_xattrs_match(const struct linux_binprm *bprm,
313 struct aa_profile *profile)
314{
315 int i;
316 size_t size;
317 struct dentry *d;
318 char *value = NULL;
319 int value_size = 0, ret = profile->xattr_count;
320
321 if (!bprm || !profile->xattr_count)
322 return 0;
323
324 d = bprm->file->f_path.dentry;
325
326 for (i = 0; i < profile->xattr_count; i++) {
327 size = vfs_getxattr_alloc(d, profile->xattrs[i], &value,
328 value_size, GFP_KERNEL);
329 if (size < 0) {
330 ret = -EINVAL;
331 goto out;
332 }
333
334 /* Check the xattr value, not just presence */
335 if (profile->xattr_lens[i]) {
336 if (profile->xattr_lens[i] != size) {
337 ret = -EINVAL;
338 goto out;
339 }
340
341 if (memcmp(value, profile->xattr_values[i], size)) {
342 ret = -EINVAL;
343 goto out;
344 }
345 }
346 }
347
348out:
349 kfree(value);
350 return ret;
351}
352
353/**
305 * __attach_match_ - find an attachment match 354 * __attach_match_ - find an attachment match
355 * @bprm - binprm structure of transitioning task
306 * @name - to match against (NOT NULL) 356 * @name - to match against (NOT NULL)
307 * @head - profile list to walk (NOT NULL) 357 * @head - profile list to walk (NOT NULL)
308 * @info - info message if there was an error (NOT NULL) 358 * @info - info message if there was an error (NOT NULL)
@@ -316,11 +366,12 @@ static int change_profile_perms(struct aa_profile *profile,
316 * 366 *
317 * Returns: profile or NULL if no match found 367 * Returns: profile or NULL if no match found
318 */ 368 */
319static struct aa_profile *__attach_match(const char *name, 369static struct aa_profile *__attach_match(const struct linux_binprm *bprm,
370 const char *name,
320 struct list_head *head, 371 struct list_head *head,
321 const char **info) 372 const char **info)
322{ 373{
323 int len = 0; 374 int len = 0, xattrs = 0;
324 bool conflict = false; 375 bool conflict = false;
325 struct aa_profile *profile, *candidate = NULL; 376 struct aa_profile *profile, *candidate = NULL;
326 377
@@ -329,26 +380,56 @@ static struct aa_profile *__attach_match(const char *name,
329 &profile->label == ns_unconfined(profile->ns)) 380 &profile->label == ns_unconfined(profile->ns))
330 continue; 381 continue;
331 382
383 /* Find the "best" matching profile. Profiles must
384 * match the path and extended attributes (if any)
385 * associated with the file. A more specific path
386 * match will be preferred over a less specific one,
387 * and a match with more matching extended attributes
388 * will be preferred over one with fewer. If the best
389 * match has both the same level of path specificity
390 * and the same number of matching extended attributes
391 * as another profile, signal a conflict and refuse to
392 * match.
393 */
332 if (profile->xmatch) { 394 if (profile->xmatch) {
333 if (profile->xmatch_len >= len) { 395 unsigned int state;
334 unsigned int state; 396 u32 perm;
335 u32 perm; 397
336 398 if (profile->xmatch_len < len)
337 state = aa_dfa_match(profile->xmatch, 399 continue;
338 DFA_START, name); 400
339 perm = dfa_user_allow(profile->xmatch, state); 401 state = aa_dfa_match(profile->xmatch,
340 /* any accepting state means a valid match. */ 402 DFA_START, name);
341 if (perm & MAY_EXEC) { 403 perm = dfa_user_allow(profile->xmatch, state);
342 if (profile->xmatch_len == len) { 404 /* any accepting state means a valid match. */
405 if (perm & MAY_EXEC) {
406 int ret = aa_xattrs_match(bprm, profile);
407
408 /* Fail matching if the xattrs don't match */
409 if (ret < 0)
410 continue;
411
412 /* The new match isn't more specific
413 * than the current best match
414 */
415 if (profile->xmatch_len == len &&
416 ret <= xattrs) {
417 /* Match is equivalent, so conflict */
418 if (ret == xattrs)
343 conflict = true; 419 conflict = true;
344 continue; 420 continue;
345 }
346 candidate = profile;
347 len = profile->xmatch_len;
348 conflict = false;
349 } 421 }
422
423 /* Either the same length with more matching
424 * xattrs, or a longer match
425 */
426 candidate = profile;
427 len = profile->xmatch_len;
428 xattrs = ret;
429 conflict = false;
350 } 430 }
351 } else if (!strcmp(profile->base.name, name)) 431 } else if (!strcmp(profile->base.name, name) &&
432 aa_xattrs_match(bprm, profile) >= 0)
352 /* exact non-re match, no more searching required */ 433 /* exact non-re match, no more searching required */
353 return profile; 434 return profile;
354 } 435 }
@@ -363,6 +444,7 @@ static struct aa_profile *__attach_match(const char *name,
363 444
364/** 445/**
365 * find_attach - do attachment search for unconfined processes 446 * find_attach - do attachment search for unconfined processes
447 * @bprm - binprm structure of transitioning task
366 * @ns: the current namespace (NOT NULL) 448 * @ns: the current namespace (NOT NULL)
367 * @list: list to search (NOT NULL) 449 * @list: list to search (NOT NULL)
368 * @name: the executable name to match against (NOT NULL) 450 * @name: the executable name to match against (NOT NULL)
@@ -370,13 +452,14 @@ static struct aa_profile *__attach_match(const char *name,
370 * 452 *
371 * Returns: label or NULL if no match found 453 * Returns: label or NULL if no match found
372 */ 454 */
373static struct aa_label *find_attach(struct aa_ns *ns, struct list_head *list, 455static struct aa_label *find_attach(const struct linux_binprm *bprm,
456 struct aa_ns *ns, struct list_head *list,
374 const char *name, const char **info) 457 const char *name, const char **info)
375{ 458{
376 struct aa_profile *profile; 459 struct aa_profile *profile;
377 460
378 rcu_read_lock(); 461 rcu_read_lock();
379 profile = aa_get_profile(__attach_match(name, list, info)); 462 profile = aa_get_profile(__attach_match(bprm, name, list, info));
380 rcu_read_unlock(); 463 rcu_read_unlock();
381 464
382 return profile ? &profile->label : NULL; 465 return profile ? &profile->label : NULL;
@@ -432,6 +515,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
432/** 515/**
433 * x_to_label - get target label for a given xindex 516 * x_to_label - get target label for a given xindex
434 * @profile: current profile (NOT NULL) 517 * @profile: current profile (NOT NULL)
518 * @bprm: binprm structure of transitioning task
435 * @name: name to lookup (NOT NULL) 519 * @name: name to lookup (NOT NULL)
436 * @xindex: index into x transition table 520 * @xindex: index into x transition table
437 * @lookupname: returns: name used in lookup if one was specified (NOT NULL) 521 * @lookupname: returns: name used in lookup if one was specified (NOT NULL)
@@ -441,6 +525,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
441 * Returns: refcounted label or NULL if not found available 525 * Returns: refcounted label or NULL if not found available
442 */ 526 */
443static struct aa_label *x_to_label(struct aa_profile *profile, 527static struct aa_label *x_to_label(struct aa_profile *profile,
528 const struct linux_binprm *bprm,
444 const char *name, u32 xindex, 529 const char *name, u32 xindex,
445 const char **lookupname, 530 const char **lookupname,
446 const char **info) 531 const char **info)
@@ -468,11 +553,11 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
468 case AA_X_NAME: 553 case AA_X_NAME:
469 if (xindex & AA_X_CHILD) 554 if (xindex & AA_X_CHILD)
470 /* released by caller */ 555 /* released by caller */
471 new = find_attach(ns, &profile->base.profiles, 556 new = find_attach(bprm, ns, &profile->base.profiles,
472 name, info); 557 name, info);
473 else 558 else
474 /* released by caller */ 559 /* released by caller */
475 new = find_attach(ns, &ns->base.profiles, 560 new = find_attach(bprm, ns, &ns->base.profiles,
476 name, info); 561 name, info);
477 *lookupname = name; 562 *lookupname = name;
478 break; 563 break;
@@ -512,6 +597,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
512 bool *secure_exec) 597 bool *secure_exec)
513{ 598{
514 struct aa_label *new = NULL; 599 struct aa_label *new = NULL;
600 struct aa_profile *component;
601 struct label_it i;
515 const char *info = NULL, *name = NULL, *target = NULL; 602 const char *info = NULL, *name = NULL, *target = NULL;
516 unsigned int state = profile->file.start; 603 unsigned int state = profile->file.start;
517 struct aa_perms perms = {}; 604 struct aa_perms perms = {};
@@ -536,8 +623,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
536 } 623 }
537 624
538 if (profile_unconfined(profile)) { 625 if (profile_unconfined(profile)) {
539 new = find_attach(profile->ns, &profile->ns->base.profiles, 626 new = find_attach(bprm, profile->ns,
540 name, &info); 627 &profile->ns->base.profiles, name, &info);
541 if (new) { 628 if (new) {
542 AA_DEBUG("unconfined attached to new label"); 629 AA_DEBUG("unconfined attached to new label");
543 return new; 630 return new;
@@ -550,7 +637,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
550 state = aa_str_perms(profile->file.dfa, state, name, cond, &perms); 637 state = aa_str_perms(profile->file.dfa, state, name, cond, &perms);
551 if (perms.allow & MAY_EXEC) { 638 if (perms.allow & MAY_EXEC) {
552 /* exec permission determine how to transition */ 639 /* exec permission determine how to transition */
553 new = x_to_label(profile, name, perms.xindex, &target, &info); 640 new = x_to_label(profile, bprm, name, perms.xindex, &target,
641 &info);
554 if (new && new->proxy == profile->label.proxy && info) { 642 if (new && new->proxy == profile->label.proxy && info) {
555 /* hack ix fallback - improve how this is detected */ 643 /* hack ix fallback - improve how this is detected */
556 goto audit; 644 goto audit;
@@ -559,6 +647,20 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
559 info = "profile transition not found"; 647 info = "profile transition not found";
560 /* remove MAY_EXEC to audit as failure */ 648 /* remove MAY_EXEC to audit as failure */
561 perms.allow &= ~MAY_EXEC; 649 perms.allow &= ~MAY_EXEC;
650 } else {
651 /* verify that each component's xattr requirements are
652 * met, and fail execution otherwise
653 */
654 label_for_each(i, new, component) {
655 if (aa_xattrs_match(bprm, component) < 0) {
656 error = -EACCES;
657 info = "required xattrs not present";
658 perms.allow &= ~MAY_EXEC;
659 aa_put_label(new);
660 new = NULL;
661 goto audit;
662 }
663 }
562 } 664 }
563 } else if (COMPLAIN_MODE(profile)) { 665 } else if (COMPLAIN_MODE(profile)) {
564 /* no exec permission - learning mode */ 666 /* no exec permission - learning mode */
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index 17fe41a9cac3..02bde92ebb5c 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -148,6 +148,12 @@ struct aa_profile {
148 struct aa_policydb policy; 148 struct aa_policydb policy;
149 struct aa_file_rules file; 149 struct aa_file_rules file;
150 struct aa_caps caps; 150 struct aa_caps caps;
151
152 int xattr_count;
153 char **xattrs;
154 size_t *xattr_lens;
155 char **xattr_values;
156
151 struct aa_rlimit rlimits; 157 struct aa_rlimit rlimits;
152 158
153 struct aa_loaddata *rawdata; 159 struct aa_loaddata *rawdata;
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index a8e096a88e62..7fee546ba10d 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -210,6 +210,7 @@ static void aa_free_data(void *ptr, void *arg)
210void aa_free_profile(struct aa_profile *profile) 210void aa_free_profile(struct aa_profile *profile)
211{ 211{
212 struct rhashtable *rht; 212 struct rhashtable *rht;
213 int i;
213 214
214 AA_DEBUG("%s(%p)\n", __func__, profile); 215 AA_DEBUG("%s(%p)\n", __func__, profile);
215 216
@@ -227,6 +228,13 @@ void aa_free_profile(struct aa_profile *profile)
227 aa_free_cap_rules(&profile->caps); 228 aa_free_cap_rules(&profile->caps);
228 aa_free_rlimit_rules(&profile->rlimits); 229 aa_free_rlimit_rules(&profile->rlimits);
229 230
231 for (i = 0; i < profile->xattr_count; i++) {
232 kzfree(profile->xattrs[i]);
233 kzfree(profile->xattr_values[i]);
234 }
235 kzfree(profile->xattrs);
236 kzfree(profile->xattr_lens);
237 kzfree(profile->xattr_values);
230 kzfree(profile->dirname); 238 kzfree(profile->dirname);
231 aa_put_dfa(profile->xmatch); 239 aa_put_dfa(profile->xmatch);
232 aa_put_dfa(profile->policy.dfa); 240 aa_put_dfa(profile->policy.dfa);
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index 40c8dc617b13..98d019185e57 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -203,6 +203,15 @@ static bool inbounds(struct aa_ext *e, size_t size)
203 return (size <= e->end - e->pos); 203 return (size <= e->end - e->pos);
204} 204}
205 205
206static void *kvmemdup(const void *src, size_t len)
207{
208 void *p = kvmalloc(len, GFP_KERNEL);
209
210 if (p)
211 memcpy(p, src, len);
212 return p;
213}
214
206/** 215/**
207 * aa_u16_chunck - test and do bounds checking for a u16 size based chunk 216 * aa_u16_chunck - test and do bounds checking for a u16 size based chunk
208 * @e: serialized data read head (NOT NULL) 217 * @e: serialized data read head (NOT NULL)
@@ -522,6 +531,68 @@ fail:
522 return 0; 531 return 0;
523} 532}
524 533
534static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile)
535{
536 void *pos = e->pos;
537
538 if (unpack_nameX(e, AA_STRUCT, "xattrs")) {
539 int i, size;
540
541 size = unpack_array(e, NULL);
542 profile->xattr_count = size;
543 profile->xattrs = kcalloc(size, sizeof(char *),
544 GFP_KERNEL);
545 if (!profile->xattrs)
546 goto fail;
547 for (i = 0; i < size; i++) {
548 if (!unpack_strdup(e, &profile->xattrs[i], NULL))
549 goto fail;
550 }
551 if (!unpack_nameX(e, AA_ARRAYEND, NULL))
552 goto fail;
553 if (!unpack_nameX(e, AA_STRUCTEND, NULL))
554 goto fail;
555 }
556
557 if (unpack_nameX(e, AA_STRUCT, "xattr_values")) {
558 int i, size;
559
560 size = unpack_array(e, NULL);
561
562 /* Must be the same number of xattr values as xattrs */
563 if (size != profile->xattr_count)
564 goto fail;
565
566 profile->xattr_lens = kcalloc(size, sizeof(size_t),
567 GFP_KERNEL);
568 if (!profile->xattr_lens)
569 goto fail;
570
571 profile->xattr_values = kcalloc(size, sizeof(char *),
572 GFP_KERNEL);
573 if (!profile->xattr_values)
574 goto fail;
575
576 for (i = 0; i < size; i++) {
577 profile->xattr_lens[i] = unpack_blob(e,
578 &profile->xattr_values[i], NULL);
579 profile->xattr_values[i] =
580 kvmemdup(profile->xattr_values[i],
581 profile->xattr_lens[i]);
582 }
583
584 if (!unpack_nameX(e, AA_ARRAYEND, NULL))
585 goto fail;
586 if (!unpack_nameX(e, AA_STRUCTEND, NULL))
587 goto fail;
588 }
589 return 1;
590
591fail:
592 e->pos = pos;
593 return 0;
594}
595
525static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) 596static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
526{ 597{
527 void *pos = e->pos; 598 void *pos = e->pos;
@@ -556,15 +627,6 @@ fail:
556 return 0; 627 return 0;
557} 628}
558 629
559static void *kvmemdup(const void *src, size_t len)
560{
561 void *p = kvmalloc(len, GFP_KERNEL);
562
563 if (p)
564 memcpy(p, src, len);
565 return p;
566}
567
568static u32 strhash(const void *data, u32 len, u32 seed) 630static u32 strhash(const void *data, u32 len, u32 seed)
569{ 631{
570 const char * const *key = data; 632 const char * const *key = data;
@@ -719,6 +781,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
719 goto fail; 781 goto fail;
720 } 782 }
721 783
784 if (!unpack_xattrs(e, profile)) {
785 info = "failed to unpack profile xattrs";
786 goto fail;
787 }
788
722 if (!unpack_rlimits(e, profile)) { 789 if (!unpack_rlimits(e, profile)) {
723 info = "failed to unpack profile rlimits"; 790 info = "failed to unpack profile rlimits";
724 goto fail; 791 goto fail;