Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6: (43 commits) TOMOYO: Fix wrong domainname validation. SELINUX: add /sys/fs/selinux mount point to put selinuxfs CRED: Fix load_flat_shared_library() to initialise bprm correctly SELinux: introduce path_has_perm flex_array: allow 0 length elements flex_arrays: allow zero length flex arrays flex_array: flex_array_prealloc takes a number of elements, not an end SELinux: pass last path component in may_create SELinux: put name based create rules in a hashtable SELinux: generic hashtab entry counter SELinux: calculate and print hashtab stats with a generic function SELinux: skip filename trans rules if ttype does not match parent dir SELinux: rename filename_compute_type argument to *type instead of *con SELinux: fix comment to state filename_compute_type takes an objname not a qstr SMACK: smack_file_lock can use the struct path LSM: separate LSM_AUDIT_DATA_DENTRY from LSM_AUDIT_DATA_PATH LSM: split LSM_AUDIT_DATA_FS into _PATH and _INODE SELINUX: Make selinux cache VFS RCU walks safe SECURITY: Move exec_permission RCU checks into security modules SELinux: security_read_policy should take a size_t not ssize_t ...
This commit is contained in:
commit
b0ca118dba
39 changed files with 604 additions and 285 deletions
|
@ -5592,10 +5592,11 @@ M: James Morris <jmorris@namei.org>
|
|||
M: Eric Paris <eparis@parisplace.org>
|
||||
L: selinux@tycho.nsa.gov (subscribers-only, general discussion)
|
||||
W: http://selinuxproject.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6.git
|
||||
T: git git://git.infradead.org/users/eparis/selinux.git
|
||||
S: Supported
|
||||
F: include/linux/selinux*
|
||||
F: security/selinux/
|
||||
F: scripts/selinux/
|
||||
|
||||
APPARMOR SECURITY MODULE
|
||||
M: John Johansen <john.johansen@canonical.com>
|
||||
|
|
|
@ -820,6 +820,8 @@ static int load_flat_shared_library(int id, struct lib_info *libs)
|
|||
int res;
|
||||
char buf[16];
|
||||
|
||||
memset(&bprm, 0, sizeof(bprm));
|
||||
|
||||
/* Create the file name */
|
||||
sprintf(buf, "/lib/lib%d.so", id);
|
||||
|
||||
|
@ -835,6 +837,12 @@ static int load_flat_shared_library(int id, struct lib_info *libs)
|
|||
if (!bprm.cred)
|
||||
goto out;
|
||||
|
||||
/* We don't really care about recalculating credentials at this point
|
||||
* as we're past the point of no return and are dealing with shared
|
||||
* libraries.
|
||||
*/
|
||||
bprm.cred_prepared = 1;
|
||||
|
||||
res = prepare_binprm(&bprm);
|
||||
|
||||
if (!IS_ERR_VALUE(res))
|
||||
|
|
|
@ -417,7 +417,6 @@ extern const kernel_cap_t __cap_init_eff_set;
|
|||
|
||||
# define CAP_EMPTY_SET ((kernel_cap_t){{ 0, 0 }})
|
||||
# define CAP_FULL_SET ((kernel_cap_t){{ ~0, ~0 }})
|
||||
# define CAP_INIT_EFF_SET ((kernel_cap_t){{ ~CAP_TO_MASK(CAP_SETPCAP), ~0 }})
|
||||
# define CAP_FS_SET ((kernel_cap_t){{ CAP_FS_MASK_B0 \
|
||||
| CAP_TO_MASK(CAP_LINUX_IMMUTABLE), \
|
||||
CAP_FS_MASK_B1 } })
|
||||
|
@ -427,11 +426,7 @@ extern const kernel_cap_t __cap_init_eff_set;
|
|||
|
||||
#endif /* _KERNEL_CAPABILITY_U32S != 2 */
|
||||
|
||||
#define CAP_INIT_INH_SET CAP_EMPTY_SET
|
||||
|
||||
# define cap_clear(c) do { (c) = __cap_empty_set; } while (0)
|
||||
# define cap_set_full(c) do { (c) = __cap_full_set; } while (0)
|
||||
# define cap_set_init_eff(c) do { (c) = __cap_init_eff_set; } while (0)
|
||||
|
||||
#define cap_raise(c, flag) ((c).cap[CAP_TO_INDEX(flag)] |= CAP_TO_MASK(flag))
|
||||
#define cap_lower(c, flag) ((c).cap[CAP_TO_INDEX(flag)] &= ~CAP_TO_MASK(flag))
|
||||
|
|
|
@ -83,13 +83,6 @@ extern struct group_info init_groups;
|
|||
#define INIT_IDS
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Because of the reduced scope of CAP_SETPCAP when filesystem
|
||||
* capabilities are in effect, it is safe to allow CAP_SETPCAP to
|
||||
* be available in the default configuration.
|
||||
*/
|
||||
# define CAP_INIT_BSET CAP_FULL_SET
|
||||
|
||||
#ifdef CONFIG_RCU_BOOST
|
||||
#define INIT_TASK_RCU_BOOST() \
|
||||
.rcu_boost_mutex = NULL,
|
||||
|
|
|
@ -276,6 +276,19 @@ static inline key_serial_t key_serial(struct key *key)
|
|||
return key ? key->serial : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* key_is_instantiated - Determine if a key has been positively instantiated
|
||||
* @key: The key to check.
|
||||
*
|
||||
* Return true if the specified key has been positively instantiated, false
|
||||
* otherwise.
|
||||
*/
|
||||
static inline bool key_is_instantiated(const struct key *key)
|
||||
{
|
||||
return test_bit(KEY_FLAG_INSTANTIATED, &key->flags) &&
|
||||
!test_bit(KEY_FLAG_NEGATIVE, &key->flags);
|
||||
}
|
||||
|
||||
#define rcu_dereference_key(KEY) \
|
||||
(rcu_dereference_protected((KEY)->payload.rcudata, \
|
||||
rwsem_is_locked(&((struct key *)(KEY))->sem)))
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/sysctl.h>
|
||||
|
||||
#define KMOD_PATH_LEN 256
|
||||
|
||||
|
@ -109,6 +110,8 @@ call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
|
|||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
extern struct ctl_table usermodehelper_table[];
|
||||
|
||||
extern void usermodehelper_init(void);
|
||||
|
||||
extern int usermodehelper_disable(void);
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
/* Auxiliary data to use in generating the audit record. */
|
||||
struct common_audit_data {
|
||||
char type;
|
||||
#define LSM_AUDIT_DATA_FS 1
|
||||
#define LSM_AUDIT_DATA_PATH 1
|
||||
#define LSM_AUDIT_DATA_NET 2
|
||||
#define LSM_AUDIT_DATA_CAP 3
|
||||
#define LSM_AUDIT_DATA_IPC 4
|
||||
|
@ -35,12 +35,13 @@ struct common_audit_data {
|
|||
#define LSM_AUDIT_DATA_KEY 6
|
||||
#define LSM_AUDIT_DATA_NONE 7
|
||||
#define LSM_AUDIT_DATA_KMOD 8
|
||||
#define LSM_AUDIT_DATA_INODE 9
|
||||
#define LSM_AUDIT_DATA_DENTRY 10
|
||||
struct task_struct *tsk;
|
||||
union {
|
||||
struct {
|
||||
struct path path;
|
||||
struct inode *inode;
|
||||
} fs;
|
||||
struct path path;
|
||||
struct dentry *dentry;
|
||||
struct inode *inode;
|
||||
struct {
|
||||
int netif;
|
||||
struct sock *sk;
|
||||
|
|
|
@ -22,12 +22,8 @@
|
|||
*/
|
||||
|
||||
const kernel_cap_t __cap_empty_set = CAP_EMPTY_SET;
|
||||
const kernel_cap_t __cap_full_set = CAP_FULL_SET;
|
||||
const kernel_cap_t __cap_init_eff_set = CAP_INIT_EFF_SET;
|
||||
|
||||
EXPORT_SYMBOL(__cap_empty_set);
|
||||
EXPORT_SYMBOL(__cap_full_set);
|
||||
EXPORT_SYMBOL(__cap_init_eff_set);
|
||||
|
||||
int file_caps_enabled = 1;
|
||||
|
||||
|
|
|
@ -49,10 +49,10 @@ struct cred init_cred = {
|
|||
.magic = CRED_MAGIC,
|
||||
#endif
|
||||
.securebits = SECUREBITS_DEFAULT,
|
||||
.cap_inheritable = CAP_INIT_INH_SET,
|
||||
.cap_inheritable = CAP_EMPTY_SET,
|
||||
.cap_permitted = CAP_FULL_SET,
|
||||
.cap_effective = CAP_INIT_EFF_SET,
|
||||
.cap_bset = CAP_INIT_BSET,
|
||||
.cap_effective = CAP_FULL_SET,
|
||||
.cap_bset = CAP_FULL_SET,
|
||||
.user = INIT_USER,
|
||||
.user_ns = &init_user_ns,
|
||||
.group_info = &init_groups,
|
||||
|
|
100
kernel/kmod.c
100
kernel/kmod.c
|
@ -25,6 +25,7 @@
|
|||
#include <linux/kmod.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
@ -43,6 +44,13 @@ extern int max_threads;
|
|||
|
||||
static struct workqueue_struct *khelper_wq;
|
||||
|
||||
#define CAP_BSET (void *)1
|
||||
#define CAP_PI (void *)2
|
||||
|
||||
static kernel_cap_t usermodehelper_bset = CAP_FULL_SET;
|
||||
static kernel_cap_t usermodehelper_inheritable = CAP_FULL_SET;
|
||||
static DEFINE_SPINLOCK(umh_sysctl_lock);
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
|
||||
/*
|
||||
|
@ -132,6 +140,7 @@ EXPORT_SYMBOL(__request_module);
|
|||
static int ____call_usermodehelper(void *data)
|
||||
{
|
||||
struct subprocess_info *sub_info = data;
|
||||
struct cred *new;
|
||||
int retval;
|
||||
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
|
@ -153,6 +162,19 @@ static int ____call_usermodehelper(void *data)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
retval = -ENOMEM;
|
||||
new = prepare_kernel_cred(current);
|
||||
if (!new)
|
||||
goto fail;
|
||||
|
||||
spin_lock(&umh_sysctl_lock);
|
||||
new->cap_bset = cap_intersect(usermodehelper_bset, new->cap_bset);
|
||||
new->cap_inheritable = cap_intersect(usermodehelper_inheritable,
|
||||
new->cap_inheritable);
|
||||
spin_unlock(&umh_sysctl_lock);
|
||||
|
||||
commit_creds(new);
|
||||
|
||||
retval = kernel_execve(sub_info->path,
|
||||
(const char *const *)sub_info->argv,
|
||||
(const char *const *)sub_info->envp);
|
||||
|
@ -420,6 +442,84 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
|
|||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_exec);
|
||||
|
||||
static int proc_cap_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
struct ctl_table t;
|
||||
unsigned long cap_array[_KERNEL_CAPABILITY_U32S];
|
||||
kernel_cap_t new_cap;
|
||||
int err, i;
|
||||
|
||||
if (write && (!capable(CAP_SETPCAP) ||
|
||||
!capable(CAP_SYS_MODULE)))
|
||||
return -EPERM;
|
||||
|
||||
/*
|
||||
* convert from the global kernel_cap_t to the ulong array to print to
|
||||
* userspace if this is a read.
|
||||
*/
|
||||
spin_lock(&umh_sysctl_lock);
|
||||
for (i = 0; i < _KERNEL_CAPABILITY_U32S; i++) {
|
||||
if (table->data == CAP_BSET)
|
||||
cap_array[i] = usermodehelper_bset.cap[i];
|
||||
else if (table->data == CAP_PI)
|
||||
cap_array[i] = usermodehelper_inheritable.cap[i];
|
||||
else
|
||||
BUG();
|
||||
}
|
||||
spin_unlock(&umh_sysctl_lock);
|
||||
|
||||
t = *table;
|
||||
t.data = &cap_array;
|
||||
|
||||
/*
|
||||
* actually read or write and array of ulongs from userspace. Remember
|
||||
* these are least significant 32 bits first
|
||||
*/
|
||||
err = proc_doulongvec_minmax(&t, write, buffer, lenp, ppos);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* convert from the sysctl array of ulongs to the kernel_cap_t
|
||||
* internal representation
|
||||
*/
|
||||
for (i = 0; i < _KERNEL_CAPABILITY_U32S; i++)
|
||||
new_cap.cap[i] = cap_array[i];
|
||||
|
||||
/*
|
||||
* Drop everything not in the new_cap (but don't add things)
|
||||
*/
|
||||
spin_lock(&umh_sysctl_lock);
|
||||
if (write) {
|
||||
if (table->data == CAP_BSET)
|
||||
usermodehelper_bset = cap_intersect(usermodehelper_bset, new_cap);
|
||||
if (table->data == CAP_PI)
|
||||
usermodehelper_inheritable = cap_intersect(usermodehelper_inheritable, new_cap);
|
||||
}
|
||||
spin_unlock(&umh_sysctl_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ctl_table usermodehelper_table[] = {
|
||||
{
|
||||
.procname = "bset",
|
||||
.data = CAP_BSET,
|
||||
.maxlen = _KERNEL_CAPABILITY_U32S * sizeof(unsigned long),
|
||||
.mode = 0600,
|
||||
.proc_handler = proc_cap_handler,
|
||||
},
|
||||
{
|
||||
.procname = "inheritable",
|
||||
.data = CAP_PI,
|
||||
.maxlen = _KERNEL_CAPABILITY_U32S * sizeof(unsigned long),
|
||||
.mode = 0600,
|
||||
.proc_handler = proc_cap_handler,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
void __init usermodehelper_init(void)
|
||||
{
|
||||
khelper_wq = create_singlethread_workqueue("khelper");
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
#include <linux/kprobes.h>
|
||||
#include <linux/pipe_fs_i.h>
|
||||
#include <linux/oom.h>
|
||||
#include <linux/kmod.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/processor.h>
|
||||
|
@ -615,6 +616,11 @@ static struct ctl_table kern_table[] = {
|
|||
.mode = 0555,
|
||||
.child = random_table,
|
||||
},
|
||||
{
|
||||
.procname = "usermodehelper",
|
||||
.mode = 0555,
|
||||
.child = usermodehelper_table,
|
||||
},
|
||||
{
|
||||
.procname = "overflowuid",
|
||||
.data = &overflowuid,
|
||||
|
|
|
@ -88,8 +88,11 @@ struct flex_array *flex_array_alloc(int element_size, unsigned int total,
|
|||
gfp_t flags)
|
||||
{
|
||||
struct flex_array *ret;
|
||||
int max_size = FLEX_ARRAY_NR_BASE_PTRS *
|
||||
FLEX_ARRAY_ELEMENTS_PER_PART(element_size);
|
||||
int max_size = 0;
|
||||
|
||||
if (element_size)
|
||||
max_size = FLEX_ARRAY_NR_BASE_PTRS *
|
||||
FLEX_ARRAY_ELEMENTS_PER_PART(element_size);
|
||||
|
||||
/* max_size will end up 0 if element_size > PAGE_SIZE */
|
||||
if (total > max_size)
|
||||
|
@ -183,15 +186,18 @@ __fa_get_part(struct flex_array *fa, int part_nr, gfp_t flags)
|
|||
int flex_array_put(struct flex_array *fa, unsigned int element_nr, void *src,
|
||||
gfp_t flags)
|
||||
{
|
||||
int part_nr = fa_element_to_part_nr(fa, element_nr);
|
||||
int part_nr;
|
||||
struct flex_array_part *part;
|
||||
void *dst;
|
||||
|
||||
if (element_nr >= fa->total_nr_elements)
|
||||
return -ENOSPC;
|
||||
if (!fa->element_size)
|
||||
return 0;
|
||||
if (elements_fit_in_base(fa))
|
||||
part = (struct flex_array_part *)&fa->parts[0];
|
||||
else {
|
||||
part_nr = fa_element_to_part_nr(fa, element_nr);
|
||||
part = __fa_get_part(fa, part_nr, flags);
|
||||
if (!part)
|
||||
return -ENOMEM;
|
||||
|
@ -211,15 +217,18 @@ EXPORT_SYMBOL(flex_array_put);
|
|||
*/
|
||||
int flex_array_clear(struct flex_array *fa, unsigned int element_nr)
|
||||
{
|
||||
int part_nr = fa_element_to_part_nr(fa, element_nr);
|
||||
int part_nr;
|
||||
struct flex_array_part *part;
|
||||
void *dst;
|
||||
|
||||
if (element_nr >= fa->total_nr_elements)
|
||||
return -ENOSPC;
|
||||
if (!fa->element_size)
|
||||
return 0;
|
||||
if (elements_fit_in_base(fa))
|
||||
part = (struct flex_array_part *)&fa->parts[0];
|
||||
else {
|
||||
part_nr = fa_element_to_part_nr(fa, element_nr);
|
||||
part = fa->parts[part_nr];
|
||||
if (!part)
|
||||
return -EINVAL;
|
||||
|
@ -264,6 +273,8 @@ int flex_array_prealloc(struct flex_array *fa, unsigned int start,
|
|||
|
||||
if (end >= fa->total_nr_elements)
|
||||
return -ENOSPC;
|
||||
if (!fa->element_size)
|
||||
return 0;
|
||||
if (elements_fit_in_base(fa))
|
||||
return 0;
|
||||
start_part = fa_element_to_part_nr(fa, start);
|
||||
|
@ -291,14 +302,17 @@ EXPORT_SYMBOL(flex_array_prealloc);
|
|||
*/
|
||||
void *flex_array_get(struct flex_array *fa, unsigned int element_nr)
|
||||
{
|
||||
int part_nr = fa_element_to_part_nr(fa, element_nr);
|
||||
int part_nr;
|
||||
struct flex_array_part *part;
|
||||
|
||||
if (!fa->element_size)
|
||||
return NULL;
|
||||
if (element_nr >= fa->total_nr_elements)
|
||||
return NULL;
|
||||
if (elements_fit_in_base(fa))
|
||||
part = (struct flex_array_part *)&fa->parts[0];
|
||||
else {
|
||||
part_nr = fa_element_to_part_nr(fa, element_nr);
|
||||
part = fa->parts[part_nr];
|
||||
if (!part)
|
||||
return NULL;
|
||||
|
@ -353,7 +367,7 @@ int flex_array_shrink(struct flex_array *fa)
|
|||
int part_nr;
|
||||
int ret = 0;
|
||||
|
||||
if (!fa->total_nr_elements)
|
||||
if (!fa->total_nr_elements || !fa->element_size)
|
||||
return 0;
|
||||
if (elements_fit_in_base(fa))
|
||||
return ret;
|
||||
|
|
|
@ -212,10 +212,12 @@ static void dns_resolver_describe(const struct key *key, struct seq_file *m)
|
|||
int err = key->type_data.x[0];
|
||||
|
||||
seq_puts(m, key->description);
|
||||
if (err)
|
||||
seq_printf(m, ": %d", err);
|
||||
else
|
||||
seq_printf(m, ": %u", key->datalen);
|
||||
if (key_is_instantiated(key)) {
|
||||
if (err)
|
||||
seq_printf(m, ": %d", err);
|
||||
else
|
||||
seq_printf(m, ": %u", key->datalen);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -167,6 +167,7 @@ config INTEL_TXT
|
|||
config LSM_MMAP_MIN_ADDR
|
||||
int "Low address space for LSM to protect from user allocation"
|
||||
depends on SECURITY && SECURITY_SELINUX
|
||||
default 32768 if ARM
|
||||
default 65536
|
||||
help
|
||||
This is the portion of low virtual memory which should be protected
|
||||
|
|
|
@ -529,15 +529,10 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
|
|||
new->suid = new->fsuid = new->euid;
|
||||
new->sgid = new->fsgid = new->egid;
|
||||
|
||||
/* For init, we want to retain the capabilities set in the initial
|
||||
* task. Thus we skip the usual capability rules
|
||||
*/
|
||||
if (!is_global_init(current)) {
|
||||
if (effective)
|
||||
new->cap_effective = new->cap_permitted;
|
||||
else
|
||||
cap_clear(new->cap_effective);
|
||||
}
|
||||
if (effective)
|
||||
new->cap_effective = new->cap_permitted;
|
||||
else
|
||||
cap_clear(new->cap_effective);
|
||||
bprm->cap_effective = effective;
|
||||
|
||||
/*
|
||||
|
|
|
@ -109,11 +109,13 @@ extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
|
|||
const struct cred *cred,
|
||||
struct key_type *type,
|
||||
const void *description,
|
||||
key_match_func_t match);
|
||||
key_match_func_t match,
|
||||
bool no_state_check);
|
||||
|
||||
extern key_ref_t search_my_process_keyrings(struct key_type *type,
|
||||
const void *description,
|
||||
key_match_func_t match,
|
||||
bool no_state_check,
|
||||
const struct cred *cred);
|
||||
extern key_ref_t search_process_keyrings(struct key_type *type,
|
||||
const void *description,
|
||||
|
|
|
@ -206,8 +206,14 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,
|
|||
goto error5;
|
||||
}
|
||||
|
||||
/* wait for the key to finish being constructed */
|
||||
ret = wait_for_key_construction(key, 1);
|
||||
if (ret < 0)
|
||||
goto error6;
|
||||
|
||||
ret = key->serial;
|
||||
|
||||
error6:
|
||||
key_put(key);
|
||||
error5:
|
||||
key_type_put(ktype);
|
||||
|
|
|
@ -176,13 +176,15 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m)
|
|||
else
|
||||
seq_puts(m, "[anon]");
|
||||
|
||||
rcu_read_lock();
|
||||
klist = rcu_dereference(keyring->payload.subscriptions);
|
||||
if (klist)
|
||||
seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys);
|
||||
else
|
||||
seq_puts(m, ": empty");
|
||||
rcu_read_unlock();
|
||||
if (key_is_instantiated(keyring)) {
|
||||
rcu_read_lock();
|
||||
klist = rcu_dereference(keyring->payload.subscriptions);
|
||||
if (klist)
|
||||
seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys);
|
||||
else
|
||||
seq_puts(m, ": empty");
|
||||
rcu_read_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -271,6 +273,7 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
|
|||
* @type: The type of key to search for.
|
||||
* @description: Parameter for @match.
|
||||
* @match: Function to rule on whether or not a key is the one required.
|
||||
* @no_state_check: Don't check if a matching key is bad
|
||||
*
|
||||
* Search the supplied keyring tree for a key that matches the criteria given.
|
||||
* The root keyring and any linked keyrings must grant Search permission to the
|
||||
|
@ -303,7 +306,8 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
|
|||
const struct cred *cred,
|
||||
struct key_type *type,
|
||||
const void *description,
|
||||
key_match_func_t match)
|
||||
key_match_func_t match,
|
||||
bool no_state_check)
|
||||
{
|
||||
struct {
|
||||
struct keyring_list *keylist;
|
||||
|
@ -345,6 +349,8 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
|
|||
kflags = keyring->flags;
|
||||
if (keyring->type == type && match(keyring, description)) {
|
||||
key = keyring;
|
||||
if (no_state_check)
|
||||
goto found;
|
||||
|
||||
/* check it isn't negative and hasn't expired or been
|
||||
* revoked */
|
||||
|
@ -384,11 +390,13 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
|
|||
continue;
|
||||
|
||||
/* skip revoked keys and expired keys */
|
||||
if (kflags & (1 << KEY_FLAG_REVOKED))
|
||||
continue;
|
||||
if (!no_state_check) {
|
||||
if (kflags & (1 << KEY_FLAG_REVOKED))
|
||||
continue;
|
||||
|
||||
if (key->expiry && now.tv_sec >= key->expiry)
|
||||
continue;
|
||||
if (key->expiry && now.tv_sec >= key->expiry)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* keys that don't match */
|
||||
if (!match(key, description))
|
||||
|
@ -399,6 +407,9 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
|
|||
cred, KEY_SEARCH) < 0)
|
||||
continue;
|
||||
|
||||
if (no_state_check)
|
||||
goto found;
|
||||
|
||||
/* we set a different error code if we pass a negative key */
|
||||
if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
|
||||
err = key->type_data.reject_error;
|
||||
|
@ -478,7 +489,7 @@ key_ref_t keyring_search(key_ref_t keyring,
|
|||
return ERR_PTR(-ENOKEY);
|
||||
|
||||
return keyring_search_aux(keyring, current->cred,
|
||||
type, description, type->match);
|
||||
type, description, type->match, false);
|
||||
}
|
||||
EXPORT_SYMBOL(keyring_search);
|
||||
|
||||
|
|
|
@ -199,7 +199,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
|
|||
if (key->perm & KEY_POS_VIEW) {
|
||||
skey_ref = search_my_process_keyrings(key->type, key,
|
||||
lookup_user_key_possessed,
|
||||
cred);
|
||||
true, cred);
|
||||
if (!IS_ERR(skey_ref)) {
|
||||
key_ref_put(skey_ref);
|
||||
key_ref = make_key_ref(key, 1);
|
||||
|
|
|
@ -331,6 +331,7 @@ void key_fsgid_changed(struct task_struct *tsk)
|
|||
key_ref_t search_my_process_keyrings(struct key_type *type,
|
||||
const void *description,
|
||||
key_match_func_t match,
|
||||
bool no_state_check,
|
||||
const struct cred *cred)
|
||||
{
|
||||
key_ref_t key_ref, ret, err;
|
||||
|
@ -350,7 +351,7 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
|
|||
if (cred->thread_keyring) {
|
||||
key_ref = keyring_search_aux(
|
||||
make_key_ref(cred->thread_keyring, 1),
|
||||
cred, type, description, match);
|
||||
cred, type, description, match, no_state_check);
|
||||
if (!IS_ERR(key_ref))
|
||||
goto found;
|
||||
|
||||
|
@ -371,7 +372,7 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
|
|||
if (cred->tgcred->process_keyring) {
|
||||
key_ref = keyring_search_aux(
|
||||
make_key_ref(cred->tgcred->process_keyring, 1),
|
||||
cred, type, description, match);
|
||||
cred, type, description, match, no_state_check);
|
||||
if (!IS_ERR(key_ref))
|
||||
goto found;
|
||||
|
||||
|
@ -395,7 +396,7 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
|
|||
make_key_ref(rcu_dereference(
|
||||
cred->tgcred->session_keyring),
|
||||
1),
|
||||
cred, type, description, match);
|
||||
cred, type, description, match, no_state_check);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!IS_ERR(key_ref))
|
||||
|
@ -417,7 +418,7 @@ key_ref_t search_my_process_keyrings(struct key_type *type,
|
|||
else if (cred->user->session_keyring) {
|
||||
key_ref = keyring_search_aux(
|
||||
make_key_ref(cred->user->session_keyring, 1),
|
||||
cred, type, description, match);
|
||||
cred, type, description, match, no_state_check);
|
||||
if (!IS_ERR(key_ref))
|
||||
goto found;
|
||||
|
||||
|
@ -459,7 +460,8 @@ key_ref_t search_process_keyrings(struct key_type *type,
|
|||
|
||||
might_sleep();
|
||||
|
||||
key_ref = search_my_process_keyrings(type, description, match, cred);
|
||||
key_ref = search_my_process_keyrings(type, description, match,
|
||||
false, cred);
|
||||
if (!IS_ERR(key_ref))
|
||||
goto found;
|
||||
err = key_ref;
|
||||
|
|
|
@ -530,8 +530,7 @@ struct key *request_key_and_link(struct key_type *type,
|
|||
dest_keyring, flags);
|
||||
|
||||
/* search all the process keyrings for a key */
|
||||
key_ref = search_process_keyrings(type, description, type->match,
|
||||
cred);
|
||||
key_ref = search_process_keyrings(type, description, type->match, cred);
|
||||
|
||||
if (!IS_ERR(key_ref)) {
|
||||
key = key_ref_to_ptr(key_ref);
|
||||
|
|
|
@ -59,7 +59,8 @@ static void request_key_auth_describe(const struct key *key,
|
|||
|
||||
seq_puts(m, "key:");
|
||||
seq_puts(m, key->description);
|
||||
seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len);
|
||||
if (key_is_instantiated(key))
|
||||
seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -157,8 +157,8 @@ EXPORT_SYMBOL_GPL(user_destroy);
|
|||
void user_describe(const struct key *key, struct seq_file *m)
|
||||
{
|
||||
seq_puts(m, key->description);
|
||||
|
||||
seq_printf(m, ": %u", key->datalen);
|
||||
if (key_is_instantiated(key))
|
||||
seq_printf(m, ": %u", key->datalen);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(user_describe);
|
||||
|
|
|
@ -210,7 +210,6 @@ static inline void print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
|
|||
static void dump_common_audit_data(struct audit_buffer *ab,
|
||||
struct common_audit_data *a)
|
||||
{
|
||||
struct inode *inode = NULL;
|
||||
struct task_struct *tsk = current;
|
||||
|
||||
if (a->tsk)
|
||||
|
@ -229,33 +228,47 @@ static void dump_common_audit_data(struct audit_buffer *ab,
|
|||
case LSM_AUDIT_DATA_CAP:
|
||||
audit_log_format(ab, " capability=%d ", a->u.cap);
|
||||
break;
|
||||
case LSM_AUDIT_DATA_FS:
|
||||
if (a->u.fs.path.dentry) {
|
||||
struct dentry *dentry = a->u.fs.path.dentry;
|
||||
if (a->u.fs.path.mnt) {
|
||||
audit_log_d_path(ab, "path=", &a->u.fs.path);
|
||||
} else {
|
||||
audit_log_format(ab, " name=");
|
||||
audit_log_untrustedstring(ab,
|
||||
dentry->d_name.name);
|
||||
}
|
||||
inode = dentry->d_inode;
|
||||
} else if (a->u.fs.inode) {
|
||||
struct dentry *dentry;
|
||||
inode = a->u.fs.inode;
|
||||
dentry = d_find_alias(inode);
|
||||
if (dentry) {
|
||||
audit_log_format(ab, " name=");
|
||||
audit_log_untrustedstring(ab,
|
||||
dentry->d_name.name);
|
||||
dput(dentry);
|
||||
}
|
||||
}
|
||||
case LSM_AUDIT_DATA_PATH: {
|
||||
struct inode *inode;
|
||||
|
||||
audit_log_d_path(ab, "path=", &a->u.path);
|
||||
|
||||
inode = a->u.path.dentry->d_inode;
|
||||
if (inode)
|
||||
audit_log_format(ab, " dev=%s ino=%lu",
|
||||
inode->i_sb->s_id,
|
||||
inode->i_ino);
|
||||
break;
|
||||
}
|
||||
case LSM_AUDIT_DATA_DENTRY: {
|
||||
struct inode *inode;
|
||||
|
||||
audit_log_format(ab, " name=");
|
||||
audit_log_untrustedstring(ab, a->u.dentry->d_name.name);
|
||||
|
||||
inode = a->u.dentry->d_inode;
|
||||
if (inode)
|
||||
audit_log_format(ab, " dev=%s ino=%lu",
|
||||
inode->i_sb->s_id,
|
||||
inode->i_ino);
|
||||
break;
|
||||
}
|
||||
case LSM_AUDIT_DATA_INODE: {
|
||||
struct dentry *dentry;
|
||||
struct inode *inode;
|
||||
|
||||
inode = a->u.inode;
|
||||
dentry = d_find_alias(inode);
|
||||
if (dentry) {
|
||||
audit_log_format(ab, " name=");
|
||||
audit_log_untrustedstring(ab,
|
||||
dentry->d_name.name);
|
||||
dput(dentry);
|
||||
}
|
||||
audit_log_format(ab, " dev=%s ino=%lu", inode->i_sb->s_id,
|
||||
inode->i_ino);
|
||||
break;
|
||||
}
|
||||
case LSM_AUDIT_DATA_TASK:
|
||||
tsk = a->u.tsk;
|
||||
if (tsk && tsk->pid) {
|
||||
|
|
|
@ -526,7 +526,7 @@ int avc_audit(u32 ssid, u32 tsid,
|
|||
* during retry. However this is logically just as if the operation
|
||||
* happened a little later.
|
||||
*/
|
||||
if ((a->type == LSM_AUDIT_DATA_FS) &&
|
||||
if ((a->type == LSM_AUDIT_DATA_INODE) &&
|
||||
(flags & IPERM_FLAG_RCU))
|
||||
return -ECHILD;
|
||||
|
||||
|
|
|
@ -990,6 +990,7 @@ static void selinux_write_opts(struct seq_file *m,
|
|||
continue;
|
||||
default:
|
||||
BUG();
|
||||
return;
|
||||
};
|
||||
/* we need a comma before each option */
|
||||
seq_putc(m, ',');
|
||||
|
@ -1443,6 +1444,7 @@ static int task_has_capability(struct task_struct *tsk,
|
|||
printk(KERN_ERR
|
||||
"SELinux: out of range capability %d\n", cap);
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);
|
||||
|
@ -1487,8 +1489,8 @@ static int inode_has_perm(const struct cred *cred,
|
|||
|
||||
if (!adp) {
|
||||
adp = &ad;
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.inode = inode;
|
||||
COMMON_AUDIT_DATA_INIT(&ad, INODE);
|
||||
ad.u.inode = inode;
|
||||
}
|
||||
|
||||
return avc_has_perm_flags(sid, isec->sid, isec->sclass, perms, adp, flags);
|
||||
|
@ -1498,16 +1500,29 @@ static int inode_has_perm(const struct cred *cred,
|
|||
the dentry to help the auditing code to more easily generate the
|
||||
pathname if needed. */
|
||||
static inline int dentry_has_perm(const struct cred *cred,
|
||||
struct vfsmount *mnt,
|
||||
struct dentry *dentry,
|
||||
u32 av)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct common_audit_data ad;
|
||||
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path.mnt = mnt;
|
||||
ad.u.fs.path.dentry = dentry;
|
||||
COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
|
||||
ad.u.dentry = dentry;
|
||||
return inode_has_perm(cred, inode, av, &ad, 0);
|
||||
}
|
||||
|
||||
/* Same as inode_has_perm, but pass explicit audit data containing
|
||||
the path to help the auditing code to more easily generate the
|
||||
pathname if needed. */
|
||||
static inline int path_has_perm(const struct cred *cred,
|
||||
struct path *path,
|
||||
u32 av)
|
||||
{
|
||||
struct inode *inode = path->dentry->d_inode;
|
||||
struct common_audit_data ad;
|
||||
|
||||
COMMON_AUDIT_DATA_INIT(&ad, PATH);
|
||||
ad.u.path = *path;
|
||||
return inode_has_perm(cred, inode, av, &ad, 0);
|
||||
}
|
||||
|
||||
|
@ -1529,8 +1544,8 @@ static int file_has_perm(const struct cred *cred,
|
|||
u32 sid = cred_sid(cred);
|
||||
int rc;
|
||||
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path = file->f_path;
|
||||
COMMON_AUDIT_DATA_INIT(&ad, PATH);
|
||||
ad.u.path = file->f_path;
|
||||
|
||||
if (sid != fsec->sid) {
|
||||
rc = avc_has_perm(sid, fsec->sid,
|
||||
|
@ -1568,8 +1583,8 @@ static int may_create(struct inode *dir,
|
|||
sid = tsec->sid;
|
||||
newsid = tsec->create_sid;
|
||||
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path.dentry = dentry;
|
||||
COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
|
||||
ad.u.dentry = dentry;
|
||||
|
||||
rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR,
|
||||
DIR__ADD_NAME | DIR__SEARCH,
|
||||
|
@ -1621,8 +1636,8 @@ static int may_link(struct inode *dir,
|
|||
dsec = dir->i_security;
|
||||
isec = dentry->d_inode->i_security;
|
||||
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path.dentry = dentry;
|
||||
COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
|
||||
ad.u.dentry = dentry;
|
||||
|
||||
av = DIR__SEARCH;
|
||||
av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
|
||||
|
@ -1667,9 +1682,9 @@ static inline int may_rename(struct inode *old_dir,
|
|||
old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
|
||||
new_dsec = new_dir->i_security;
|
||||
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
|
||||
|
||||
ad.u.fs.path.dentry = old_dentry;
|
||||
ad.u.dentry = old_dentry;
|
||||
rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR,
|
||||
DIR__REMOVE_NAME | DIR__SEARCH, &ad);
|
||||
if (rc)
|
||||
|
@ -1685,7 +1700,7 @@ static inline int may_rename(struct inode *old_dir,
|
|||
return rc;
|
||||
}
|
||||
|
||||
ad.u.fs.path.dentry = new_dentry;
|
||||
ad.u.dentry = new_dentry;
|
||||
av = DIR__ADD_NAME | DIR__SEARCH;
|
||||
if (new_dentry->d_inode)
|
||||
av |= DIR__REMOVE_NAME;
|
||||
|
@ -1895,7 +1910,7 @@ static int selinux_quota_on(struct dentry *dentry)
|
|||
{
|
||||
const struct cred *cred = current_cred();
|
||||
|
||||
return dentry_has_perm(cred, NULL, dentry, FILE__QUOTAON);
|
||||
return dentry_has_perm(cred, dentry, FILE__QUOTAON);
|
||||
}
|
||||
|
||||
static int selinux_syslog(int type)
|
||||
|
@ -1992,8 +2007,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
|
|||
return rc;
|
||||
}
|
||||
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path = bprm->file->f_path;
|
||||
COMMON_AUDIT_DATA_INIT(&ad, PATH);
|
||||
ad.u.path = bprm->file->f_path;
|
||||
|
||||
if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
|
||||
new_tsec->sid = old_tsec->sid;
|
||||
|
@ -2121,7 +2136,7 @@ static inline void flush_unauthorized_files(const struct cred *cred,
|
|||
|
||||
/* Revalidate access to inherited open files. */
|
||||
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, INODE);
|
||||
|
||||
spin_lock(&files->file_lock);
|
||||
for (;;) {
|
||||
|
@ -2469,8 +2484,8 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
|||
if (flags & MS_KERNMOUNT)
|
||||
return 0;
|
||||
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path.dentry = sb->s_root;
|
||||
COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
|
||||
ad.u.dentry = sb->s_root;
|
||||
return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad);
|
||||
}
|
||||
|
||||
|
@ -2479,8 +2494,8 @@ static int selinux_sb_statfs(struct dentry *dentry)
|
|||
const struct cred *cred = current_cred();
|
||||
struct common_audit_data ad;
|
||||
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path.dentry = dentry->d_sb->s_root;
|
||||
COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
|
||||
ad.u.dentry = dentry->d_sb->s_root;
|
||||
return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
|
||||
}
|
||||
|
||||
|
@ -2496,8 +2511,7 @@ static int selinux_mount(char *dev_name,
|
|||
return superblock_has_perm(cred, path->mnt->mnt_sb,
|
||||
FILESYSTEM__REMOUNT, NULL);
|
||||
else
|
||||
return dentry_has_perm(cred, path->mnt, path->dentry,
|
||||
FILE__MOUNTON);
|
||||
return path_has_perm(cred, path, FILE__MOUNTON);
|
||||
}
|
||||
|
||||
static int selinux_umount(struct vfsmount *mnt, int flags)
|
||||
|
@ -2630,14 +2644,14 @@ static int selinux_inode_readlink(struct dentry *dentry)
|
|||
{
|
||||
const struct cred *cred = current_cred();
|
||||
|
||||
return dentry_has_perm(cred, NULL, dentry, FILE__READ);
|
||||
return dentry_has_perm(cred, dentry, FILE__READ);
|
||||
}
|
||||
|
||||
static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
|
||||
return dentry_has_perm(cred, NULL, dentry, FILE__READ);
|
||||
return dentry_has_perm(cred, dentry, FILE__READ);
|
||||
}
|
||||
|
||||
static int selinux_inode_permission(struct inode *inode, int mask, unsigned flags)
|
||||
|
@ -2654,8 +2668,8 @@ static int selinux_inode_permission(struct inode *inode, int mask, unsigned flag
|
|||
if (!mask)
|
||||
return 0;
|
||||
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.inode = inode;
|
||||
COMMON_AUDIT_DATA_INIT(&ad, INODE);
|
||||
ad.u.inode = inode;
|
||||
|
||||
if (from_access)
|
||||
ad.selinux_audit_data.auditdeny |= FILE__AUDIT_ACCESS;
|
||||
|
@ -2680,16 +2694,20 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
|
|||
|
||||
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
|
||||
ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET))
|
||||
return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR);
|
||||
return dentry_has_perm(cred, dentry, FILE__SETATTR);
|
||||
|
||||
return dentry_has_perm(cred, NULL, dentry, FILE__WRITE);
|
||||
return dentry_has_perm(cred, dentry, FILE__WRITE);
|
||||
}
|
||||
|
||||
static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
struct path path;
|
||||
|
||||
return dentry_has_perm(cred, mnt, dentry, FILE__GETATTR);
|
||||
path.dentry = dentry;
|
||||
path.mnt = mnt;
|
||||
|
||||
return path_has_perm(cred, &path, FILE__GETATTR);
|
||||
}
|
||||
|
||||
static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
|
||||
|
@ -2710,7 +2728,7 @@ static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
|
|||
|
||||
/* Not an attribute we recognize, so just check the
|
||||
ordinary setattr permission. */
|
||||
return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR);
|
||||
return dentry_has_perm(cred, dentry, FILE__SETATTR);
|
||||
}
|
||||
|
||||
static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
|
||||
|
@ -2733,8 +2751,8 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
|
|||
if (!inode_owner_or_capable(inode))
|
||||
return -EPERM;
|
||||
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path.dentry = dentry;
|
||||
COMMON_AUDIT_DATA_INIT(&ad, DENTRY);
|
||||
ad.u.dentry = dentry;
|
||||
|
||||
rc = avc_has_perm(sid, isec->sid, isec->sclass,
|
||||
FILE__RELABELFROM, &ad);
|
||||
|
@ -2797,14 +2815,14 @@ static int selinux_inode_getxattr(struct dentry *dentry, const char *name)
|
|||
{
|
||||
const struct cred *cred = current_cred();
|
||||
|
||||
return dentry_has_perm(cred, NULL, dentry, FILE__GETATTR);
|
||||
return dentry_has_perm(cred, dentry, FILE__GETATTR);
|
||||
}
|
||||
|
||||
static int selinux_inode_listxattr(struct dentry *dentry)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
|
||||
return dentry_has_perm(cred, NULL, dentry, FILE__GETATTR);
|
||||
return dentry_has_perm(cred, dentry, FILE__GETATTR);
|
||||
}
|
||||
|
||||
static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
|
||||
|
|
|
@ -30,13 +30,14 @@
|
|||
#define POLICYDB_VERSION_PERMISSIVE 23
|
||||
#define POLICYDB_VERSION_BOUNDARY 24
|
||||
#define POLICYDB_VERSION_FILENAME_TRANS 25
|
||||
#define POLICYDB_VERSION_ROLETRANS 26
|
||||
|
||||
/* Range of policy versions we understand*/
|
||||
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
|
||||
#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
|
||||
#define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
|
||||
#else
|
||||
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_FILENAME_TRANS
|
||||
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_ROLETRANS
|
||||
#endif
|
||||
|
||||
/* Mask for just the mount related flags */
|
||||
|
@ -85,7 +86,7 @@ extern int selinux_policycap_openperm;
|
|||
int security_mls_enabled(void);
|
||||
|
||||
int security_load_policy(void *data, size_t len);
|
||||
int security_read_policy(void **data, ssize_t *len);
|
||||
int security_read_policy(void **data, size_t *len);
|
||||
size_t security_policydb_len(void);
|
||||
|
||||
int security_policycap_supported(unsigned int req_cap);
|
||||
|
@ -111,8 +112,8 @@ void security_compute_av_user(u32 ssid, u32 tsid,
|
|||
int security_transition_sid(u32 ssid, u32 tsid, u16 tclass,
|
||||
const struct qstr *qstr, u32 *out_sid);
|
||||
|
||||
int security_transition_sid_user(u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 *out_sid);
|
||||
int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass,
|
||||
const char *objname, u32 *out_sid);
|
||||
|
||||
int security_member_sid(u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 *out_sid);
|
||||
|
|
|
@ -141,6 +141,7 @@ static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
|
|||
break;
|
||||
default:
|
||||
BUG();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list)
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/percpu.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/kobject.h>
|
||||
|
||||
/* selinuxfs pseudo filesystem for exporting the security policy API.
|
||||
Based on the proc code and the fs/nfsd/nfsctl.c code. */
|
||||
|
@ -753,11 +754,13 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
|
|||
static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
|
||||
{
|
||||
char *scon = NULL, *tcon = NULL;
|
||||
char *namebuf = NULL, *objname = NULL;
|
||||
u32 ssid, tsid, newsid;
|
||||
u16 tclass;
|
||||
ssize_t length;
|
||||
char *newcon = NULL;
|
||||
u32 len;
|
||||
int nargs;
|
||||
|
||||
length = task_has_security(current, SECURITY__COMPUTE_CREATE);
|
||||
if (length)
|
||||
|
@ -773,10 +776,18 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
|
|||
if (!tcon)
|
||||
goto out;
|
||||
|
||||
length = -EINVAL;
|
||||
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
|
||||
length = -ENOMEM;
|
||||
namebuf = kzalloc(size + 1, GFP_KERNEL);
|
||||
if (!namebuf)
|
||||
goto out;
|
||||
|
||||
length = -EINVAL;
|
||||
nargs = sscanf(buf, "%s %s %hu %s", scon, tcon, &tclass, namebuf);
|
||||
if (nargs < 3 || nargs > 4)
|
||||
goto out;
|
||||
if (nargs == 4)
|
||||
objname = namebuf;
|
||||
|
||||
length = security_context_to_sid(scon, strlen(scon) + 1, &ssid);
|
||||
if (length)
|
||||
goto out;
|
||||
|
@ -785,7 +796,8 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
|
|||
if (length)
|
||||
goto out;
|
||||
|
||||
length = security_transition_sid_user(ssid, tsid, tclass, &newsid);
|
||||
length = security_transition_sid_user(ssid, tsid, tclass,
|
||||
objname, &newsid);
|
||||
if (length)
|
||||
goto out;
|
||||
|
||||
|
@ -804,6 +816,7 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
|
|||
length = len;
|
||||
out:
|
||||
kfree(newcon);
|
||||
kfree(namebuf);
|
||||
kfree(tcon);
|
||||
kfree(scon);
|
||||
return length;
|
||||
|
@ -1901,6 +1914,7 @@ static struct file_system_type sel_fs_type = {
|
|||
};
|
||||
|
||||
struct vfsmount *selinuxfs_mount;
|
||||
static struct kobject *selinuxfs_kobj;
|
||||
|
||||
static int __init init_sel_fs(void)
|
||||
{
|
||||
|
@ -1908,9 +1922,16 @@ static int __init init_sel_fs(void)
|
|||
|
||||
if (!selinux_enabled)
|
||||
return 0;
|
||||
|
||||
selinuxfs_kobj = kobject_create_and_add("selinux", fs_kobj);
|
||||
if (!selinuxfs_kobj)
|
||||
return -ENOMEM;
|
||||
|
||||
err = register_filesystem(&sel_fs_type);
|
||||
if (err)
|
||||
if (err) {
|
||||
kobject_put(selinuxfs_kobj);
|
||||
return err;
|
||||
}
|
||||
|
||||
selinuxfs_mount = kern_mount(&sel_fs_type);
|
||||
if (IS_ERR(selinuxfs_mount)) {
|
||||
|
@ -1927,6 +1948,7 @@ __initcall(init_sel_fs);
|
|||
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
||||
void exit_sel_fs(void)
|
||||
{
|
||||
kobject_put(selinuxfs_kobj);
|
||||
unregister_filesystem(&sel_fs_type);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -128,6 +128,11 @@ static struct policydb_compat_info policydb_compat[] = {
|
|||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_ROLETRANS,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct policydb_compat_info *policydb_lookup_compat(int version)
|
||||
|
@ -179,6 +184,43 @@ static int roles_init(struct policydb *p)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static u32 filenametr_hash(struct hashtab *h, const void *k)
|
||||
{
|
||||
const struct filename_trans *ft = k;
|
||||
unsigned long hash;
|
||||
unsigned int byte_num;
|
||||
unsigned char focus;
|
||||
|
||||
hash = ft->stype ^ ft->ttype ^ ft->tclass;
|
||||
|
||||
byte_num = 0;
|
||||
while ((focus = ft->name[byte_num++]))
|
||||
hash = partial_name_hash(focus, hash);
|
||||
return hash & (h->size - 1);
|
||||
}
|
||||
|
||||
static int filenametr_cmp(struct hashtab *h, const void *k1, const void *k2)
|
||||
{
|
||||
const struct filename_trans *ft1 = k1;
|
||||
const struct filename_trans *ft2 = k2;
|
||||
int v;
|
||||
|
||||
v = ft1->stype - ft2->stype;
|
||||
if (v)
|
||||
return v;
|
||||
|
||||
v = ft1->ttype - ft2->ttype;
|
||||
if (v)
|
||||
return v;
|
||||
|
||||
v = ft1->tclass - ft2->tclass;
|
||||
if (v)
|
||||
return v;
|
||||
|
||||
return strcmp(ft1->name, ft2->name);
|
||||
|
||||
}
|
||||
|
||||
static u32 rangetr_hash(struct hashtab *h, const void *k)
|
||||
{
|
||||
const struct range_trans *key = k;
|
||||
|
@ -231,15 +273,22 @@ static int policydb_init(struct policydb *p)
|
|||
if (rc)
|
||||
goto out;
|
||||
|
||||
p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp, (1 << 10));
|
||||
if (!p->filename_trans)
|
||||
goto out;
|
||||
|
||||
p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256);
|
||||
if (!p->range_tr)
|
||||
goto out;
|
||||
|
||||
ebitmap_init(&p->filename_trans_ttypes);
|
||||
ebitmap_init(&p->policycaps);
|
||||
ebitmap_init(&p->permissive_map);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
hashtab_destroy(p->filename_trans);
|
||||
hashtab_destroy(p->range_tr);
|
||||
for (i = 0; i < SYM_NUM; i++)
|
||||
hashtab_destroy(p->symtab[i].table);
|
||||
return rc;
|
||||
|
@ -417,32 +466,26 @@ static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) =
|
|||
};
|
||||
|
||||
#ifdef DEBUG_HASHES
|
||||
static void symtab_hash_eval(struct symtab *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SYM_NUM; i++) {
|
||||
struct hashtab *h = s[i].table;
|
||||
struct hashtab_info info;
|
||||
|
||||
hashtab_stat(h, &info);
|
||||
printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, "
|
||||
"longest chain length %d\n", symtab_name[i], h->nel,
|
||||
info.slots_used, h->size, info.max_chain_len);
|
||||
}
|
||||
}
|
||||
|
||||
static void rangetr_hash_eval(struct hashtab *h)
|
||||
static void hash_eval(struct hashtab *h, const char *hash_name)
|
||||
{
|
||||
struct hashtab_info info;
|
||||
|
||||
hashtab_stat(h, &info);
|
||||
printk(KERN_DEBUG "SELinux: rangetr: %d entries and %d/%d buckets used, "
|
||||
"longest chain length %d\n", h->nel,
|
||||
printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, "
|
||||
"longest chain length %d\n", hash_name, h->nel,
|
||||
info.slots_used, h->size, info.max_chain_len);
|
||||
}
|
||||
|
||||
static void symtab_hash_eval(struct symtab *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SYM_NUM; i++)
|
||||
hash_eval(s[i].table, symtab_name[i]);
|
||||
}
|
||||
|
||||
#else
|
||||
static inline void rangetr_hash_eval(struct hashtab *h)
|
||||
static inline void hash_eval(struct hashtab *h, char *hash_name)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
@ -675,6 +718,16 @@ static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) =
|
|||
cat_destroy,
|
||||
};
|
||||
|
||||
static int filenametr_destroy(void *key, void *datum, void *p)
|
||||
{
|
||||
struct filename_trans *ft = key;
|
||||
kfree(ft->name);
|
||||
kfree(key);
|
||||
kfree(datum);
|
||||
cond_resched();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int range_tr_destroy(void *key, void *datum, void *p)
|
||||
{
|
||||
struct mls_range *rt = datum;
|
||||
|
@ -709,7 +762,6 @@ void policydb_destroy(struct policydb *p)
|
|||
int i;
|
||||
struct role_allow *ra, *lra = NULL;
|
||||
struct role_trans *tr, *ltr = NULL;
|
||||
struct filename_trans *ft, *nft;
|
||||
|
||||
for (i = 0; i < SYM_NUM; i++) {
|
||||
cond_resched();
|
||||
|
@ -773,6 +825,9 @@ void policydb_destroy(struct policydb *p)
|
|||
}
|
||||
kfree(lra);
|
||||
|
||||
hashtab_map(p->filename_trans, filenametr_destroy, NULL);
|
||||
hashtab_destroy(p->filename_trans);
|
||||
|
||||
hashtab_map(p->range_tr, range_tr_destroy, NULL);
|
||||
hashtab_destroy(p->range_tr);
|
||||
|
||||
|
@ -788,14 +843,7 @@ void policydb_destroy(struct policydb *p)
|
|||
flex_array_free(p->type_attr_map_array);
|
||||
}
|
||||
|
||||
ft = p->filename_trans;
|
||||
while (ft) {
|
||||
nft = ft->next;
|
||||
kfree(ft->name);
|
||||
kfree(ft);
|
||||
ft = nft;
|
||||
}
|
||||
|
||||
ebitmap_destroy(&p->filename_trans_ttypes);
|
||||
ebitmap_destroy(&p->policycaps);
|
||||
ebitmap_destroy(&p->permissive_map);
|
||||
|
||||
|
@ -1795,7 +1843,7 @@ static int range_read(struct policydb *p, void *fp)
|
|||
rt = NULL;
|
||||
r = NULL;
|
||||
}
|
||||
rangetr_hash_eval(p->range_tr);
|
||||
hash_eval(p->range_tr, "rangetr");
|
||||
rc = 0;
|
||||
out:
|
||||
kfree(rt);
|
||||
|
@ -1805,9 +1853,10 @@ static int range_read(struct policydb *p, void *fp)
|
|||
|
||||
static int filename_trans_read(struct policydb *p, void *fp)
|
||||
{
|
||||
struct filename_trans *ft, *last;
|
||||
u32 nel, len;
|
||||
struct filename_trans *ft;
|
||||
struct filename_trans_datum *otype;
|
||||
char *name;
|
||||
u32 nel, len;
|
||||
__le32 buf[4];
|
||||
int rc, i;
|
||||
|
||||
|
@ -1816,25 +1865,23 @@ static int filename_trans_read(struct policydb *p, void *fp)
|
|||
|
||||
rc = next_entry(buf, fp, sizeof(u32));
|
||||
if (rc)
|
||||
goto out;
|
||||
return rc;
|
||||
nel = le32_to_cpu(buf[0]);
|
||||
|
||||
last = p->filename_trans;
|
||||
while (last && last->next)
|
||||
last = last->next;
|
||||
|
||||
for (i = 0; i < nel; i++) {
|
||||
ft = NULL;
|
||||
otype = NULL;
|
||||
name = NULL;
|
||||
|
||||
rc = -ENOMEM;
|
||||
ft = kzalloc(sizeof(*ft), GFP_KERNEL);
|
||||
if (!ft)
|
||||
goto out;
|
||||
|
||||
/* add it to the tail of the list */
|
||||
if (!last)
|
||||
p->filename_trans = ft;
|
||||
else
|
||||
last->next = ft;
|
||||
last = ft;
|
||||
rc = -ENOMEM;
|
||||
otype = kmalloc(sizeof(*otype), GFP_KERNEL);
|
||||
if (!otype)
|
||||
goto out;
|
||||
|
||||
/* length of the path component string */
|
||||
rc = next_entry(buf, fp, sizeof(u32));
|
||||
|
@ -1862,10 +1909,22 @@ static int filename_trans_read(struct policydb *p, void *fp)
|
|||
ft->stype = le32_to_cpu(buf[0]);
|
||||
ft->ttype = le32_to_cpu(buf[1]);
|
||||
ft->tclass = le32_to_cpu(buf[2]);
|
||||
ft->otype = le32_to_cpu(buf[3]);
|
||||
|
||||
otype->otype = le32_to_cpu(buf[3]);
|
||||
|
||||
rc = ebitmap_set_bit(&p->filename_trans_ttypes, ft->ttype, 1);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
hashtab_insert(p->filename_trans, ft, otype);
|
||||
}
|
||||
rc = 0;
|
||||
hash_eval(p->filename_trans, "filenametr");
|
||||
return 0;
|
||||
out:
|
||||
kfree(ft);
|
||||
kfree(name);
|
||||
kfree(otype);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -2266,6 +2325,11 @@ int policydb_read(struct policydb *p, void *fp)
|
|||
p->symtab[i].nprim = nprim;
|
||||
}
|
||||
|
||||
rc = -EINVAL;
|
||||
p->process_class = string_to_security_class(p, "process");
|
||||
if (!p->process_class)
|
||||
goto bad;
|
||||
|
||||
rc = avtab_read(&p->te_avtab, fp, p);
|
||||
if (rc)
|
||||
goto bad;
|
||||
|
@ -2298,8 +2362,17 @@ int policydb_read(struct policydb *p, void *fp)
|
|||
tr->role = le32_to_cpu(buf[0]);
|
||||
tr->type = le32_to_cpu(buf[1]);
|
||||
tr->new_role = le32_to_cpu(buf[2]);
|
||||
if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
|
||||
rc = next_entry(buf, fp, sizeof(u32));
|
||||
if (rc)
|
||||
goto bad;
|
||||
tr->tclass = le32_to_cpu(buf[0]);
|
||||
} else
|
||||
tr->tclass = p->process_class;
|
||||
|
||||
if (!policydb_role_isvalid(p, tr->role) ||
|
||||
!policydb_type_isvalid(p, tr->type) ||
|
||||
!policydb_class_isvalid(p, tr->tclass) ||
|
||||
!policydb_role_isvalid(p, tr->new_role))
|
||||
goto bad;
|
||||
ltr = tr;
|
||||
|
@ -2340,11 +2413,6 @@ int policydb_read(struct policydb *p, void *fp)
|
|||
if (rc)
|
||||
goto bad;
|
||||
|
||||
rc = -EINVAL;
|
||||
p->process_class = string_to_security_class(p, "process");
|
||||
if (!p->process_class)
|
||||
goto bad;
|
||||
|
||||
rc = -EINVAL;
|
||||
p->process_trans_perms = string_to_av_perm(p, p->process_class, "transition");
|
||||
p->process_trans_perms |= string_to_av_perm(p, p->process_class, "dyntransition");
|
||||
|
@ -2517,8 +2585,9 @@ static int cat_write(void *vkey, void *datum, void *ptr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int role_trans_write(struct role_trans *r, void *fp)
|
||||
static int role_trans_write(struct policydb *p, void *fp)
|
||||
{
|
||||
struct role_trans *r = p->role_tr;
|
||||
struct role_trans *tr;
|
||||
u32 buf[3];
|
||||
size_t nel;
|
||||
|
@ -2538,6 +2607,12 @@ static int role_trans_write(struct role_trans *r, void *fp)
|
|||
rc = put_entry(buf, sizeof(u32), 3, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
|
||||
buf[0] = cpu_to_le32(tr->tclass);
|
||||
rc = put_entry(buf, sizeof(u32), 1, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -3045,7 +3120,7 @@ static int genfs_write(struct policydb *p, void *fp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int range_count(void *key, void *data, void *ptr)
|
||||
static int hashtab_cnt(void *key, void *data, void *ptr)
|
||||
{
|
||||
int *cnt = ptr;
|
||||
*cnt = *cnt + 1;
|
||||
|
@ -3093,7 +3168,7 @@ static int range_write(struct policydb *p, void *fp)
|
|||
|
||||
/* count the number of entries in the hashtab */
|
||||
nel = 0;
|
||||
rc = hashtab_map(p->range_tr, range_count, &nel);
|
||||
rc = hashtab_map(p->range_tr, hashtab_cnt, &nel);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
@ -3110,43 +3185,60 @@ static int range_write(struct policydb *p, void *fp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int filename_write_helper(void *key, void *data, void *ptr)
|
||||
{
|
||||
__le32 buf[4];
|
||||
struct filename_trans *ft = key;
|
||||
struct filename_trans_datum *otype = data;
|
||||
void *fp = ptr;
|
||||
int rc;
|
||||
u32 len;
|
||||
|
||||
len = strlen(ft->name);
|
||||
buf[0] = cpu_to_le32(len);
|
||||
rc = put_entry(buf, sizeof(u32), 1, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = put_entry(ft->name, sizeof(char), len, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
buf[0] = ft->stype;
|
||||
buf[1] = ft->ttype;
|
||||
buf[2] = ft->tclass;
|
||||
buf[3] = otype->otype;
|
||||
|
||||
rc = put_entry(buf, sizeof(u32), 4, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int filename_trans_write(struct policydb *p, void *fp)
|
||||
{
|
||||
struct filename_trans *ft;
|
||||
u32 len, nel = 0;
|
||||
__le32 buf[4];
|
||||
u32 nel;
|
||||
__le32 buf[1];
|
||||
int rc;
|
||||
|
||||
for (ft = p->filename_trans; ft; ft = ft->next)
|
||||
nel++;
|
||||
nel = 0;
|
||||
rc = hashtab_map(p->filename_trans, hashtab_cnt, &nel);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
buf[0] = cpu_to_le32(nel);
|
||||
rc = put_entry(buf, sizeof(u32), 1, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
for (ft = p->filename_trans; ft; ft = ft->next) {
|
||||
len = strlen(ft->name);
|
||||
buf[0] = cpu_to_le32(len);
|
||||
rc = put_entry(buf, sizeof(u32), 1, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = hashtab_map(p->filename_trans, filename_write_helper, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = put_entry(ft->name, sizeof(char), len, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
buf[0] = ft->stype;
|
||||
buf[1] = ft->ttype;
|
||||
buf[2] = ft->tclass;
|
||||
buf[3] = ft->otype;
|
||||
|
||||
rc = put_entry(buf, sizeof(u32), 4, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the configuration data in a policy database
|
||||
* structure to a policy database binary representation
|
||||
|
@ -3249,7 +3341,7 @@ int policydb_write(struct policydb *p, void *fp)
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = role_trans_write(p->role_tr, fp);
|
||||
rc = role_trans_write(p, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
|
|
@ -72,17 +72,20 @@ struct role_datum {
|
|||
|
||||
struct role_trans {
|
||||
u32 role; /* current role */
|
||||
u32 type; /* program executable type */
|
||||
u32 type; /* program executable type, or new object type */
|
||||
u32 tclass; /* process class, or new object class */
|
||||
u32 new_role; /* new role */
|
||||
struct role_trans *next;
|
||||
};
|
||||
|
||||
struct filename_trans {
|
||||
struct filename_trans *next;
|
||||
u32 stype; /* current process */
|
||||
u32 ttype; /* parent dir context */
|
||||
u16 tclass; /* class of new object */
|
||||
const char *name; /* last path component */
|
||||
};
|
||||
|
||||
struct filename_trans_datum {
|
||||
u32 otype; /* expected of new object */
|
||||
};
|
||||
|
||||
|
@ -227,7 +230,10 @@ struct policydb {
|
|||
struct role_trans *role_tr;
|
||||
|
||||
/* file transitions with the last path component */
|
||||
struct filename_trans *filename_trans;
|
||||
/* quickly exclude lookups when parent ttype has no rules */
|
||||
struct ebitmap filename_trans_ttypes;
|
||||
/* actual set of filename_trans rules */
|
||||
struct hashtab *filename_trans;
|
||||
|
||||
/* bools indexed by (value - 1) */
|
||||
struct cond_bool_datum **bool_val_to_struct;
|
||||
|
|
|
@ -1359,26 +1359,35 @@ static int compute_sid_handle_invalid_context(
|
|||
}
|
||||
|
||||
static void filename_compute_type(struct policydb *p, struct context *newcontext,
|
||||
u32 scon, u32 tcon, u16 tclass,
|
||||
const struct qstr *qstr)
|
||||
u32 stype, u32 ttype, u16 tclass,
|
||||
const char *objname)
|
||||
{
|
||||
struct filename_trans *ft;
|
||||
for (ft = p->filename_trans; ft; ft = ft->next) {
|
||||
if (ft->stype == scon &&
|
||||
ft->ttype == tcon &&
|
||||
ft->tclass == tclass &&
|
||||
!strcmp(ft->name, qstr->name)) {
|
||||
newcontext->type = ft->otype;
|
||||
return;
|
||||
}
|
||||
}
|
||||
struct filename_trans ft;
|
||||
struct filename_trans_datum *otype;
|
||||
|
||||
/*
|
||||
* Most filename trans rules are going to live in specific directories
|
||||
* like /dev or /var/run. This bitmap will quickly skip rule searches
|
||||
* if the ttype does not contain any rules.
|
||||
*/
|
||||
if (!ebitmap_get_bit(&p->filename_trans_ttypes, ttype))
|
||||
return;
|
||||
|
||||
ft.stype = stype;
|
||||
ft.ttype = ttype;
|
||||
ft.tclass = tclass;
|
||||
ft.name = objname;
|
||||
|
||||
otype = hashtab_search(p->filename_trans, &ft);
|
||||
if (otype)
|
||||
newcontext->type = otype->otype;
|
||||
}
|
||||
|
||||
static int security_compute_sid(u32 ssid,
|
||||
u32 tsid,
|
||||
u16 orig_tclass,
|
||||
u32 specified,
|
||||
const struct qstr *qstr,
|
||||
const char *objname,
|
||||
u32 *out_sid,
|
||||
bool kern)
|
||||
{
|
||||
|
@ -1478,23 +1487,21 @@ static int security_compute_sid(u32 ssid,
|
|||
newcontext.type = avdatum->data;
|
||||
}
|
||||
|
||||
/* if we have a qstr this is a file trans check so check those rules */
|
||||
if (qstr)
|
||||
/* if we have a objname this is a file trans check so check those rules */
|
||||
if (objname)
|
||||
filename_compute_type(&policydb, &newcontext, scontext->type,
|
||||
tcontext->type, tclass, qstr);
|
||||
tcontext->type, tclass, objname);
|
||||
|
||||
/* Check for class-specific changes. */
|
||||
if (tclass == policydb.process_class) {
|
||||
if (specified & AVTAB_TRANSITION) {
|
||||
/* Look for a role transition rule. */
|
||||
for (roletr = policydb.role_tr; roletr;
|
||||
roletr = roletr->next) {
|
||||
if (roletr->role == scontext->role &&
|
||||
roletr->type == tcontext->type) {
|
||||
/* Use the role transition rule. */
|
||||
newcontext.role = roletr->new_role;
|
||||
break;
|
||||
}
|
||||
if (specified & AVTAB_TRANSITION) {
|
||||
/* Look for a role transition rule. */
|
||||
for (roletr = policydb.role_tr; roletr; roletr = roletr->next) {
|
||||
if ((roletr->role == scontext->role) &&
|
||||
(roletr->type == tcontext->type) &&
|
||||
(roletr->tclass == tclass)) {
|
||||
/* Use the role transition rule. */
|
||||
newcontext.role = roletr->new_role;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1541,13 +1548,14 @@ int security_transition_sid(u32 ssid, u32 tsid, u16 tclass,
|
|||
const struct qstr *qstr, u32 *out_sid)
|
||||
{
|
||||
return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION,
|
||||
qstr, out_sid, true);
|
||||
qstr ? qstr->name : NULL, out_sid, true);
|
||||
}
|
||||
|
||||
int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass, u32 *out_sid)
|
||||
int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass,
|
||||
const char *objname, u32 *out_sid)
|
||||
{
|
||||
return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION,
|
||||
NULL, out_sid, false);
|
||||
objname, out_sid, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3190,7 +3198,7 @@ int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
|
|||
* @len: length of data in bytes
|
||||
*
|
||||
*/
|
||||
int security_read_policy(void **data, ssize_t *len)
|
||||
int security_read_policy(void **data, size_t *len)
|
||||
{
|
||||
int rc;
|
||||
struct policy_file fp;
|
||||
|
|
|
@ -316,22 +316,17 @@ static inline void smk_ad_setfield_u_tsk(struct smk_audit_info *a,
|
|||
static inline void smk_ad_setfield_u_fs_path_dentry(struct smk_audit_info *a,
|
||||
struct dentry *d)
|
||||
{
|
||||
a->a.u.fs.path.dentry = d;
|
||||
}
|
||||
static inline void smk_ad_setfield_u_fs_path_mnt(struct smk_audit_info *a,
|
||||
struct vfsmount *m)
|
||||
{
|
||||
a->a.u.fs.path.mnt = m;
|
||||
a->a.u.dentry = d;
|
||||
}
|
||||
static inline void smk_ad_setfield_u_fs_inode(struct smk_audit_info *a,
|
||||
struct inode *i)
|
||||
{
|
||||
a->a.u.fs.inode = i;
|
||||
a->a.u.inode = i;
|
||||
}
|
||||
static inline void smk_ad_setfield_u_fs_path(struct smk_audit_info *a,
|
||||
struct path p)
|
||||
{
|
||||
a->a.u.fs.path = p;
|
||||
a->a.u.path = p;
|
||||
}
|
||||
static inline void smk_ad_setfield_u_net_sk(struct smk_audit_info *a,
|
||||
struct sock *sk)
|
||||
|
|
|
@ -383,7 +383,7 @@ static int smack_sb_statfs(struct dentry *dentry)
|
|||
int rc;
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
|
||||
rc = smk_curacc(sbp->smk_floor, MAY_READ, &ad);
|
||||
|
@ -407,7 +407,7 @@ static int smack_sb_mount(char *dev_name, struct path *path,
|
|||
struct superblock_smack *sbp = path->mnt->mnt_sb->s_security;
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
|
||||
smk_ad_setfield_u_fs_path(&ad, *path);
|
||||
|
||||
return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad);
|
||||
|
@ -425,10 +425,13 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
|
|||
{
|
||||
struct superblock_smack *sbp;
|
||||
struct smk_audit_info ad;
|
||||
struct path path;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, mnt->mnt_root);
|
||||
smk_ad_setfield_u_fs_path_mnt(&ad, mnt);
|
||||
path.dentry = mnt->mnt_root;
|
||||
path.mnt = mnt;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
|
||||
smk_ad_setfield_u_fs_path(&ad, path);
|
||||
|
||||
sbp = mnt->mnt_sb->s_security;
|
||||
return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad);
|
||||
|
@ -563,7 +566,7 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir,
|
|||
struct smk_audit_info ad;
|
||||
int rc;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry);
|
||||
|
||||
isp = smk_of_inode(old_dentry->d_inode);
|
||||
|
@ -592,7 +595,7 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry)
|
|||
struct smk_audit_info ad;
|
||||
int rc;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
|
||||
/*
|
||||
|
@ -623,7 +626,7 @@ static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry)
|
|||
struct smk_audit_info ad;
|
||||
int rc;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
|
||||
/*
|
||||
|
@ -663,7 +666,7 @@ static int smack_inode_rename(struct inode *old_inode,
|
|||
char *isp;
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry);
|
||||
|
||||
isp = smk_of_inode(old_dentry->d_inode);
|
||||
|
@ -700,7 +703,7 @@ static int smack_inode_permission(struct inode *inode, int mask, unsigned flags)
|
|||
/* May be droppable after audit */
|
||||
if (flags & IPERM_FLAG_RCU)
|
||||
return -ECHILD;
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE);
|
||||
smk_ad_setfield_u_fs_inode(&ad, inode);
|
||||
return smk_curacc(smk_of_inode(inode), mask, &ad);
|
||||
}
|
||||
|
@ -720,7 +723,7 @@ static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr)
|
|||
*/
|
||||
if (iattr->ia_valid & ATTR_FORCE)
|
||||
return 0;
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
|
||||
return smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad);
|
||||
|
@ -736,10 +739,13 @@ static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr)
|
|||
static int smack_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
|
||||
{
|
||||
struct smk_audit_info ad;
|
||||
struct path path;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
smk_ad_setfield_u_fs_path_mnt(&ad, mnt);
|
||||
path.dentry = dentry;
|
||||
path.mnt = mnt;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
|
||||
smk_ad_setfield_u_fs_path(&ad, path);
|
||||
return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad);
|
||||
}
|
||||
|
||||
|
@ -784,7 +790,7 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,
|
|||
} else
|
||||
rc = cap_inode_setxattr(dentry, name, value, size, flags);
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
|
||||
if (rc == 0)
|
||||
|
@ -845,7 +851,7 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name)
|
|||
{
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
|
||||
return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad);
|
||||
|
@ -877,7 +883,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name)
|
|||
} else
|
||||
rc = cap_inode_removexattr(dentry, name);
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
if (rc == 0)
|
||||
rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad);
|
||||
|
@ -1047,7 +1053,7 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd,
|
|||
int rc = 0;
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
|
||||
smk_ad_setfield_u_fs_path(&ad, file->f_path);
|
||||
|
||||
if (_IOC_DIR(cmd) & _IOC_WRITE)
|
||||
|
@ -1070,8 +1076,8 @@ static int smack_file_lock(struct file *file, unsigned int cmd)
|
|||
{
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, file->f_path.dentry);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
|
||||
smk_ad_setfield_u_fs_path(&ad, file->f_path);
|
||||
return smk_curacc(file->f_security, MAY_WRITE, &ad);
|
||||
}
|
||||
|
||||
|
@ -1089,7 +1095,7 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd,
|
|||
struct smk_audit_info ad;
|
||||
int rc;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
|
||||
smk_ad_setfield_u_fs_path(&ad, file->f_path);
|
||||
|
||||
switch (cmd) {
|
||||
|
|
|
@ -108,10 +108,9 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head)
|
|||
head->read_user_buf += len;
|
||||
w += len;
|
||||
}
|
||||
if (*w) {
|
||||
head->r.w[0] = w;
|
||||
head->r.w[0] = w;
|
||||
if (*w)
|
||||
return false;
|
||||
}
|
||||
/* Add '\0' for query. */
|
||||
if (head->poll) {
|
||||
if (!head->read_user_buf_avail ||
|
||||
|
@ -459,8 +458,16 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
|
|||
if (profile == &tomoyo_default_profile)
|
||||
return -EINVAL;
|
||||
if (!strcmp(data, "COMMENT")) {
|
||||
const struct tomoyo_path_info *old_comment = profile->comment;
|
||||
profile->comment = tomoyo_get_name(cp);
|
||||
static DEFINE_SPINLOCK(lock);
|
||||
const struct tomoyo_path_info *new_comment
|
||||
= tomoyo_get_name(cp);
|
||||
const struct tomoyo_path_info *old_comment;
|
||||
if (!new_comment)
|
||||
return -ENOMEM;
|
||||
spin_lock(&lock);
|
||||
old_comment = profile->comment;
|
||||
profile->comment = new_comment;
|
||||
spin_unlock(&lock);
|
||||
tomoyo_put_name(old_comment);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1011,7 +1011,6 @@ int tomoyo_path_perm(const u8 operation, struct path *path)
|
|||
break;
|
||||
case TOMOYO_TYPE_RMDIR:
|
||||
case TOMOYO_TYPE_CHROOT:
|
||||
case TOMOYO_TYPE_UMOUNT:
|
||||
tomoyo_add_slash(&buf);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ void *tomoyo_commit_ok(void *data, const unsigned int size)
|
|||
memset(data, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
kfree(ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -143,6 +143,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
|
|||
goto out;
|
||||
}
|
||||
requested_dev_name = tomoyo_realpath_from_path(&path);
|
||||
path_put(&path);
|
||||
if (!requested_dev_name) {
|
||||
error = -ENOENT;
|
||||
goto out;
|
||||
|
|
|
@ -390,7 +390,7 @@ bool tomoyo_correct_domain(const unsigned char *domainname)
|
|||
if (!cp)
|
||||
break;
|
||||
if (*domainname != '/' ||
|
||||
!tomoyo_correct_word2(domainname, cp - domainname - 1))
|
||||
!tomoyo_correct_word2(domainname, cp - domainname))
|
||||
goto out;
|
||||
domainname = cp + 1;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue