Commit 5825a95f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'selinux-pr-20190917' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux

Pull selinux updates from Paul Moore:

 - Add LSM hooks, and SELinux access control hooks, for dnotify,
   fanotify, and inotify watches. This has been discussed with both the
   LSM and fs/notify folks and everybody is good with these new hooks.

 - The LSM stacking changes missed a few calls to current_security() in
   the SELinux code; we fix those and remove current_security() for
   good.

 - Improve our network object labeling cache so that we always return
   the object's label, even when under memory pressure. Previously we
   would return an error if we couldn't allocate a new cache entry, now
   we always return the label even if we can't create a new cache entry
   for it.

 - Convert the sidtab atomic_t counter to a normal u32 with
   READ/WRITE_ONCE() and memory barrier protection.

 - A few patches to policydb.c to clean things up (remove forward
   declarations, long lines, bad variable names, etc)

* tag 'selinux-pr-20190917' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
  lsm: remove current_security()
  selinux: fix residual uses of current_security() for the SELinux blob
  selinux: avoid atomic_t usage in sidtab
  fanotify, inotify, dnotify, security: add security hook for fs notifications
  selinux: always return a secid from the network caches if we find one
  selinux: policydb - rename type_val_to_struct_array
  selinux: policydb - fix some checkpatch.pl warnings
  selinux: shuffle around policydb.c to get rid of forward declarations
parents 3c6a6910 15322a0d
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/sched/signal.h> #include <linux/sched/signal.h>
#include <linux/dnotify.h> #include <linux/dnotify.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/security.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/fdtable.h> #include <linux/fdtable.h>
...@@ -279,6 +280,17 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) ...@@ -279,6 +280,17 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
goto out_err; goto out_err;
} }
/*
* convert the userspace DN_* "arg" to the internal FS_*
* defined in fsnotify
*/
mask = convert_arg(arg);
error = security_path_notify(&filp->f_path, mask,
FSNOTIFY_OBJ_TYPE_INODE);
if (error)
goto out_err;
/* expect most fcntl to add new rather than augment old */ /* expect most fcntl to add new rather than augment old */
dn = kmem_cache_alloc(dnotify_struct_cache, GFP_KERNEL); dn = kmem_cache_alloc(dnotify_struct_cache, GFP_KERNEL);
if (!dn) { if (!dn) {
...@@ -293,9 +305,6 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) ...@@ -293,9 +305,6 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
goto out_err; goto out_err;
} }
/* convert the userspace DN_* "arg" to the internal FS_* defines in fsnotify */
mask = convert_arg(arg);
/* set up the new_fsn_mark and new_dn_mark */ /* set up the new_fsn_mark and new_dn_mark */
new_fsn_mark = &new_dn_mark->fsn_mark; new_fsn_mark = &new_dn_mark->fsn_mark;
fsnotify_init_mark(new_fsn_mark, dnotify_group); fsnotify_init_mark(new_fsn_mark, dnotify_group);
......
...@@ -528,7 +528,8 @@ static const struct file_operations fanotify_fops = { ...@@ -528,7 +528,8 @@ static const struct file_operations fanotify_fops = {
}; };
static int fanotify_find_path(int dfd, const char __user *filename, static int fanotify_find_path(int dfd, const char __user *filename,
struct path *path, unsigned int flags) struct path *path, unsigned int flags, __u64 mask,
unsigned int obj_type)
{ {
int ret; int ret;
...@@ -567,8 +568,15 @@ static int fanotify_find_path(int dfd, const char __user *filename, ...@@ -567,8 +568,15 @@ static int fanotify_find_path(int dfd, const char __user *filename,
/* you can only watch an inode if you have read permissions on it */ /* you can only watch an inode if you have read permissions on it */
ret = inode_permission(path->dentry->d_inode, MAY_READ); ret = inode_permission(path->dentry->d_inode, MAY_READ);
if (ret) {
path_put(path);
goto out;
}
ret = security_path_notify(path, mask, obj_type);
if (ret) if (ret)
path_put(path); path_put(path);
out: out:
return ret; return ret;
} }
...@@ -947,6 +955,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -947,6 +955,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
__kernel_fsid_t __fsid, *fsid = NULL; __kernel_fsid_t __fsid, *fsid = NULL;
u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS; u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS; unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
unsigned int obj_type;
int ret; int ret;
pr_debug("%s: fanotify_fd=%d flags=%x dfd=%d pathname=%p mask=%llx\n", pr_debug("%s: fanotify_fd=%d flags=%x dfd=%d pathname=%p mask=%llx\n",
...@@ -961,8 +970,13 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -961,8 +970,13 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
switch (mark_type) { switch (mark_type) {
case FAN_MARK_INODE: case FAN_MARK_INODE:
obj_type = FSNOTIFY_OBJ_TYPE_INODE;
break;
case FAN_MARK_MOUNT: case FAN_MARK_MOUNT:
obj_type = FSNOTIFY_OBJ_TYPE_VFSMOUNT;
break;
case FAN_MARK_FILESYSTEM: case FAN_MARK_FILESYSTEM:
obj_type = FSNOTIFY_OBJ_TYPE_SB;
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -1030,7 +1044,8 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -1030,7 +1044,8 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
goto fput_and_out; goto fput_and_out;
} }
ret = fanotify_find_path(dfd, pathname, &path, flags); ret = fanotify_find_path(dfd, pathname, &path, flags,
(mask & ALL_FSNOTIFY_EVENTS), obj_type);
if (ret) if (ret)
goto fput_and_out; goto fput_and_out;
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/memcontrol.h> #include <linux/memcontrol.h>
#include <linux/security.h>
#include "inotify.h" #include "inotify.h"
#include "../fdinfo.h" #include "../fdinfo.h"
...@@ -331,7 +332,8 @@ static const struct file_operations inotify_fops = { ...@@ -331,7 +332,8 @@ static const struct file_operations inotify_fops = {
/* /*
* find_inode - resolve a user-given path to a specific inode * find_inode - resolve a user-given path to a specific inode
*/ */
static int inotify_find_inode(const char __user *dirname, struct path *path, unsigned flags) static int inotify_find_inode(const char __user *dirname, struct path *path,
unsigned int flags, __u64 mask)
{ {
int error; int error;
...@@ -340,8 +342,15 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns ...@@ -340,8 +342,15 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns
return error; return error;
/* you can only watch an inode if you have read permissions on it */ /* you can only watch an inode if you have read permissions on it */
error = inode_permission(path->dentry->d_inode, MAY_READ); error = inode_permission(path->dentry->d_inode, MAY_READ);
if (error) {
path_put(path);
return error;
}
error = security_path_notify(path, mask,
FSNOTIFY_OBJ_TYPE_INODE);
if (error) if (error)
path_put(path); path_put(path);
return error; return error;
} }
...@@ -733,7 +742,8 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname, ...@@ -733,7 +742,8 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
if (mask & IN_ONLYDIR) if (mask & IN_ONLYDIR)
flags |= LOOKUP_DIRECTORY; flags |= LOOKUP_DIRECTORY;
ret = inotify_find_inode(pathname, &path, flags); ret = inotify_find_inode(pathname, &path, flags,
(mask & IN_ALL_EVENTS));
if (ret) if (ret)
goto fput_and_out; goto fput_and_out;
......
...@@ -386,7 +386,6 @@ static inline void put_cred(const struct cred *_cred) ...@@ -386,7 +386,6 @@ static inline void put_cred(const struct cred *_cred)
#define current_fsgid() (current_cred_xxx(fsgid)) #define current_fsgid() (current_cred_xxx(fsgid))
#define current_cap() (current_cred_xxx(cap_effective)) #define current_cap() (current_cred_xxx(cap_effective))
#define current_user() (current_cred_xxx(user)) #define current_user() (current_cred_xxx(user))
#define current_security() (current_cred_xxx(security))
extern struct user_namespace init_user_ns; extern struct user_namespace init_user_ns;
#ifdef CONFIG_USER_NS #ifdef CONFIG_USER_NS
......
...@@ -339,6 +339,9 @@ ...@@ -339,6 +339,9 @@
* Check for permission to change root directory. * Check for permission to change root directory.
* @path contains the path structure. * @path contains the path structure.
* Return 0 if permission is granted. * Return 0 if permission is granted.
* @path_notify:
* Check permissions before setting a watch on events as defined by @mask,
* on an object at @path, whose type is defined by @obj_type.
* @inode_readlink: * @inode_readlink:
* Check the permission to read the symbolic link. * Check the permission to read the symbolic link.
* @dentry contains the dentry structure for the file link. * @dentry contains the dentry structure for the file link.
...@@ -1535,7 +1538,9 @@ union security_list_options { ...@@ -1535,7 +1538,9 @@ union security_list_options {
int (*path_chown)(const struct path *path, kuid_t uid, kgid_t gid); int (*path_chown)(const struct path *path, kuid_t uid, kgid_t gid);
int (*path_chroot)(const struct path *path); int (*path_chroot)(const struct path *path);
#endif #endif
/* Needed for inode based security check */
int (*path_notify)(const struct path *path, u64 mask,
unsigned int obj_type);
int (*inode_alloc_security)(struct inode *inode); int (*inode_alloc_security)(struct inode *inode);
void (*inode_free_security)(struct inode *inode); void (*inode_free_security)(struct inode *inode);
int (*inode_init_security)(struct inode *inode, struct inode *dir, int (*inode_init_security)(struct inode *inode, struct inode *dir,
...@@ -1860,6 +1865,8 @@ struct security_hook_heads { ...@@ -1860,6 +1865,8 @@ struct security_hook_heads {
struct hlist_head path_chown; struct hlist_head path_chown;
struct hlist_head path_chroot; struct hlist_head path_chroot;
#endif #endif
/* Needed for inode based modules as well */
struct hlist_head path_notify;
struct hlist_head inode_alloc_security; struct hlist_head inode_alloc_security;
struct hlist_head inode_free_security; struct hlist_head inode_free_security;
struct hlist_head inode_init_security; struct hlist_head inode_init_security;
......
...@@ -259,7 +259,8 @@ int security_dentry_create_files_as(struct dentry *dentry, int mode, ...@@ -259,7 +259,8 @@ int security_dentry_create_files_as(struct dentry *dentry, int mode,
struct qstr *name, struct qstr *name,
const struct cred *old, const struct cred *old,
struct cred *new); struct cred *new);
int security_path_notify(const struct path *path, u64 mask,
unsigned int obj_type);
int security_inode_alloc(struct inode *inode); int security_inode_alloc(struct inode *inode);
void security_inode_free(struct inode *inode); void security_inode_free(struct inode *inode);
int security_inode_init_security(struct inode *inode, struct inode *dir, int security_inode_init_security(struct inode *inode, struct inode *dir,
...@@ -387,7 +388,6 @@ int security_ismaclabel(const char *name); ...@@ -387,7 +388,6 @@ int security_ismaclabel(const char *name);
int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid); int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
void security_release_secctx(char *secdata, u32 seclen); void security_release_secctx(char *secdata, u32 seclen);
void security_inode_invalidate_secctx(struct inode *inode); void security_inode_invalidate_secctx(struct inode *inode);
int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen); int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen); int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
...@@ -621,6 +621,12 @@ static inline int security_move_mount(const struct path *from_path, ...@@ -621,6 +621,12 @@ static inline int security_move_mount(const struct path *from_path,
return 0; return 0;
} }
static inline int security_path_notify(const struct path *path, u64 mask,
unsigned int obj_type)
{
return 0;
}
static inline int security_inode_alloc(struct inode *inode) static inline int security_inode_alloc(struct inode *inode)
{ {
return 0; return 0;
......
...@@ -870,6 +870,12 @@ int security_move_mount(const struct path *from_path, const struct path *to_path ...@@ -870,6 +870,12 @@ int security_move_mount(const struct path *from_path, const struct path *to_path
return call_int_hook(move_mount, 0, from_path, to_path); return call_int_hook(move_mount, 0, from_path, to_path);
} }
int security_path_notify(const struct path *path, u64 mask,
unsigned int obj_type)
{
return call_int_hook(path_notify, 0, path, mask, obj_type);
}
int security_inode_alloc(struct inode *inode) int security_inode_alloc(struct inode *inode)
{ {
int rc = lsm_inode_alloc(inode); int rc = lsm_inode_alloc(inode);
......
...@@ -89,6 +89,8 @@ ...@@ -89,6 +89,8 @@
#include <linux/kernfs.h> #include <linux/kernfs.h>
#include <linux/stringhash.h> /* for hashlen_string() */ #include <linux/stringhash.h> /* for hashlen_string() */
#include <uapi/linux/mount.h> #include <uapi/linux/mount.h>
#include <linux/fsnotify.h>
#include <linux/fanotify.h>
#include "avc.h" #include "avc.h"
#include "objsec.h" #include "objsec.h"
...@@ -3275,6 +3277,50 @@ static int selinux_inode_removexattr(struct dentry *dentry, const char *name) ...@@ -3275,6 +3277,50 @@ static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
return -EACCES; return -EACCES;
} }
static int selinux_path_notify(const struct path *path, u64 mask,
unsigned int obj_type)
{
int ret;
u32 perm;
struct common_audit_data ad;
ad.type = LSM_AUDIT_DATA_PATH;
ad.u.path = *path;
/*
* Set permission needed based on the type of mark being set.
* Performs an additional check for sb watches.
*/
switch (obj_type) {
case FSNOTIFY_OBJ_TYPE_VFSMOUNT:
perm = FILE__WATCH_MOUNT;
break;
case FSNOTIFY_OBJ_TYPE_SB:
perm = FILE__WATCH_SB;
ret = superblock_has_perm(current_cred(), path->dentry->d_sb,
FILESYSTEM__WATCH, &ad);
if (ret)
return ret;
break;
case FSNOTIFY_OBJ_TYPE_INODE:
perm = FILE__WATCH;
break;
default:
return -EINVAL;
}
/* blocking watches require the file:watch_with_perm permission */
if (mask & (ALL_FSNOTIFY_PERM_EVENTS))
perm |= FILE__WATCH_WITH_PERM;
/* watches on read-like events need the file:watch_reads permission */
if (mask & (FS_ACCESS | FS_ACCESS_PERM | FS_CLOSE_NOWRITE))
perm |= FILE__WATCH_READS;
return path_has_perm(current_cred(), path, perm);
}
/* /*
* Copy the inode security context value to the user. * Copy the inode security context value to the user.
* *
...@@ -3403,7 +3449,7 @@ static int selinux_inode_copy_up_xattr(const char *name) ...@@ -3403,7 +3449,7 @@ static int selinux_inode_copy_up_xattr(const char *name)
static int selinux_kernfs_init_security(struct kernfs_node *kn_dir, static int selinux_kernfs_init_security(struct kernfs_node *kn_dir,
struct kernfs_node *kn) struct kernfs_node *kn)
{ {
const struct task_security_struct *tsec = current_security(); const struct task_security_struct *tsec = selinux_cred(current_cred());
u32 parent_sid, newsid, clen; u32 parent_sid, newsid, clen;
int rc; int rc;
char *context; char *context;
...@@ -6818,6 +6864,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { ...@@ -6818,6 +6864,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(inode_getsecid, selinux_inode_getsecid), LSM_HOOK_INIT(inode_getsecid, selinux_inode_getsecid),
LSM_HOOK_INIT(inode_copy_up, selinux_inode_copy_up), LSM_HOOK_INIT(inode_copy_up, selinux_inode_copy_up),
LSM_HOOK_INIT(inode_copy_up_xattr, selinux_inode_copy_up_xattr), LSM_HOOK_INIT(inode_copy_up_xattr, selinux_inode_copy_up_xattr),
LSM_HOOK_INIT(path_notify, selinux_path_notify),
LSM_HOOK_INIT(kernfs_init_security, selinux_kernfs_init_security), LSM_HOOK_INIT(kernfs_init_security, selinux_kernfs_init_security),
......
...@@ -7,7 +7,8 @@ ...@@ -7,7 +7,8 @@
#define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \ #define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \
"rename", "execute", "quotaon", "mounton", "audit_access", \ "rename", "execute", "quotaon", "mounton", "audit_access", \
"open", "execmod" "open", "execmod", "watch", "watch_mount", "watch_sb", \
"watch_with_perm", "watch_reads"
#define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \ #define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \
"listen", "accept", "getopt", "setopt", "shutdown", "recvfrom", \ "listen", "accept", "getopt", "setopt", "shutdown", "recvfrom", \
...@@ -60,7 +61,7 @@ struct security_class_mapping secclass_map[] = { ...@@ -60,7 +61,7 @@ struct security_class_mapping secclass_map[] = {
{ "filesystem", { "filesystem",
{ "mount", "remount", "unmount", "getattr", { "mount", "remount", "unmount", "getattr",
"relabelfrom", "relabelto", "associate", "quotamod", "relabelfrom", "relabelto", "associate", "quotamod",
"quotaget", NULL } }, "quotaget", "watch", NULL } },
{ "file", { "file",
{ COMMON_FILE_PERMS, { COMMON_FILE_PERMS,
"execute_no_trans", "entrypoint", NULL } }, "execute_no_trans", "entrypoint", NULL } },
......
...@@ -37,16 +37,6 @@ struct task_security_struct { ...@@ -37,16 +37,6 @@ struct task_security_struct {
u32 sockcreate_sid; /* fscreate SID */ u32 sockcreate_sid; /* fscreate SID */
}; };
/*
* get the subjective security ID of the current task
*/
static inline u32 current_sid(void)
{
const struct task_security_struct *tsec = current_security();
return tsec->sid;
}
enum label_initialized { enum label_initialized {
LABEL_INVALID, /* invalid or not initialized */ LABEL_INVALID, /* invalid or not initialized */
LABEL_INITIALIZED, /* initialized */ LABEL_INITIALIZED, /* initialized */
...@@ -185,4 +175,14 @@ static inline struct ipc_security_struct *selinux_ipc( ...@@ -185,4 +175,14 @@ static inline struct ipc_security_struct *selinux_ipc(
return ipc->security + selinux_blob_sizes.lbs_ipc; return ipc->security + selinux_blob_sizes.lbs_ipc;
} }
/*
* get the subjective security ID of the current task
*/
static inline u32 current_sid(void)
{
const struct task_security_struct *tsec = selinux_cred(current_cred());
return tsec->sid;
}
#endif /* _SELINUX_OBJSEC_H_ */ #endif /* _SELINUX_OBJSEC_H_ */
...@@ -132,9 +132,9 @@ static void sel_netif_destroy(struct sel_netif *netif) ...@@ -132,9 +132,9 @@ static void sel_netif_destroy(struct sel_netif *netif)
*/ */
static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid) static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid)
{ {
int ret; int ret = 0;
struct sel_netif *netif; struct sel_netif *netif;
struct sel_netif *new = NULL; struct sel_netif *new;
struct net_device *dev; struct net_device *dev;
/* NOTE: we always use init's network namespace since we don't /* NOTE: we always use init's network namespace since we don't
...@@ -151,32 +151,27 @@ static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid) ...@@ -151,32 +151,27 @@ static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid)
netif = sel_netif_find(ns, ifindex); netif = sel_netif_find(ns, ifindex);
if (netif != NULL) { if (netif != NULL) {
*sid = netif->nsec.sid; *sid = netif->nsec.sid;
ret = 0;
goto out; goto out;
} }
new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (new == NULL) { ret = security_netif_sid(&selinux_state, dev->name, sid);
ret = -ENOMEM;
goto out;
}
ret = security_netif_sid(&selinux_state, dev->name, &new->nsec.sid);
if (ret != 0)
goto out;
new->nsec.ns = ns;
new->nsec.ifindex = ifindex;
ret = sel_netif_insert(new);
if (ret != 0) if (ret != 0)
goto out; goto out;
*sid = new->nsec.sid; new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (new) {
new->nsec.ns = ns;
new->nsec.ifindex = ifindex;
new->nsec.sid = *sid;
if (sel_netif_insert(new))
kfree(new);
}
out: out:
spin_unlock_bh(&sel_netif_lock); spin_unlock_bh(&sel_netif_lock);
dev_put(dev); dev_put(dev);
if (unlikely(ret)) { if (unlikely(ret))
pr_warn("SELinux: failure in %s(), unable to determine network interface label (%d)\n", pr_warn("SELinux: failure in %s(), unable to determine network interface label (%d)\n",
__func__, ifindex); __func__, ifindex);
kfree(new);
}
return ret; return ret;
} }
......
...@@ -189,9 +189,9 @@ static void sel_netnode_insert(struct sel_netnode *node) ...@@ -189,9 +189,9 @@ static void sel_netnode_insert(struct sel_netnode *node)
*/ */
static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid) static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
{ {
int ret = -ENOMEM; int ret;
struct sel_netnode *node; struct sel_netnode *node;
struct sel_netnode *new = NULL; struct sel_netnode *new;
spin_lock_bh(&sel_netnode_lock); spin_lock_bh(&sel_netnode_lock);
node = sel_netnode_find(addr, family); node = sel_netnode_find(addr, family);
...@@ -200,38 +200,36 @@ static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid) ...@@ -200,38 +200,36 @@ static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
spin_unlock_bh(&sel_netnode_lock); spin_unlock_bh(&sel_netnode_lock);
return 0; return 0;
} }
new = kzalloc(sizeof(*new), GFP_ATOMIC); new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (new == NULL)
goto out;
switch (family) { switch (family) {
case PF_INET: case PF_INET:
ret = security_node_sid(&selinux_state, PF_INET, ret = security_node_sid(&selinux_state, PF_INET,
addr, sizeof(struct in_addr), sid); addr, sizeof(struct in_addr), sid);
new->nsec.addr.ipv4 = *(__be32 *)addr; if (new)
new->nsec.addr.ipv4 = *(__be32 *)addr;
break; break;
case PF_INET6: case PF_INET6:
ret = security_node_sid(&selinux_state, PF_INET6, ret = security_node_sid(&selinux_state, PF_INET6,
addr, sizeof(struct in6_addr), sid); addr, sizeof(struct in6_addr), sid);
new->nsec.addr.ipv6 = *(struct in6_addr *)addr; if (new)
new->nsec.addr.ipv6 = *(struct in6_addr *)addr;
break; break;
default: default:
BUG(); BUG();
ret = -EINVAL; ret = -EINVAL;
} }
if (ret != 0) if (ret == 0 && new) {
goto out; new->nsec.family = family;
new->nsec.sid = *sid;
new->nsec.family = family; sel_netnode_insert(new);
new->nsec.sid = *sid; } else
sel_netnode_insert(new); kfree(new);
out:
spin_unlock_bh(&sel_netnode_lock); spin_unlock_bh(&sel_netnode_lock);
if (unlikely(ret)) { if (unlikely(ret))
pr_warn("SELinux: failure in %s(), unable to determine network node label\n", pr_warn("SELinux: failure in %s(), unable to determine network node label\n",
__func__); __func__);
kfree(new);
}
return ret; return ret;
} }
......
...@@ -137,9 +137,9 @@ static void sel_netport_insert(struct sel_netport *port) ...@@ -137,9 +137,9 @@ static void sel_netport_insert(struct sel_netport *port)
*/ */
static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid) static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)
{ {
int ret = -ENOMEM; int ret;
struct sel_netport *port; struct sel_netport *port;
struct sel_netport *new = NULL; struct sel_netport *new;
spin_lock_bh(&sel_netport_lock); spin_lock_bh(&sel_netport_lock);
port = sel_netport_find(protocol, pnum); port = sel_netport_find(protocol, pnum);
...@@ -148,25 +148,23 @@ static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid) ...@@ -148,25 +148,23 @@ static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)
spin_unlock_bh(&sel_netport_lock); spin_unlock_bh(&sel_netport_lock);
return 0; return 0;
} }
new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (new == NULL)
goto out;
ret = security_port_sid(&selinux_state, protocol, pnum, sid); ret = security_port_sid(&selinux_state, protocol, pnum, sid);
if (ret != 0) if (ret != 0)
goto out; goto out;
new = kzalloc(sizeof(*new), GFP_ATOMIC);
new->psec.port = pnum; if (new) {
new->psec.protocol = protocol; new->psec.port = pnum;
new->psec.sid = *sid; new->psec.protocol = protocol;
sel_netport_insert(new); new->psec.sid = *sid;
sel_netport_insert(new);
}
out: out:
spin_unlock_bh(&sel_netport_lock); spin_unlock_bh(&sel_netport_lock);
if (unlikely(ret)) { if (unlikely(ret))
pr_warn("SELinux: failure in %s(), unable to determine network port label\n", pr_warn("SELinux: failure in %s(), unable to determine network port label\n",
__func__); __func__);
kfree(new);
}
return ret; return ret;
} }
......
This diff is collapsed.
...@@ -253,7 +253,7 @@ struct policydb { ...@@ -253,7 +253,7 @@ struct policydb {
struct class_datum **class_val_to_struct; struct class_datum **class_val_to_struct;
struct role_datum **role_val_to_struct; struct role_datum **role_val_to_struct;
struct user_datum **user_val_to_struct; struct user_datum **user_val_to_struct;
struct type_datum **type_val_to_struct_array; struct type_datum **type_val_to_struct;
/* type enforcement access vectors and transitions */ /* type enforcement access vectors and transitions */
struct avtab te_avtab; struct avtab te_avtab;
......
...@@ -542,13 +542,13 @@ static void type_attribute_bounds_av(struct policydb *policydb, ...@@ -542,13 +542,13 @@ static void type_attribute_bounds_av(struct policydb *policydb,
struct type_datum *target; struct type_datum *target;
u32 masked = 0; u32 masked = 0;
source = policydb->type_val_to_struct_array[scontext->type - 1]; source = policydb->type_val_to_struct[scontext->type - 1];
BUG_ON(!source); BUG_ON(!source);
if (!source->bounds) if (!source->bounds)
return; return;
target = policydb->type_val_to_struct_array[tcontext->type - 1]; target = policydb->type_val_to_struct[tcontext->type - 1];
BUG_ON(!target); BUG_ON(!target);
memset(&lo_avd, 0, sizeof(lo_avd)); memset(&lo_avd, 0, sizeof(lo_avd));
...@@ -891,7 +891,7 @@ int security_bounded_transition(struct selinux_state *state, ...@@ -891,7 +891,7 @@ int security_bounded_transition(struct selinux_state *state,
index = new_context->type; index = new_context->type;
while (true) { while (true) {
type = policydb->type_val_to_struct_array[index - 1]; type = policydb->type_val_to_struct[index - 1];
BUG_ON(!type); BUG_ON(!type);
/* not bounded anymore */ /* not bounded anymore */
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/atomic.h> #include <asm/barrier.h>
#include "flask.h" #include "flask.h"
#include "security.h" #include "security.h"
#include "sidtab.h" #include "sidtab.h"
...@@ -23,14 +23,14 @@ int sidtab_init(struct sidtab *s) ...@@ -23,14 +23,14 @@ int sidtab_init(struct sidtab *s)
memset(s->roots, 0, sizeof(s->roots)); memset(s->roots, 0, sizeof(s->roots));
/* max count is SIDTAB_MAX so valid index is always < SIDTAB_MAX */
for (i = 0; i < SIDTAB_RCACHE_SIZE; i++) for (i = 0; i < SIDTAB_RCACHE_SIZE; i++)
atomic_set(&s->rcache[i], -1); s->rcache[i] = SIDTAB_MAX;
for (i = 0; i < SECINITSID_NUM; i++) for (i = 0; i < SECINITSID_NUM; i++)
s->isids[i].set = 0; s->isids[i].set = 0;
atomic_set(&s->count, 0); s->count = 0;
s->convert = NULL; s->convert = NULL;
spin_lock_init(&s->lock); spin_lock_init(&s->lock);
...@@ -130,14 +130,12 @@ static struct context *sidtab_do_lookup(struct sidtab *s, u32 index, int alloc) ...@@ -130,14 +130,12 @@ static struct context *sidtab_do_lookup(struct sidtab *s, u32 index, int alloc)
static struct context *sidtab_lookup(struct sidtab *s, u32 index) static struct context *sidtab_lookup(struct sidtab *s, u32 index)
{ {
u32 count = (u32)atomic_read(&s->count); /* read entries only after reading count */
u32 count = smp_load_acquire(&s->count);
if (index >= count) if (index >= count)
return NULL; return NULL;
/* read entries after reading count */
smp_rmb();
return sidtab_do_lookup(s, index, 0); return sidtab_do_lookup(s, index, 0);
} }
...@@ -210,10 +208,10 @@ static int sidtab_find_context(union sidtab_entry_inner entry, ...@@ -210,10 +208,10 @@ static int sidtab_find_context(union sidtab_entry_inner entry,
static void sidtab_rcache_update(struct sidtab *s, u32 index, u32 pos) static void sidtab_rcache_update(struct sidtab *s, u32 index, u32 pos)
{ {
while (pos > 0) { while (pos > 0) {
atomic_set(&s->rcache[pos], atomic_read(&s->rcache[pos - 1])); WRITE_ONCE(s->rcache[pos], READ_ONCE(s->rcache[pos - 1]));
--pos; --pos;
} }
atomic_set(&s->rcache[0], (int)index); WRITE_ONCE(s->rcache[0], index);
} }
static void sidtab_rcache_push(struct sidtab *s, u32 index) static void sidtab_rcache_push(struct sidtab *s, u32 index)
...@@ -227,14 +225,14 @@ static int sidtab_rcache_search(struct sidtab *s, struct context *context, ...@@ -227,14 +225,14 @@ static int sidtab_rcache_search(struct sidtab *s, struct context *context,
u32 i; u32 i;
for (i = 0; i < SIDTAB_RCACHE_SIZE; i++) { for (i = 0; i < SIDTAB_RCACHE_SIZE; i++) {
int v = atomic_read(&s->rcache[i]); u32 v = READ_ONCE(s->rcache[i]);
if (v < 0) if (v >= SIDTAB_MAX)
continue; continue;
if (context_cmp(sidtab_do_lookup(s, (u32)v, 0), context)) { if (context_cmp(sidtab_do_lookup(s, v, 0), context)) {
sidtab_rcache_update(s, (u32)v, i); sidtab_rcache_update(s, v, i);
*index = (u32)v; *index = v;
return 0; return 0;
} }
} }
...@@ -245,8 +243,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context, ...@@ -245,8 +243,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
u32 *index) u32 *index)
{ {
unsigned long flags; unsigned long flags;
u32 count = (u32)atomic_read(&s->count); u32 count, count_locked, level, pos;
u32 count_locked, level, pos;
struct sidtab_convert_params *convert; struct sidtab_convert_params *convert;
struct context *dst, *dst_convert; struct context *dst, *dst_convert;
int rc; int rc;
...@@ -255,11 +252,10 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context, ...@@ -255,11 +252,10 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
if (rc == 0) if (rc == 0)
return 0; return 0;
/* read entries only after reading count */
count = smp_load_acquire(&s->count);
level = sidtab_level_from_count(count); level = sidtab_level_from_count(count);
/* read entries after reading count */
smp_rmb();
pos = 0; pos = 0;
rc = sidtab_find_context(s->roots[level], &pos, count, level, rc = sidtab_find_context(s->roots[level], &pos, count, level,
context, index); context, index);
...@@ -272,7 +268,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context, ...@@ -272,7 +268,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
spin_lock_irqsave(&s->lock, flags); spin_lock_irqsave(&s->lock, flags);
convert = s->convert; convert = s->convert;
count_locked = (u32)atomic_read(&s->count); count_locked = s->count;
level = sidtab_level_from_count(count_locked); level = sidtab_level_from_count(count_locked);
/* if count has changed before we acquired the lock, then catch up */ /* if count has changed before we acquired the lock, then catch up */
...@@ -320,7 +316,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context, ...@@ -320,7 +316,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
} }
/* at this point we know the insert won't fail */ /* at this point we know the insert won't fail */
atomic_set(&convert->target->count, count + 1); convert->target->count = count + 1;
} }
if (context->len) if (context->len)
...@@ -331,9 +327,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context, ...@@ -331,9 +327,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
*index = count; *index = count;
/* write entries before writing new count */ /* write entries before writing new count */
smp_wmb(); smp_store_release(&s->count, count + 1);
atomic_set(&s->count, count + 1);
rc = 0; rc = 0;
out_unlock: out_unlock:
...@@ -423,7 +417,7 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params) ...@@ -423,7 +417,7 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params)
return -EBUSY; return -EBUSY;
} }
count = (u32)atomic_read(&s->count); count = s->count;
level = sidtab_level_from_count(count); level = sidtab_level_from_count(count);
/* allocate last leaf in the new sidtab (to avoid race with /* allocate last leaf in the new sidtab (to avoid race with
...@@ -436,7 +430,7 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params) ...@@ -436,7 +430,7 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params)
} }
/* set count in case no new entries are added during conversion */ /* set count in case no new entries are added during conversion */
atomic_set(&params->target->count, count); params->target->count = count;
/* enable live convert of new entries */ /* enable live convert of new entries */
s->convert = params; s->convert = params;
......
...@@ -40,8 +40,8 @@ union sidtab_entry_inner { ...@@ -40,8 +40,8 @@ union sidtab_entry_inner {
#define SIDTAB_LEAF_ENTRIES \ #define SIDTAB_LEAF_ENTRIES \
(SIDTAB_NODE_ALLOC_SIZE / sizeof(struct sidtab_entry_leaf)) (SIDTAB_NODE_ALLOC_SIZE / sizeof(struct sidtab_entry_leaf))
#define SIDTAB_MAX_BITS 31 /* limited to INT_MAX due to atomic_t range */ #define SIDTAB_MAX_BITS 32
#define SIDTAB_MAX (((u32)1 << SIDTAB_MAX_BITS) - 1) #define SIDTAB_MAX U32_MAX
/* ensure enough tree levels for SIDTAB_MAX entries */ /* ensure enough tree levels for SIDTAB_MAX entries */
#define SIDTAB_MAX_LEVEL \ #define SIDTAB_MAX_LEVEL \
DIV_ROUND_UP(SIDTAB_MAX_BITS - size_to_shift(SIDTAB_LEAF_ENTRIES), \ DIV_ROUND_UP(SIDTAB_MAX_BITS - size_to_shift(SIDTAB_LEAF_ENTRIES), \
...@@ -69,13 +69,22 @@ struct sidtab_convert_params { ...@@ -69,13 +69,22 @@ struct sidtab_convert_params {
#define SIDTAB_RCACHE_SIZE 3 #define SIDTAB_RCACHE_SIZE 3
struct sidtab { struct sidtab {
/*
* lock-free read access only for as many items as a prior read of
* 'count'
*/
union sidtab_entry_inner roots[SIDTAB_MAX_LEVEL + 1]; union sidtab_entry_inner roots[SIDTAB_MAX_LEVEL + 1];
atomic_t count; /*
* access atomically via {READ|WRITE}_ONCE(); only increment under
* spinlock
*/
u32 count;
/* access only under spinlock */
struct sidtab_convert_params *convert; struct sidtab_convert_params *convert;
spinlock_t lock; spinlock_t lock;
/* reverse lookup cache */ /* reverse lookup cache - access atomically via {READ|WRITE}_ONCE() */
atomic_t rcache[SIDTAB_RCACHE_SIZE]; u32 rcache[SIDTAB_RCACHE_SIZE];
/* index == SID - 1 (no entry for SECSID_NULL) */ /* index == SID - 1 (no entry for SECSID_NULL) */
struct sidtab_isid_entry isids[SECINITSID_NUM]; struct sidtab_isid_entry isids[SECINITSID_NUM];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment