1919#include <linux/syscalls.h>
2020#include <linux/tracehook.h>
2121#include <linux/personality.h>
22+ #include <linux/xattr.h>
2223
2324#include "include/audit.h"
2425#include "include/apparmorfs.h"
@@ -301,8 +302,57 @@ static int change_profile_perms(struct aa_profile *profile,
301302 return label_match (profile , target , stack , start , true, request , perms );
302303}
303304
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+ */
312+ static 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+
348+ out :
349+ kfree (value );
350+ return ret ;
351+ }
352+
304353/**
305354 * __attach_match_ - find an attachment match
355+ * @bprm - binprm structure of transitioning task
306356 * @name - to match against (NOT NULL)
307357 * @head - profile list to walk (NOT NULL)
308358 * @info - info message if there was an error (NOT NULL)
@@ -316,11 +366,12 @@ static int change_profile_perms(struct aa_profile *profile,
316366 *
317367 * Returns: profile or NULL if no match found
318368 */
319- static struct aa_profile * __attach_match (const char * name ,
369+ static struct aa_profile * __attach_match (const struct linux_binprm * bprm ,
370+ const char * name ,
320371 struct list_head * head ,
321372 const char * * info )
322373{
323- int len = 0 ;
374+ int len = 0 , xattrs = 0 ;
324375 bool conflict = false;
325376 struct aa_profile * profile , * candidate = NULL ;
326377
@@ -329,26 +380,56 @@ static struct aa_profile *__attach_match(const char *name,
329380 & profile -> label == ns_unconfined (profile -> ns ))
330381 continue ;
331382
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+ */
332394 if (profile -> xmatch ) {
333- if (profile -> xmatch_len >= len ) {
334- unsigned int state ;
335- u32 perm ;
336-
337- state = aa_dfa_match (profile -> xmatch ,
338- DFA_START , name );
339- perm = dfa_user_allow (profile -> xmatch , state );
340- /* any accepting state means a valid match. */
341- if (perm & MAY_EXEC ) {
342- if (profile -> xmatch_len == len ) {
395+ unsigned int state ;
396+ u32 perm ;
397+
398+ if (profile -> xmatch_len < len )
399+ continue ;
400+
401+ state = aa_dfa_match (profile -> xmatch ,
402+ DFA_START , name );
403+ perm = dfa_user_allow (profile -> xmatch , state );
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 )
343419 conflict = true;
344- continue ;
345- }
346- candidate = profile ;
347- len = profile -> xmatch_len ;
348- conflict = false;
420+ continue ;
349421 }
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;
350430 }
351- } else if (!strcmp (profile -> base .name , name ))
431+ } else if (!strcmp (profile -> base .name , name ) &&
432+ aa_xattrs_match (bprm , profile ) >= 0 )
352433 /* exact non-re match, no more searching required */
353434 return profile ;
354435 }
@@ -363,20 +444,22 @@ static struct aa_profile *__attach_match(const char *name,
363444
364445/**
365446 * find_attach - do attachment search for unconfined processes
447+ * @bprm - binprm structure of transitioning task
366448 * @ns: the current namespace (NOT NULL)
367449 * @list: list to search (NOT NULL)
368450 * @name: the executable name to match against (NOT NULL)
369451 * @info: info message if there was an error
370452 *
371453 * Returns: label or NULL if no match found
372454 */
373- static struct aa_label * find_attach (struct aa_ns * ns , struct list_head * list ,
455+ static struct aa_label * find_attach (const struct linux_binprm * bprm ,
456+ struct aa_ns * ns , struct list_head * list ,
374457 const char * name , const char * * info )
375458{
376459 struct aa_profile * profile ;
377460
378461 rcu_read_lock ();
379- profile = aa_get_profile (__attach_match (name , list , info ));
462+ profile = aa_get_profile (__attach_match (bprm , name , list , info ));
380463 rcu_read_unlock ();
381464
382465 return profile ? & profile -> label : NULL ;
@@ -432,6 +515,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
432515/**
433516 * x_to_label - get target label for a given xindex
434517 * @profile: current profile (NOT NULL)
518+ * @bprm: binprm structure of transitioning task
435519 * @name: name to lookup (NOT NULL)
436520 * @xindex: index into x transition table
437521 * @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,
441525 * Returns: refcounted label or NULL if not found available
442526 */
443527static struct aa_label * x_to_label (struct aa_profile * profile ,
528+ const struct linux_binprm * bprm ,
444529 const char * name , u32 xindex ,
445530 const char * * lookupname ,
446531 const char * * info )
@@ -468,11 +553,11 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
468553 case AA_X_NAME :
469554 if (xindex & AA_X_CHILD )
470555 /* released by caller */
471- new = find_attach (ns , & profile -> base .profiles ,
556+ new = find_attach (bprm , ns , & profile -> base .profiles ,
472557 name , info );
473558 else
474559 /* released by caller */
475- new = find_attach (ns , & ns -> base .profiles ,
560+ new = find_attach (bprm , ns , & ns -> base .profiles ,
476561 name , info );
477562 * lookupname = name ;
478563 break ;
@@ -512,6 +597,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
512597 bool * secure_exec )
513598{
514599 struct aa_label * new = NULL ;
600+ struct aa_profile * component ;
601+ struct label_it i ;
515602 const char * info = NULL , * name = NULL , * target = NULL ;
516603 unsigned int state = profile -> file .start ;
517604 struct aa_perms perms = {};
@@ -536,8 +623,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
536623 }
537624
538625 if (profile_unconfined (profile )) {
539- new = find_attach (profile -> ns , & profile -> ns -> base . profiles ,
540- name , & info );
626+ new = find_attach (bprm , profile -> ns ,
627+ & profile -> ns -> base . profiles , name , & info );
541628 if (new ) {
542629 AA_DEBUG ("unconfined attached to new label" );
543630 return new ;
@@ -550,7 +637,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
550637 state = aa_str_perms (profile -> file .dfa , state , name , cond , & perms );
551638 if (perms .allow & MAY_EXEC ) {
552639 /* 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 );
554642 if (new && new -> proxy == profile -> label .proxy && info ) {
555643 /* hack ix fallback - improve how this is detected */
556644 goto audit ;
@@ -559,6 +647,20 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
559647 info = "profile transition not found" ;
560648 /* remove MAY_EXEC to audit as failure */
561649 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+ }
562664 }
563665 } else if (COMPLAIN_MODE (profile )) {
564666 /* no exec permission - learning mode */
0 commit comments