summaryrefslogtreecommitdiffstats
path: root/security/selinux
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-05-21 20:27:36 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-21 20:27:36 -0700
commitcb60e3e65c1b96a4d6444a7a13dc7dd48bc15a2b (patch)
tree4322be35db678f6299348a76ad60a2023954af7d /security/selinux
parent99262a3dafa3290866512ddfb32609198f8973e9 (diff)
parentff2bb047c4bce9742e94911eeb44b4d6ff4734ab (diff)
downloadlinux-stable-cb60e3e65c1b96a4d6444a7a13dc7dd48bc15a2b.tar.gz
linux-stable-cb60e3e65c1b96a4d6444a7a13dc7dd48bc15a2b.tar.bz2
linux-stable-cb60e3e65c1b96a4d6444a7a13dc7dd48bc15a2b.zip
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: "New notable features: - The seccomp work from Will Drewry - PR_{GET,SET}_NO_NEW_PRIVS from Andy Lutomirski - Longer security labels for Smack from Casey Schaufler - Additional ptrace restriction modes for Yama by Kees Cook" Fix up trivial context conflicts in arch/x86/Kconfig and include/linux/filter.h * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (65 commits) apparmor: fix long path failure due to disconnected path apparmor: fix profile lookup for unconfined ima: fix filename hint to reflect script interpreter name KEYS: Don't check for NULL key pointer in key_validate() Smack: allow for significantly longer Smack labels v4 gfp flags for security_inode_alloc()? Smack: recursive tramsmute Yama: replace capable() with ns_capable() TOMOYO: Accept manager programs which do not start with / . KEYS: Add invalidation support KEYS: Do LRU discard in full keyrings KEYS: Permit in-place link replacement in keyring list KEYS: Perform RCU synchronisation on keys prior to key destruction KEYS: Announce key type (un)registration KEYS: Reorganise keys Makefile KEYS: Move the key config into security/keys/Kconfig KEYS: Use the compat keyctl() syscall wrapper on Sparc64 for Sparc32 compat Yama: remove an unused variable samples/seccomp: fix dependencies on arch macros Yama: add additional ptrace scopes ...
Diffstat (limited to 'security/selinux')
-rw-r--r--security/selinux/avc.c130
-rw-r--r--security/selinux/hooks.c268
-rw-r--r--security/selinux/include/avc.h100
-rw-r--r--security/selinux/include/security.h4
-rw-r--r--security/selinux/netif.c6
-rw-r--r--security/selinux/netnode.c6
-rw-r--r--security/selinux/netport.c6
-rw-r--r--security/selinux/selinuxfs.c11
-rw-r--r--security/selinux/ss/context.h20
-rw-r--r--security/selinux/ss/mls.c24
-rw-r--r--security/selinux/ss/policydb.c44
-rw-r--r--security/selinux/ss/policydb.h14
-rw-r--r--security/selinux/ss/services.c56
13 files changed, 383 insertions, 306 deletions
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 8ee42b2a5f19..68d82daed257 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -65,14 +65,8 @@ struct avc_cache {
};
struct avc_callback_node {
- int (*callback) (u32 event, u32 ssid, u32 tsid,
- u16 tclass, u32 perms,
- u32 *out_retained);
+ int (*callback) (u32 event);
u32 events;
- u32 ssid;
- u32 tsid;
- u16 tclass;
- u32 perms;
struct avc_callback_node *next;
};
@@ -436,9 +430,9 @@ static void avc_audit_pre_callback(struct audit_buffer *ab, void *a)
{
struct common_audit_data *ad = a;
audit_log_format(ab, "avc: %s ",
- ad->selinux_audit_data->slad->denied ? "denied" : "granted");
- avc_dump_av(ab, ad->selinux_audit_data->slad->tclass,
- ad->selinux_audit_data->slad->audited);
+ ad->selinux_audit_data->denied ? "denied" : "granted");
+ avc_dump_av(ab, ad->selinux_audit_data->tclass,
+ ad->selinux_audit_data->audited);
audit_log_format(ab, " for ");
}
@@ -452,25 +446,23 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
{
struct common_audit_data *ad = a;
audit_log_format(ab, " ");
- avc_dump_query(ab, ad->selinux_audit_data->slad->ssid,
- ad->selinux_audit_data->slad->tsid,
- ad->selinux_audit_data->slad->tclass);
+ avc_dump_query(ab, ad->selinux_audit_data->ssid,
+ ad->selinux_audit_data->tsid,
+ ad->selinux_audit_data->tclass);
}
/* This is the slow part of avc audit with big stack footprint */
-static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
+noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
u32 requested, u32 audited, u32 denied,
struct common_audit_data *a,
unsigned flags)
{
struct common_audit_data stack_data;
- struct selinux_audit_data sad = {0,};
- struct selinux_late_audit_data slad;
+ struct selinux_audit_data sad;
if (!a) {
a = &stack_data;
- COMMON_AUDIT_DATA_INIT(a, NONE);
- a->selinux_audit_data = &sad;
+ a->type = LSM_AUDIT_DATA_NONE;
}
/*
@@ -484,104 +476,34 @@ static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
(flags & MAY_NOT_BLOCK))
return -ECHILD;
- slad.tclass = tclass;
- slad.requested = requested;
- slad.ssid = ssid;
- slad.tsid = tsid;
- slad.audited = audited;
- slad.denied = denied;
+ sad.tclass = tclass;
+ sad.requested = requested;
+ sad.ssid = ssid;
+ sad.tsid = tsid;
+ sad.audited = audited;
+ sad.denied = denied;
+
+ a->selinux_audit_data = &sad;
- a->selinux_audit_data->slad = &slad;
common_lsm_audit(a, avc_audit_pre_callback, avc_audit_post_callback);
return 0;
}
/**
- * avc_audit - Audit the granting or denial of permissions.
- * @ssid: source security identifier
- * @tsid: target security identifier
- * @tclass: target security class
- * @requested: requested permissions
- * @avd: access vector decisions
- * @result: result from avc_has_perm_noaudit
- * @a: auxiliary audit data
- * @flags: VFS walk flags
- *
- * Audit the granting or denial of permissions in accordance
- * with the policy. This function is typically called by
- * avc_has_perm() after a permission check, but can also be
- * called directly by callers who use avc_has_perm_noaudit()
- * in order to separate the permission check from the auditing.
- * For example, this separation is useful when the permission check must
- * be performed under a lock, to allow the lock to be released
- * before calling the auditing code.
- */
-inline int avc_audit(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- struct av_decision *avd, int result, struct common_audit_data *a,
- unsigned flags)
-{
- u32 denied, audited;
- denied = requested & ~avd->allowed;
- if (unlikely(denied)) {
- audited = denied & avd->auditdeny;
- /*
- * a->selinux_audit_data->auditdeny is TRICKY! Setting a bit in
- * this field means that ANY denials should NOT be audited if
- * the policy contains an explicit dontaudit rule for that
- * permission. Take notice that this is unrelated to the
- * actual permissions that were denied. As an example lets
- * assume:
- *
- * denied == READ
- * avd.auditdeny & ACCESS == 0 (not set means explicit rule)
- * selinux_audit_data->auditdeny & ACCESS == 1
- *
- * We will NOT audit the denial even though the denied
- * permission was READ and the auditdeny checks were for
- * ACCESS
- */
- if (a &&
- a->selinux_audit_data->auditdeny &&
- !(a->selinux_audit_data->auditdeny & avd->auditdeny))
- audited = 0;
- } else if (result)
- audited = denied = requested;
- else
- audited = requested & avd->auditallow;
- if (likely(!audited))
- return 0;
-
- return slow_avc_audit(ssid, tsid, tclass,
- requested, audited, denied,
- a, flags);
-}
-
-/**
* avc_add_callback - Register a callback for security events.
* @callback: callback function
* @events: security events
- * @ssid: source security identifier or %SECSID_WILD
- * @tsid: target security identifier or %SECSID_WILD
- * @tclass: target security class
- * @perms: permissions
*
- * Register a callback function for events in the set @events
- * related to the SID pair (@ssid, @tsid)
- * and the permissions @perms, interpreting
- * @perms based on @tclass. Returns %0 on success or
- * -%ENOMEM if insufficient memory exists to add the callback.
+ * Register a callback function for events in the set @events.
+ * Returns %0 on success or -%ENOMEM if insufficient memory
+ * exists to add the callback.
*/
-int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
- u16 tclass, u32 perms,
- u32 *out_retained),
- u32 events, u32 ssid, u32 tsid,
- u16 tclass, u32 perms)
+int __init avc_add_callback(int (*callback)(u32 event), u32 events)
{
struct avc_callback_node *c;
int rc = 0;
- c = kmalloc(sizeof(*c), GFP_ATOMIC);
+ c = kmalloc(sizeof(*c), GFP_KERNEL);
if (!c) {
rc = -ENOMEM;
goto out;
@@ -589,9 +511,6 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
c->callback = callback;
c->events = events;
- c->ssid = ssid;
- c->tsid = tsid;
- c->perms = perms;
c->next = avc_callbacks;
avc_callbacks = c;
out:
@@ -731,8 +650,7 @@ int avc_ss_reset(u32 seqno)
for (c = avc_callbacks; c; c = c->next) {
if (c->events & AVC_CALLBACK_RESET) {
- tmprc = c->callback(AVC_CALLBACK_RESET,
- 0, 0, 0, 0, NULL);
+ tmprc = c->callback(AVC_CALLBACK_RESET);
/* save the first error encountered for the return
value and continue processing the callbacks */
if (!rc)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index d85b793c9321..fa2341b68331 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1420,16 +1420,13 @@ static int cred_has_capability(const struct cred *cred,
int cap, int audit)
{
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
struct av_decision avd;
u16 sclass;
u32 sid = cred_sid(cred);
u32 av = CAP_TO_MASK(cap);
int rc;
- COMMON_AUDIT_DATA_INIT(&ad, CAP);
- ad.selinux_audit_data = &sad;
- ad.tsk = current;
+ ad.type = LSM_AUDIT_DATA_CAP;
ad.u.cap = cap;
switch (CAP_TO_INDEX(cap)) {
@@ -1488,20 +1485,6 @@ static int inode_has_perm(const struct cred *cred,
return avc_has_perm_flags(sid, isec->sid, isec->sclass, perms, adp, flags);
}
-static int inode_has_perm_noadp(const struct cred *cred,
- struct inode *inode,
- u32 perms,
- unsigned flags)
-{
- struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
-
- COMMON_AUDIT_DATA_INIT(&ad, INODE);
- ad.u.inode = inode;
- ad.selinux_audit_data = &sad;
- return inode_has_perm(cred, inode, perms, &ad, flags);
-}
-
/* Same as inode_has_perm, but pass explicit audit data containing
the dentry to help the auditing code to more easily generate the
pathname if needed. */
@@ -1511,11 +1494,9 @@ static inline int dentry_has_perm(const struct cred *cred,
{
struct inode *inode = dentry->d_inode;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
- COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
+ ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
- ad.selinux_audit_data = &sad;
return inode_has_perm(cred, inode, av, &ad, 0);
}
@@ -1528,11 +1509,9 @@ static inline int path_has_perm(const struct cred *cred,
{
struct inode *inode = path->dentry->d_inode;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
- COMMON_AUDIT_DATA_INIT(&ad, PATH);
+ ad.type = LSM_AUDIT_DATA_PATH;
ad.u.path = *path;
- ad.selinux_audit_data = &sad;
return inode_has_perm(cred, inode, av, &ad, 0);
}
@@ -1551,13 +1530,11 @@ static int file_has_perm(const struct cred *cred,
struct file_security_struct *fsec = file->f_security;
struct inode *inode = file->f_path.dentry->d_inode;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
u32 sid = cred_sid(cred);
int rc;
- COMMON_AUDIT_DATA_INIT(&ad, PATH);
+ ad.type = LSM_AUDIT_DATA_PATH;
ad.u.path = file->f_path;
- ad.selinux_audit_data = &sad;
if (sid != fsec->sid) {
rc = avc_has_perm(sid, fsec->sid,
@@ -1587,7 +1564,6 @@ static int may_create(struct inode *dir,
struct superblock_security_struct *sbsec;
u32 sid, newsid;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
int rc;
dsec = dir->i_security;
@@ -1596,9 +1572,8 @@ static int may_create(struct inode *dir,
sid = tsec->sid;
newsid = tsec->create_sid;
- COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
+ ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
- ad.selinux_audit_data = &sad;
rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR,
DIR__ADD_NAME | DIR__SEARCH,
@@ -1643,7 +1618,6 @@ static int may_link(struct inode *dir,
{
struct inode_security_struct *dsec, *isec;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
u32 sid = current_sid();
u32 av;
int rc;
@@ -1651,9 +1625,8 @@ static int may_link(struct inode *dir,
dsec = dir->i_security;
isec = dentry->d_inode->i_security;
- COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
+ ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
- ad.selinux_audit_data = &sad;
av = DIR__SEARCH;
av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
@@ -1688,7 +1661,6 @@ static inline int may_rename(struct inode *old_dir,
{
struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
u32 sid = current_sid();
u32 av;
int old_is_dir, new_is_dir;
@@ -1699,8 +1671,7 @@ static inline int may_rename(struct inode *old_dir,
old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
new_dsec = new_dir->i_security;
- COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = old_dentry;
rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR,
@@ -1986,7 +1957,6 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
struct task_security_struct *new_tsec;
struct inode_security_struct *isec;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
struct inode *inode = bprm->file->f_path.dentry->d_inode;
int rc;
@@ -2016,6 +1986,13 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
new_tsec->sid = old_tsec->exec_sid;
/* Reset exec SID on execve. */
new_tsec->exec_sid = 0;
+
+ /*
+ * Minimize confusion: if no_new_privs and a transition is
+ * explicitly requested, then fail the exec.
+ */
+ if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)
+ return -EPERM;
} else {
/* Check for a default transition on this program. */
rc = security_transition_sid(old_tsec->sid, isec->sid,
@@ -2025,11 +2002,11 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
return rc;
}
- COMMON_AUDIT_DATA_INIT(&ad, PATH);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_PATH;
ad.u.path = bprm->file->f_path;
- if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
+ if ((bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) ||
+ (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS))
new_tsec->sid = old_tsec->sid;
if (new_tsec->sid == old_tsec->sid) {
@@ -2115,8 +2092,6 @@ static int selinux_bprm_secureexec(struct linux_binprm *bprm)
static inline void flush_unauthorized_files(const struct cred *cred,
struct files_struct *files)
{
- struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
struct file *file, *devnull = NULL;
struct tty_struct *tty;
struct fdtable *fdt;
@@ -2128,21 +2103,17 @@ static inline void flush_unauthorized_files(const struct cred *cred,
spin_lock(&tty_files_lock);
if (!list_empty(&tty->tty_files)) {
struct tty_file_private *file_priv;
- struct inode *inode;
/* Revalidate access to controlling tty.
- Use inode_has_perm on the tty inode directly rather
+ Use path_has_perm on the tty path directly rather
than using file_has_perm, as this particular open
file may belong to another process and we are only
interested in the inode-based check here. */
file_priv = list_first_entry(&tty->tty_files,
struct tty_file_private, list);
file = file_priv->file;
- inode = file->f_path.dentry->d_inode;
- if (inode_has_perm_noadp(cred, inode,
- FILE__READ | FILE__WRITE, 0)) {
+ if (path_has_perm(cred, &file->f_path, FILE__READ | FILE__WRITE))
drop_tty = 1;
- }
}
spin_unlock(&tty_files_lock);
tty_kref_put(tty);
@@ -2152,10 +2123,6 @@ static inline void flush_unauthorized_files(const struct cred *cred,
no_tty();
/* Revalidate access to inherited open files. */
-
- COMMON_AUDIT_DATA_INIT(&ad, INODE);
- ad.selinux_audit_data = &sad;
-
spin_lock(&files->file_lock);
for (;;) {
unsigned long set, i;
@@ -2492,7 +2459,6 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
{
const struct cred *cred = current_cred();
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
int rc;
rc = superblock_doinit(sb, data);
@@ -2503,8 +2469,7 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
if (flags & MS_KERNMOUNT)
return 0;
- COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = sb->s_root;
return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad);
}
@@ -2513,10 +2478,8 @@ static int selinux_sb_statfs(struct dentry *dentry)
{
const struct cred *cred = current_cred();
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
- COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry->d_sb->s_root;
return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
}
@@ -2676,14 +2639,35 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na
return dentry_has_perm(cred, dentry, FILE__READ);
}
+static noinline int audit_inode_permission(struct inode *inode,
+ u32 perms, u32 audited, u32 denied,
+ unsigned flags)
+{
+ struct common_audit_data ad;
+ struct inode_security_struct *isec = inode->i_security;
+ int rc;
+
+ ad.type = LSM_AUDIT_DATA_INODE;
+ ad.u.inode = inode;
+
+ rc = slow_avc_audit(current_sid(), isec->sid, isec->sclass, perms,
+ audited, denied, &ad, flags);
+ if (rc)
+ return rc;
+ return 0;
+}
+
static int selinux_inode_permission(struct inode *inode, int mask)
{
const struct cred *cred = current_cred();
- struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
u32 perms;
bool from_access;
unsigned flags = mask & MAY_NOT_BLOCK;
+ struct inode_security_struct *isec;
+ u32 sid;
+ struct av_decision avd;
+ int rc, rc2;
+ u32 audited, denied;
from_access = mask & MAY_ACCESS;
mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND);
@@ -2692,22 +2676,34 @@ static int selinux_inode_permission(struct inode *inode, int mask)
if (!mask)
return 0;
- COMMON_AUDIT_DATA_INIT(&ad, INODE);
- ad.selinux_audit_data = &sad;
- ad.u.inode = inode;
+ validate_creds(cred);
- if (from_access)
- ad.selinux_audit_data->auditdeny |= FILE__AUDIT_ACCESS;
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
perms = file_mask_to_av(inode->i_mode, mask);
- return inode_has_perm(cred, inode, perms, &ad, flags);
+ sid = cred_sid(cred);
+ isec = inode->i_security;
+
+ rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd);
+ audited = avc_audit_required(perms, &avd, rc,
+ from_access ? FILE__AUDIT_ACCESS : 0,
+ &denied);
+ if (likely(!audited))
+ return rc;
+
+ rc2 = audit_inode_permission(inode, perms, audited, denied, flags);
+ if (rc2)
+ return rc2;
+ return rc;
}
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
{
const struct cred *cred = current_cred();
unsigned int ia_valid = iattr->ia_valid;
+ __u32 av = FILE__WRITE;
/* ATTR_FORCE is just used for ATTR_KILL_S[UG]ID. */
if (ia_valid & ATTR_FORCE) {
@@ -2721,7 +2717,10 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET))
return dentry_has_perm(cred, dentry, FILE__SETATTR);
- return dentry_has_perm(cred, dentry, FILE__WRITE);
+ if (ia_valid & ATTR_SIZE)
+ av |= FILE__OPEN;
+
+ return dentry_has_perm(cred, dentry, av);
}
static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
@@ -2763,7 +2762,6 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
struct inode_security_struct *isec = inode->i_security;
struct superblock_security_struct *sbsec;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
u32 newsid, sid = current_sid();
int rc = 0;
@@ -2777,8 +2775,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
if (!inode_owner_or_capable(inode))
return -EPERM;
- COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
rc = avc_has_perm(sid, isec->sid, isec->sclass,
@@ -2788,8 +2785,25 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
rc = security_context_to_sid(value, size, &newsid);
if (rc == -EINVAL) {
- if (!capable(CAP_MAC_ADMIN))
+ if (!capable(CAP_MAC_ADMIN)) {
+ struct audit_buffer *ab;
+ size_t audit_size;
+ const char *str;
+
+ /* We strip a nul only if it is at the end, otherwise the
+ * context contains a nul and we should audit that */
+ str = value;
+ if (str[size - 1] == '\0')
+ audit_size = size - 1;
+ else
+ audit_size = size;
+ ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR);
+ audit_log_format(ab, "op=setxattr invalid_context=");
+ audit_log_n_untrustedstring(ab, value, audit_size);
+ audit_log_end(ab);
+
return rc;
+ }
rc = security_context_to_sid_force(value, size, &newsid);
}
if (rc)
@@ -2969,7 +2983,7 @@ static int selinux_file_permission(struct file *file, int mask)
if (sid == fsec->sid && fsec->isid == isec->sid &&
fsec->pseqno == avc_policy_seqno())
- /* No change since dentry_open check. */
+ /* No change since file_open check. */
return 0;
return selinux_revalidate_file_permission(file, mask);
@@ -3228,15 +3242,13 @@ static int selinux_file_receive(struct file *file)
return file_has_perm(cred, file, file_to_av(file));
}
-static int selinux_dentry_open(struct file *file, const struct cred *cred)
+static int selinux_file_open(struct file *file, const struct cred *cred)
{
struct file_security_struct *fsec;
- struct inode *inode;
struct inode_security_struct *isec;
- inode = file->f_path.dentry->d_inode;
fsec = file->f_security;
- isec = inode->i_security;
+ isec = file->f_path.dentry->d_inode->i_security;
/*
* Save inode label and policy sequence number
* at open-time so that selinux_file_permission
@@ -3254,7 +3266,7 @@ static int selinux_dentry_open(struct file *file, const struct cred *cred)
* new inode label or new policy.
* This check is not redundant - do not remove.
*/
- return inode_has_perm_noadp(cred, inode, open_file_to_av(file), 0);
+ return path_has_perm(cred, &file->f_path, open_file_to_av(file));
}
/* task security operations */
@@ -3373,12 +3385,10 @@ static int selinux_kernel_module_request(char *kmod_name)
{
u32 sid;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
sid = task_sid(current);
- COMMON_AUDIT_DATA_INIT(&ad, KMOD);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_KMOD;
ad.u.kmod_name = kmod_name;
return avc_has_perm(sid, SECINITSID_KERNEL, SECCLASS_SYSTEM,
@@ -3751,15 +3761,13 @@ static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms)
{
struct sk_security_struct *sksec = sk->sk_security;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
struct lsm_network_audit net = {0,};
u32 tsid = task_sid(task);
if (sksec->sid == SECINITSID_KERNEL)
return 0;
- COMMON_AUDIT_DATA_INIT(&ad, NET);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->sk = sk;
@@ -3839,7 +3847,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
char *addrp;
struct sk_security_struct *sksec = sk->sk_security;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
struct lsm_network_audit net = {0,};
struct sockaddr_in *addr4 = NULL;
struct sockaddr_in6 *addr6 = NULL;
@@ -3866,8 +3873,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
snum, &sid);
if (err)
goto out;
- COMMON_AUDIT_DATA_INIT(&ad, NET);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->sport = htons(snum);
ad.u.net->family = family;
@@ -3901,8 +3907,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
if (err)
goto out;
- COMMON_AUDIT_DATA_INIT(&ad, NET);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->sport = htons(snum);
ad.u.net->family = family;
@@ -3937,7 +3942,6 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
if (sksec->sclass == SECCLASS_TCP_SOCKET ||
sksec->sclass == SECCLASS_DCCP_SOCKET) {
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
struct lsm_network_audit net = {0,};
struct sockaddr_in *addr4 = NULL;
struct sockaddr_in6 *addr6 = NULL;
@@ -3963,8 +3967,7 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ?
TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT;
- COMMON_AUDIT_DATA_INIT(&ad, NET);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->dport = htons(snum);
ad.u.net->family = sk->sk_family;
@@ -4056,12 +4059,10 @@ static int selinux_socket_unix_stream_connect(struct sock *sock,
struct sk_security_struct *sksec_other = other->sk_security;
struct sk_security_struct *sksec_new = newsk->sk_security;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
struct lsm_network_audit net = {0,};
int err;
- COMMON_AUDIT_DATA_INIT(&ad, NET);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->sk = other;
@@ -4090,11 +4091,9 @@ static int selinux_socket_unix_may_send(struct socket *sock,
struct sk_security_struct *ssec = sock->sk->sk_security;
struct sk_security_struct *osec = other->sk->sk_security;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
struct lsm_network_audit net = {0,};
- COMMON_AUDIT_DATA_INIT(&ad, NET);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->sk = other->sk;
@@ -4132,12 +4131,10 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
struct sk_security_struct *sksec = sk->sk_security;
u32 sk_sid = sksec->sid;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
struct lsm_network_audit net = {0,};
char *addrp;
- COMMON_AUDIT_DATA_INIT(&ad, NET);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->netif = skb->skb_iif;
ad.u.net->family = family;
@@ -4167,7 +4164,6 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
u16 family = sk->sk_family;
u32 sk_sid = sksec->sid;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
struct lsm_network_audit net = {0,};
char *addrp;
u8 secmark_active;
@@ -4192,8 +4188,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (!secmark_active && !peerlbl_active)
return 0;
- COMMON_AUDIT_DATA_INIT(&ad, NET);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->netif = skb->skb_iif;
ad.u.net->family = family;
@@ -4531,7 +4526,6 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
char *addrp;
u32 peer_sid;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
struct lsm_network_audit net = {0,};
u8 secmark_active;
u8 netlbl_active;
@@ -4549,8 +4543,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
return NF_DROP;
- COMMON_AUDIT_DATA_INIT(&ad, NET);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->netif = ifindex;
ad.u.net->family = family;
@@ -4640,7 +4633,6 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
struct sock *sk = skb->sk;
struct sk_security_struct *sksec;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
struct lsm_network_audit net = {0,};
char *addrp;
u8 proto;
@@ -4649,8 +4641,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
return NF_ACCEPT;
sksec = sk->sk_security;
- COMMON_AUDIT_DATA_INIT(&ad, NET);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->netif = ifindex;
ad.u.net->family = family;
@@ -4675,7 +4666,6 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
u32 peer_sid;
struct sock *sk;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
struct lsm_network_audit net = {0,};
char *addrp;
u8 secmark_active;
@@ -4722,8 +4712,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
secmark_perm = PACKET__SEND;
}
- COMMON_AUDIT_DATA_INIT(&ad, NET);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->netif = ifindex;
ad.u.net->family = family;
@@ -4841,13 +4830,11 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
{
struct ipc_security_struct *isec;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
u32 sid = current_sid();
isec = ipc_perms->security;
- COMMON_AUDIT_DATA_INIT(&ad, IPC);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = ipc_perms->key;
return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
@@ -4868,7 +4855,6 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
{
struct ipc_security_struct *isec;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
u32 sid = current_sid();
int rc;
@@ -4878,8 +4864,7 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
isec = msq->q_perm.security;
- COMMON_AUDIT_DATA_INIT(&ad, IPC);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = msq->q_perm.key;
rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
@@ -4900,13 +4885,11 @@ static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
{
struct ipc_security_struct *isec;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
u32 sid = current_sid();
isec = msq->q_perm.security;
- COMMON_AUDIT_DATA_INIT(&ad, IPC);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = msq->q_perm.key;
return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
@@ -4946,7 +4929,6 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
struct ipc_security_struct *isec;
struct msg_security_struct *msec;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
u32 sid = current_sid();
int rc;
@@ -4967,8 +4949,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
return rc;
}
- COMMON_AUDIT_DATA_INIT(&ad, IPC);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = msq->q_perm.key;
/* Can this process write to the queue? */
@@ -4993,15 +4974,13 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
struct ipc_security_struct *isec;
struct msg_security_struct *msec;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
u32 sid = task_sid(target);
int rc;
isec = msq->q_perm.security;
msec = msg->security;
- COMMON_AUDIT_DATA_INIT(&ad, IPC);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = msq->q_perm.key;
rc = avc_has_perm(sid, isec->sid,
@@ -5017,7 +4996,6 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp)
{
struct ipc_security_struct *isec;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
u32 sid = current_sid();
int rc;
@@ -5027,8 +5005,7 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp)
isec = shp->shm_perm.security;
- COMMON_AUDIT_DATA_INIT(&ad, IPC);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = shp->shm_perm.key;
rc = avc_has_perm(sid, isec->sid, SECCLASS_SHM,
@@ -5049,13 +5026,11 @@ static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
{
struct ipc_security_struct *isec;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
u32 sid = current_sid();
isec = shp->shm_perm.security;
- COMMON_AUDIT_DATA_INIT(&ad, IPC);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = shp->shm_perm.key;
return avc_has_perm(sid, isec->sid, SECCLASS_SHM,
@@ -5113,7 +5088,6 @@ static int selinux_sem_alloc_security(struct sem_array *sma)
{
struct ipc_security_struct *isec;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
u32 sid = current_sid();
int rc;
@@ -5123,8 +5097,7 @@ static int selinux_sem_alloc_security(struct sem_array *sma)
isec = sma->sem_perm.security;
- COMMON_AUDIT_DATA_INIT(&ad, IPC);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = sma->sem_perm.key;
rc = avc_has_perm(sid, isec->sid, SECCLASS_SEM,
@@ -5145,13 +5118,11 @@ static int selinux_sem_associate(struct sem_array *sma, int semflg)
{
struct ipc_security_struct *isec;
struct common_audit_data ad;
- struct selinux_audit_data sad = {0,};
u32 sid = current_sid();
isec = sma->sem_perm.security;
- COMMON_AUDIT_DATA_INIT(&ad, IPC);
- ad.selinux_audit_data = &sad;
+ ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = sma->sem_perm.key;
return avc_has_perm(sid, isec->sid, SECCLASS_SEM,
@@ -5331,8 +5302,23 @@ static int selinux_setprocattr(struct task_struct *p,
}
error = security_context_to_sid(value, size, &sid);
if (error == -EINVAL && !strcmp(name, "fscreate")) {
- if (!capable(CAP_MAC_ADMIN))
+ if (!capable(CAP_MAC_ADMIN)) {
+ struct audit_buffer *ab;
+ size_t audit_size;
+
+ /* We strip a nul only if it is at the end, otherwise the
+ * context contains a nul and we should audit that */
+ if (str[size - 1] == '\0')
+ audit_size = size - 1;
+ else
+ audit_size = size;
+ ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR);
+ audit_log_format(ab, "op=fscreate invalid_context=");
+ audit_log_n_untrustedstring(ab, value, audit_size);
+ audit_log_end(ab);
+
return error;
+ }
error = security_context_to_sid_force(value, size,
&sid);
}
@@ -5592,7 +5578,7 @@ static struct security_operations selinux_ops = {
.file_send_sigiotask = selinux_file_send_sigiotask,
.file_receive = selinux_file_receive,
- .dentry_open = selinux_dentry_open,
+ .file_open = selinux_file_open,
.task_create = selinux_task_create,
.cred_alloc_blank = selinux_cred_alloc_blank,
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
index 1931370233d7..92d0ab561db8 100644
--- a/security/selinux/include/avc.h
+++ b/security/selinux/include/avc.h
@@ -49,7 +49,7 @@ struct avc_cache_stats {
/*
* We only need this data after we have decided to send an audit message.
*/
-struct selinux_late_audit_data {
+struct selinux_audit_data {
u32 ssid;
u32 tsid;
u16 tclass;
@@ -60,28 +60,86 @@ struct selinux_late_audit_data {
};
/*
- * We collect this at the beginning or during an selinux security operation
- */
-struct selinux_audit_data {
- /*
- * auditdeny is a bit tricky and unintuitive. See the
- * comments in avc.c for it's meaning and usage.
- */
- u32 auditdeny;
- struct selinux_late_audit_data *slad;
-};
-
-/*
* AVC operations
*/
void __init avc_init(void);
-int avc_audit(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- struct av_decision *avd,
- int result,
- struct common_audit_data *a, unsigned flags);
+static inline u32 avc_audit_required(u32 requested,
+ struct av_decision *avd,
+ int result,
+ u32 auditdeny,
+ u32 *deniedp)
+{
+ u32 denied, audited;
+ denied = requested & ~avd->allowed;
+ if (unlikely(denied)) {
+ audited = denied & avd->auditdeny;
+ /*
+ * auditdeny is TRICKY! Setting a bit in
+ * this field means that ANY denials should NOT be audited if
+ * the policy contains an explicit dontaudit rule for that
+ * permission. Take notice that this is unrelated to the
+ * actual permissions that were denied. As an example lets
+ * assume:
+ *
+ * denied == READ
+ * avd.auditdeny & ACCESS == 0 (not set means explicit rule)
+ * auditdeny & ACCESS == 1
+ *
+ * We will NOT audit the denial even though the denied
+ * permission was READ and the auditdeny checks were for
+ * ACCESS
+ */
+ if (auditdeny && !(auditdeny & avd->auditdeny))
+ audited = 0;
+ } else if (result)
+ audited = denied = requested;
+ else
+ audited = requested & avd->auditallow;
+ *deniedp = denied;
+ return audited;
+}
+
+int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
+ u32 requested, u32 audited, u32 denied,
+ struct common_audit_data *a,
+ unsigned flags);
+
+/**
+ * avc_audit - Audit the granting or denial of permissions.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions
+ * @avd: access vector decisions
+ * @result: result from avc_has_perm_noaudit
+ * @a: auxiliary audit data
+ * @flags: VFS walk flags
+ *
+ * Audit the granting or denial of permissions in accordance
+ * with the policy. This function is typically called by
+ * avc_has_perm() after a permission check, but can also be
+ * called directly by callers who use avc_has_perm_noaudit()
+ * in order to separate the permission check from the auditing.
+ * For example, this separation is useful when the permission check must
+ * be performed under a lock, to allow the lock to be released
+ * before calling the auditing code.
+ */
+static inline int avc_audit(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct av_decision *avd,
+ int result,
+ struct common_audit_data *a, unsigned flags)
+{
+ u32 audited, denied;
+ audited = avc_audit_required(requested, avd, result, 0, &denied);
+ if (likely(!audited))
+ return 0;
+ return slow_avc_audit(ssid, tsid, tclass,
+ requested, audited, denied,
+ a, flags);
+}
#define AVC_STRICT 1 /* Ignore permissive mode. */
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
@@ -112,11 +170,7 @@ u32 avc_policy_seqno(void);
#define AVC_CALLBACK_AUDITDENY_ENABLE 64
#define AVC_CALLBACK_AUDITDENY_DISABLE 128
-int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
- u16 tclass, u32 perms,
- u32 *out_retained),
- u32 events, u32 ssid, u32 tsid,
- u16 tclass, u32 perms);
+int avc_add_callback(int (*callback)(u32 event), u32 events);
/* Exported to selinuxfs */
int avc_get_hash_stats(char *page);
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index d871e8ad2103..dde2005407aa 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -31,13 +31,15 @@
#define POLICYDB_VERSION_BOUNDARY 24
#define POLICYDB_VERSION_FILENAME_TRANS 25
#define POLICYDB_VERSION_ROLETRANS 26
+#define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27
+#define POLICYDB_VERSION_DEFAULT_TYPE 28
/* Range of policy versions we understand*/
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
#define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
#else
-#define POLICYDB_VERSION_MAX POLICYDB_VERSION_ROLETRANS
+#define POLICYDB_VERSION_MAX POLICYDB_VERSION_DEFAULT_TYPE
#endif
/* Mask for just the mount related flags */
diff --git a/security/selinux/netif.c b/security/selinux/netif.c
index 326f22cbe405..47a49d1a6f6a 100644
--- a/security/selinux/netif.c
+++ b/security/selinux/netif.c
@@ -252,8 +252,7 @@ static void sel_netif_flush(void)
spin_unlock_bh(&sel_netif_lock);
}
-static int sel_netif_avc_callback(u32 event, u32 ssid, u32 tsid,
- u16 class, u32 perms, u32 *retained)
+static int sel_netif_avc_callback(u32 event)
{
if (event == AVC_CALLBACK_RESET) {
sel_netif_flush();
@@ -292,8 +291,7 @@ static __init int sel_netif_init(void)
register_netdevice_notifier(&sel_netif_netdev_notifier);
- err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET,
- SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+ err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET);
if (err)
panic("avc_add_callback() failed, error %d\n", err);
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
index 86365857c088..28f911cdd7c7 100644
--- a/security/selinux/netnode.c
+++ b/security/selinux/netnode.c
@@ -297,8 +297,7 @@ static void sel_netnode_flush(void)
spin_unlock_bh(&sel_netnode_lock);
}
-static int sel_netnode_avc_callback(u32 event, u32 ssid, u32 tsid,
- u16 class, u32 perms, u32 *retained)
+static int sel_netnode_avc_callback(u32 event)
{
if (event == AVC_CALLBACK_RESET) {
sel_netnode_flush();
@@ -320,8 +319,7 @@ static __init int sel_netnode_init(void)
sel_netnode_hash[iter].size = 0;
}
- ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET,
- SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+ ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET);
if (ret != 0)
panic("avc_add_callback() failed, error %d\n", ret);
diff --git a/security/selinux/netport.c b/security/selinux/netport.c
index 7b9eb1faf68b..d35379781c2c 100644
--- a/security/selinux/netport.c
+++ b/security/selinux/netport.c
@@ -234,8 +234,7 @@ static void sel_netport_flush(void)
spin_unlock_bh(&sel_netport_lock);
}
-static int sel_netport_avc_callback(u32 event, u32 ssid, u32 tsid,
- u16 class, u32 perms, u32 *retained)
+static int sel_netport_avc_callback(u32 event)
{
if (event == AVC_CALLBACK_RESET) {
sel_netport_flush();
@@ -257,8 +256,7 @@ static __init int sel_netport_init(void)
sel_netport_hash[iter].size = 0;
}
- ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET,
- SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+ ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET);
if (ret != 0)
panic("avc_add_callback() failed, error %d\n", ret);
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index d7018bfa1f00..4e93f9ef970b 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -496,6 +496,7 @@ static const struct file_operations sel_policy_ops = {
.read = sel_read_policy,
.mmap = sel_mmap_policy,
.release = sel_release_policy,
+ .llseek = generic_file_llseek,
};
static ssize_t sel_write_load(struct file *file, const char __user *buf,
@@ -1232,6 +1233,7 @@ static int sel_make_bools(void)
kfree(bool_pending_names[i]);
kfree(bool_pending_names);
kfree(bool_pending_values);
+ bool_num = 0;
bool_pending_names = NULL;
bool_pending_values = NULL;
@@ -1532,11 +1534,6 @@ static int sel_make_initcon_files(struct dentry *dir)
return 0;
}
-static inline unsigned int sel_div(unsigned long a, unsigned long b)
-{
- return a / b - (a % b < 0);
-}
-
static inline unsigned long sel_class_to_ino(u16 class)
{
return (class * (SEL_VEC_MAX + 1)) | SEL_CLASS_INO_OFFSET;
@@ -1544,7 +1541,7 @@ static inline unsigned long sel_class_to_ino(u16 class)
static inline u16 sel_ino_to_class(unsigned long ino)
{
- return sel_div(ino & SEL_INO_MASK, SEL_VEC_MAX + 1);
+ return (ino & SEL_INO_MASK) / (SEL_VEC_MAX + 1);
}
static inline unsigned long sel_perm_to_ino(u16 class, u32 perm)
@@ -1831,7 +1828,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
[SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO},
[SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO},
[SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO},
- [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUSR},
+ [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO},
/* last one */ {""}
};
ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h
index 45e8fb0515f8..212e3479a0d9 100644
--- a/security/selinux/ss/context.h
+++ b/security/selinux/ss/context.h
@@ -74,6 +74,26 @@ out:
return rc;
}
+/*
+ * Sets both levels in the MLS range of 'dst' to the high level of 'src'.
+ */
+static inline int mls_context_cpy_high(struct context *dst, struct context *src)
+{
+ int rc;
+
+ dst->range.level[0].sens = src->range.level[1].sens;
+ rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[1].cat);
+ if (rc)
+ goto out;
+
+ dst->range.level[1].sens = src->range.level[1].sens;
+ rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[1].cat);
+ if (rc)
+ ebitmap_destroy(&dst->range.level[0].cat);
+out:
+ return rc;
+}
+
static inline int mls_context_cmp(struct context *c1, struct context *c2)
{
return ((c1->range.level[0].sens == c2->range.level[0].sens) &&
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index fbf9c5816c71..40de8d3f208e 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -517,6 +517,8 @@ int mls_compute_sid(struct context *scontext,
{
struct range_trans rtr;
struct mls_range *r;
+ struct class_datum *cladatum;
+ int default_range = 0;
if (!policydb.mls_enabled)
return 0;
@@ -530,6 +532,28 @@ int mls_compute_sid(struct context *scontext,
r = hashtab_search(policydb.range_tr, &rtr);
if (r)
return mls_range_set(newcontext, r);
+
+ if (tclass && tclass <= policydb.p_classes.nprim) {
+ cladatum = policydb.class_val_to_struct[tclass - 1];
+ if (cladatum)
+ default_range = cladatum->default_range;
+ }
+
+ switch (default_range) {
+ case DEFAULT_SOURCE_LOW:
+ return mls_context_cpy_low(newcontext, scontext);
+ case DEFAULT_SOURCE_HIGH:
+ return mls_context_cpy_high(newcontext, scontext);
+ case DEFAULT_SOURCE_LOW_HIGH:
+ return mls_context_cpy(newcontext, scontext);
+ case DEFAULT_TARGET_LOW:
+ return mls_context_cpy_low(newcontext, tcontext);
+ case DEFAULT_TARGET_HIGH:
+ return mls_context_cpy_high(newcontext, tcontext);
+ case DEFAULT_TARGET_LOW_HIGH:
+ return mls_context_cpy(newcontext, tcontext);
+ }
+
/* Fallthrough */
case AVTAB_CHANGE:
if ((tclass == policydb.process_class) || (sock == true))
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index a7f61d52f05c..9cd9b7c661ec 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -133,6 +133,16 @@ static struct policydb_compat_info policydb_compat[] = {
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
},
+ {
+ .version = POLICYDB_VERSION_NEW_OBJECT_DEFAULTS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_DEFAULT_TYPE,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
};
static struct policydb_compat_info *policydb_lookup_compat(int version)
@@ -1306,6 +1316,23 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)
goto bad;
}
+ if (p->policyvers >= POLICYDB_VERSION_NEW_OBJECT_DEFAULTS) {
+ rc = next_entry(buf, fp, sizeof(u32) * 3);
+ if (rc)
+ goto bad;
+
+ cladatum->default_user = le32_to_cpu(buf[0]);
+ cladatum->default_role = le32_to_cpu(buf[1]);
+ cladatum->default_range = le32_to_cpu(buf[2]);
+ }
+
+ if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) {
+ rc = next_entry(buf, fp, sizeof(u32) * 1);
+ if (rc)
+ goto bad;
+ cladatum->default_type = le32_to_cpu(buf[0]);
+ }
+
rc = hashtab_insert(h, key, cladatum);
if (rc)
goto bad;
@@ -2832,6 +2859,23 @@ static int class_write(void *vkey, void *datum, void *ptr)
if (rc)
return rc;
+ if (p->policyvers >= POLICYDB_VERSION_NEW_OBJECT_DEFAULTS) {
+ buf[0] = cpu_to_le32(cladatum->default_user);
+ buf[1] = cpu_to_le32(cladatum->default_role);
+ buf[2] = cpu_to_le32(cladatum->default_range);
+
+ rc = put_entry(buf, sizeof(uint32_t), 3, fp);
+ if (rc)
+ return rc;
+ }
+
+ if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) {
+ buf[0] = cpu_to_le32(cladatum->default_type);
+ rc = put_entry(buf, sizeof(uint32_t), 1, fp);
+ if (rc)
+ return rc;
+ }
+
return 0;
}
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index b846c0387180..da637471d4ce 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -60,6 +60,20 @@ struct class_datum {
struct symtab permissions; /* class-specific permission symbol table */
struct constraint_node *constraints; /* constraints on class permissions */
struct constraint_node *validatetrans; /* special transition rules */
+/* Options how a new object user, role, and type should be decided */
+#define DEFAULT_SOURCE 1
+#define DEFAULT_TARGET 2
+ char default_user;
+ char default_role;
+ char default_type;
+/* Options how a new object range should be decided */
+#define DEFAULT_SOURCE_LOW 1
+#define DEFAULT_SOURCE_HIGH 2
+#define DEFAULT_SOURCE_LOW_HIGH 3
+#define DEFAULT_TARGET_LOW 4
+#define DEFAULT_TARGET_HIGH 5
+#define DEFAULT_TARGET_LOW_HIGH 6
+ char default_range;
};
/* Role attributes */
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 185f849a26f6..4321b8fc8863 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -1018,9 +1018,11 @@ static int context_struct_to_string(struct context *context, char **scontext, u3
if (context->len) {
*scontext_len = context->len;
- *scontext = kstrdup(context->str, GFP_ATOMIC);
- if (!(*scontext))
- return -ENOMEM;
+ if (scontext) {
+ *scontext = kstrdup(context->str, GFP_ATOMIC);
+ if (!(*scontext))
+ return -ENOMEM;
+ }
return 0;
}
@@ -1389,6 +1391,7 @@ static int security_compute_sid(u32 ssid,
u32 *out_sid,
bool kern)
{
+ struct class_datum *cladatum = NULL;
struct context *scontext = NULL, *tcontext = NULL, newcontext;
struct role_trans *roletr = NULL;
struct avtab_key avkey;
@@ -1437,12 +1440,20 @@ static int security_compute_sid(u32 ssid,
goto out_unlock;
}
+ if (tclass && tclass <= policydb.p_classes.nprim)
+ cladatum = policydb.class_val_to_struct[tclass - 1];
+
/* Set the user identity. */
switch (specified) {
case AVTAB_TRANSITION:
case AVTAB_CHANGE:
- /* Use the process user identity. */
- newcontext.user = scontext->user;
+ if (cladatum && cladatum->default_user == DEFAULT_TARGET) {
+ newcontext.user = tcontext->user;
+ } else {
+ /* notice this gets both DEFAULT_SOURCE and unset */
+ /* Use the process user identity. */
+ newcontext.user = scontext->user;
+ }
break;
case AVTAB_MEMBER:
/* Use the related object owner. */
@@ -1450,16 +1461,31 @@ static int security_compute_sid(u32 ssid,
break;
}
- /* Set the role and type to default values. */
- if ((tclass == policydb.process_class) || (sock == true)) {
- /* Use the current role and type of process. */
+ /* Set the role to default values. */
+ if (cladatum && cladatum->default_role == DEFAULT_SOURCE) {
newcontext.role = scontext->role;
- newcontext.type = scontext->type;
+ } else if (cladatum && cladatum->default_role == DEFAULT_TARGET) {
+ newcontext.role = tcontext->role;
} else {
- /* Use the well-defined object role. */
- newcontext.role = OBJECT_R_VAL;
- /* Use the type of the related object. */
+ if ((tclass == policydb.process_class) || (sock == true))
+ newcontext.role = scontext->role;
+ else
+ newcontext.role = OBJECT_R_VAL;
+ }
+
+ /* Set the type to default values. */
+ if (cladatum && cladatum->default_type == DEFAULT_SOURCE) {
+ newcontext.type = scontext->type;
+ } else if (cladatum && cladatum->default_type == DEFAULT_TARGET) {
newcontext.type = tcontext->type;
+ } else {
+ if ((tclass == policydb.process_class) || (sock == true)) {
+ /* Use the type of process. */
+ newcontext.type = scontext->type;
+ } else {
+ /* Use the type of the related object. */
+ newcontext.type = tcontext->type;
+ }
}
/* Look for a type transition/member/change rule. */
@@ -3018,8 +3044,7 @@ out:
static int (*aurule_callback)(void) = audit_update_lsm_rules;
-static int aurule_avc_callback(u32 event, u32 ssid, u32 tsid,
- u16 class, u32 perms, u32 *retained)
+static int aurule_avc_callback(u32 event)
{
int err = 0;
@@ -3032,8 +3057,7 @@ static int __init aurule_init(void)
{
int err;
- err = avc_add_callback(aurule_avc_callback, AVC_CALLBACK_RESET,
- SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+ err = avc_add_callback(aurule_avc_callback, AVC_CALLBACK_RESET);
if (err)
panic("avc_add_callback() failed, error %d\n", err);