diff options
Diffstat (limited to 'security/selinux')
-rw-r--r-- | security/selinux/hooks.c | 244 | ||||
-rw-r--r-- | security/selinux/include/audit.h | 4 | ||||
-rw-r--r-- | security/selinux/include/avc.h | 15 | ||||
-rw-r--r-- | security/selinux/include/objsec.h | 1 | ||||
-rw-r--r-- | security/selinux/include/security.h | 7 | ||||
-rw-r--r-- | security/selinux/netnode.c | 1 | ||||
-rw-r--r-- | security/selinux/netport.c | 3 | ||||
-rw-r--r-- | security/selinux/selinuxfs.c | 15 | ||||
-rw-r--r-- | security/selinux/ss/avtab.c | 2 | ||||
-rw-r--r-- | security/selinux/ss/context.h | 27 | ||||
-rw-r--r-- | security/selinux/ss/mls.c | 19 | ||||
-rw-r--r-- | security/selinux/ss/mls.h | 3 | ||||
-rw-r--r-- | security/selinux/ss/policydb.c | 15 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 450 | ||||
-rw-r--r-- | security/selinux/ss/sidtab.c | 76 | ||||
-rw-r--r-- | security/selinux/ss/sidtab.h | 7 |
16 files changed, 530 insertions, 359 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 1c864c0efe2..91200feb3f9 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -9,7 +9,8 @@ * James Morris <jmorris@redhat.com> * * Copyright (C) 2001,2002 Networks Associates Technology, Inc. - * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> + * Copyright (C) 2003-2008 Red Hat, Inc., James Morris <jmorris@redhat.com> + * Eric Paris <eparis@redhat.com> * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * <dgoeddel@trustedcs.com> * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P. @@ -42,9 +43,7 @@ #include <linux/fdtable.h> #include <linux/namei.h> #include <linux/mount.h> -#include <linux/ext2_fs.h> #include <linux/proc_fs.h> -#include <linux/kd.h> #include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv6.h> #include <linux/tty.h> @@ -53,7 +52,7 @@ #include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */ #include <net/net_namespace.h> #include <net/netlabel.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <asm/ioctls.h> #include <asm/atomic.h> #include <linux/bitops.h> @@ -104,7 +103,9 @@ int selinux_enforcing; static int __init enforcing_setup(char *str) { - selinux_enforcing = simple_strtol(str, NULL, 0); + unsigned long enforcing; + if (!strict_strtoul(str, 0, &enforcing)) + selinux_enforcing = enforcing ? 1 : 0; return 1; } __setup("enforcing=", enforcing_setup); @@ -115,7 +116,9 @@ int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE; static int __init selinux_enabled_setup(char *str) { - selinux_enabled = simple_strtol(str, NULL, 0); + unsigned long enabled; + if (!strict_strtoul(str, 0, &enabled)) + selinux_enabled = enabled ? 1 : 0; return 1; } __setup("selinux=", selinux_enabled_setup); @@ -123,13 +126,11 @@ __setup("selinux=", selinux_enabled_setup); int selinux_enabled = 1; #endif -/* Original (dummy) security module. */ -static struct security_operations *original_ops; -/* Minimal support for a secondary security module, - just to allow the use of the dummy or capability modules. - The owlsm module can alternatively be used as a secondary - module as long as CONFIG_OWLSM_FD is not enabled. */ +/* + * Minimal support for a secondary security module, + * just to allow the use of the capability module. + */ static struct security_operations *secondary_ops; /* Lists of inode and superblock security structures initialized @@ -554,13 +555,15 @@ static int selinux_set_mnt_opts(struct super_block *sb, struct task_security_struct *tsec = current->security; struct superblock_security_struct *sbsec = sb->s_security; const char *name = sb->s_type->name; - struct inode *inode = sbsec->sb->s_root->d_inode; - struct inode_security_struct *root_isec = inode->i_security; + struct dentry *root = sb->s_root; + struct inode *root_inode = root->d_inode; + struct inode_security_struct *root_isec = root_inode->i_security; u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0; u32 defcontext_sid = 0; char **mount_options = opts->mnt_opts; int *flags = opts->mnt_opts_flags; int num_opts = opts->num_mnt_opts; + bool can_xattr = false; mutex_lock(&sbsec->lock); @@ -594,7 +597,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, */ if (sbsec->initialized && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) && (num_opts == 0)) - goto out; + goto out; /* * parse the mount options, check if they are valid sids. @@ -664,14 +667,24 @@ static int selinux_set_mnt_opts(struct super_block *sb, goto out; } - if (strcmp(sb->s_type->name, "proc") == 0) + if (strcmp(name, "proc") == 0) sbsec->proc = 1; + /* + * test if the fs supports xattrs, fs_use might make use of this if the + * fs has no definition in policy. + */ + if (root_inode->i_op->getxattr) { + rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0); + if (rc >= 0 || rc == -ENODATA) + can_xattr = true; + } + /* Determine the labeling behavior to use for this filesystem type. */ - rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid); + rc = security_fs_use(name, &sbsec->behavior, &sbsec->sid, can_xattr); if (rc) { printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n", - __func__, sb->s_type->name, rc); + __func__, name, rc); goto out; } @@ -956,6 +969,57 @@ out_err: return rc; } +void selinux_write_opts(struct seq_file *m, struct security_mnt_opts *opts) +{ + int i; + char *prefix; + + for (i = 0; i < opts->num_mnt_opts; i++) { + char *has_comma = strchr(opts->mnt_opts[i], ','); + + switch (opts->mnt_opts_flags[i]) { + case CONTEXT_MNT: + prefix = CONTEXT_STR; + break; + case FSCONTEXT_MNT: + prefix = FSCONTEXT_STR; + break; + case ROOTCONTEXT_MNT: + prefix = ROOTCONTEXT_STR; + break; + case DEFCONTEXT_MNT: + prefix = DEFCONTEXT_STR; + break; + default: + BUG(); + }; + /* we need a comma before each option */ + seq_putc(m, ','); + seq_puts(m, prefix); + if (has_comma) + seq_putc(m, '\"'); + seq_puts(m, opts->mnt_opts[i]); + if (has_comma) + seq_putc(m, '\"'); + } +} + +static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb) +{ + struct security_mnt_opts opts; + int rc; + + rc = selinux_get_mnt_opts(sb, &opts); + if (rc) + return rc; + + selinux_write_opts(m, &opts); + + security_free_mnt_opts(&opts); + + return rc; +} + static inline u16 inode_mode_to_security_class(umode_t mode) { switch (mode & S_IFMT) { @@ -1682,14 +1746,23 @@ static inline u32 file_to_av(struct file *file) /* Hook functions begin here. */ -static int selinux_ptrace(struct task_struct *parent, struct task_struct *child) +static int selinux_ptrace(struct task_struct *parent, + struct task_struct *child, + unsigned int mode) { int rc; - rc = secondary_ops->ptrace(parent, child); + rc = secondary_ops->ptrace(parent, child, mode); if (rc) return rc; + if (mode == PTRACE_MODE_READ) { + struct task_security_struct *tsec = parent->security; + struct task_security_struct *csec = child->security; + return avc_has_perm(tsec->sid, csec->sid, + SECCLASS_FILE, FILE__READ, NULL); + } + return task_has_perm(parent, child, PROCESS__PTRACE); } @@ -2495,7 +2568,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, } if (value && len) { - rc = security_sid_to_context(newsid, &context, &clen); + rc = security_sid_to_context_force(newsid, &context, &clen); if (rc) { kfree(namep); return rc; @@ -2669,6 +2742,11 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, return rc; rc = security_context_to_sid(value, size, &newsid); + if (rc == -EINVAL) { + if (!capable(CAP_MAC_ADMIN)) + return rc; + rc = security_context_to_sid_force(value, size, &newsid); + } if (rc) return rc; @@ -2690,7 +2768,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, } static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, - const void *value, size_t size, + const void *value, size_t size, int flags) { struct inode *inode = dentry->d_inode; @@ -2703,10 +2781,11 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, return; } - rc = security_context_to_sid(value, size, &newsid); + rc = security_context_to_sid_force(value, size, &newsid); if (rc) { - printk(KERN_WARNING "%s: unable to obtain SID for context " - "%s, rc=%d\n", __func__, (char *)value, -rc); + printk(KERN_ERR "SELinux: unable to map context to SID" + "for (%s, %lu), rc=%d\n", + inode->i_sb->s_id, inode->i_ino, -rc); return; } @@ -2735,9 +2814,7 @@ static int selinux_inode_removexattr(struct dentry *dentry, const char *name) } /* - * Copy the in-core inode security context value to the user. If the - * getxattr() prior to this succeeded, check to see if we need to - * canonicalize the value to be finally returned to the user. + * Copy the inode security context value to the user. * * Permission check is handled by selinux_inode_getxattr hook. */ @@ -2746,12 +2823,33 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name u32 size; int error; char *context = NULL; + struct task_security_struct *tsec = current->security; struct inode_security_struct *isec = inode->i_security; if (strcmp(name, XATTR_SELINUX_SUFFIX)) return -EOPNOTSUPP; - error = security_sid_to_context(isec->sid, &context, &size); + /* + * If the caller has CAP_MAC_ADMIN, then get the raw context + * value even if it is not defined by current policy; otherwise, + * use the in-core value under current policy. + * Use the non-auditing forms of the permission checks since + * getxattr may be called by unprivileged processes commonly + * and lack of permission just means that we fall back to the + * in-core context value, not a denial. + */ + error = secondary_ops->capable(current, CAP_MAC_ADMIN); + if (!error) + error = avc_has_perm_noaudit(tsec->sid, tsec->sid, + SECCLASS_CAPABILITY2, + CAPABILITY2__MAC_ADMIN, + 0, + NULL); + if (!error) + error = security_sid_to_context_force(isec->sid, &context, + &size); + else + error = security_sid_to_context(isec->sid, &context, &size); if (error) return error; error = size; @@ -2865,46 +2963,16 @@ static void selinux_file_free_security(struct file *file) static int selinux_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - int error = 0; - - switch (cmd) { - case FIONREAD: - /* fall through */ - case FIBMAP: - /* fall through */ - case FIGETBSZ: - /* fall through */ - case EXT2_IOC_GETFLAGS: - /* fall through */ - case EXT2_IOC_GETVERSION: - error = file_has_perm(current, file, FILE__GETATTR); - break; - - case EXT2_IOC_SETFLAGS: - /* fall through */ - case EXT2_IOC_SETVERSION: - error = file_has_perm(current, file, FILE__SETATTR); - break; - - /* sys_ioctl() checks */ - case FIONBIO: - /* fall through */ - case FIOASYNC: - error = file_has_perm(current, file, 0); - break; + u32 av = 0; - case KDSKBENT: - case KDSKBSENT: - error = task_has_capability(current, CAP_SYS_TTY_CONFIG); - break; + if (_IOC_DIR(cmd) & _IOC_WRITE) + av |= FILE__WRITE; + if (_IOC_DIR(cmd) & _IOC_READ) + av |= FILE__READ; + if (!av) + av = FILE__IOCTL; - /* default case assumes that the command will go - * to the file's ioctl() function. - */ - default: - error = file_has_perm(current, file, FILE__IOCTL); - } - return error; + return file_has_perm(current, file, av); } static int file_map_prot_check(struct file *file, unsigned long prot, int shared) @@ -3663,7 +3731,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in struct sockaddr_in6 *addr6 = NULL; unsigned short snum; struct sock *sk = sock->sk; - u32 sid, node_perm, addrlen; + u32 sid, node_perm; tsec = current->security; isec = SOCK_INODE(sock)->i_security; @@ -3671,12 +3739,10 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in if (family == PF_INET) { addr4 = (struct sockaddr_in *)address; snum = ntohs(addr4->sin_port); - addrlen = sizeof(addr4->sin_addr.s_addr); addrp = (char *)&addr4->sin_addr.s_addr; } else { addr6 = (struct sockaddr_in6 *)address; snum = ntohs(addr6->sin6_port); - addrlen = sizeof(addr6->sin6_addr.s6_addr); addrp = (char *)&addr6->sin6_addr.s6_addr; } @@ -5047,24 +5113,6 @@ static void selinux_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid) *secid = isec->sid; } -/* module stacking operations */ -static int selinux_register_security(const char *name, struct security_operations *ops) -{ - if (secondary_ops != original_ops) { - printk(KERN_ERR "%s: There is already a secondary security " - "module registered.\n", __func__); - return -EINVAL; - } - - secondary_ops = ops; - - printk(KERN_INFO "%s: Registering secondary module %s\n", - __func__, - name); - - return 0; -} - static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode) { if (inode) @@ -5153,6 +5201,12 @@ static int selinux_setprocattr(struct task_struct *p, size--; } error = security_context_to_sid(value, size, &sid); + if (error == -EINVAL && !strcmp(name, "fscreate")) { + if (!capable(CAP_MAC_ADMIN)) + return error; + error = security_context_to_sid_force(value, size, + &sid); + } if (error) return error; } @@ -5186,12 +5240,12 @@ static int selinux_setprocattr(struct task_struct *p, struct task_struct *g, *t; struct mm_struct *mm = p->mm; read_lock(&tasklist_lock); - do_each_thread(g, t) + do_each_thread(g, t) { if (t->mm == mm && t != p) { read_unlock(&tasklist_lock); return -EPERM; } - while_each_thread(g, t); + } while_each_thread(g, t); read_unlock(&tasklist_lock); } @@ -5343,10 +5397,10 @@ static struct security_operations selinux_ops = { .sb_free_security = selinux_sb_free_security, .sb_copy_data = selinux_sb_copy_data, .sb_kern_mount = selinux_sb_kern_mount, + .sb_show_options = selinux_sb_show_options, .sb_statfs = selinux_sb_statfs, .sb_mount = selinux_mount, .sb_umount = selinux_umount, - .sb_get_mnt_opts = selinux_get_mnt_opts, .sb_set_mnt_opts = selinux_set_mnt_opts, .sb_clone_mnt_opts = selinux_sb_clone_mnt_opts, .sb_parse_opts_str = selinux_parse_opts_str, @@ -5378,7 +5432,7 @@ static struct security_operations selinux_ops = { .inode_listsecurity = selinux_inode_listsecurity, .inode_need_killpriv = selinux_inode_need_killpriv, .inode_killpriv = selinux_inode_killpriv, - .inode_getsecid = selinux_inode_getsecid, + .inode_getsecid = selinux_inode_getsecid, .file_permission = selinux_file_permission, .file_alloc_security = selinux_file_alloc_security, @@ -5419,7 +5473,7 @@ static struct security_operations selinux_ops = { .task_to_inode = selinux_task_to_inode, .ipc_permission = selinux_ipc_permission, - .ipc_getsecid = selinux_ipc_getsecid, + .ipc_getsecid = selinux_ipc_getsecid, .msg_msg_alloc_security = selinux_msg_msg_alloc_security, .msg_msg_free_security = selinux_msg_msg_free_security, @@ -5443,8 +5497,6 @@ static struct security_operations selinux_ops = { .sem_semctl = selinux_sem_semctl, .sem_semop = selinux_sem_semop, - .register_security = selinux_register_security, - .d_instantiate = selinux_d_instantiate, .getprocattr = selinux_getprocattr, @@ -5538,7 +5590,7 @@ static __init int selinux_init(void) 0, SLAB_PANIC, NULL); avc_init(); - original_ops = secondary_ops = security_ops; + secondary_ops = security_ops; if (!secondary_ops) panic("SELinux: No initial security operations\n"); if (register_security(&selinux_ops)) diff --git a/security/selinux/include/audit.h b/security/selinux/include/audit.h index 6c8b9ef1557..1bdf973433c 100644 --- a/security/selinux/include/audit.h +++ b/security/selinux/include/audit.h @@ -1,7 +1,7 @@ /* * SELinux support for the Audit LSM hooks * - * Most of below header was moved from include/linux/selinux.h which + * Most of below header was moved from include/linux/selinux.h which * is released under below copyrights: * * Author: James Morris <jmorris@redhat.com> @@ -52,7 +52,7 @@ void selinux_audit_rule_free(void *rule); * -errno on failure. */ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *rule, - struct audit_context *actx); + struct audit_context *actx); /** * selinux_audit_rule_known - check to see if rule contains selinux fields. diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 8e23d7a873a..7b9769f5e77 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -75,13 +75,12 @@ struct avc_audit_data { /* Initialize an AVC audit data structure. */ #define AVC_AUDIT_DATA_INIT(_d,_t) \ - { memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; } + { memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; } /* * AVC statistics */ -struct avc_cache_stats -{ +struct avc_cache_stats { unsigned int lookups; unsigned int hits; unsigned int misses; @@ -97,8 +96,8 @@ struct avc_cache_stats void __init avc_init(void); void avc_audit(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - struct av_decision *avd, int result, struct avc_audit_data *auditdata); + u16 tclass, u32 requested, + struct av_decision *avd, int result, struct avc_audit_data *auditdata); #define AVC_STRICT 1 /* Ignore permissive mode. */ int avc_has_perm_noaudit(u32 ssid, u32 tsid, @@ -107,8 +106,8 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, struct av_decision *avd); int avc_has_perm(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - struct avc_audit_data *auditdata); + u16 tclass, u32 requested, + struct avc_audit_data *auditdata); u32 avc_policy_seqno(void); @@ -122,7 +121,7 @@ u32 avc_policy_seqno(void); #define AVC_CALLBACK_AUDITDENY_DISABLE 128 int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, - u16 tclass, u32 perms, + u16 tclass, u32 perms, u32 *out_retained), u32 events, u32 ssid, u32 tsid, u16 tclass, u32 perms); diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 032c2357dad..91070ab874c 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -44,7 +44,6 @@ struct inode_security_struct { u16 sclass; /* security class of this object */ unsigned char initialized; /* initialization flag */ struct mutex lock; - unsigned char inherit; /* inherit SID from parent entry */ }; struct file_security_struct { diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index ad30ac4273d..44cba2e21dc 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -93,12 +93,17 @@ int security_change_sid(u32 ssid, u32 tsid, int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len); +int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len); + int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *out_sid); int security_context_to_sid_default(const char *scontext, u32 scontext_len, u32 *out_sid, u32 def_sid, gfp_t gfp_flags); +int security_context_to_sid_force(const char *scontext, u32 scontext_len, + u32 *sid); + int security_get_user_sids(u32 callsid, char *username, u32 **sids, u32 *nel); @@ -131,7 +136,7 @@ int security_get_allow_unknown(void); #define SECURITY_FS_USE_MNTPOINT 6 /* use mountpoint labeling */ int security_fs_use(const char *fstype, unsigned int *behavior, - u32 *sid); + u32 *sid, bool can_xattr); int security_genfs_sid(const char *fstype, char *name, u16 sclass, u32 *sid); diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index b6ccd09379f..7100072bb1b 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c @@ -38,7 +38,6 @@ #include <linux/ipv6.h> #include <net/ip.h> #include <net/ipv6.h> -#include <asm/bug.h> #include "netnode.h" #include "objsec.h" diff --git a/security/selinux/netport.c b/security/selinux/netport.c index 90b4cff7c35..fe7fba67f19 100644 --- a/security/selinux/netport.c +++ b/security/selinux/netport.c @@ -37,7 +37,6 @@ #include <linux/ipv6.h> #include <net/ip.h> #include <net/ipv6.h> -#include <asm/bug.h> #include "netport.h" #include "objsec.h" @@ -272,7 +271,7 @@ static __init int sel_netport_init(void) } ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET, - SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); + SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); 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 ac1ccc13a70..69c9dccc8cf 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -27,7 +27,7 @@ #include <linux/seq_file.h> #include <linux/percpu.h> #include <linux/audit.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> /* selinuxfs pseudo filesystem for exporting the security policy API. Based on the proc code and the fs/nfsd/nfsctl.c code. */ @@ -57,14 +57,18 @@ int selinux_compat_net = SELINUX_COMPAT_NET_VALUE; static int __init checkreqprot_setup(char *str) { - selinux_checkreqprot = simple_strtoul(str, NULL, 0) ? 1 : 0; + unsigned long checkreqprot; + if (!strict_strtoul(str, 0, &checkreqprot)) + selinux_checkreqprot = checkreqprot ? 1 : 0; return 1; } __setup("checkreqprot=", checkreqprot_setup); static int __init selinux_compat_net_setup(char *str) { - selinux_compat_net = simple_strtoul(str, NULL, 0) ? 1 : 0; + unsigned long compat_net; + if (!strict_strtoul(str, 0, &compat_net)) + selinux_compat_net = compat_net ? 1 : 0; return 1; } __setup("selinux_compat_net=", selinux_compat_net_setup); @@ -352,11 +356,6 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, length = count; out1: - - printk(KERN_INFO "SELinux: policy loaded with handle_unknown=%s\n", - (security_get_reject_unknown() ? "reject" : - (security_get_allow_unknown() ? "allow" : "deny"))); - audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, "policy loaded auid=%u ses=%u", audit_get_loginuid(current), diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 9e6626362bf..a1be97f8bee 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -311,7 +311,7 @@ void avtab_hash_eval(struct avtab *h, char *tag) } printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, " - "longest chain length %d sum of chain length^2 %Lu\n", + "longest chain length %d sum of chain length^2 %llu\n", tag, h->nel, slots_used, h->nslot, max_chain_len, chain2_len_sum); } diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h index b9a6f7fc62f..658c2bd17da 100644 --- a/security/selinux/ss/context.h +++ b/security/selinux/ss/context.h @@ -28,6 +28,8 @@ struct context { u32 role; u32 type; struct mls_range range; + char *str; /* string representation if context cannot be mapped. */ + u32 len; /* length of string in bytes */ }; static inline void mls_context_init(struct context *c) @@ -106,20 +108,43 @@ static inline void context_init(struct context *c) static inline int context_cpy(struct context *dst, struct context *src) { + int rc; + dst->user = src->user; dst->role = src->role; dst->type = src->type; - return mls_context_cpy(dst, src); + if (src->str) { + dst->str = kstrdup(src->str, GFP_ATOMIC); + if (!dst->str) + return -ENOMEM; + dst->len = src->len; + } else { + dst->str = NULL; + dst->len = 0; + } + rc = mls_context_cpy(dst, src); + if (rc) { + kfree(dst->str); + return rc; + } + return 0; } static inline void context_destroy(struct context *c) { c->user = c->role = c->type = 0; + kfree(c->str); + c->str = NULL; + c->len = 0; mls_context_destroy(c); } static inline int context_cmp(struct context *c1, struct context *c2) { + if (c1->len && c2->len) + return (c1->len == c2->len && !strcmp(c1->str, c2->str)); + if (c1->len || c2->len) + return 0; return ((c1->user == c2->user) && (c1->role == c2->role) && (c1->type == c2->type) && diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 8b1706b7b3c..77d745da48b 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -239,7 +239,8 @@ int mls_context_isvalid(struct policydb *p, struct context *c) * Policy read-lock must be held for sidtab lookup. * */ -int mls_context_to_sid(char oldc, +int mls_context_to_sid(struct policydb *pol, + char oldc, char **scontext, struct context *context, struct sidtab *s, @@ -286,7 +287,7 @@ int mls_context_to_sid(char oldc, *p++ = 0; for (l = 0; l < 2; l++) { - levdatum = hashtab_search(policydb.p_levels.table, scontextp); + levdatum = hashtab_search(pol->p_levels.table, scontextp); if (!levdatum) { rc = -EINVAL; goto out; @@ -311,7 +312,7 @@ int mls_context_to_sid(char oldc, *rngptr++ = 0; } - catdatum = hashtab_search(policydb.p_cats.table, + catdatum = hashtab_search(pol->p_cats.table, scontextp); if (!catdatum) { rc = -EINVAL; @@ -327,7 +328,7 @@ int mls_context_to_sid(char oldc, if (rngptr) { int i; - rngdatum = hashtab_search(policydb.p_cats.table, rngptr); + rngdatum = hashtab_search(pol->p_cats.table, rngptr); if (!rngdatum) { rc = -EINVAL; goto out; @@ -395,7 +396,7 @@ int mls_from_string(char *str, struct context *context, gfp_t gfp_mask) if (!tmpstr) { rc = -ENOMEM; } else { - rc = mls_context_to_sid(':', &tmpstr, context, + rc = mls_context_to_sid(&policydb, ':', &tmpstr, context, NULL, SECSID_NULL); kfree(freestr); } @@ -436,13 +437,13 @@ int mls_setup_user_range(struct context *fromcon, struct user_datum *user, struct mls_level *usercon_clr = &(usercon->range.level[1]); /* Honor the user's default level if we can */ - if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) { + if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) *usercon_sen = *user_def; - } else if (mls_level_between(fromcon_sen, user_def, user_clr)) { + else if (mls_level_between(fromcon_sen, user_def, user_clr)) *usercon_sen = *fromcon_sen; - } else if (mls_level_between(fromcon_clr, user_low, user_def)) { + else if (mls_level_between(fromcon_clr, user_low, user_def)) *usercon_sen = *user_low; - } else + else return -EINVAL; /* Lower the clearance of available contexts diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h index 0fdf6257ef6..1276715aaa8 100644 --- a/security/selinux/ss/mls.h +++ b/security/selinux/ss/mls.h @@ -30,7 +30,8 @@ int mls_context_isvalid(struct policydb *p, struct context *c); int mls_range_isvalid(struct policydb *p, struct mls_range *r); int mls_level_isvalid(struct policydb *p, struct mls_level *l); -int mls_context_to_sid(char oldc, +int mls_context_to_sid(struct policydb *p, + char oldc, char **scontext, struct context *context, struct sidtab *s, diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 84f8cc73c7d..2391761ae42 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1478,7 +1478,8 @@ int policydb_read(struct policydb *p, void *fp) struct ocontext *l, *c, *newc; struct genfs *genfs_p, *genfs, *newgenfs; int i, j, rc; - __le32 buf[8]; + __le32 buf[4]; + u32 nodebuf[8]; u32 len, len2, config, nprim, nel, nel2; char *policydb_str; struct policydb_compat_info *info; @@ -1749,11 +1750,11 @@ int policydb_read(struct policydb *p, void *fp) goto bad; break; case OCON_NODE: - rc = next_entry(buf, fp, sizeof(u32) * 2); + rc = next_entry(nodebuf, fp, sizeof(u32) * 2); if (rc < 0) goto bad; - c->u.node.addr = le32_to_cpu(buf[0]); - c->u.node.mask = le32_to_cpu(buf[1]); + c->u.node.addr = nodebuf[0]; /* network order */ + c->u.node.mask = nodebuf[1]; /* network order */ rc = context_read_and_validate(&c->context[0], p, fp); if (rc) goto bad; @@ -1782,13 +1783,13 @@ int policydb_read(struct policydb *p, void *fp) case OCON_NODE6: { int k; - rc = next_entry(buf, fp, sizeof(u32) * 8); + rc = next_entry(nodebuf, fp, sizeof(u32) * 8); if (rc < 0) goto bad; for (k = 0; k < 4; k++) - c->u.node6.addr[k] = le32_to_cpu(buf[k]); + c->u.node6.addr[k] = nodebuf[k]; for (k = 0; k < 4; k++) - c->u.node6.mask[k] = le32_to_cpu(buf[k+4]); + c->u.node6.mask[k] = nodebuf[k+4]; if (context_read_and_validate(&c->context[0], p, fp)) goto bad; break; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index dcc2e1c4fd8..8e42da12010 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -71,14 +71,6 @@ int selinux_policycap_openperm; extern const struct selinux_class_perm selinux_class_perm; static DEFINE_RWLOCK(policy_rwlock); -#define POLICY_RDLOCK read_lock(&policy_rwlock) -#define POLICY_WRLOCK write_lock_irq(&policy_rwlock) -#define POLICY_RDUNLOCK read_unlock(&policy_rwlock) -#define POLICY_WRUNLOCK write_unlock_irq(&policy_rwlock) - -static DEFINE_MUTEX(load_mutex); -#define LOAD_LOCK mutex_lock(&load_mutex) -#define LOAD_UNLOCK mutex_unlock(&load_mutex) static struct sidtab sidtab; struct policydb policydb; @@ -332,7 +324,7 @@ static int context_struct_compute_av(struct context *scontext, goto inval_class; if (unlikely(tclass > policydb.p_classes.nprim)) if (tclass > kdefs->cts_len || - !kdefs->class_to_string[tclass - 1] || + !kdefs->class_to_string[tclass] || !policydb.allow_unknown) goto inval_class; @@ -415,9 +407,19 @@ static int context_struct_compute_av(struct context *scontext, return 0; inval_class: - printk(KERN_ERR "SELinux: %s: unrecognized class %d\n", __func__, - tclass); - return -EINVAL; + if (!tclass || tclass > kdefs->cts_len || + !kdefs->class_to_string[tclass]) { + if (printk_ratelimit()) + printk(KERN_ERR "SELinux: %s: unrecognized class %d\n", + __func__, tclass); + return -EINVAL; + } + + /* + * Known to the kernel, but not to the policy. + * Handle as a denial (allowed is 0). + */ + return 0; } /* @@ -429,7 +431,7 @@ int security_permissive_sid(u32 sid) u32 type; int rc; - POLICY_RDLOCK; + read_lock(&policy_rwlock); context = sidtab_search(&sidtab, sid); BUG_ON(!context); @@ -441,7 +443,7 @@ int security_permissive_sid(u32 sid) */ rc = ebitmap_get_bit(&policydb.permissive_map, type); - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -486,7 +488,7 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, if (!ss_initialized) return 0; - POLICY_RDLOCK; + read_lock(&policy_rwlock); /* * Remap extended Netlink classes for old policy versions. @@ -543,7 +545,7 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, } out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -578,7 +580,7 @@ int security_compute_av(u32 ssid, return 0; } - POLICY_RDLOCK; + read_lock(&policy_rwlock); scontext = sidtab_search(&sidtab, ssid); if (!scontext) { @@ -598,7 +600,7 @@ int security_compute_av(u32 ssid, rc = context_struct_compute_av(scontext, tcontext, tclass, requested, avd); out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -616,6 +618,14 @@ static int context_struct_to_string(struct context *context, char **scontext, u3 *scontext = NULL; *scontext_len = 0; + if (context->len) { + *scontext_len = context->len; + *scontext = kstrdup(context->str, GFP_ATOMIC); + if (!(*scontext)) + return -ENOMEM; + return 0; + } + /* Compute the size of the context. */ *scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1; *scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1; @@ -655,17 +665,8 @@ const char *security_get_initial_sid_context(u32 sid) return initial_sid_to_string[sid]; } -/** - * security_sid_to_context - Obtain a context for a given SID. - * @sid: security identifier, SID - * @scontext: security context - * @scontext_len: length in bytes - * - * Write the string representation of the context associated with @sid - * into a dynamically allocated string of the correct size. Set @scontext - * to point to this string and set @scontext_len to the length of the string. - */ -int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len) +static int security_sid_to_context_core(u32 sid, char **scontext, + u32 *scontext_len, int force) { struct context *context; int rc = 0; @@ -692,8 +693,11 @@ int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len) rc = -EINVAL; goto out; } - POLICY_RDLOCK; - context = sidtab_search(&sidtab, sid); + read_lock(&policy_rwlock); + if (force) + context = sidtab_search_force(&sidtab, sid); + else + context = sidtab_search(&sidtab, sid); if (!context) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", __func__, sid); @@ -702,59 +706,54 @@ int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len) } rc = context_struct_to_string(context, scontext, scontext_len); out_unlock: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); out: return rc; } -static int security_context_to_sid_core(const char *scontext, u32 scontext_len, - u32 *sid, u32 def_sid, gfp_t gfp_flags) +/** + * security_sid_to_context - Obtain a context for a given SID. + * @sid: security identifier, SID + * @scontext: security context + * @scontext_len: length in bytes + * + * Write the string representation of the context associated with @sid + * into a dynamically allocated string of the correct size. Set @scontext + * to point to this string and set @scontext_len to the length of the string. + */ +int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len) +{ + return security_sid_to_context_core(sid, scontext, scontext_len, 0); +} + +int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len) +{ + return security_sid_to_context_core(sid, scontext, scontext_len, 1); +} + +/* + * Caveat: Mutates scontext. + */ +static int string_to_context_struct(struct policydb *pol, + struct sidtab *sidtabp, + char *scontext, + u32 scontext_len, + struct context *ctx, + u32 def_sid) { - char *scontext2; - struct context context; struct role_datum *role; struct type_datum *typdatum; struct user_datum *usrdatum; char *scontextp, *p, oldc; int rc = 0; - if (!ss_initialized) { - int i; - - for (i = 1; i < SECINITSID_NUM; i++) { - if (!strcmp(initial_sid_to_string[i], scontext)) { - *sid = i; - goto out; - } - } - *sid = SECINITSID_KERNEL; - goto out; - } - *sid = SECSID_NULL; - - /* Copy the string so that we can modify the copy as we parse it. - The string should already by null terminated, but we append a - null suffix to the copy to avoid problems with the existing - attr package, which doesn't view the null terminator as part - of the attribute value. */ - scontext2 = kmalloc(scontext_len+1, gfp_flags); - if (!scontext2) { - rc = -ENOMEM; - goto out; - } - memcpy(scontext2, scontext, scontext_len); - scontext2[scontext_len] = 0; - - context_init(&context); - *sid = SECSID_NULL; - - POLICY_RDLOCK; + context_init(ctx); /* Parse the security context. */ rc = -EINVAL; - scontextp = (char *) scontext2; + scontextp = (char *) scontext; /* Extract the user. */ p = scontextp; @@ -762,15 +761,15 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len, p++; if (*p == 0) - goto out_unlock; + goto out; *p++ = 0; - usrdatum = hashtab_search(policydb.p_users.table, scontextp); + usrdatum = hashtab_search(pol->p_users.table, scontextp); if (!usrdatum) - goto out_unlock; + goto out; - context.user = usrdatum->value; + ctx->user = usrdatum->value; /* Extract role. */ scontextp = p; @@ -778,14 +777,14 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len, p++; if (*p == 0) - goto out_unlock; + goto out; *p++ = 0; - role = hashtab_search(policydb.p_roles.table, scontextp); + role = hashtab_search(pol->p_roles.table, scontextp); if (!role) - goto out_unlock; - context.role = role->value; + goto out; + ctx->role = role->value; /* Extract type. */ scontextp = p; @@ -794,33 +793,87 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len, oldc = *p; *p++ = 0; - typdatum = hashtab_search(policydb.p_types.table, scontextp); + typdatum = hashtab_search(pol->p_types.table, scontextp); if (!typdatum) - goto out_unlock; + goto out; - context.type = typdatum->value; + ctx->type = typdatum->value; - rc = mls_context_to_sid(oldc, &p, &context, &sidtab, def_sid); + rc = mls_context_to_sid(pol, oldc, &p, ctx, sidtabp, def_sid); if (rc) - goto out_unlock; + goto out; - if ((p - scontext2) < scontext_len) { + if ((p - scontext) < scontext_len) { rc = -EINVAL; - goto out_unlock; + goto out; } /* Check the validity of the new context. */ - if (!policydb_context_isvalid(&policydb, &context)) { + if (!policydb_context_isvalid(pol, ctx)) { rc = -EINVAL; - goto out_unlock; + context_destroy(ctx); + goto out; } - /* Obtain the new sid. */ + rc = 0; +out: + return rc; +} + +static int security_context_to_sid_core(const char *scontext, u32 scontext_len, + u32 *sid, u32 def_sid, gfp_t gfp_flags, + int force) +{ + char *scontext2, *str = NULL; + struct context context; + int rc = 0; + + if (!ss_initialized) { + int i; + + for (i = 1; i < SECINITSID_NUM; i++) { + if (!strcmp(initial_sid_to_string[i], scontext)) { + *sid = i; + return 0; + } + } + *sid = SECINITSID_KERNEL; + return 0; + } + *sid = SECSID_NULL; + + /* Copy the string so that we can modify the copy as we parse it. */ + scontext2 = kmalloc(scontext_len+1, gfp_flags); + if (!scontext2) + return -ENOMEM; + memcpy(scontext2, scontext, scontext_len); + scontext2[scontext_len] = 0; + + if (force) { + /* Save another copy for storing in uninterpreted form */ + str = kstrdup(scontext2, gfp_flags); + if (!str) { + kfree(scontext2); + return -ENOMEM; + } + } + + read_lock(&policy_rwlock); + rc = string_to_context_struct(&policydb, &sidtab, + scontext2, scontext_len, + &context, def_sid); + if (rc == -EINVAL && force) { + context.str = str; + context.len = scontext_len; + str = NULL; + } else if (rc) + goto out; rc = sidtab_context_to_sid(&sidtab, &context, sid); -out_unlock: - POLICY_RDUNLOCK; - context_destroy(&context); - kfree(scontext2); + if (rc) + context_destroy(&context); out: + read_unlock(&policy_rwlock); + kfree(scontext2); + kfree(str); return rc; } @@ -838,7 +891,7 @@ out: int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid) { return security_context_to_sid_core(scontext, scontext_len, - sid, SECSID_NULL, GFP_KERNEL); + sid, SECSID_NULL, GFP_KERNEL, 0); } /** @@ -855,6 +908,7 @@ int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid) * The default SID is passed to the MLS layer to be used to allow * kernel labeling of the MLS field if the MLS field is not present * (for upgrading to MLS without full relabel). + * Implicitly forces adding of the context even if it cannot be mapped yet. * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient * memory is available, or 0 on success. */ @@ -862,7 +916,14 @@ int security_context_to_sid_default(const char *scontext, u32 scontext_len, u32 *sid, u32 def_sid, gfp_t gfp_flags) { return security_context_to_sid_core(scontext, scontext_len, - sid, def_sid, gfp_flags); + sid, def_sid, gfp_flags, 1); +} + +int security_context_to_sid_force(const char *scontext, u32 scontext_len, + u32 *sid) +{ + return security_context_to_sid_core(scontext, scontext_len, + sid, SECSID_NULL, GFP_KERNEL, 1); } static int compute_sid_handle_invalid_context( @@ -922,7 +983,7 @@ static int security_compute_sid(u32 ssid, context_init(&newcontext); - POLICY_RDLOCK; + read_lock(&policy_rwlock); scontext = sidtab_search(&sidtab, ssid); if (!scontext) { @@ -1027,7 +1088,7 @@ static int security_compute_sid(u32 ssid, /* Obtain the sid for the context. */ rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid); out_unlock: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); context_destroy(&newcontext); out: return rc; @@ -1110,6 +1171,7 @@ static int validate_classes(struct policydb *p) const struct selinux_class_perm *kdefs = &selinux_class_perm; const char *def_class, *def_perm, *pol_class; struct symtab *perms; + bool print_unknown_handle = 0; if (p->allow_unknown) { u32 num_classes = kdefs->cts_len; @@ -1130,6 +1192,7 @@ static int validate_classes(struct policydb *p) return -EINVAL; if (p->allow_unknown) p->undefined_perms[i-1] = ~0U; + print_unknown_handle = 1; continue; } pol_class = p->p_class_val_to_name[i-1]; @@ -1159,6 +1222,7 @@ static int validate_classes(struct policydb *p) return -EINVAL; if (p->allow_unknown) p->undefined_perms[class_val-1] |= perm_val; + print_unknown_handle = 1; continue; } perdatum = hashtab_search(perms->table, def_perm); @@ -1206,6 +1270,7 @@ static int validate_classes(struct policydb *p) return -EINVAL; if (p->allow_unknown) p->undefined_perms[class_val-1] |= (1 << j); + print_unknown_handle = 1; continue; } perdatum = hashtab_search(perms->table, def_perm); @@ -1223,6 +1288,9 @@ static int validate_classes(struct policydb *p) } } } + if (print_unknown_handle) + printk(KERN_INFO "SELinux: the above unknown classes and permissions will be %s\n", + (security_get_allow_unknown() ? "allowed" : "denied")); return 0; } @@ -1246,9 +1314,12 @@ static inline int convert_context_handle_invalid_context(struct context *context char *s; u32 len; - context_struct_to_string(context, &s, &len); - printk(KERN_ERR "SELinux: context %s is invalid\n", s); - kfree(s); + if (!context_struct_to_string(context, &s, &len)) { + printk(KERN_WARNING + "SELinux: Context %s would be invalid if enforcing\n", + s); + kfree(s); + } } return rc; } @@ -1280,6 +1351,37 @@ static int convert_context(u32 key, args = p; + if (c->str) { + struct context ctx; + s = kstrdup(c->str, GFP_KERNEL); + if (!s) { + rc = -ENOMEM; + goto out; + } + rc = string_to_context_struct(args->newp, NULL, s, + c->len, &ctx, SECSID_NULL); + kfree(s); + if (!rc) { + printk(KERN_INFO + "SELinux: Context %s became valid (mapped).\n", + c->str); + /* Replace string with mapped representation. */ + kfree(c->str); + memcpy(c, &ctx, sizeof(*c)); + goto out; + } else if (rc == -EINVAL) { + /* Retain string representation for later mapping. */ + rc = 0; + goto out; + } else { + /* Other error condition, e.g. ENOMEM. */ + printk(KERN_ERR + "SELinux: Unable to map context %s, rc = %d.\n", + c->str, -rc); + goto out; + } + } + rc = context_cpy(&oldc, c); if (rc) goto out; @@ -1319,13 +1421,21 @@ static int convert_context(u32 key, } context_destroy(&oldc); + rc = 0; out: return rc; bad: - context_struct_to_string(&oldc, &s, &len); + /* Map old representation to string and save it. */ + if (context_struct_to_string(&oldc, &s, &len)) + return -ENOMEM; context_destroy(&oldc); - printk(KERN_ERR "SELinux: invalidating context %s\n", s); - kfree(s); + context_destroy(c); + c->str = s; + c->len = len; + printk(KERN_INFO + "SELinux: Context %s became invalid (unmapped).\n", + c->str); + rc = 0; goto out; } @@ -1359,17 +1469,13 @@ int security_load_policy(void *data, size_t len) int rc = 0; struct policy_file file = { data, len }, *fp = &file; - LOAD_LOCK; - if (!ss_initialized) { avtab_cache_init(); if (policydb_read(&policydb, fp)) { - LOAD_UNLOCK; avtab_cache_destroy(); return -EINVAL; } if (policydb_load_isids(&policydb, &sidtab)) { - LOAD_UNLOCK; policydb_destroy(&policydb); avtab_cache_destroy(); return -EINVAL; @@ -1378,7 +1484,6 @@ int security_load_policy(void *data, size_t len) if (validate_classes(&policydb)) { printk(KERN_ERR "SELinux: the definition of a class is incorrect\n"); - LOAD_UNLOCK; sidtab_destroy(&sidtab); policydb_destroy(&policydb); avtab_cache_destroy(); @@ -1388,7 +1493,6 @@ int security_load_policy(void *data, size_t len) policydb_loaded_version = policydb.policyvers; ss_initialized = 1; seqno = ++latest_granting; - LOAD_UNLOCK; selinux_complete_init(); avc_ss_reset(seqno); selnl_notify_policyload(seqno); @@ -1401,12 +1505,13 @@ int security_load_policy(void *data, size_t len) sidtab_hash_eval(&sidtab, "sids"); #endif - if (policydb_read(&newpolicydb, fp)) { - LOAD_UNLOCK; + if (policydb_read(&newpolicydb, fp)) return -EINVAL; - } - sidtab_init(&newsidtab); + if (sidtab_init(&newsidtab)) { + policydb_destroy(&newpolicydb); + return -ENOMEM; + } /* Verify that the kernel defined classes are correct. */ if (validate_classes(&newpolicydb)) { @@ -1429,25 +1534,28 @@ int security_load_policy(void *data, size_t len) goto err; } - /* Convert the internal representations of contexts - in the new SID table and remove invalid SIDs. */ + /* + * Convert the internal representations of contexts + * in the new SID table. + */ args.oldp = &policydb; args.newp = &newpolicydb; - sidtab_map_remove_on_error(&newsidtab, convert_context, &args); + rc = sidtab_map(&newsidtab, convert_context, &args); + if (rc) + goto err; /* Save the old policydb and SID table to free later. */ memcpy(&oldpolicydb, &policydb, sizeof policydb); sidtab_set(&oldsidtab, &sidtab); /* Install the new policydb and SID table. */ - POLICY_WRLOCK; + write_lock_irq(&policy_rwlock); memcpy(&policydb, &newpolicydb, sizeof policydb); sidtab_set(&sidtab, &newsidtab); security_load_policycaps(); seqno = ++latest_granting; policydb_loaded_version = policydb.policyvers; - POLICY_WRUNLOCK; - LOAD_UNLOCK; + write_unlock_irq(&policy_rwlock); /* Free the old policydb and SID table. */ policydb_destroy(&oldpolicydb); @@ -1461,7 +1569,6 @@ int security_load_policy(void *data, size_t len) return 0; err: - LOAD_UNLOCK; sidtab_destroy(&newsidtab); policydb_destroy(&newpolicydb); return rc; @@ -1479,7 +1586,7 @@ int security_port_sid(u8 protocol, u16 port, u32 *out_sid) struct ocontext *c; int rc = 0; - POLICY_RDLOCK; + read_lock(&policy_rwlock); c = policydb.ocontexts[OCON_PORT]; while (c) { @@ -1504,7 +1611,7 @@ int security_port_sid(u8 protocol, u16 port, u32 *out_sid) } out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -1518,7 +1625,7 @@ int security_netif_sid(char *name, u32 *if_sid) int rc = 0; struct ocontext *c; - POLICY_RDLOCK; + read_lock(&policy_rwlock); c = policydb.ocontexts[OCON_NETIF]; while (c) { @@ -1545,7 +1652,7 @@ int security_netif_sid(char *name, u32 *if_sid) *if_sid = SECINITSID_NETIF; out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -1577,7 +1684,7 @@ int security_node_sid(u16 domain, int rc = 0; struct ocontext *c; - POLICY_RDLOCK; + read_lock(&policy_rwlock); switch (domain) { case AF_INET: { @@ -1632,7 +1739,7 @@ int security_node_sid(u16 domain, } out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -1671,7 +1778,9 @@ int security_get_user_sids(u32 fromsid, if (!ss_initialized) goto out; - POLICY_RDLOCK; + read_lock(&policy_rwlock); + + context_init(&usercon); fromcon = sidtab_search(&sidtab, fromsid); if (!fromcon) { @@ -1722,7 +1831,7 @@ int security_get_user_sids(u32 fromsid, } out_unlock: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); if (rc || !mynel) { kfree(mysids); goto out; @@ -1775,7 +1884,7 @@ int security_genfs_sid(const char *fstype, while (path[0] == '/' && path[1] == '/') path++; - POLICY_RDLOCK; + read_lock(&policy_rwlock); for (genfs = policydb.genfs; genfs; genfs = genfs->next) { cmp = strcmp(fstype, genfs->fstype); @@ -1812,7 +1921,7 @@ int security_genfs_sid(const char *fstype, *sid = c->sid[0]; out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -1825,12 +1934,13 @@ out: int security_fs_use( const char *fstype, unsigned int *behavior, - u32 *sid) + u32 *sid, + bool can_xattr) { int rc = 0; struct ocontext *c; - POLICY_RDLOCK; + read_lock(&policy_rwlock); c = policydb.ocontexts[OCON_FSUSE]; while (c) { @@ -1839,6 +1949,7 @@ int security_fs_use( c = c->next; } + /* look for labeling behavior defined in policy */ if (c) { *behavior = c->v.behavior; if (!c->sid[0]) { @@ -1849,18 +1960,27 @@ int security_fs_use( goto out; } *sid = c->sid[0]; + goto out; + } + + /* labeling behavior not in policy, use xattrs if possible */ + if (can_xattr) { + *behavior = SECURITY_FS_USE_XATTR; + *sid = SECINITSID_FS; + goto out; + } + + /* no behavior in policy and can't use xattrs, try GENFS */ + rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid); + if (rc) { + *behavior = SECURITY_FS_USE_NONE; + rc = 0; } else { - rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid); - if (rc) { - *behavior = SECURITY_FS_USE_NONE; - rc = 0; - } else { - *behavior = SECURITY_FS_USE_GENFS; - } + *behavior = SECURITY_FS_USE_GENFS; } out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -1868,7 +1988,7 @@ int security_get_bools(int *len, char ***names, int **values) { int i, rc = -ENOMEM; - POLICY_RDLOCK; + read_lock(&policy_rwlock); *names = NULL; *values = NULL; @@ -1898,7 +2018,7 @@ int security_get_bools(int *len, char ***names, int **values) } rc = 0; out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; err: if (*names) { @@ -1916,7 +2036,7 @@ int security_set_bools(int len, int *values) int lenp, seqno = 0; struct cond_node *cur; - POLICY_WRLOCK; + write_lock_irq(&policy_rwlock); lenp = policydb.p_bools.nprim; if (len != lenp) { @@ -1950,7 +2070,7 @@ int security_set_bools(int len, int *values) seqno = ++latest_granting; out: - POLICY_WRUNLOCK; + write_unlock_irq(&policy_rwlock); if (!rc) { avc_ss_reset(seqno); selnl_notify_policyload(seqno); @@ -1964,7 +2084,7 @@ int security_get_bool_value(int bool) int rc = 0; int len; - POLICY_RDLOCK; + read_lock(&policy_rwlock); len = policydb.p_bools.nprim; if (bool >= len) { @@ -1974,7 +2094,7 @@ int security_get_bool_value(int bool) rc = policydb.bool_val_to_struct[bool]->state; out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -2029,7 +2149,7 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid) context_init(&newcon); - POLICY_RDLOCK; + read_lock(&policy_rwlock); context1 = sidtab_search(&sidtab, sid); if (!context1) { printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", @@ -2071,7 +2191,7 @@ bad: } out_unlock: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); context_destroy(&newcon); out: return rc; @@ -2128,7 +2248,7 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, return 0; } - POLICY_RDLOCK; + read_lock(&policy_rwlock); nlbl_ctx = sidtab_search(&sidtab, nlbl_sid); if (!nlbl_ctx) { @@ -2147,7 +2267,7 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, rc = (mls_context_cmp(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES); out_slowpath: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); if (rc == 0) /* at present NetLabel SIDs/labels really only carry MLS * information so if the MLS portion of the NetLabel SID @@ -2177,7 +2297,7 @@ int security_get_classes(char ***classes, int *nclasses) { int rc = -ENOMEM; - POLICY_RDLOCK; + read_lock(&policy_rwlock); *nclasses = policydb.p_classes.nprim; *classes = kcalloc(*nclasses, sizeof(*classes), GFP_ATOMIC); @@ -2194,7 +2314,7 @@ int security_get_classes(char ***classes, int *nclasses) } out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -2216,7 +2336,7 @@ int security_get_permissions(char *class, char ***perms, int *nperms) int rc = -ENOMEM, i; struct class_datum *match; - POLICY_RDLOCK; + read_lock(&policy_rwlock); match = hashtab_search(policydb.p_classes.table, class); if (!match) { @@ -2244,11 +2364,11 @@ int security_get_permissions(char *class, char ***perms, int *nperms) goto err; out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; err: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); for (i = 0; i < *nperms; i++) kfree((*perms)[i]); kfree(*perms); @@ -2279,9 +2399,9 @@ int security_policycap_supported(unsigned int req_cap) { int rc; - POLICY_RDLOCK; + read_lock(&policy_rwlock); rc = ebitmap_get_bit(&policydb.policycaps, req_cap); - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } @@ -2345,7 +2465,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) context_init(&tmprule->au_ctxt); - POLICY_RDLOCK; + read_lock(&policy_rwlock); tmprule->au_seqno = latest_granting; @@ -2382,7 +2502,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) break; } - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); if (rc) { selinux_audit_rule_free(tmprule); @@ -2420,7 +2540,7 @@ int selinux_audit_rule_known(struct audit_krule *rule) } int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, - struct audit_context *actx) + struct audit_context *actx) { struct context *ctxt; struct mls_level *level; @@ -2433,7 +2553,7 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, return -ENOENT; } - POLICY_RDLOCK; + read_lock(&policy_rwlock); if (rule->au_seqno < latest_granting) { audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR, @@ -2527,14 +2647,14 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, } out: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return match; } 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) + u16 class, u32 perms, u32 *retained) { int err = 0; @@ -2615,7 +2735,7 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, return 0; } - POLICY_RDLOCK; + read_lock(&policy_rwlock); if (secattr->flags & NETLBL_SECATTR_CACHE) { *sid = *(u32 *)secattr->cache->data; @@ -2660,7 +2780,7 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, } netlbl_secattr_to_sid_return: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; netlbl_secattr_to_sid_return_cleanup: ebitmap_destroy(&ctx_new.range.level[0].cat); @@ -2685,7 +2805,7 @@ int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr) if (!ss_initialized) return 0; - POLICY_RDLOCK; + read_lock(&policy_rwlock); ctx = sidtab_search(&sidtab, sid); if (ctx == NULL) goto netlbl_sid_to_secattr_failure; @@ -2696,12 +2816,12 @@ int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr) rc = mls_export_netlbl_cat(ctx, secattr); if (rc != 0) goto netlbl_sid_to_secattr_failure; - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return 0; netlbl_sid_to_secattr_failure: - POLICY_RDUNLOCK; + read_unlock(&policy_rwlock); return rc; } #endif /* CONFIG_NETLABEL */ diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c index 4a516ff4bcd..a81ded10412 100644 --- a/security/selinux/ss/sidtab.c +++ b/security/selinux/ss/sidtab.c @@ -14,10 +14,6 @@ #define SIDTAB_HASH(sid) \ (sid & SIDTAB_HASH_MASK) -#define INIT_SIDTAB_LOCK(s) spin_lock_init(&s->lock) -#define SIDTAB_LOCK(s, x) spin_lock_irqsave(&s->lock, x) -#define SIDTAB_UNLOCK(s, x) spin_unlock_irqrestore(&s->lock, x) - int sidtab_init(struct sidtab *s) { int i; @@ -30,7 +26,7 @@ int sidtab_init(struct sidtab *s) s->nel = 0; s->next_sid = 1; s->shutdown = 0; - INIT_SIDTAB_LOCK(s); + spin_lock_init(&s->lock); return 0; } @@ -86,7 +82,7 @@ out: return rc; } -struct context *sidtab_search(struct sidtab *s, u32 sid) +static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force) { int hvalue; struct sidtab_node *cur; @@ -99,7 +95,10 @@ struct context *sidtab_search(struct sidtab *s, u32 sid) while (cur != NULL && sid > cur->sid) cur = cur->next; - if (cur == NULL || sid != cur->sid) { + if (force && cur && sid == cur->sid && cur->context.len) + return &cur->context; + + if (cur == NULL || sid != cur->sid || cur->context.len) { /* Remap invalid SIDs to the unlabeled SID. */ sid = SECINITSID_UNLABELED; hvalue = SIDTAB_HASH(sid); @@ -113,6 +112,16 @@ struct context *sidtab_search(struct sidtab *s, u32 sid) return &cur->context; } +struct context *sidtab_search(struct sidtab *s, u32 sid) +{ + return sidtab_search_core(s, sid, 0); +} + +struct context *sidtab_search_force(struct sidtab *s, u32 sid) +{ + return sidtab_search_core(s, sid, 1); +} + int sidtab_map(struct sidtab *s, int (*apply) (u32 sid, struct context *context, @@ -138,43 +147,6 @@ out: return rc; } -void sidtab_map_remove_on_error(struct sidtab *s, - int (*apply) (u32 sid, - struct context *context, - void *args), - void *args) -{ - int i, ret; - struct sidtab_node *last, *cur, *temp; - - if (!s) - return; - - for (i = 0; i < SIDTAB_SIZE; i++) { - last = NULL; - cur = s->htable[i]; - while (cur != NULL) { - ret = apply(cur->sid, &cur->context, args); - if (ret) { - if (last) - last->next = cur->next; - else - s->htable[i] = cur->next; - temp = cur; - cur = cur->next; - context_destroy(&temp->context); - kfree(temp); - s->nel--; - } else { - last = cur; - cur = cur->next; - } - } - } - - return; -} - static inline u32 sidtab_search_context(struct sidtab *s, struct context *context) { @@ -204,7 +176,7 @@ int sidtab_context_to_sid(struct sidtab *s, sid = sidtab_search_context(s, context); if (!sid) { - SIDTAB_LOCK(s, flags); + spin_lock_irqsave(&s->lock, flags); /* Rescan now that we hold the lock. */ sid = sidtab_search_context(s, context); if (sid) @@ -215,11 +187,15 @@ int sidtab_context_to_sid(struct sidtab *s, goto unlock_out; } sid = s->next_sid++; + if (context->len) + printk(KERN_INFO + "SELinux: Context %s is not valid (left unmapped).\n", + context->str); ret = sidtab_insert(s, sid, context); if (ret) s->next_sid--; unlock_out: - SIDTAB_UNLOCK(s, flags); + spin_unlock_irqrestore(&s->lock, flags); } if (ret) @@ -284,19 +260,19 @@ void sidtab_set(struct sidtab *dst, struct sidtab *src) { unsigned long flags; - SIDTAB_LOCK(src, flags); + spin_lock_irqsave(&src->lock, flags); dst->htable = src->htable; dst->nel = src->nel; dst->next_sid = src->next_sid; dst->shutdown = 0; - SIDTAB_UNLOCK(src, flags); + spin_unlock_irqrestore(&src->lock, flags); } void sidtab_shutdown(struct sidtab *s) { unsigned long flags; - SIDTAB_LOCK(s, flags); + spin_lock_irqsave(&s->lock, flags); s->shutdown = 1; - SIDTAB_UNLOCK(s, flags); + spin_unlock_irqrestore(&s->lock, flags); } diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h index 2fe9dfa3eb3..64ea5b1cdea 100644 --- a/security/selinux/ss/sidtab.h +++ b/security/selinux/ss/sidtab.h @@ -32,6 +32,7 @@ struct sidtab { int sidtab_init(struct sidtab *s); int sidtab_insert(struct sidtab *s, u32 sid, struct context *context); struct context *sidtab_search(struct sidtab *s, u32 sid); +struct context *sidtab_search_force(struct sidtab *s, u32 sid); int sidtab_map(struct sidtab *s, int (*apply) (u32 sid, @@ -39,12 +40,6 @@ int sidtab_map(struct sidtab *s, void *args), void *args); -void sidtab_map_remove_on_error(struct sidtab *s, - int (*apply) (u32 sid, - struct context *context, - void *args), - void *args); - int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid); |