aboutsummaryrefslogtreecommitdiffstats
path: root/security/selinux
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2011-04-21 20:23:20 -0400
committerEric Paris <eparis@redhat.com>2011-04-25 16:24:41 -0400
commit0dc1ba24f7fff659725eecbba2c9ad679a0954cd (patch)
treead5831b52b38ca8157dd3ba4e5dfb75768bd372f /security/selinux
parent1c9904297451f558191e211a48d8838b4bf792b0 (diff)
SELINUX: Make selinux cache VFS RCU walks safe
Now that the security modules can decide whether they support the dcache RCU walk or not it's possible to make selinux a bit more RCU friendly. The SELinux AVC and security server access decision code is RCU safe. A specific piece of the LSM audit code may not be RCU safe. This patch makes the VFS RCU walk retry if it would hit the non RCU safe chunk of code. It will normally just work under RCU. This is done simply by passing the VFS RCU state as a flag down into the avc_audit() code and returning ECHILD there if it would have an issue. Based-on-patch-by: Andi Kleen <ak@linux.intel.com> Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'security/selinux')
-rw-r--r--security/selinux/avc.c36
-rw-r--r--security/selinux/hooks.c26
-rw-r--r--security/selinux/include/avc.h18
3 files changed, 55 insertions, 25 deletions
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 9da6420e2056..1d027e29ce8d 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -471,6 +471,7 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
471 * @avd: access vector decisions 471 * @avd: access vector decisions
472 * @result: result from avc_has_perm_noaudit 472 * @result: result from avc_has_perm_noaudit
473 * @a: auxiliary audit data 473 * @a: auxiliary audit data
474 * @flags: VFS walk flags
474 * 475 *
475 * Audit the granting or denial of permissions in accordance 476 * Audit the granting or denial of permissions in accordance
476 * with the policy. This function is typically called by 477 * with the policy. This function is typically called by
@@ -481,9 +482,10 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
481 * be performed under a lock, to allow the lock to be released 482 * be performed under a lock, to allow the lock to be released
482 * before calling the auditing code. 483 * before calling the auditing code.
483 */ 484 */
484void avc_audit(u32 ssid, u32 tsid, 485int avc_audit(u32 ssid, u32 tsid,
485 u16 tclass, u32 requested, 486 u16 tclass, u32 requested,
486 struct av_decision *avd, int result, struct common_audit_data *a) 487 struct av_decision *avd, int result, struct common_audit_data *a,
488 unsigned flags)
487{ 489{
488 struct common_audit_data stack_data; 490 struct common_audit_data stack_data;
489 u32 denied, audited; 491 u32 denied, audited;
@@ -515,11 +517,24 @@ void avc_audit(u32 ssid, u32 tsid,
515 else 517 else
516 audited = requested & avd->auditallow; 518 audited = requested & avd->auditallow;
517 if (!audited) 519 if (!audited)
518 return; 520 return 0;
521
519 if (!a) { 522 if (!a) {
520 a = &stack_data; 523 a = &stack_data;
521 COMMON_AUDIT_DATA_INIT(a, NONE); 524 COMMON_AUDIT_DATA_INIT(a, NONE);
522 } 525 }
526
527 /*
528 * When in a RCU walk do the audit on the RCU retry. This is because
529 * the collection of the dname in an inode audit message is not RCU
530 * safe. Note this may drop some audits when the situation changes
531 * during retry. However this is logically just as if the operation
532 * happened a little later.
533 */
534 if ((a->type == LSM_AUDIT_DATA_FS) &&
535 (flags & IPERM_FLAG_RCU))
536 return -ECHILD;
537
523 a->selinux_audit_data.tclass = tclass; 538 a->selinux_audit_data.tclass = tclass;
524 a->selinux_audit_data.requested = requested; 539 a->selinux_audit_data.requested = requested;
525 a->selinux_audit_data.ssid = ssid; 540 a->selinux_audit_data.ssid = ssid;
@@ -529,6 +544,7 @@ void avc_audit(u32 ssid, u32 tsid,
529 a->lsm_pre_audit = avc_audit_pre_callback; 544 a->lsm_pre_audit = avc_audit_pre_callback;
530 a->lsm_post_audit = avc_audit_post_callback; 545 a->lsm_post_audit = avc_audit_post_callback;
531 common_lsm_audit(a); 546 common_lsm_audit(a);
547 return 0;
532} 548}
533 549
534/** 550/**
@@ -793,6 +809,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
793 * @tclass: target security class 809 * @tclass: target security class
794 * @requested: requested permissions, interpreted based on @tclass 810 * @requested: requested permissions, interpreted based on @tclass
795 * @auditdata: auxiliary audit data 811 * @auditdata: auxiliary audit data
812 * @flags: VFS walk flags
796 * 813 *
797 * Check the AVC to determine whether the @requested permissions are granted 814 * Check the AVC to determine whether the @requested permissions are granted
798 * for the SID pair (@ssid, @tsid), interpreting the permissions 815 * for the SID pair (@ssid, @tsid), interpreting the permissions
@@ -802,14 +819,19 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
802 * permissions are granted, -%EACCES if any permissions are denied, or 819 * permissions are granted, -%EACCES if any permissions are denied, or
803 * another -errno upon other errors. 820 * another -errno upon other errors.
804 */ 821 */
805int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, 822int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass,
806 u32 requested, struct common_audit_data *auditdata) 823 u32 requested, struct common_audit_data *auditdata,
824 unsigned flags)
807{ 825{
808 struct av_decision avd; 826 struct av_decision avd;
809 int rc; 827 int rc, rc2;
810 828
811 rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd); 829 rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
812 avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata); 830
831 rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata,
832 flags);
833 if (rc2)
834 return rc2;
813 return rc; 835 return rc;
814} 836}
815 837
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 9a220be17a3f..ed5f29aa0a38 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1447,8 +1447,11 @@ static int task_has_capability(struct task_struct *tsk,
1447 } 1447 }
1448 1448
1449 rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd); 1449 rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);
1450 if (audit == SECURITY_CAP_AUDIT) 1450 if (audit == SECURITY_CAP_AUDIT) {
1451 avc_audit(sid, sid, sclass, av, &avd, rc, &ad); 1451 int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0);
1452 if (rc2)
1453 return rc2;
1454 }
1452 return rc; 1455 return rc;
1453} 1456}
1454 1457
@@ -1468,7 +1471,8 @@ static int task_has_system(struct task_struct *tsk,
1468static int inode_has_perm(const struct cred *cred, 1471static int inode_has_perm(const struct cred *cred,
1469 struct inode *inode, 1472 struct inode *inode,
1470 u32 perms, 1473 u32 perms,
1471 struct common_audit_data *adp) 1474 struct common_audit_data *adp,
1475 unsigned flags)
1472{ 1476{
1473 struct inode_security_struct *isec; 1477 struct inode_security_struct *isec;
1474 struct common_audit_data ad; 1478 struct common_audit_data ad;
@@ -1488,7 +1492,7 @@ static int inode_has_perm(const struct cred *cred,
1488 ad.u.fs.inode = inode; 1492 ad.u.fs.inode = inode;
1489 } 1493 }
1490 1494
1491 return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp); 1495 return avc_has_perm_flags(sid, isec->sid, isec->sclass, perms, adp, flags);
1492} 1496}
1493 1497
1494/* Same as inode_has_perm, but pass explicit audit data containing 1498/* Same as inode_has_perm, but pass explicit audit data containing
@@ -1505,7 +1509,7 @@ static inline int dentry_has_perm(const struct cred *cred,
1505 COMMON_AUDIT_DATA_INIT(&ad, FS); 1509 COMMON_AUDIT_DATA_INIT(&ad, FS);
1506 ad.u.fs.path.mnt = mnt; 1510 ad.u.fs.path.mnt = mnt;
1507 ad.u.fs.path.dentry = dentry; 1511 ad.u.fs.path.dentry = dentry;
1508 return inode_has_perm(cred, inode, av, &ad); 1512 return inode_has_perm(cred, inode, av, &ad, 0);
1509} 1513}
1510 1514
1511/* Check whether a task can use an open file descriptor to 1515/* Check whether a task can use an open file descriptor to
@@ -1541,7 +1545,7 @@ static int file_has_perm(const struct cred *cred,
1541 /* av is zero if only checking access to the descriptor. */ 1545 /* av is zero if only checking access to the descriptor. */
1542 rc = 0; 1546 rc = 0;
1543 if (av) 1547 if (av)
1544 rc = inode_has_perm(cred, inode, av, &ad); 1548 rc = inode_has_perm(cred, inode, av, &ad, 0);
1545 1549
1546out: 1550out:
1547 return rc; 1551 return rc;
@@ -2103,7 +2107,7 @@ static inline void flush_unauthorized_files(const struct cred *cred,
2103 file = file_priv->file; 2107 file = file_priv->file;
2104 inode = file->f_path.dentry->d_inode; 2108 inode = file->f_path.dentry->d_inode;
2105 if (inode_has_perm(cred, inode, 2109 if (inode_has_perm(cred, inode,
2106 FILE__READ | FILE__WRITE, NULL)) { 2110 FILE__READ | FILE__WRITE, NULL, 0)) {
2107 drop_tty = 1; 2111 drop_tty = 1;
2108 } 2112 }
2109 } 2113 }
@@ -2649,10 +2653,6 @@ static int selinux_inode_permission(struct inode *inode, int mask, unsigned flag
2649 if (!mask) 2653 if (!mask)
2650 return 0; 2654 return 0;
2651 2655
2652 /* May be droppable after audit */
2653 if (flags & IPERM_FLAG_RCU)
2654 return -ECHILD;
2655
2656 COMMON_AUDIT_DATA_INIT(&ad, FS); 2656 COMMON_AUDIT_DATA_INIT(&ad, FS);
2657 ad.u.fs.inode = inode; 2657 ad.u.fs.inode = inode;
2658 2658
@@ -2661,7 +2661,7 @@ static int selinux_inode_permission(struct inode *inode, int mask, unsigned flag
2661 2661
2662 perms = file_mask_to_av(inode->i_mode, mask); 2662 perms = file_mask_to_av(inode->i_mode, mask);
2663 2663
2664 return inode_has_perm(cred, inode, perms, &ad); 2664 return inode_has_perm(cred, inode, perms, &ad, flags);
2665} 2665}
2666 2666
2667static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) 2667static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
@@ -3208,7 +3208,7 @@ static int selinux_dentry_open(struct file *file, const struct cred *cred)
3208 * new inode label or new policy. 3208 * new inode label or new policy.
3209 * This check is not redundant - do not remove. 3209 * This check is not redundant - do not remove.
3210 */ 3210 */
3211 return inode_has_perm(cred, inode, open_file_to_av(file), NULL); 3211 return inode_has_perm(cred, inode, open_file_to_av(file), NULL, 0);
3212} 3212}
3213 3213
3214/* task security operations */ 3214/* task security operations */
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
index 5615081b73ec..e77b2ac2908b 100644
--- a/security/selinux/include/avc.h
+++ b/security/selinux/include/avc.h
@@ -54,11 +54,11 @@ struct avc_cache_stats {
54 54
55void __init avc_init(void); 55void __init avc_init(void);
56 56
57void avc_audit(u32 ssid, u32 tsid, 57int avc_audit(u32 ssid, u32 tsid,
58 u16 tclass, u32 requested, 58 u16 tclass, u32 requested,
59 struct av_decision *avd, 59 struct av_decision *avd,
60 int result, 60 int result,
61 struct common_audit_data *a); 61 struct common_audit_data *a, unsigned flags);
62 62
63#define AVC_STRICT 1 /* Ignore permissive mode. */ 63#define AVC_STRICT 1 /* Ignore permissive mode. */
64int avc_has_perm_noaudit(u32 ssid, u32 tsid, 64int avc_has_perm_noaudit(u32 ssid, u32 tsid,
@@ -66,9 +66,17 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
66 unsigned flags, 66 unsigned flags,
67 struct av_decision *avd); 67 struct av_decision *avd);
68 68
69int avc_has_perm(u32 ssid, u32 tsid, 69int avc_has_perm_flags(u32 ssid, u32 tsid,
70 u16 tclass, u32 requested, 70 u16 tclass, u32 requested,
71 struct common_audit_data *auditdata); 71 struct common_audit_data *auditdata,
72 unsigned);
73
74static inline int avc_has_perm(u32 ssid, u32 tsid,
75 u16 tclass, u32 requested,
76 struct common_audit_data *auditdata)
77{
78 return avc_has_perm_flags(ssid, tsid, tclass, requested, auditdata, 0);
79}
72 80
73u32 avc_policy_seqno(void); 81u32 avc_policy_seqno(void);
74 82