mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-30 23:54:04 +08:00
Merge branch 'next-general' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: - Extend LSM stacking to allow sharing of cred, file, ipc, inode, and task blobs. This paves the way for more full-featured LSMs to be merged, and is specifically aimed at LandLock and SARA LSMs. This work is from Casey and Kees. - There's a new LSM from Micah Morton: "SafeSetID gates the setid family of syscalls to restrict UID/GID transitions from a given UID/GID to only those approved by a system-wide whitelist." This feature is currently shipping in ChromeOS. * 'next-general' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (62 commits) keys: fix missing __user in KEYCTL_PKEY_QUERY LSM: Update list of SECURITYFS users in Kconfig LSM: Ignore "security=" when "lsm=" is specified LSM: Update function documentation for cap_capable security: mark expected switch fall-throughs and add a missing break tomoyo: Bump version. LSM: fix return value check in safesetid_init_securityfs() LSM: SafeSetID: add selftest LSM: SafeSetID: remove unused include LSM: SafeSetID: 'depend' on CONFIG_SECURITY LSM: Add 'name' field for SafeSetID in DEFINE_LSM LSM: add SafeSetID module that gates setid calls LSM: add SafeSetID module that gates setid calls tomoyo: Allow multiple use_group lines. tomoyo: Coding style fix. tomoyo: Swicth from cred->security to task_struct->security. security: keys: annotate implicit fall throughs security: keys: annotate implicit fall throughs security: keys: annotate implicit fall through capabilities:: annotate implicit fall through ...
This commit is contained in:
commit
ae5906ceee
107
Documentation/admin-guide/LSM/SafeSetID.rst
Normal file
107
Documentation/admin-guide/LSM/SafeSetID.rst
Normal file
@ -0,0 +1,107 @@
|
||||
=========
|
||||
SafeSetID
|
||||
=========
|
||||
SafeSetID is an LSM module that gates the setid family of syscalls to restrict
|
||||
UID/GID transitions from a given UID/GID to only those approved by a
|
||||
system-wide whitelist. These restrictions also prohibit the given UIDs/GIDs
|
||||
from obtaining auxiliary privileges associated with CAP_SET{U/G}ID, such as
|
||||
allowing a user to set up user namespace UID mappings.
|
||||
|
||||
|
||||
Background
|
||||
==========
|
||||
In absence of file capabilities, processes spawned on a Linux system that need
|
||||
to switch to a different user must be spawned with CAP_SETUID privileges.
|
||||
CAP_SETUID is granted to programs running as root or those running as a non-root
|
||||
user that have been explicitly given the CAP_SETUID runtime capability. It is
|
||||
often preferable to use Linux runtime capabilities rather than file
|
||||
capabilities, since using file capabilities to run a program with elevated
|
||||
privileges opens up possible security holes since any user with access to the
|
||||
file can exec() that program to gain the elevated privileges.
|
||||
|
||||
While it is possible to implement a tree of processes by giving full
|
||||
CAP_SET{U/G}ID capabilities, this is often at odds with the goals of running a
|
||||
tree of processes under non-root user(s) in the first place. Specifically,
|
||||
since CAP_SETUID allows changing to any user on the system, including the root
|
||||
user, it is an overpowered capability for what is needed in this scenario,
|
||||
especially since programs often only call setuid() to drop privileges to a
|
||||
lesser-privileged user -- not elevate privileges. Unfortunately, there is no
|
||||
generally feasible way in Linux to restrict the potential UIDs that a user can
|
||||
switch to through setuid() beyond allowing a switch to any user on the system.
|
||||
This SafeSetID LSM seeks to provide a solution for restricting setid
|
||||
capabilities in such a way.
|
||||
|
||||
The main use case for this LSM is to allow a non-root program to transition to
|
||||
other untrusted uids without full blown CAP_SETUID capabilities. The non-root
|
||||
program would still need CAP_SETUID to do any kind of transition, but the
|
||||
additional restrictions imposed by this LSM would mean it is a "safer" version
|
||||
of CAP_SETUID since the non-root program cannot take advantage of CAP_SETUID to
|
||||
do any unapproved actions (e.g. setuid to uid 0 or create/enter new user
|
||||
namespace). The higher level goal is to allow for uid-based sandboxing of system
|
||||
services without having to give out CAP_SETUID all over the place just so that
|
||||
non-root programs can drop to even-lesser-privileged uids. This is especially
|
||||
relevant when one non-root daemon on the system should be allowed to spawn other
|
||||
processes as different uids, but its undesirable to give the daemon a
|
||||
basically-root-equivalent CAP_SETUID.
|
||||
|
||||
|
||||
Other Approaches Considered
|
||||
===========================
|
||||
|
||||
Solve this problem in userspace
|
||||
-------------------------------
|
||||
For candidate applications that would like to have restricted setid capabilities
|
||||
as implemented in this LSM, an alternative option would be to simply take away
|
||||
setid capabilities from the application completely and refactor the process
|
||||
spawning semantics in the application (e.g. by using a privileged helper program
|
||||
to do process spawning and UID/GID transitions). Unfortunately, there are a
|
||||
number of semantics around process spawning that would be affected by this, such
|
||||
as fork() calls where the program doesn???t immediately call exec() after the
|
||||
fork(), parent processes specifying custom environment variables or command line
|
||||
args for spawned child processes, or inheritance of file handles across a
|
||||
fork()/exec(). Because of this, as solution that uses a privileged helper in
|
||||
userspace would likely be less appealing to incorporate into existing projects
|
||||
that rely on certain process-spawning semantics in Linux.
|
||||
|
||||
Use user namespaces
|
||||
-------------------
|
||||
Another possible approach would be to run a given process tree in its own user
|
||||
namespace and give programs in the tree setid capabilities. In this way,
|
||||
programs in the tree could change to any desired UID/GID in the context of their
|
||||
own user namespace, and only approved UIDs/GIDs could be mapped back to the
|
||||
initial system user namespace, affectively preventing privilege escalation.
|
||||
Unfortunately, it is not generally feasible to use user namespaces in isolation,
|
||||
without pairing them with other namespace types, which is not always an option.
|
||||
Linux checks for capabilities based off of the user namespace that ???owns??? some
|
||||
entity. For example, Linux has the notion that network namespaces are owned by
|
||||
the user namespace in which they were created. A consequence of this is that
|
||||
capability checks for access to a given network namespace are done by checking
|
||||
whether a task has the given capability in the context of the user namespace
|
||||
that owns the network namespace -- not necessarily the user namespace under
|
||||
which the given task runs. Therefore spawning a process in a new user namespace
|
||||
effectively prevents it from accessing the network namespace owned by the
|
||||
initial namespace. This is a deal-breaker for any application that expects to
|
||||
retain the CAP_NET_ADMIN capability for the purpose of adjusting network
|
||||
configurations. Using user namespaces in isolation causes problems regarding
|
||||
other system interactions, including use of pid namespaces and device creation.
|
||||
|
||||
Use an existing LSM
|
||||
-------------------
|
||||
None of the other in-tree LSMs have the capability to gate setid transitions, or
|
||||
even employ the security_task_fix_setuid hook at all. SELinux says of that hook:
|
||||
"Since setuid only affects the current process, and since the SELinux controls
|
||||
are not based on the Linux identity attributes, SELinux does not need to control
|
||||
this operation."
|
||||
|
||||
|
||||
Directions for use
|
||||
==================
|
||||
This LSM hooks the setid syscalls to make sure transitions are allowed if an
|
||||
applicable restriction policy is in place. Policies are configured through
|
||||
securityfs by writing to the safesetid/add_whitelist_policy and
|
||||
safesetid/flush_whitelist_policies files at the location where securityfs is
|
||||
mounted. The format for adding a policy is '<UID>:<UID>', using literal
|
||||
numbers, such as '123:456'. To flush the policies, any write to the file is
|
||||
sufficient. Again, configuring a policy for a UID will prevent that UID from
|
||||
obtaining auxiliary setid privileges, such as allowing a user to set up user
|
||||
namespace UID mappings.
|
@ -17,9 +17,8 @@ MAC extensions, other extensions can be built using the LSM to provide
|
||||
specific changes to system operation when these tweaks are not available
|
||||
in the core functionality of Linux itself.
|
||||
|
||||
Without a specific LSM built into the kernel, the default LSM will be the
|
||||
Linux capabilities system. Most LSMs choose to extend the capabilities
|
||||
system, building their checks on top of the defined capability hooks.
|
||||
The Linux capabilities modules will always be included. This may be
|
||||
followed by any number of "minor" modules and at most one "major" module.
|
||||
For more details on capabilities, see ``capabilities(7)`` in the Linux
|
||||
man-pages project.
|
||||
|
||||
@ -30,6 +29,14 @@ order in which checks are made. The capability module will always
|
||||
be first, followed by any "minor" modules (e.g. Yama) and then
|
||||
the one "major" module (e.g. SELinux) if there is one configured.
|
||||
|
||||
Process attributes associated with "major" security modules should
|
||||
be accessed and maintained using the special files in ``/proc/.../attr``.
|
||||
A security module may maintain a module specific subdirectory there,
|
||||
named after the module. ``/proc/.../attr/smack`` is provided by the Smack
|
||||
security module and contains all its special files. The files directly
|
||||
in ``/proc/.../attr`` remain as legacy interfaces for modules that provide
|
||||
subdirectories.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
@ -39,3 +46,4 @@ the one "major" module (e.g. SELinux) if there is one configured.
|
||||
Smack
|
||||
tomoyo
|
||||
Yama
|
||||
SafeSetID
|
||||
|
@ -2333,6 +2333,10 @@
|
||||
|
||||
lsm.debug [SECURITY] Enable LSM initialization debugging output.
|
||||
|
||||
lsm=lsm1,...,lsmN
|
||||
[SECURITY] Choose order of LSM initialization. This
|
||||
overrides CONFIG_LSM, and the "security=" parameter.
|
||||
|
||||
machvec= [IA-64] Force the use of a particular machine-vector
|
||||
(machvec) in a generic kernel.
|
||||
Example: machvec=hpzx1_swiotlb
|
||||
@ -4110,11 +4114,9 @@
|
||||
Note: increases power consumption, thus should only be
|
||||
enabled if running jitter sensitive (HPC/RT) workloads.
|
||||
|
||||
security= [SECURITY] Choose a security module to enable at boot.
|
||||
If this boot parameter is not specified, only the first
|
||||
security module asking for security registration will be
|
||||
loaded. An invalid security module name will be treated
|
||||
as if no module has been chosen.
|
||||
security= [SECURITY] Choose a legacy "major" security module to
|
||||
enable at boot. This has been deprecated by the
|
||||
"lsm=" parameter.
|
||||
|
||||
selinux= [SELINUX] Disable or enable SELinux at boot time.
|
||||
Format: { "0" | "1" }
|
||||
|
11
MAINTAINERS
11
MAINTAINERS
@ -15557,12 +15557,11 @@ F: mm/shmem.c
|
||||
TOMOYO SECURITY MODULE
|
||||
M: Kentaro Takeda <takedakn@nttdata.co.jp>
|
||||
M: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
|
||||
L: tomoyo-dev-en@lists.sourceforge.jp (subscribers-only, for developers in English)
|
||||
L: tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for users in English)
|
||||
L: tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese)
|
||||
L: tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese)
|
||||
W: http://tomoyo.sourceforge.jp/
|
||||
T: quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.5.x/tomoyo-lsm/patches/
|
||||
L: tomoyo-dev-en@lists.osdn.me (subscribers-only, for developers in English)
|
||||
L: tomoyo-users-en@lists.osdn.me (subscribers-only, for users in English)
|
||||
L: tomoyo-dev@lists.osdn.me (subscribers-only, for developers in Japanese)
|
||||
L: tomoyo-users@lists.osdn.me (subscribers-only, for users in Japanese)
|
||||
W: https://tomoyo.osdn.jp/
|
||||
S: Maintained
|
||||
F: security/tomoyo/
|
||||
|
||||
|
@ -143,6 +143,10 @@ struct pid_entry {
|
||||
NOD(NAME, (S_IFREG|(MODE)), \
|
||||
NULL, &proc_single_file_operations, \
|
||||
{ .proc_show = show } )
|
||||
#define ATTR(LSM, NAME, MODE) \
|
||||
NOD(NAME, (S_IFREG|(MODE)), \
|
||||
NULL, &proc_pid_attr_operations, \
|
||||
{ .lsm = LSM })
|
||||
|
||||
/*
|
||||
* Count the number of hardlinks for the pid_entry table, excluding the .
|
||||
@ -2521,7 +2525,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf,
|
||||
if (!task)
|
||||
return -ESRCH;
|
||||
|
||||
length = security_getprocattr(task,
|
||||
length = security_getprocattr(task, PROC_I(inode)->op.lsm,
|
||||
(char*)file->f_path.dentry->d_name.name,
|
||||
&p);
|
||||
put_task_struct(task);
|
||||
@ -2570,7 +2574,9 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
|
||||
if (rv < 0)
|
||||
goto out_free;
|
||||
|
||||
rv = security_setprocattr(file->f_path.dentry->d_name.name, page, count);
|
||||
rv = security_setprocattr(PROC_I(inode)->op.lsm,
|
||||
file->f_path.dentry->d_name.name, page,
|
||||
count);
|
||||
mutex_unlock(¤t->signal->cred_guard_mutex);
|
||||
out_free:
|
||||
kfree(page);
|
||||
@ -2584,13 +2590,53 @@ static const struct file_operations proc_pid_attr_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
#define LSM_DIR_OPS(LSM) \
|
||||
static int proc_##LSM##_attr_dir_iterate(struct file *filp, \
|
||||
struct dir_context *ctx) \
|
||||
{ \
|
||||
return proc_pident_readdir(filp, ctx, \
|
||||
LSM##_attr_dir_stuff, \
|
||||
ARRAY_SIZE(LSM##_attr_dir_stuff)); \
|
||||
} \
|
||||
\
|
||||
static const struct file_operations proc_##LSM##_attr_dir_ops = { \
|
||||
.read = generic_read_dir, \
|
||||
.iterate = proc_##LSM##_attr_dir_iterate, \
|
||||
.llseek = default_llseek, \
|
||||
}; \
|
||||
\
|
||||
static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \
|
||||
struct dentry *dentry, unsigned int flags) \
|
||||
{ \
|
||||
return proc_pident_lookup(dir, dentry, \
|
||||
LSM##_attr_dir_stuff, \
|
||||
ARRAY_SIZE(LSM##_attr_dir_stuff)); \
|
||||
} \
|
||||
\
|
||||
static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \
|
||||
.lookup = proc_##LSM##_attr_dir_lookup, \
|
||||
.getattr = pid_getattr, \
|
||||
.setattr = proc_setattr, \
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECURITY_SMACK
|
||||
static const struct pid_entry smack_attr_dir_stuff[] = {
|
||||
ATTR("smack", "current", 0666),
|
||||
};
|
||||
LSM_DIR_OPS(smack);
|
||||
#endif
|
||||
|
||||
static const struct pid_entry attr_dir_stuff[] = {
|
||||
REG("current", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
|
||||
REG("prev", S_IRUGO, proc_pid_attr_operations),
|
||||
REG("exec", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
|
||||
REG("fscreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
|
||||
REG("keycreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
|
||||
REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
|
||||
ATTR(NULL, "current", 0666),
|
||||
ATTR(NULL, "prev", 0444),
|
||||
ATTR(NULL, "exec", 0666),
|
||||
ATTR(NULL, "fscreate", 0666),
|
||||
ATTR(NULL, "keycreate", 0666),
|
||||
ATTR(NULL, "sockcreate", 0666),
|
||||
#ifdef CONFIG_SECURITY_SMACK
|
||||
DIR("smack", 0555,
|
||||
proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops),
|
||||
#endif
|
||||
};
|
||||
|
||||
static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx)
|
||||
|
@ -82,6 +82,7 @@ union proc_op {
|
||||
int (*proc_show)(struct seq_file *m,
|
||||
struct pid_namespace *ns, struct pid *pid,
|
||||
struct task_struct *task);
|
||||
const char *lsm;
|
||||
};
|
||||
|
||||
struct proc_inode {
|
||||
|
@ -209,6 +209,7 @@ extern bool has_ns_capability_noaudit(struct task_struct *t,
|
||||
extern bool capable(int cap);
|
||||
extern bool ns_capable(struct user_namespace *ns, int cap);
|
||||
extern bool ns_capable_noaudit(struct user_namespace *ns, int cap);
|
||||
extern bool ns_capable_setid(struct user_namespace *ns, int cap);
|
||||
#else
|
||||
static inline bool has_capability(struct task_struct *t, int cap)
|
||||
{
|
||||
@ -240,6 +241,10 @@ static inline bool ns_capable_noaudit(struct user_namespace *ns, int cap)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
static inline bool ns_capable_setid(struct user_namespace *ns, int cap)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_MULTIUSER */
|
||||
extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode);
|
||||
extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <linux/capability.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/selinux.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/uidgid.h>
|
||||
#include <linux/sched.h>
|
||||
|
@ -1270,7 +1270,7 @@
|
||||
* @cred contains the credentials to use.
|
||||
* @ns contains the user namespace we want the capability in
|
||||
* @cap contains the capability <include/linux/capability.h>.
|
||||
* @audit contains whether to write an audit message or not
|
||||
* @opts contains options for the capable check <include/linux/security.h>
|
||||
* Return 0 if the capability is granted for @tsk.
|
||||
* @syslog:
|
||||
* Check permission before accessing the kernel message ring or changing
|
||||
@ -1446,8 +1446,10 @@ union security_list_options {
|
||||
const kernel_cap_t *effective,
|
||||
const kernel_cap_t *inheritable,
|
||||
const kernel_cap_t *permitted);
|
||||
int (*capable)(const struct cred *cred, struct user_namespace *ns,
|
||||
int cap, int audit);
|
||||
int (*capable)(const struct cred *cred,
|
||||
struct user_namespace *ns,
|
||||
int cap,
|
||||
unsigned int opts);
|
||||
int (*quotactl)(int cmds, int type, int id, struct super_block *sb);
|
||||
int (*quota_on)(struct dentry *dentry);
|
||||
int (*syslog)(int type);
|
||||
@ -2027,6 +2029,18 @@ struct security_hook_list {
|
||||
char *lsm;
|
||||
} __randomize_layout;
|
||||
|
||||
/*
|
||||
* Security blob size or offset data.
|
||||
*/
|
||||
struct lsm_blob_sizes {
|
||||
int lbs_cred;
|
||||
int lbs_file;
|
||||
int lbs_inode;
|
||||
int lbs_ipc;
|
||||
int lbs_msg_msg;
|
||||
int lbs_task;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initializing a security_hook_list structure takes
|
||||
* up a lot of space in a source file. This macro takes
|
||||
@ -2042,9 +2056,21 @@ extern char *lsm_names;
|
||||
extern void security_add_hooks(struct security_hook_list *hooks, int count,
|
||||
char *lsm);
|
||||
|
||||
#define LSM_FLAG_LEGACY_MAJOR BIT(0)
|
||||
#define LSM_FLAG_EXCLUSIVE BIT(1)
|
||||
|
||||
enum lsm_order {
|
||||
LSM_ORDER_FIRST = -1, /* This is only for capabilities. */
|
||||
LSM_ORDER_MUTABLE = 0,
|
||||
};
|
||||
|
||||
struct lsm_info {
|
||||
const char *name; /* Required. */
|
||||
enum lsm_order order; /* Optional: default is LSM_ORDER_MUTABLE */
|
||||
unsigned long flags; /* Optional: flags describing LSM */
|
||||
int *enabled; /* Optional: controlled by CONFIG_LSM */
|
||||
int (*init)(void); /* Required. */
|
||||
struct lsm_blob_sizes *blobs; /* Optional: for blob sharing. */
|
||||
};
|
||||
|
||||
extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
|
||||
@ -2084,17 +2110,6 @@ static inline void security_delete_hooks(struct security_hook_list *hooks,
|
||||
#define __lsm_ro_after_init __ro_after_init
|
||||
#endif /* CONFIG_SECURITY_WRITABLE_HOOKS */
|
||||
|
||||
extern int __init security_module_enable(const char *module);
|
||||
extern void __init capability_add_hooks(void);
|
||||
#ifdef CONFIG_SECURITY_YAMA
|
||||
extern void __init yama_add_hooks(void);
|
||||
#else
|
||||
static inline void __init yama_add_hooks(void) { }
|
||||
#endif
|
||||
#ifdef CONFIG_SECURITY_LOADPIN
|
||||
void __init loadpin_add_hooks(void);
|
||||
#else
|
||||
static inline void loadpin_add_hooks(void) { };
|
||||
#endif
|
||||
extern int lsm_inode_alloc(struct inode *inode);
|
||||
|
||||
#endif /* ! __LINUX_LSM_HOOKS_H */
|
||||
|
@ -54,9 +54,12 @@ struct xattr;
|
||||
struct xfrm_sec_ctx;
|
||||
struct mm_struct;
|
||||
|
||||
/* Default (no) options for the capable function */
|
||||
#define CAP_OPT_NONE 0x0
|
||||
/* If capable should audit the security request */
|
||||
#define SECURITY_CAP_NOAUDIT 0
|
||||
#define SECURITY_CAP_AUDIT 1
|
||||
#define CAP_OPT_NOAUDIT BIT(1)
|
||||
/* If capable is being called by a setid function */
|
||||
#define CAP_OPT_INSETID BIT(2)
|
||||
|
||||
/* LSM Agnostic defines for sb_set_mnt_opts */
|
||||
#define SECURITY_LSM_NATIVE_LABELS 1
|
||||
@ -72,7 +75,7 @@ enum lsm_event {
|
||||
|
||||
/* These functions are in security/commoncap.c */
|
||||
extern int cap_capable(const struct cred *cred, struct user_namespace *ns,
|
||||
int cap, int audit);
|
||||
int cap, unsigned int opts);
|
||||
extern int cap_settime(const struct timespec64 *ts, const struct timezone *tz);
|
||||
extern int cap_ptrace_access_check(struct task_struct *child, unsigned int mode);
|
||||
extern int cap_ptrace_traceme(struct task_struct *parent);
|
||||
@ -207,10 +210,10 @@ int security_capset(struct cred *new, const struct cred *old,
|
||||
const kernel_cap_t *effective,
|
||||
const kernel_cap_t *inheritable,
|
||||
const kernel_cap_t *permitted);
|
||||
int security_capable(const struct cred *cred, struct user_namespace *ns,
|
||||
int cap);
|
||||
int security_capable_noaudit(const struct cred *cred, struct user_namespace *ns,
|
||||
int cap);
|
||||
int security_capable(const struct cred *cred,
|
||||
struct user_namespace *ns,
|
||||
int cap,
|
||||
unsigned int opts);
|
||||
int security_quotactl(int cmds, int type, int id, struct super_block *sb);
|
||||
int security_quota_on(struct dentry *dentry);
|
||||
int security_syslog(int type);
|
||||
@ -366,8 +369,10 @@ int security_sem_semctl(struct kern_ipc_perm *sma, int cmd);
|
||||
int security_sem_semop(struct kern_ipc_perm *sma, struct sembuf *sops,
|
||||
unsigned nsops, int alter);
|
||||
void security_d_instantiate(struct dentry *dentry, struct inode *inode);
|
||||
int security_getprocattr(struct task_struct *p, char *name, char **value);
|
||||
int security_setprocattr(const char *name, void *value, size_t size);
|
||||
int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
|
||||
char **value);
|
||||
int security_setprocattr(const char *lsm, const char *name, void *value,
|
||||
size_t size);
|
||||
int security_netlink_send(struct sock *sk, struct sk_buff *skb);
|
||||
int security_ismaclabel(const char *name);
|
||||
int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
|
||||
@ -462,14 +467,11 @@ static inline int security_capset(struct cred *new,
|
||||
}
|
||||
|
||||
static inline int security_capable(const struct cred *cred,
|
||||
struct user_namespace *ns, int cap)
|
||||
struct user_namespace *ns,
|
||||
int cap,
|
||||
unsigned int opts)
|
||||
{
|
||||
return cap_capable(cred, ns, cap, SECURITY_CAP_AUDIT);
|
||||
}
|
||||
|
||||
static inline int security_capable_noaudit(const struct cred *cred,
|
||||
struct user_namespace *ns, int cap) {
|
||||
return cap_capable(cred, ns, cap, SECURITY_CAP_NOAUDIT);
|
||||
return cap_capable(cred, ns, cap, opts);
|
||||
}
|
||||
|
||||
static inline int security_quotactl(int cmds, int type, int id,
|
||||
@ -1112,15 +1114,18 @@ static inline int security_sem_semop(struct kern_ipc_perm *sma,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void security_d_instantiate(struct dentry *dentry, struct inode *inode)
|
||||
static inline void security_d_instantiate(struct dentry *dentry,
|
||||
struct inode *inode)
|
||||
{ }
|
||||
|
||||
static inline int security_getprocattr(struct task_struct *p, char *name, char **value)
|
||||
static inline int security_getprocattr(struct task_struct *p, const char *lsm,
|
||||
char *name, char **value)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int security_setprocattr(char *name, void *value, size_t size)
|
||||
static inline int security_setprocattr(const char *lsm, char *name,
|
||||
void *value, size_t size)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* SELinux services exported to the rest of the kernel.
|
||||
*
|
||||
* Author: James Morris <jmorris@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2005 Red Hat, Inc., James Morris <jmorris@redhat.com>
|
||||
* Copyright (C) 2006 Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
|
||||
* Copyright (C) 2006 IBM Corporation, Timothy R. Chavez <tinytim@us.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef _LINUX_SELINUX_H
|
||||
#define _LINUX_SELINUX_H
|
||||
|
||||
struct selinux_audit_rule;
|
||||
struct audit_context;
|
||||
struct kern_ipc_perm;
|
||||
|
||||
#ifdef CONFIG_SECURITY_SELINUX
|
||||
|
||||
/**
|
||||
* selinux_is_enabled - is SELinux enabled?
|
||||
*/
|
||||
bool selinux_is_enabled(void);
|
||||
#else
|
||||
|
||||
static inline bool selinux_is_enabled(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_SECURITY_SELINUX */
|
||||
|
||||
#endif /* _LINUX_SELINUX_H */
|
@ -93,9 +93,7 @@ static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy)
|
||||
break;
|
||||
case _LINUX_CAPABILITY_VERSION_2:
|
||||
warn_deprecated_v2();
|
||||
/*
|
||||
* fall through - v3 is otherwise equivalent to v2.
|
||||
*/
|
||||
/* fall through - v3 is otherwise equivalent to v2. */
|
||||
case _LINUX_CAPABILITY_VERSION_3:
|
||||
*tocopy = _LINUX_CAPABILITY_U32S_3;
|
||||
break;
|
||||
@ -299,7 +297,7 @@ bool has_ns_capability(struct task_struct *t,
|
||||
int ret;
|
||||
|
||||
rcu_read_lock();
|
||||
ret = security_capable(__task_cred(t), ns, cap);
|
||||
ret = security_capable(__task_cred(t), ns, cap, CAP_OPT_NONE);
|
||||
rcu_read_unlock();
|
||||
|
||||
return (ret == 0);
|
||||
@ -340,7 +338,7 @@ bool has_ns_capability_noaudit(struct task_struct *t,
|
||||
int ret;
|
||||
|
||||
rcu_read_lock();
|
||||
ret = security_capable_noaudit(__task_cred(t), ns, cap);
|
||||
ret = security_capable(__task_cred(t), ns, cap, CAP_OPT_NOAUDIT);
|
||||
rcu_read_unlock();
|
||||
|
||||
return (ret == 0);
|
||||
@ -363,7 +361,9 @@ bool has_capability_noaudit(struct task_struct *t, int cap)
|
||||
return has_ns_capability_noaudit(t, &init_user_ns, cap);
|
||||
}
|
||||
|
||||
static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit)
|
||||
static bool ns_capable_common(struct user_namespace *ns,
|
||||
int cap,
|
||||
unsigned int opts)
|
||||
{
|
||||
int capable;
|
||||
|
||||
@ -372,8 +372,7 @@ static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit)
|
||||
BUG();
|
||||
}
|
||||
|
||||
capable = audit ? security_capable(current_cred(), ns, cap) :
|
||||
security_capable_noaudit(current_cred(), ns, cap);
|
||||
capable = security_capable(current_cred(), ns, cap, opts);
|
||||
if (capable == 0) {
|
||||
current->flags |= PF_SUPERPRIV;
|
||||
return true;
|
||||
@ -394,7 +393,7 @@ static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit)
|
||||
*/
|
||||
bool ns_capable(struct user_namespace *ns, int cap)
|
||||
{
|
||||
return ns_capable_common(ns, cap, true);
|
||||
return ns_capable_common(ns, cap, CAP_OPT_NONE);
|
||||
}
|
||||
EXPORT_SYMBOL(ns_capable);
|
||||
|
||||
@ -412,10 +411,29 @@ EXPORT_SYMBOL(ns_capable);
|
||||
*/
|
||||
bool ns_capable_noaudit(struct user_namespace *ns, int cap)
|
||||
{
|
||||
return ns_capable_common(ns, cap, false);
|
||||
return ns_capable_common(ns, cap, CAP_OPT_NOAUDIT);
|
||||
}
|
||||
EXPORT_SYMBOL(ns_capable_noaudit);
|
||||
|
||||
/**
|
||||
* ns_capable_setid - Determine if the current task has a superior capability
|
||||
* in effect, while signalling that this check is being done from within a
|
||||
* setid syscall.
|
||||
* @ns: The usernamespace we want the capability in
|
||||
* @cap: The capability to be tested for
|
||||
*
|
||||
* Return true if the current task has the given superior capability currently
|
||||
* available for use, false if not.
|
||||
*
|
||||
* This sets PF_SUPERPRIV on the task if the capability is available on the
|
||||
* assumption that it's about to be used.
|
||||
*/
|
||||
bool ns_capable_setid(struct user_namespace *ns, int cap)
|
||||
{
|
||||
return ns_capable_common(ns, cap, CAP_OPT_INSETID);
|
||||
}
|
||||
EXPORT_SYMBOL(ns_capable_setid);
|
||||
|
||||
/**
|
||||
* capable - Determine if the current task has a superior capability in effect
|
||||
* @cap: The capability to be tested for
|
||||
@ -448,10 +466,11 @@ EXPORT_SYMBOL(capable);
|
||||
bool file_ns_capable(const struct file *file, struct user_namespace *ns,
|
||||
int cap)
|
||||
{
|
||||
|
||||
if (WARN_ON_ONCE(!cap_valid(cap)))
|
||||
return false;
|
||||
|
||||
if (security_capable(file->f_cred, ns, cap) == 0)
|
||||
if (security_capable(file->f_cred, ns, cap, CAP_OPT_NONE) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@ -500,10 +519,12 @@ bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns)
|
||||
{
|
||||
int ret = 0; /* An absent tracer adds no restrictions */
|
||||
const struct cred *cred;
|
||||
|
||||
rcu_read_lock();
|
||||
cred = rcu_dereference(tsk->ptracer_cred);
|
||||
if (cred)
|
||||
ret = security_capable_noaudit(cred, ns, CAP_SYS_PTRACE);
|
||||
ret = security_capable(cred, ns, CAP_SYS_PTRACE,
|
||||
CAP_OPT_NOAUDIT);
|
||||
rcu_read_unlock();
|
||||
return (ret == 0);
|
||||
}
|
||||
|
@ -760,19 +760,6 @@ bool creds_are_invalid(const struct cred *cred)
|
||||
{
|
||||
if (cred->magic != CRED_MAGIC)
|
||||
return true;
|
||||
#ifdef CONFIG_SECURITY_SELINUX
|
||||
/*
|
||||
* cred->security == NULL if security_cred_alloc_blank() or
|
||||
* security_prepare_creds() returned an error.
|
||||
*/
|
||||
if (selinux_is_enabled() && cred->security) {
|
||||
if ((unsigned long) cred->security < PAGE_SIZE)
|
||||
return true;
|
||||
if ((*(u32 *)cred->security & 0xffffff00) ==
|
||||
(POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL(creds_are_invalid);
|
||||
|
@ -445,8 +445,8 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
|
||||
* behavior of privileged children.
|
||||
*/
|
||||
if (!task_no_new_privs(current) &&
|
||||
security_capable_noaudit(current_cred(), current_user_ns(),
|
||||
CAP_SYS_ADMIN) != 0)
|
||||
security_capable(current_cred(), current_user_ns(),
|
||||
CAP_SYS_ADMIN, CAP_OPT_NOAUDIT) != 0)
|
||||
return ERR_PTR(-EACCES);
|
||||
|
||||
/* Allocate a new seccomp_filter */
|
||||
|
10
kernel/sys.c
10
kernel/sys.c
@ -516,7 +516,7 @@ long __sys_setreuid(uid_t ruid, uid_t euid)
|
||||
new->uid = kruid;
|
||||
if (!uid_eq(old->uid, kruid) &&
|
||||
!uid_eq(old->euid, kruid) &&
|
||||
!ns_capable(old->user_ns, CAP_SETUID))
|
||||
!ns_capable_setid(old->user_ns, CAP_SETUID))
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -525,7 +525,7 @@ long __sys_setreuid(uid_t ruid, uid_t euid)
|
||||
if (!uid_eq(old->uid, keuid) &&
|
||||
!uid_eq(old->euid, keuid) &&
|
||||
!uid_eq(old->suid, keuid) &&
|
||||
!ns_capable(old->user_ns, CAP_SETUID))
|
||||
!ns_capable_setid(old->user_ns, CAP_SETUID))
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -584,7 +584,7 @@ long __sys_setuid(uid_t uid)
|
||||
old = current_cred();
|
||||
|
||||
retval = -EPERM;
|
||||
if (ns_capable(old->user_ns, CAP_SETUID)) {
|
||||
if (ns_capable_setid(old->user_ns, CAP_SETUID)) {
|
||||
new->suid = new->uid = kuid;
|
||||
if (!uid_eq(kuid, old->uid)) {
|
||||
retval = set_user(new);
|
||||
@ -646,7 +646,7 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
|
||||
old = current_cred();
|
||||
|
||||
retval = -EPERM;
|
||||
if (!ns_capable(old->user_ns, CAP_SETUID)) {
|
||||
if (!ns_capable_setid(old->user_ns, CAP_SETUID)) {
|
||||
if (ruid != (uid_t) -1 && !uid_eq(kruid, old->uid) &&
|
||||
!uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid))
|
||||
goto error;
|
||||
@ -814,7 +814,7 @@ long __sys_setfsuid(uid_t uid)
|
||||
|
||||
if (uid_eq(kuid, old->uid) || uid_eq(kuid, old->euid) ||
|
||||
uid_eq(kuid, old->suid) || uid_eq(kuid, old->fsuid) ||
|
||||
ns_capable(old->user_ns, CAP_SETUID)) {
|
||||
ns_capable_setid(old->user_ns, CAP_SETUID)) {
|
||||
if (!uid_eq(kuid, old->fsuid)) {
|
||||
new->fsuid = kuid;
|
||||
if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)
|
||||
|
@ -40,8 +40,7 @@ config SECURITYFS
|
||||
bool "Enable the securityfs filesystem"
|
||||
help
|
||||
This will build the securityfs filesystem. It is currently used by
|
||||
the TPM bios character driver and IMA, an integrity provider. It is
|
||||
not used by SELinux or SMACK.
|
||||
various security modules (AppArmor, IMA, SafeSetID, TOMOYO, TPM).
|
||||
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
@ -236,45 +235,19 @@ source "security/tomoyo/Kconfig"
|
||||
source "security/apparmor/Kconfig"
|
||||
source "security/loadpin/Kconfig"
|
||||
source "security/yama/Kconfig"
|
||||
source "security/safesetid/Kconfig"
|
||||
|
||||
source "security/integrity/Kconfig"
|
||||
|
||||
choice
|
||||
prompt "Default security module"
|
||||
default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX
|
||||
default DEFAULT_SECURITY_SMACK if SECURITY_SMACK
|
||||
default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
|
||||
default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR
|
||||
default DEFAULT_SECURITY_DAC
|
||||
|
||||
config LSM
|
||||
string "Ordered list of enabled LSMs"
|
||||
default "yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor"
|
||||
help
|
||||
Select the security module that will be used by default if the
|
||||
kernel parameter security= is not specified.
|
||||
A comma-separated list of LSMs, in initialization order.
|
||||
Any LSMs left off this list will be ignored. This can be
|
||||
controlled at boot with the "lsm=" parameter.
|
||||
|
||||
config DEFAULT_SECURITY_SELINUX
|
||||
bool "SELinux" if SECURITY_SELINUX=y
|
||||
|
||||
config DEFAULT_SECURITY_SMACK
|
||||
bool "Simplified Mandatory Access Control" if SECURITY_SMACK=y
|
||||
|
||||
config DEFAULT_SECURITY_TOMOYO
|
||||
bool "TOMOYO" if SECURITY_TOMOYO=y
|
||||
|
||||
config DEFAULT_SECURITY_APPARMOR
|
||||
bool "AppArmor" if SECURITY_APPARMOR=y
|
||||
|
||||
config DEFAULT_SECURITY_DAC
|
||||
bool "Unix Discretionary Access Controls"
|
||||
|
||||
endchoice
|
||||
|
||||
config DEFAULT_SECURITY
|
||||
string
|
||||
default "selinux" if DEFAULT_SECURITY_SELINUX
|
||||
default "smack" if DEFAULT_SECURITY_SMACK
|
||||
default "tomoyo" if DEFAULT_SECURITY_TOMOYO
|
||||
default "apparmor" if DEFAULT_SECURITY_APPARMOR
|
||||
default "" if DEFAULT_SECURITY_DAC
|
||||
If unsure, leave this as the default.
|
||||
|
||||
endmenu
|
||||
|
||||
|
@ -10,6 +10,7 @@ subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo
|
||||
subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
|
||||
subdir-$(CONFIG_SECURITY_YAMA) += yama
|
||||
subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
|
||||
subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid
|
||||
|
||||
# always enable default capabilities
|
||||
obj-y += commoncap.o
|
||||
@ -25,6 +26,7 @@ obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/
|
||||
obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/
|
||||
obj-$(CONFIG_SECURITY_YAMA) += yama/
|
||||
obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
|
||||
obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/
|
||||
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
|
||||
|
||||
# Object integrity file lists
|
||||
|
@ -14,22 +14,6 @@ config SECURITY_APPARMOR
|
||||
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
config SECURITY_APPARMOR_BOOTPARAM_VALUE
|
||||
int "AppArmor boot parameter default value"
|
||||
depends on SECURITY_APPARMOR
|
||||
range 0 1
|
||||
default 1
|
||||
help
|
||||
This option sets the default value for the kernel parameter
|
||||
'apparmor', which allows AppArmor to be enabled or disabled
|
||||
at boot. If this option is set to 0 (zero), the AppArmor
|
||||
kernel parameter will default to 0, disabling AppArmor at
|
||||
boot. If this option is set to 1 (one), the AppArmor
|
||||
kernel parameter will default to 1, enabling AppArmor at
|
||||
boot.
|
||||
|
||||
If you are unsure how to answer this question, answer 1.
|
||||
|
||||
config SECURITY_APPARMOR_HASH
|
||||
bool "Enable introspection of sha1 hashes for loaded profiles"
|
||||
depends on SECURITY_APPARMOR
|
||||
|
@ -110,13 +110,13 @@ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
|
||||
* profile_capable - test if profile allows use of capability @cap
|
||||
* @profile: profile being enforced (NOT NULL, NOT unconfined)
|
||||
* @cap: capability to test if allowed
|
||||
* @audit: whether an audit record should be generated
|
||||
* @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated
|
||||
* @sa: audit data (MAY BE NULL indicating no auditing)
|
||||
*
|
||||
* Returns: 0 if allowed else -EPERM
|
||||
*/
|
||||
static int profile_capable(struct aa_profile *profile, int cap, int audit,
|
||||
struct common_audit_data *sa)
|
||||
static int profile_capable(struct aa_profile *profile, int cap,
|
||||
unsigned int opts, struct common_audit_data *sa)
|
||||
{
|
||||
int error;
|
||||
|
||||
@ -126,7 +126,7 @@ static int profile_capable(struct aa_profile *profile, int cap, int audit,
|
||||
else
|
||||
error = -EPERM;
|
||||
|
||||
if (audit == SECURITY_CAP_NOAUDIT) {
|
||||
if (opts & CAP_OPT_NOAUDIT) {
|
||||
if (!COMPLAIN_MODE(profile))
|
||||
return error;
|
||||
/* audit the cap request in complain mode but note that it
|
||||
@ -142,13 +142,13 @@ static int profile_capable(struct aa_profile *profile, int cap, int audit,
|
||||
* aa_capable - test permission to use capability
|
||||
* @label: label being tested for capability (NOT NULL)
|
||||
* @cap: capability to be tested
|
||||
* @audit: whether an audit record should be generated
|
||||
* @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated
|
||||
*
|
||||
* Look up capability in profile capability set.
|
||||
*
|
||||
* Returns: 0 on success, or else an error code.
|
||||
*/
|
||||
int aa_capable(struct aa_label *label, int cap, int audit)
|
||||
int aa_capable(struct aa_label *label, int cap, unsigned int opts)
|
||||
{
|
||||
struct aa_profile *profile;
|
||||
int error = 0;
|
||||
@ -156,7 +156,7 @@ int aa_capable(struct aa_label *label, int cap, int audit)
|
||||
|
||||
sa.u.cap = cap;
|
||||
error = fn_for_each_confined(label, profile,
|
||||
profile_capable(profile, cap, audit, &sa));
|
||||
profile_capable(profile, cap, opts, &sa));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -572,7 +572,7 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
|
||||
stack = NULL;
|
||||
break;
|
||||
}
|
||||
/* fall through to X_NAME */
|
||||
/* fall through - to X_NAME */
|
||||
case AA_X_NAME:
|
||||
if (xindex & AA_X_CHILD)
|
||||
/* released by caller */
|
||||
@ -975,7 +975,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
}
|
||||
aa_put_label(cred_label(bprm->cred));
|
||||
/* transfer reference, released when cred is freed */
|
||||
cred_label(bprm->cred) = new;
|
||||
set_cred_label(bprm->cred, new);
|
||||
|
||||
done:
|
||||
aa_put_label(label);
|
||||
|
@ -40,7 +40,7 @@ struct aa_caps {
|
||||
|
||||
extern struct aa_sfs_entry aa_sfs_entry_caps[];
|
||||
|
||||
int aa_capable(struct aa_label *label, int cap, int audit);
|
||||
int aa_capable(struct aa_label *label, int cap, unsigned int opts);
|
||||
|
||||
static inline void aa_free_cap_rules(struct aa_caps *caps)
|
||||
{
|
||||
|
@ -23,8 +23,22 @@
|
||||
#include "policy_ns.h"
|
||||
#include "task.h"
|
||||
|
||||
#define cred_label(X) ((X)->security)
|
||||
static inline struct aa_label *cred_label(const struct cred *cred)
|
||||
{
|
||||
struct aa_label **blob = cred->security + apparmor_blob_sizes.lbs_cred;
|
||||
|
||||
AA_BUG(!blob);
|
||||
return *blob;
|
||||
}
|
||||
|
||||
static inline void set_cred_label(const struct cred *cred,
|
||||
struct aa_label *label)
|
||||
{
|
||||
struct aa_label **blob = cred->security + apparmor_blob_sizes.lbs_cred;
|
||||
|
||||
AA_BUG(!blob);
|
||||
*blob = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_cred_raw_label - obtain cred's label
|
||||
|
@ -32,7 +32,10 @@ struct path;
|
||||
AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_LOCK | \
|
||||
AA_EXEC_MMAP | AA_MAY_LINK)
|
||||
|
||||
#define file_ctx(X) ((struct aa_file_ctx *)(X)->f_security)
|
||||
static inline struct aa_file_ctx *file_ctx(struct file *file)
|
||||
{
|
||||
return file->f_security + apparmor_blob_sizes.lbs_file;
|
||||
}
|
||||
|
||||
/* struct aa_file_ctx - the AppArmor context the file was opened in
|
||||
* @lock: lock to update the ctx
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/lsm_hooks.h>
|
||||
|
||||
#include "match.h"
|
||||
|
||||
@ -55,6 +56,9 @@ const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
|
||||
size_t *ns_len);
|
||||
void aa_info_message(const char *str);
|
||||
|
||||
/* Security blob offsets */
|
||||
extern struct lsm_blob_sizes apparmor_blob_sizes;
|
||||
|
||||
/**
|
||||
* aa_strneq - compare null terminated @str to a non null terminated substring
|
||||
* @str: a null terminated string
|
||||
|
@ -14,7 +14,10 @@
|
||||
#ifndef __AA_TASK_H
|
||||
#define __AA_TASK_H
|
||||
|
||||
#define task_ctx(X) ((X)->security)
|
||||
static inline struct aa_task_ctx *task_ctx(struct task_struct *task)
|
||||
{
|
||||
return task->security + apparmor_blob_sizes.lbs_task;
|
||||
}
|
||||
|
||||
/*
|
||||
* struct aa_task_ctx - information for current task label change
|
||||
@ -36,17 +39,6 @@ int aa_set_current_hat(struct aa_label *label, u64 token);
|
||||
int aa_restore_previous_label(u64 cookie);
|
||||
struct aa_label *aa_get_task_label(struct task_struct *task);
|
||||
|
||||
/**
|
||||
* aa_alloc_task_ctx - allocate a new task_ctx
|
||||
* @flags: gfp flags for allocation
|
||||
*
|
||||
* Returns: allocated buffer or NULL on failure
|
||||
*/
|
||||
static inline struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags)
|
||||
{
|
||||
return kzalloc(sizeof(struct aa_task_ctx), flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_free_task_ctx - free a task_ctx
|
||||
* @ctx: task_ctx to free (MAYBE NULL)
|
||||
@ -57,8 +49,6 @@ static inline void aa_free_task_ctx(struct aa_task_ctx *ctx)
|
||||
aa_put_label(ctx->nnp);
|
||||
aa_put_label(ctx->previous);
|
||||
aa_put_label(ctx->onexec);
|
||||
|
||||
kzfree(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,8 @@ static int profile_tracer_perm(struct aa_profile *tracer,
|
||||
aad(sa)->label = &tracer->label;
|
||||
aad(sa)->peer = tracee;
|
||||
aad(sa)->request = 0;
|
||||
aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, 1);
|
||||
aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE,
|
||||
CAP_OPT_NONE);
|
||||
|
||||
return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb);
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ DEFINE_PER_CPU(struct aa_buffers, aa_buffers);
|
||||
static void apparmor_cred_free(struct cred *cred)
|
||||
{
|
||||
aa_put_label(cred_label(cred));
|
||||
cred_label(cred) = NULL;
|
||||
set_cred_label(cred, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -68,7 +68,7 @@ static void apparmor_cred_free(struct cred *cred)
|
||||
*/
|
||||
static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
|
||||
{
|
||||
cred_label(cred) = NULL;
|
||||
set_cred_label(cred, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
|
||||
static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
|
||||
gfp_t gfp)
|
||||
{
|
||||
cred_label(new) = aa_get_newest_label(cred_label(old));
|
||||
set_cred_label(new, aa_get_newest_label(cred_label(old)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -87,26 +87,21 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
|
||||
*/
|
||||
static void apparmor_cred_transfer(struct cred *new, const struct cred *old)
|
||||
{
|
||||
cred_label(new) = aa_get_newest_label(cred_label(old));
|
||||
set_cred_label(new, aa_get_newest_label(cred_label(old)));
|
||||
}
|
||||
|
||||
static void apparmor_task_free(struct task_struct *task)
|
||||
{
|
||||
|
||||
aa_free_task_ctx(task_ctx(task));
|
||||
task_ctx(task) = NULL;
|
||||
}
|
||||
|
||||
static int apparmor_task_alloc(struct task_struct *task,
|
||||
unsigned long clone_flags)
|
||||
{
|
||||
struct aa_task_ctx *new = aa_alloc_task_ctx(GFP_KERNEL);
|
||||
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
struct aa_task_ctx *new = task_ctx(task);
|
||||
|
||||
aa_dup_task_ctx(new, task_ctx(current));
|
||||
task_ctx(task) = new;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -177,14 +172,14 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
|
||||
}
|
||||
|
||||
static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
|
||||
int cap, int audit)
|
||||
int cap, unsigned int opts)
|
||||
{
|
||||
struct aa_label *label;
|
||||
int error = 0;
|
||||
|
||||
label = aa_get_newest_cred_label(cred);
|
||||
if (!unconfined(label))
|
||||
error = aa_capable(label, cap, audit);
|
||||
error = aa_capable(label, cap, opts);
|
||||
aa_put_label(label);
|
||||
|
||||
return error;
|
||||
@ -434,21 +429,21 @@ static int apparmor_file_open(struct file *file)
|
||||
|
||||
static int apparmor_file_alloc_security(struct file *file)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
/* freed by apparmor_file_free_security */
|
||||
struct aa_file_ctx *ctx = file_ctx(file);
|
||||
struct aa_label *label = begin_current_label_crit_section();
|
||||
file->f_security = aa_alloc_file_ctx(label, GFP_KERNEL);
|
||||
if (!file_ctx(file))
|
||||
error = -ENOMEM;
|
||||
end_current_label_crit_section(label);
|
||||
|
||||
return error;
|
||||
spin_lock_init(&ctx->lock);
|
||||
rcu_assign_pointer(ctx->label, aa_get_label(label));
|
||||
end_current_label_crit_section(label);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void apparmor_file_free_security(struct file *file)
|
||||
{
|
||||
aa_free_file_ctx(file_ctx(file));
|
||||
struct aa_file_ctx *ctx = file_ctx(file);
|
||||
|
||||
if (ctx)
|
||||
aa_put_label(rcu_access_pointer(ctx->label));
|
||||
}
|
||||
|
||||
static int common_file_perm(const char *op, struct file *file, u32 mask)
|
||||
@ -1151,6 +1146,15 @@ static int apparmor_inet_conn_request(struct sock *sk, struct sk_buff *skb,
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The cred blob is a pointer to, not an instance of, an aa_task_ctx.
|
||||
*/
|
||||
struct lsm_blob_sizes apparmor_blob_sizes __lsm_ro_after_init = {
|
||||
.lbs_cred = sizeof(struct aa_task_ctx *),
|
||||
.lbs_file = sizeof(struct aa_file_ctx),
|
||||
.lbs_task = sizeof(struct aa_task_ctx),
|
||||
};
|
||||
|
||||
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
|
||||
LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
|
||||
@ -1333,8 +1337,8 @@ bool aa_g_paranoid_load = true;
|
||||
module_param_named(paranoid_load, aa_g_paranoid_load, aabool, S_IRUGO);
|
||||
|
||||
/* Boot time disable flag */
|
||||
static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE;
|
||||
module_param_named(enabled, apparmor_enabled, bool, S_IRUGO);
|
||||
static int apparmor_enabled __lsm_ro_after_init = 1;
|
||||
module_param_named(enabled, apparmor_enabled, int, 0444);
|
||||
|
||||
static int __init apparmor_enabled_setup(char *str)
|
||||
{
|
||||
@ -1479,14 +1483,8 @@ static int param_set_mode(const char *val, const struct kernel_param *kp)
|
||||
static int __init set_init_ctx(void)
|
||||
{
|
||||
struct cred *cred = (struct cred *)current->real_cred;
|
||||
struct aa_task_ctx *ctx;
|
||||
|
||||
ctx = aa_alloc_task_ctx(GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
cred_label(cred) = aa_get_label(ns_unconfined(root_ns));
|
||||
task_ctx(current) = ctx;
|
||||
set_cred_label(cred, aa_get_label(ns_unconfined(root_ns)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1665,12 +1663,6 @@ static int __init apparmor_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (!apparmor_enabled || !security_module_enable("apparmor")) {
|
||||
aa_info_message("AppArmor disabled by boot time parameter");
|
||||
apparmor_enabled = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
aa_secids_init();
|
||||
|
||||
error = aa_setup_dfa_engine();
|
||||
@ -1731,5 +1723,8 @@ alloc_out:
|
||||
|
||||
DEFINE_LSM(apparmor) = {
|
||||
.name = "apparmor",
|
||||
.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
|
||||
.enabled = &apparmor_enabled,
|
||||
.blobs = &apparmor_blob_sizes,
|
||||
.init = apparmor_init,
|
||||
};
|
||||
|
@ -124,7 +124,7 @@ int aa_task_setrlimit(struct aa_label *label, struct task_struct *task,
|
||||
*/
|
||||
|
||||
if (label != peer &&
|
||||
aa_capable(label, CAP_SYS_RESOURCE, SECURITY_CAP_NOAUDIT) != 0)
|
||||
aa_capable(label, CAP_SYS_RESOURCE, CAP_OPT_NOAUDIT) != 0)
|
||||
error = fn_for_each(label, profile,
|
||||
audit_resource(profile, resource,
|
||||
new_rlim->rlim_max, peer,
|
||||
|
@ -81,7 +81,7 @@ int aa_replace_current_label(struct aa_label *label)
|
||||
*/
|
||||
aa_get_label(label);
|
||||
aa_put_label(cred_label(new));
|
||||
cred_label(new) = label;
|
||||
set_cred_label(new, label);
|
||||
|
||||
commit_creds(new);
|
||||
return 0;
|
||||
@ -138,7 +138,7 @@ int aa_set_current_hat(struct aa_label *label, u64 token)
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
cred_label(new) = aa_get_newest_label(label);
|
||||
set_cred_label(new, aa_get_newest_label(label));
|
||||
/* clear exec on switching context */
|
||||
aa_put_label(ctx->onexec);
|
||||
ctx->onexec = NULL;
|
||||
@ -172,7 +172,7 @@ int aa_restore_previous_label(u64 token)
|
||||
return -ENOMEM;
|
||||
|
||||
aa_put_label(cred_label(new));
|
||||
cred_label(new) = aa_get_newest_label(ctx->previous);
|
||||
set_cred_label(new, aa_get_newest_label(ctx->previous));
|
||||
AA_BUG(!cred_label(new));
|
||||
/* clear exec && prev information when restoring to previous context */
|
||||
aa_clear_task_ctx_trans(ctx);
|
||||
|
@ -57,7 +57,7 @@ static void warn_setuid_and_fcaps_mixed(const char *fname)
|
||||
* @cred: The credentials to use
|
||||
* @ns: The user namespace in which we need the capability
|
||||
* @cap: The capability to check for
|
||||
* @audit: Whether to write an audit message or not
|
||||
* @opts: Bitmask of options defined in include/linux/security.h
|
||||
*
|
||||
* Determine whether the nominated task has the specified capability amongst
|
||||
* its effective set, returning 0 if it does, -ve if it does not.
|
||||
@ -68,7 +68,7 @@ static void warn_setuid_and_fcaps_mixed(const char *fname)
|
||||
* kernel's capable() and has_capability() returns 1 for this case.
|
||||
*/
|
||||
int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
|
||||
int cap, int audit)
|
||||
int cap, unsigned int opts)
|
||||
{
|
||||
struct user_namespace *ns = targ_ns;
|
||||
|
||||
@ -222,12 +222,11 @@ int cap_capget(struct task_struct *target, kernel_cap_t *effective,
|
||||
*/
|
||||
static inline int cap_inh_is_capped(void)
|
||||
{
|
||||
|
||||
/* they are so limited unless the current task has the CAP_SETPCAP
|
||||
* capability
|
||||
*/
|
||||
if (cap_capable(current_cred(), current_cred()->user_ns,
|
||||
CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0)
|
||||
CAP_SETPCAP, CAP_OPT_NONE) == 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@ -1208,8 +1207,9 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
|| ((old->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/
|
||||
|| (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
|
||||
|| (cap_capable(current_cred(),
|
||||
current_cred()->user_ns, CAP_SETPCAP,
|
||||
SECURITY_CAP_AUDIT) != 0) /*[4]*/
|
||||
current_cred()->user_ns,
|
||||
CAP_SETPCAP,
|
||||
CAP_OPT_NONE) != 0) /*[4]*/
|
||||
/*
|
||||
* [1] no changing of bits that are locked
|
||||
* [2] no unlocking of locks
|
||||
@ -1304,9 +1304,10 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
|
||||
{
|
||||
int cap_sys_admin = 0;
|
||||
|
||||
if (cap_capable(current_cred(), &init_user_ns, CAP_SYS_ADMIN,
|
||||
SECURITY_CAP_NOAUDIT) == 0)
|
||||
if (cap_capable(current_cred(), &init_user_ns,
|
||||
CAP_SYS_ADMIN, CAP_OPT_NOAUDIT) == 0)
|
||||
cap_sys_admin = 1;
|
||||
|
||||
return cap_sys_admin;
|
||||
}
|
||||
|
||||
@ -1325,7 +1326,7 @@ int cap_mmap_addr(unsigned long addr)
|
||||
|
||||
if (addr < dac_mmap_min_addr) {
|
||||
ret = cap_capable(current_cred(), &init_user_ns, CAP_SYS_RAWIO,
|
||||
SECURITY_CAP_AUDIT);
|
||||
CAP_OPT_NONE);
|
||||
/* set PF_SUPERPRIV if it turns out we allow the low mmap */
|
||||
if (ret == 0)
|
||||
current->flags |= PF_SUPERPRIV;
|
||||
@ -1362,10 +1363,17 @@ struct security_hook_list capability_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(vm_enough_memory, cap_vm_enough_memory),
|
||||
};
|
||||
|
||||
void __init capability_add_hooks(void)
|
||||
static int __init capability_init(void)
|
||||
{
|
||||
security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks),
|
||||
"capability");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_LSM(capability) = {
|
||||
.name = "capability",
|
||||
.order = LSM_ORDER_FIRST,
|
||||
.init = capability_init,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_SECURITY */
|
||||
|
@ -114,6 +114,7 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint,
|
||||
break;
|
||||
case CREDS_CHECK:
|
||||
iint->ima_creds_status = status;
|
||||
break;
|
||||
case FILE_CHECK:
|
||||
case POST_SETATTR:
|
||||
iint->ima_file_status = status;
|
||||
|
@ -938,10 +938,12 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
case Opt_uid_gt:
|
||||
case Opt_euid_gt:
|
||||
entry->uid_op = &uid_gt;
|
||||
/* fall through */
|
||||
case Opt_uid_lt:
|
||||
case Opt_euid_lt:
|
||||
if ((token == Opt_uid_lt) || (token == Opt_euid_lt))
|
||||
entry->uid_op = &uid_lt;
|
||||
/* fall through */
|
||||
case Opt_uid_eq:
|
||||
case Opt_euid_eq:
|
||||
uid_token = (token == Opt_uid_eq) ||
|
||||
@ -970,9 +972,11 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
break;
|
||||
case Opt_fowner_gt:
|
||||
entry->fowner_op = &uid_gt;
|
||||
/* fall through */
|
||||
case Opt_fowner_lt:
|
||||
if (token == Opt_fowner_lt)
|
||||
entry->fowner_op = &uid_lt;
|
||||
/* fall through */
|
||||
case Opt_fowner_eq:
|
||||
ima_log_string_op(ab, "fowner", args[0].from,
|
||||
entry->fowner_op);
|
||||
|
@ -83,6 +83,7 @@ static void ima_show_template_data_ascii(struct seq_file *m,
|
||||
/* skip ':' and '\0' */
|
||||
buf_ptr += 2;
|
||||
buflen -= buf_ptr - field_data->data;
|
||||
/* fall through */
|
||||
case DATA_FMT_DIGEST:
|
||||
case DATA_FMT_HEX:
|
||||
if (!buflen)
|
||||
|
@ -1752,7 +1752,7 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
||||
return -EINVAL;
|
||||
return keyctl_pkey_query((key_serial_t)arg2,
|
||||
(const char __user *)arg4,
|
||||
(struct keyctl_pkey_query *)arg5);
|
||||
(struct keyctl_pkey_query __user *)arg5);
|
||||
|
||||
case KEYCTL_PKEY_ENCRYPT:
|
||||
case KEYCTL_PKEY_DECRYPT:
|
||||
|
@ -246,6 +246,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
|
||||
(ASSOC_ARRAY_KEY_CHUNK_SIZE - 8));
|
||||
n--;
|
||||
offset = 1;
|
||||
/* fall through */
|
||||
default:
|
||||
offset += sizeof(chunk) - 1;
|
||||
offset += (level - 3) * sizeof(chunk);
|
||||
|
@ -380,6 +380,7 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
|
||||
case -EAGAIN: /* no key */
|
||||
if (ret)
|
||||
break;
|
||||
/* fall through */
|
||||
case -ENOKEY: /* negative key */
|
||||
ret = key_ref;
|
||||
break;
|
||||
@ -404,6 +405,7 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
|
||||
case -EAGAIN: /* no key */
|
||||
if (ret)
|
||||
break;
|
||||
/* fall through */
|
||||
case -ENOKEY: /* negative key */
|
||||
ret = key_ref;
|
||||
break;
|
||||
@ -424,6 +426,7 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
|
||||
case -EAGAIN: /* no key */
|
||||
if (ret)
|
||||
break;
|
||||
/* fall through */
|
||||
case -ENOKEY: /* negative key */
|
||||
ret = key_ref;
|
||||
break;
|
||||
|
@ -273,16 +273,19 @@ static int construct_get_dest_keyring(struct key **_dest_keyring)
|
||||
}
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
case KEY_REQKEY_DEFL_THREAD_KEYRING:
|
||||
dest_keyring = key_get(cred->thread_keyring);
|
||||
if (dest_keyring)
|
||||
break;
|
||||
|
||||
/* fall through */
|
||||
case KEY_REQKEY_DEFL_PROCESS_KEYRING:
|
||||
dest_keyring = key_get(cred->process_keyring);
|
||||
if (dest_keyring)
|
||||
break;
|
||||
|
||||
/* fall through */
|
||||
case KEY_REQKEY_DEFL_SESSION_KEYRING:
|
||||
rcu_read_lock();
|
||||
dest_keyring = key_get(
|
||||
@ -292,6 +295,7 @@ static int construct_get_dest_keyring(struct key **_dest_keyring)
|
||||
if (dest_keyring)
|
||||
break;
|
||||
|
||||
/* fall through */
|
||||
case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
|
||||
dest_keyring =
|
||||
key_get(cred->user->session_keyring);
|
||||
|
@ -187,13 +187,19 @@ static struct security_hook_list loadpin_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(kernel_load_data, loadpin_load_data),
|
||||
};
|
||||
|
||||
void __init loadpin_add_hooks(void)
|
||||
static int __init loadpin_init(void)
|
||||
{
|
||||
pr_info("ready to pin (currently %senforcing)\n",
|
||||
enforce ? "" : "not ");
|
||||
security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_LSM(loadpin) = {
|
||||
.name = "loadpin",
|
||||
.init = loadpin_init,
|
||||
};
|
||||
|
||||
/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
|
||||
module_param(enforce, int, 0);
|
||||
MODULE_PARM_DESC(enforce, "Enforce module/firmware pinning");
|
||||
|
14
security/safesetid/Kconfig
Normal file
14
security/safesetid/Kconfig
Normal file
@ -0,0 +1,14 @@
|
||||
config SECURITY_SAFESETID
|
||||
bool "Gate setid transitions to limit CAP_SET{U/G}ID capabilities"
|
||||
depends on SECURITY
|
||||
select SECURITYFS
|
||||
default n
|
||||
help
|
||||
SafeSetID is an LSM module that gates the setid family of syscalls to
|
||||
restrict UID/GID transitions from a given UID/GID to only those
|
||||
approved by a system-wide whitelist. These restrictions also prohibit
|
||||
the given UIDs/GIDs from obtaining auxiliary privileges associated
|
||||
with CAP_SET{U/G}ID, such as allowing a user to set up user namespace
|
||||
UID mappings.
|
||||
|
||||
If you are unsure how to answer this question, answer N.
|
7
security/safesetid/Makefile
Normal file
7
security/safesetid/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for the safesetid LSM.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SECURITY_SAFESETID) := safesetid.o
|
||||
safesetid-y := lsm.o securityfs.o
|
277
security/safesetid/lsm.c
Normal file
277
security/safesetid/lsm.c
Normal file
@ -0,0 +1,277 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* SafeSetID Linux Security Module
|
||||
*
|
||||
* Author: Micah Morton <mortonm@chromium.org>
|
||||
*
|
||||
* Copyright (C) 2018 The Chromium OS Authors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "SafeSetID: " fmt
|
||||
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/lsm_hooks.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/sched/task_stack.h>
|
||||
#include <linux/security.h>
|
||||
|
||||
/* Flag indicating whether initialization completed */
|
||||
int safesetid_initialized;
|
||||
|
||||
#define NUM_BITS 8 /* 128 buckets in hash table */
|
||||
|
||||
static DEFINE_HASHTABLE(safesetid_whitelist_hashtable, NUM_BITS);
|
||||
|
||||
/*
|
||||
* Hash table entry to store safesetid policy signifying that 'parent' user
|
||||
* can setid to 'child' user.
|
||||
*/
|
||||
struct entry {
|
||||
struct hlist_node next;
|
||||
struct hlist_node dlist; /* for deletion cleanup */
|
||||
uint64_t parent_kuid;
|
||||
uint64_t child_kuid;
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(safesetid_whitelist_hashtable_spinlock);
|
||||
|
||||
static bool check_setuid_policy_hashtable_key(kuid_t parent)
|
||||
{
|
||||
struct entry *entry;
|
||||
|
||||
rcu_read_lock();
|
||||
hash_for_each_possible_rcu(safesetid_whitelist_hashtable,
|
||||
entry, next, __kuid_val(parent)) {
|
||||
if (entry->parent_kuid == __kuid_val(parent)) {
|
||||
rcu_read_unlock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool check_setuid_policy_hashtable_key_value(kuid_t parent,
|
||||
kuid_t child)
|
||||
{
|
||||
struct entry *entry;
|
||||
|
||||
rcu_read_lock();
|
||||
hash_for_each_possible_rcu(safesetid_whitelist_hashtable,
|
||||
entry, next, __kuid_val(parent)) {
|
||||
if (entry->parent_kuid == __kuid_val(parent) &&
|
||||
entry->child_kuid == __kuid_val(child)) {
|
||||
rcu_read_unlock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int safesetid_security_capable(const struct cred *cred,
|
||||
struct user_namespace *ns,
|
||||
int cap,
|
||||
unsigned int opts)
|
||||
{
|
||||
if (cap == CAP_SETUID &&
|
||||
check_setuid_policy_hashtable_key(cred->uid)) {
|
||||
if (!(opts & CAP_OPT_INSETID)) {
|
||||
/*
|
||||
* Deny if we're not in a set*uid() syscall to avoid
|
||||
* giving powers gated by CAP_SETUID that are related
|
||||
* to functionality other than calling set*uid() (e.g.
|
||||
* allowing user to set up userns uid mappings).
|
||||
*/
|
||||
pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions",
|
||||
__kuid_val(cred->uid));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_uid_transition(kuid_t parent, kuid_t child)
|
||||
{
|
||||
if (check_setuid_policy_hashtable_key_value(parent, child))
|
||||
return 0;
|
||||
pr_warn("UID transition (%d -> %d) blocked",
|
||||
__kuid_val(parent),
|
||||
__kuid_val(child));
|
||||
/*
|
||||
* Kill this process to avoid potential security vulnerabilities
|
||||
* that could arise from a missing whitelist entry preventing a
|
||||
* privileged process from dropping to a lesser-privileged one.
|
||||
*/
|
||||
force_sig(SIGKILL, current);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether there is either an exception for user under old cred struct to
|
||||
* set*uid to user under new cred struct, or the UID transition is allowed (by
|
||||
* Linux set*uid rules) even without CAP_SETUID.
|
||||
*/
|
||||
static int safesetid_task_fix_setuid(struct cred *new,
|
||||
const struct cred *old,
|
||||
int flags)
|
||||
{
|
||||
|
||||
/* Do nothing if there are no setuid restrictions for this UID. */
|
||||
if (!check_setuid_policy_hashtable_key(old->uid))
|
||||
return 0;
|
||||
|
||||
switch (flags) {
|
||||
case LSM_SETID_RE:
|
||||
/*
|
||||
* Users for which setuid restrictions exist can only set the
|
||||
* real UID to the real UID or the effective UID, unless an
|
||||
* explicit whitelist policy allows the transition.
|
||||
*/
|
||||
if (!uid_eq(old->uid, new->uid) &&
|
||||
!uid_eq(old->euid, new->uid)) {
|
||||
return check_uid_transition(old->uid, new->uid);
|
||||
}
|
||||
/*
|
||||
* Users for which setuid restrictions exist can only set the
|
||||
* effective UID to the real UID, the effective UID, or the
|
||||
* saved set-UID, unless an explicit whitelist policy allows
|
||||
* the transition.
|
||||
*/
|
||||
if (!uid_eq(old->uid, new->euid) &&
|
||||
!uid_eq(old->euid, new->euid) &&
|
||||
!uid_eq(old->suid, new->euid)) {
|
||||
return check_uid_transition(old->euid, new->euid);
|
||||
}
|
||||
break;
|
||||
case LSM_SETID_ID:
|
||||
/*
|
||||
* Users for which setuid restrictions exist cannot change the
|
||||
* real UID or saved set-UID unless an explicit whitelist
|
||||
* policy allows the transition.
|
||||
*/
|
||||
if (!uid_eq(old->uid, new->uid))
|
||||
return check_uid_transition(old->uid, new->uid);
|
||||
if (!uid_eq(old->suid, new->suid))
|
||||
return check_uid_transition(old->suid, new->suid);
|
||||
break;
|
||||
case LSM_SETID_RES:
|
||||
/*
|
||||
* Users for which setuid restrictions exist cannot change the
|
||||
* real UID, effective UID, or saved set-UID to anything but
|
||||
* one of: the current real UID, the current effective UID or
|
||||
* the current saved set-user-ID unless an explicit whitelist
|
||||
* policy allows the transition.
|
||||
*/
|
||||
if (!uid_eq(new->uid, old->uid) &&
|
||||
!uid_eq(new->uid, old->euid) &&
|
||||
!uid_eq(new->uid, old->suid)) {
|
||||
return check_uid_transition(old->uid, new->uid);
|
||||
}
|
||||
if (!uid_eq(new->euid, old->uid) &&
|
||||
!uid_eq(new->euid, old->euid) &&
|
||||
!uid_eq(new->euid, old->suid)) {
|
||||
return check_uid_transition(old->euid, new->euid);
|
||||
}
|
||||
if (!uid_eq(new->suid, old->uid) &&
|
||||
!uid_eq(new->suid, old->euid) &&
|
||||
!uid_eq(new->suid, old->suid)) {
|
||||
return check_uid_transition(old->suid, new->suid);
|
||||
}
|
||||
break;
|
||||
case LSM_SETID_FS:
|
||||
/*
|
||||
* Users for which setuid restrictions exist cannot change the
|
||||
* filesystem UID to anything but one of: the current real UID,
|
||||
* the current effective UID or the current saved set-UID
|
||||
* unless an explicit whitelist policy allows the transition.
|
||||
*/
|
||||
if (!uid_eq(new->fsuid, old->uid) &&
|
||||
!uid_eq(new->fsuid, old->euid) &&
|
||||
!uid_eq(new->fsuid, old->suid) &&
|
||||
!uid_eq(new->fsuid, old->fsuid)) {
|
||||
return check_uid_transition(old->fsuid, new->fsuid);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_warn("Unknown setid state %d\n", flags);
|
||||
force_sig(SIGKILL, current);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add_safesetid_whitelist_entry(kuid_t parent, kuid_t child)
|
||||
{
|
||||
struct entry *new;
|
||||
|
||||
/* Return if entry already exists */
|
||||
if (check_setuid_policy_hashtable_key_value(parent, child))
|
||||
return 0;
|
||||
|
||||
new = kzalloc(sizeof(struct entry), GFP_KERNEL);
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
new->parent_kuid = __kuid_val(parent);
|
||||
new->child_kuid = __kuid_val(child);
|
||||
spin_lock(&safesetid_whitelist_hashtable_spinlock);
|
||||
hash_add_rcu(safesetid_whitelist_hashtable,
|
||||
&new->next,
|
||||
__kuid_val(parent));
|
||||
spin_unlock(&safesetid_whitelist_hashtable_spinlock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void flush_safesetid_whitelist_entries(void)
|
||||
{
|
||||
struct entry *entry;
|
||||
struct hlist_node *hlist_node;
|
||||
unsigned int bkt_loop_cursor;
|
||||
HLIST_HEAD(free_list);
|
||||
|
||||
/*
|
||||
* Could probably use hash_for_each_rcu here instead, but this should
|
||||
* be fine as well.
|
||||
*/
|
||||
spin_lock(&safesetid_whitelist_hashtable_spinlock);
|
||||
hash_for_each_safe(safesetid_whitelist_hashtable, bkt_loop_cursor,
|
||||
hlist_node, entry, next) {
|
||||
hash_del_rcu(&entry->next);
|
||||
hlist_add_head(&entry->dlist, &free_list);
|
||||
}
|
||||
spin_unlock(&safesetid_whitelist_hashtable_spinlock);
|
||||
synchronize_rcu();
|
||||
hlist_for_each_entry_safe(entry, hlist_node, &free_list, dlist) {
|
||||
hlist_del(&entry->dlist);
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
|
||||
static struct security_hook_list safesetid_security_hooks[] = {
|
||||
LSM_HOOK_INIT(task_fix_setuid, safesetid_task_fix_setuid),
|
||||
LSM_HOOK_INIT(capable, safesetid_security_capable)
|
||||
};
|
||||
|
||||
static int __init safesetid_security_init(void)
|
||||
{
|
||||
security_add_hooks(safesetid_security_hooks,
|
||||
ARRAY_SIZE(safesetid_security_hooks), "safesetid");
|
||||
|
||||
/* Report that SafeSetID successfully initialized */
|
||||
safesetid_initialized = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_LSM(safesetid_security_init) = {
|
||||
.init = safesetid_security_init,
|
||||
.name = "safesetid",
|
||||
};
|
33
security/safesetid/lsm.h
Normal file
33
security/safesetid/lsm.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* SafeSetID Linux Security Module
|
||||
*
|
||||
* Author: Micah Morton <mortonm@chromium.org>
|
||||
*
|
||||
* Copyright (C) 2018 The Chromium OS Authors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
#ifndef _SAFESETID_H
|
||||
#define _SAFESETID_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Flag indicating whether initialization completed */
|
||||
extern int safesetid_initialized;
|
||||
|
||||
/* Function type. */
|
||||
enum safesetid_whitelist_file_write_type {
|
||||
SAFESETID_WHITELIST_ADD, /* Add whitelist policy. */
|
||||
SAFESETID_WHITELIST_FLUSH, /* Flush whitelist policies. */
|
||||
};
|
||||
|
||||
/* Add entry to safesetid whitelist to allow 'parent' to setid to 'child'. */
|
||||
int add_safesetid_whitelist_entry(kuid_t parent, kuid_t child);
|
||||
|
||||
void flush_safesetid_whitelist_entries(void);
|
||||
|
||||
#endif /* _SAFESETID_H */
|
193
security/safesetid/securityfs.c
Normal file
193
security/safesetid/securityfs.c
Normal file
@ -0,0 +1,193 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* SafeSetID Linux Security Module
|
||||
*
|
||||
* Author: Micah Morton <mortonm@chromium.org>
|
||||
*
|
||||
* Copyright (C) 2018 The Chromium OS Authors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
#include <linux/security.h>
|
||||
#include <linux/cred.h>
|
||||
|
||||
#include "lsm.h"
|
||||
|
||||
static struct dentry *safesetid_policy_dir;
|
||||
|
||||
struct safesetid_file_entry {
|
||||
const char *name;
|
||||
enum safesetid_whitelist_file_write_type type;
|
||||
struct dentry *dentry;
|
||||
};
|
||||
|
||||
static struct safesetid_file_entry safesetid_files[] = {
|
||||
{.name = "add_whitelist_policy",
|
||||
.type = SAFESETID_WHITELIST_ADD},
|
||||
{.name = "flush_whitelist_policies",
|
||||
.type = SAFESETID_WHITELIST_FLUSH},
|
||||
};
|
||||
|
||||
/*
|
||||
* In the case the input buffer contains one or more invalid UIDs, the kuid_t
|
||||
* variables pointed to by 'parent' and 'child' will get updated but this
|
||||
* function will return an error.
|
||||
*/
|
||||
static int parse_safesetid_whitelist_policy(const char __user *buf,
|
||||
size_t len,
|
||||
kuid_t *parent,
|
||||
kuid_t *child)
|
||||
{
|
||||
char *kern_buf;
|
||||
char *parent_buf;
|
||||
char *child_buf;
|
||||
const char separator[] = ":";
|
||||
int ret;
|
||||
size_t first_substring_length;
|
||||
long parsed_parent;
|
||||
long parsed_child;
|
||||
|
||||
/* Duplicate string from user memory and NULL-terminate */
|
||||
kern_buf = memdup_user_nul(buf, len);
|
||||
if (IS_ERR(kern_buf))
|
||||
return PTR_ERR(kern_buf);
|
||||
|
||||
/*
|
||||
* Format of |buf| string should be <UID>:<UID>.
|
||||
* Find location of ":" in kern_buf (copied from |buf|).
|
||||
*/
|
||||
first_substring_length = strcspn(kern_buf, separator);
|
||||
if (first_substring_length == 0 || first_substring_length == len) {
|
||||
ret = -EINVAL;
|
||||
goto free_kern;
|
||||
}
|
||||
|
||||
parent_buf = kmemdup_nul(kern_buf, first_substring_length, GFP_KERNEL);
|
||||
if (!parent_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto free_kern;
|
||||
}
|
||||
|
||||
ret = kstrtol(parent_buf, 0, &parsed_parent);
|
||||
if (ret)
|
||||
goto free_both;
|
||||
|
||||
child_buf = kern_buf + first_substring_length + 1;
|
||||
ret = kstrtol(child_buf, 0, &parsed_child);
|
||||
if (ret)
|
||||
goto free_both;
|
||||
|
||||
*parent = make_kuid(current_user_ns(), parsed_parent);
|
||||
if (!uid_valid(*parent)) {
|
||||
ret = -EINVAL;
|
||||
goto free_both;
|
||||
}
|
||||
|
||||
*child = make_kuid(current_user_ns(), parsed_child);
|
||||
if (!uid_valid(*child)) {
|
||||
ret = -EINVAL;
|
||||
goto free_both;
|
||||
}
|
||||
|
||||
free_both:
|
||||
kfree(parent_buf);
|
||||
free_kern:
|
||||
kfree(kern_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t safesetid_file_write(struct file *file,
|
||||
const char __user *buf,
|
||||
size_t len,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct safesetid_file_entry *file_entry =
|
||||
file->f_inode->i_private;
|
||||
kuid_t parent;
|
||||
kuid_t child;
|
||||
int ret;
|
||||
|
||||
if (!ns_capable(current_user_ns(), CAP_MAC_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (*ppos != 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (file_entry->type) {
|
||||
case SAFESETID_WHITELIST_FLUSH:
|
||||
flush_safesetid_whitelist_entries();
|
||||
break;
|
||||
case SAFESETID_WHITELIST_ADD:
|
||||
ret = parse_safesetid_whitelist_policy(buf, len, &parent,
|
||||
&child);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = add_safesetid_whitelist_entry(parent, child);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
pr_warn("Unknown securityfs file %d\n", file_entry->type);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Return len on success so caller won't keep trying to write */
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct file_operations safesetid_file_fops = {
|
||||
.write = safesetid_file_write,
|
||||
};
|
||||
|
||||
static void safesetid_shutdown_securityfs(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(safesetid_files); ++i) {
|
||||
struct safesetid_file_entry *entry =
|
||||
&safesetid_files[i];
|
||||
securityfs_remove(entry->dentry);
|
||||
entry->dentry = NULL;
|
||||
}
|
||||
|
||||
securityfs_remove(safesetid_policy_dir);
|
||||
safesetid_policy_dir = NULL;
|
||||
}
|
||||
|
||||
static int __init safesetid_init_securityfs(void)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (!safesetid_initialized)
|
||||
return 0;
|
||||
|
||||
safesetid_policy_dir = securityfs_create_dir("safesetid", NULL);
|
||||
if (IS_ERR(safesetid_policy_dir)) {
|
||||
ret = PTR_ERR(safesetid_policy_dir);
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(safesetid_files); ++i) {
|
||||
struct safesetid_file_entry *entry =
|
||||
&safesetid_files[i];
|
||||
entry->dentry = securityfs_create_file(
|
||||
entry->name, 0200, safesetid_policy_dir,
|
||||
entry, &safesetid_file_fops);
|
||||
if (IS_ERR(entry->dentry)) {
|
||||
ret = PTR_ERR(entry->dentry);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
safesetid_shutdown_securityfs();
|
||||
return ret;
|
||||
}
|
||||
fs_initcall(safesetid_init_securityfs);
|
@ -30,20 +30,32 @@
|
||||
#include <linux/personality.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/msg.h>
|
||||
#include <net/flow.h>
|
||||
|
||||
#define MAX_LSM_EVM_XATTR 2
|
||||
|
||||
/* Maximum number of letters for an LSM name string */
|
||||
#define SECURITY_NAME_MAX 10
|
||||
/* How many LSMs were built into the kernel? */
|
||||
#define LSM_COUNT (__end_lsm_info - __start_lsm_info)
|
||||
|
||||
struct security_hook_heads security_hook_heads __lsm_ro_after_init;
|
||||
static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
|
||||
|
||||
static struct kmem_cache *lsm_file_cache;
|
||||
static struct kmem_cache *lsm_inode_cache;
|
||||
|
||||
char *lsm_names;
|
||||
static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init;
|
||||
|
||||
/* Boot-time LSM user choice */
|
||||
static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
|
||||
CONFIG_DEFAULT_SECURITY;
|
||||
static __initdata const char *chosen_lsm_order;
|
||||
static __initdata const char *chosen_major_lsm;
|
||||
|
||||
static __initconst const char * const builtin_lsm_order = CONFIG_LSM;
|
||||
|
||||
/* Ordered list of LSMs to initialize. */
|
||||
static __initdata struct lsm_info **ordered_lsms;
|
||||
static __initdata struct lsm_info *exclusive;
|
||||
|
||||
static __initdata bool debug;
|
||||
#define init_debug(...) \
|
||||
@ -52,18 +64,269 @@ static __initdata bool debug;
|
||||
pr_info(__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static void __init major_lsm_init(void)
|
||||
static bool __init is_enabled(struct lsm_info *lsm)
|
||||
{
|
||||
struct lsm_info *lsm;
|
||||
if (!lsm->enabled)
|
||||
return false;
|
||||
|
||||
return *lsm->enabled;
|
||||
}
|
||||
|
||||
/* Mark an LSM's enabled flag. */
|
||||
static int lsm_enabled_true __initdata = 1;
|
||||
static int lsm_enabled_false __initdata = 0;
|
||||
static void __init set_enabled(struct lsm_info *lsm, bool enabled)
|
||||
{
|
||||
/*
|
||||
* When an LSM hasn't configured an enable variable, we can use
|
||||
* a hard-coded location for storing the default enabled state.
|
||||
*/
|
||||
if (!lsm->enabled) {
|
||||
if (enabled)
|
||||
lsm->enabled = &lsm_enabled_true;
|
||||
else
|
||||
lsm->enabled = &lsm_enabled_false;
|
||||
} else if (lsm->enabled == &lsm_enabled_true) {
|
||||
if (!enabled)
|
||||
lsm->enabled = &lsm_enabled_false;
|
||||
} else if (lsm->enabled == &lsm_enabled_false) {
|
||||
if (enabled)
|
||||
lsm->enabled = &lsm_enabled_true;
|
||||
} else {
|
||||
*lsm->enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
/* Is an LSM already listed in the ordered LSMs list? */
|
||||
static bool __init exists_ordered_lsm(struct lsm_info *lsm)
|
||||
{
|
||||
struct lsm_info **check;
|
||||
|
||||
for (check = ordered_lsms; *check; check++)
|
||||
if (*check == lsm)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Append an LSM to the list of ordered LSMs to initialize. */
|
||||
static int last_lsm __initdata;
|
||||
static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
|
||||
{
|
||||
/* Ignore duplicate selections. */
|
||||
if (exists_ordered_lsm(lsm))
|
||||
return;
|
||||
|
||||
if (WARN(last_lsm == LSM_COUNT, "%s: out of LSM slots!?\n", from))
|
||||
return;
|
||||
|
||||
/* Enable this LSM, if it is not already set. */
|
||||
if (!lsm->enabled)
|
||||
lsm->enabled = &lsm_enabled_true;
|
||||
ordered_lsms[last_lsm++] = lsm;
|
||||
|
||||
init_debug("%s ordering: %s (%sabled)\n", from, lsm->name,
|
||||
is_enabled(lsm) ? "en" : "dis");
|
||||
}
|
||||
|
||||
/* Is an LSM allowed to be initialized? */
|
||||
static bool __init lsm_allowed(struct lsm_info *lsm)
|
||||
{
|
||||
/* Skip if the LSM is disabled. */
|
||||
if (!is_enabled(lsm))
|
||||
return false;
|
||||
|
||||
/* Not allowed if another exclusive LSM already initialized. */
|
||||
if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
|
||||
init_debug("exclusive disabled: %s\n", lsm->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __init lsm_set_blob_size(int *need, int *lbs)
|
||||
{
|
||||
int offset;
|
||||
|
||||
if (*need > 0) {
|
||||
offset = *lbs;
|
||||
*lbs += *need;
|
||||
*need = offset;
|
||||
}
|
||||
}
|
||||
|
||||
static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
|
||||
{
|
||||
if (!needed)
|
||||
return;
|
||||
|
||||
lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
|
||||
lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
|
||||
/*
|
||||
* The inode blob gets an rcu_head in addition to
|
||||
* what the modules might need.
|
||||
*/
|
||||
if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
|
||||
blob_sizes.lbs_inode = sizeof(struct rcu_head);
|
||||
lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
|
||||
lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
|
||||
lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
|
||||
lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
|
||||
}
|
||||
|
||||
/* Prepare LSM for initialization. */
|
||||
static void __init prepare_lsm(struct lsm_info *lsm)
|
||||
{
|
||||
int enabled = lsm_allowed(lsm);
|
||||
|
||||
/* Record enablement (to handle any following exclusive LSMs). */
|
||||
set_enabled(lsm, enabled);
|
||||
|
||||
/* If enabled, do pre-initialization work. */
|
||||
if (enabled) {
|
||||
if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
|
||||
exclusive = lsm;
|
||||
init_debug("exclusive chosen: %s\n", lsm->name);
|
||||
}
|
||||
|
||||
lsm_set_blob_sizes(lsm->blobs);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize a given LSM, if it is enabled. */
|
||||
static void __init initialize_lsm(struct lsm_info *lsm)
|
||||
{
|
||||
if (is_enabled(lsm)) {
|
||||
int ret;
|
||||
|
||||
for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
|
||||
init_debug("initializing %s\n", lsm->name);
|
||||
ret = lsm->init();
|
||||
WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);
|
||||
}
|
||||
}
|
||||
|
||||
/* Populate ordered LSMs list from comma-separated LSM name list. */
|
||||
static void __init ordered_lsm_parse(const char *order, const char *origin)
|
||||
{
|
||||
struct lsm_info *lsm;
|
||||
char *sep, *name, *next;
|
||||
|
||||
/* LSM_ORDER_FIRST is always first. */
|
||||
for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
|
||||
if (lsm->order == LSM_ORDER_FIRST)
|
||||
append_ordered_lsm(lsm, "first");
|
||||
}
|
||||
|
||||
/* Process "security=", if given. */
|
||||
if (chosen_major_lsm) {
|
||||
struct lsm_info *major;
|
||||
|
||||
/*
|
||||
* To match the original "security=" behavior, this
|
||||
* explicitly does NOT fallback to another Legacy Major
|
||||
* if the selected one was separately disabled: disable
|
||||
* all non-matching Legacy Major LSMs.
|
||||
*/
|
||||
for (major = __start_lsm_info; major < __end_lsm_info;
|
||||
major++) {
|
||||
if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
|
||||
strcmp(major->name, chosen_major_lsm) != 0) {
|
||||
set_enabled(major, false);
|
||||
init_debug("security=%s disabled: %s\n",
|
||||
chosen_major_lsm, major->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sep = kstrdup(order, GFP_KERNEL);
|
||||
next = sep;
|
||||
/* Walk the list, looking for matching LSMs. */
|
||||
while ((name = strsep(&next, ",")) != NULL) {
|
||||
bool found = false;
|
||||
|
||||
for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
|
||||
if (lsm->order == LSM_ORDER_MUTABLE &&
|
||||
strcmp(lsm->name, name) == 0) {
|
||||
append_ordered_lsm(lsm, origin);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
init_debug("%s ignored: %s\n", origin, name);
|
||||
}
|
||||
|
||||
/* Process "security=", if given. */
|
||||
if (chosen_major_lsm) {
|
||||
for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
|
||||
if (exists_ordered_lsm(lsm))
|
||||
continue;
|
||||
if (strcmp(lsm->name, chosen_major_lsm) == 0)
|
||||
append_ordered_lsm(lsm, "security=");
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable all LSMs not in the ordered list. */
|
||||
for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
|
||||
if (exists_ordered_lsm(lsm))
|
||||
continue;
|
||||
set_enabled(lsm, false);
|
||||
init_debug("%s disabled: %s\n", origin, lsm->name);
|
||||
}
|
||||
|
||||
kfree(sep);
|
||||
}
|
||||
|
||||
static void __init lsm_early_cred(struct cred *cred);
|
||||
static void __init lsm_early_task(struct task_struct *task);
|
||||
|
||||
static void __init ordered_lsm_init(void)
|
||||
{
|
||||
struct lsm_info **lsm;
|
||||
|
||||
ordered_lsms = kcalloc(LSM_COUNT + 1, sizeof(*ordered_lsms),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (chosen_lsm_order) {
|
||||
if (chosen_major_lsm) {
|
||||
pr_info("security= is ignored because it is superseded by lsm=\n");
|
||||
chosen_major_lsm = NULL;
|
||||
}
|
||||
ordered_lsm_parse(chosen_lsm_order, "cmdline");
|
||||
} else
|
||||
ordered_lsm_parse(builtin_lsm_order, "builtin");
|
||||
|
||||
for (lsm = ordered_lsms; *lsm; lsm++)
|
||||
prepare_lsm(*lsm);
|
||||
|
||||
init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
|
||||
init_debug("file blob size = %d\n", blob_sizes.lbs_file);
|
||||
init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
|
||||
init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
|
||||
init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
|
||||
init_debug("task blob size = %d\n", blob_sizes.lbs_task);
|
||||
|
||||
/*
|
||||
* Create any kmem_caches needed for blobs
|
||||
*/
|
||||
if (blob_sizes.lbs_file)
|
||||
lsm_file_cache = kmem_cache_create("lsm_file_cache",
|
||||
blob_sizes.lbs_file, 0,
|
||||
SLAB_PANIC, NULL);
|
||||
if (blob_sizes.lbs_inode)
|
||||
lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
|
||||
blob_sizes.lbs_inode, 0,
|
||||
SLAB_PANIC, NULL);
|
||||
|
||||
lsm_early_cred((struct cred *) current->cred);
|
||||
lsm_early_task(current);
|
||||
for (lsm = ordered_lsms; *lsm; lsm++)
|
||||
initialize_lsm(*lsm);
|
||||
|
||||
kfree(ordered_lsms);
|
||||
}
|
||||
|
||||
/**
|
||||
* security_init - initializes the security framework
|
||||
*
|
||||
@ -80,28 +343,27 @@ int __init security_init(void)
|
||||
i++)
|
||||
INIT_HLIST_HEAD(&list[i]);
|
||||
|
||||
/*
|
||||
* Load minor LSMs, with the capability module always first.
|
||||
*/
|
||||
capability_add_hooks();
|
||||
yama_add_hooks();
|
||||
loadpin_add_hooks();
|
||||
|
||||
/*
|
||||
* Load all the remaining security modules.
|
||||
*/
|
||||
major_lsm_init();
|
||||
/* Load LSMs in specified order. */
|
||||
ordered_lsm_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save user chosen LSM */
|
||||
static int __init choose_lsm(char *str)
|
||||
static int __init choose_major_lsm(char *str)
|
||||
{
|
||||
strncpy(chosen_lsm, str, SECURITY_NAME_MAX);
|
||||
chosen_major_lsm = str;
|
||||
return 1;
|
||||
}
|
||||
__setup("security=", choose_lsm);
|
||||
__setup("security=", choose_major_lsm);
|
||||
|
||||
/* Explicitly choose LSM initialization order. */
|
||||
static int __init choose_lsm_order(char *str)
|
||||
{
|
||||
chosen_lsm_order = str;
|
||||
return 1;
|
||||
}
|
||||
__setup("lsm=", choose_lsm_order);
|
||||
|
||||
/* Enable LSM order debugging. */
|
||||
static int __init enable_debug(char *str)
|
||||
@ -147,29 +409,6 @@ static int lsm_append(char *new, char **result)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* security_module_enable - Load given security module on boot ?
|
||||
* @module: the name of the module
|
||||
*
|
||||
* Each LSM must pass this method before registering its own operations
|
||||
* to avoid security registration races. This method may also be used
|
||||
* to check if your LSM is currently loaded during kernel initialization.
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* true if:
|
||||
*
|
||||
* - The passed LSM is the one chosen by user at boot time,
|
||||
* - or the passed LSM is configured as the default and the user did not
|
||||
* choose an alternate LSM at boot time.
|
||||
*
|
||||
* Otherwise, return false.
|
||||
*/
|
||||
int __init security_module_enable(const char *module)
|
||||
{
|
||||
return !strcmp(module, chosen_lsm);
|
||||
}
|
||||
|
||||
/**
|
||||
* security_add_hooks - Add a modules hooks to the hook lists.
|
||||
* @hooks: the hooks to add
|
||||
@ -209,6 +448,161 @@ int unregister_lsm_notifier(struct notifier_block *nb)
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_lsm_notifier);
|
||||
|
||||
/**
|
||||
* lsm_cred_alloc - allocate a composite cred blob
|
||||
* @cred: the cred that needs a blob
|
||||
* @gfp: allocation type
|
||||
*
|
||||
* Allocate the cred blob for all the modules
|
||||
*
|
||||
* Returns 0, or -ENOMEM if memory can't be allocated.
|
||||
*/
|
||||
static int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
|
||||
{
|
||||
if (blob_sizes.lbs_cred == 0) {
|
||||
cred->security = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cred->security = kzalloc(blob_sizes.lbs_cred, gfp);
|
||||
if (cred->security == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_early_cred - during initialization allocate a composite cred blob
|
||||
* @cred: the cred that needs a blob
|
||||
*
|
||||
* Allocate the cred blob for all the modules
|
||||
*/
|
||||
static void __init lsm_early_cred(struct cred *cred)
|
||||
{
|
||||
int rc = lsm_cred_alloc(cred, GFP_KERNEL);
|
||||
|
||||
if (rc)
|
||||
panic("%s: Early cred alloc failed.\n", __func__);
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_file_alloc - allocate a composite file blob
|
||||
* @file: the file that needs a blob
|
||||
*
|
||||
* Allocate the file blob for all the modules
|
||||
*
|
||||
* Returns 0, or -ENOMEM if memory can't be allocated.
|
||||
*/
|
||||
static int lsm_file_alloc(struct file *file)
|
||||
{
|
||||
if (!lsm_file_cache) {
|
||||
file->f_security = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
|
||||
if (file->f_security == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_inode_alloc - allocate a composite inode blob
|
||||
* @inode: the inode that needs a blob
|
||||
*
|
||||
* Allocate the inode blob for all the modules
|
||||
*
|
||||
* Returns 0, or -ENOMEM if memory can't be allocated.
|
||||
*/
|
||||
int lsm_inode_alloc(struct inode *inode)
|
||||
{
|
||||
if (!lsm_inode_cache) {
|
||||
inode->i_security = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
inode->i_security = kmem_cache_zalloc(lsm_inode_cache, GFP_NOFS);
|
||||
if (inode->i_security == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_task_alloc - allocate a composite task blob
|
||||
* @task: the task that needs a blob
|
||||
*
|
||||
* Allocate the task blob for all the modules
|
||||
*
|
||||
* Returns 0, or -ENOMEM if memory can't be allocated.
|
||||
*/
|
||||
static int lsm_task_alloc(struct task_struct *task)
|
||||
{
|
||||
if (blob_sizes.lbs_task == 0) {
|
||||
task->security = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
task->security = kzalloc(blob_sizes.lbs_task, GFP_KERNEL);
|
||||
if (task->security == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_ipc_alloc - allocate a composite ipc blob
|
||||
* @kip: the ipc that needs a blob
|
||||
*
|
||||
* Allocate the ipc blob for all the modules
|
||||
*
|
||||
* Returns 0, or -ENOMEM if memory can't be allocated.
|
||||
*/
|
||||
static int lsm_ipc_alloc(struct kern_ipc_perm *kip)
|
||||
{
|
||||
if (blob_sizes.lbs_ipc == 0) {
|
||||
kip->security = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
kip->security = kzalloc(blob_sizes.lbs_ipc, GFP_KERNEL);
|
||||
if (kip->security == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_msg_msg_alloc - allocate a composite msg_msg blob
|
||||
* @mp: the msg_msg that needs a blob
|
||||
*
|
||||
* Allocate the ipc blob for all the modules
|
||||
*
|
||||
* Returns 0, or -ENOMEM if memory can't be allocated.
|
||||
*/
|
||||
static int lsm_msg_msg_alloc(struct msg_msg *mp)
|
||||
{
|
||||
if (blob_sizes.lbs_msg_msg == 0) {
|
||||
mp->security = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
mp->security = kzalloc(blob_sizes.lbs_msg_msg, GFP_KERNEL);
|
||||
if (mp->security == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_early_task - during initialization allocate a composite task blob
|
||||
* @task: the task that needs a blob
|
||||
*
|
||||
* Allocate the task blob for all the modules
|
||||
*/
|
||||
static void __init lsm_early_task(struct task_struct *task)
|
||||
{
|
||||
int rc = lsm_task_alloc(task);
|
||||
|
||||
if (rc)
|
||||
panic("%s: Early task alloc failed.\n", __func__);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hook list operation macros.
|
||||
*
|
||||
@ -294,16 +688,12 @@ int security_capset(struct cred *new, const struct cred *old,
|
||||
effective, inheritable, permitted);
|
||||
}
|
||||
|
||||
int security_capable(const struct cred *cred, struct user_namespace *ns,
|
||||
int cap)
|
||||
int security_capable(const struct cred *cred,
|
||||
struct user_namespace *ns,
|
||||
int cap,
|
||||
unsigned int opts)
|
||||
{
|
||||
return call_int_hook(capable, 0, cred, ns, cap, SECURITY_CAP_AUDIT);
|
||||
}
|
||||
|
||||
int security_capable_noaudit(const struct cred *cred, struct user_namespace *ns,
|
||||
int cap)
|
||||
{
|
||||
return call_int_hook(capable, 0, cred, ns, cap, SECURITY_CAP_NOAUDIT);
|
||||
return call_int_hook(capable, 0, cred, ns, cap, opts);
|
||||
}
|
||||
|
||||
int security_quotactl(int cmds, int type, int id, struct super_block *sb)
|
||||
@ -468,14 +858,40 @@ EXPORT_SYMBOL(security_add_mnt_opt);
|
||||
|
||||
int security_inode_alloc(struct inode *inode)
|
||||
{
|
||||
inode->i_security = NULL;
|
||||
return call_int_hook(inode_alloc_security, 0, inode);
|
||||
int rc = lsm_inode_alloc(inode);
|
||||
|
||||
if (unlikely(rc))
|
||||
return rc;
|
||||
rc = call_int_hook(inode_alloc_security, 0, inode);
|
||||
if (unlikely(rc))
|
||||
security_inode_free(inode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void inode_free_by_rcu(struct rcu_head *head)
|
||||
{
|
||||
/*
|
||||
* The rcu head is at the start of the inode blob
|
||||
*/
|
||||
kmem_cache_free(lsm_inode_cache, head);
|
||||
}
|
||||
|
||||
void security_inode_free(struct inode *inode)
|
||||
{
|
||||
integrity_inode_free(inode);
|
||||
call_void_hook(inode_free_security, inode);
|
||||
/*
|
||||
* The inode may still be referenced in a path walk and
|
||||
* a call to security_inode_permission() can be made
|
||||
* after inode_free_security() is called. Ideally, the VFS
|
||||
* wouldn't do this, but fixing that is a much harder
|
||||
* job. For now, simply free the i_security via RCU, and
|
||||
* leave the current inode->i_security pointer intact.
|
||||
* The inode will be freed after the RCU grace period too.
|
||||
*/
|
||||
if (inode->i_security)
|
||||
call_rcu((struct rcu_head *)inode->i_security,
|
||||
inode_free_by_rcu);
|
||||
}
|
||||
|
||||
int security_dentry_init_security(struct dentry *dentry, int mode,
|
||||
@ -905,12 +1321,27 @@ int security_file_permission(struct file *file, int mask)
|
||||
|
||||
int security_file_alloc(struct file *file)
|
||||
{
|
||||
return call_int_hook(file_alloc_security, 0, file);
|
||||
int rc = lsm_file_alloc(file);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = call_int_hook(file_alloc_security, 0, file);
|
||||
if (unlikely(rc))
|
||||
security_file_free(file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void security_file_free(struct file *file)
|
||||
{
|
||||
void *blob;
|
||||
|
||||
call_void_hook(file_free_security, file);
|
||||
|
||||
blob = file->f_security;
|
||||
if (blob) {
|
||||
file->f_security = NULL;
|
||||
kmem_cache_free(lsm_file_cache, blob);
|
||||
}
|
||||
}
|
||||
|
||||
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
@ -1012,17 +1443,35 @@ int security_file_open(struct file *file)
|
||||
|
||||
int security_task_alloc(struct task_struct *task, unsigned long clone_flags)
|
||||
{
|
||||
return call_int_hook(task_alloc, 0, task, clone_flags);
|
||||
int rc = lsm_task_alloc(task);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = call_int_hook(task_alloc, 0, task, clone_flags);
|
||||
if (unlikely(rc))
|
||||
security_task_free(task);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void security_task_free(struct task_struct *task)
|
||||
{
|
||||
call_void_hook(task_free, task);
|
||||
|
||||
kfree(task->security);
|
||||
task->security = NULL;
|
||||
}
|
||||
|
||||
int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
|
||||
{
|
||||
return call_int_hook(cred_alloc_blank, 0, cred, gfp);
|
||||
int rc = lsm_cred_alloc(cred, gfp);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = call_int_hook(cred_alloc_blank, 0, cred, gfp);
|
||||
if (unlikely(rc))
|
||||
security_cred_free(cred);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void security_cred_free(struct cred *cred)
|
||||
@ -1035,11 +1484,22 @@ void security_cred_free(struct cred *cred)
|
||||
return;
|
||||
|
||||
call_void_hook(cred_free, cred);
|
||||
|
||||
kfree(cred->security);
|
||||
cred->security = NULL;
|
||||
}
|
||||
|
||||
int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
|
||||
{
|
||||
return call_int_hook(cred_prepare, 0, new, old, gfp);
|
||||
int rc = lsm_cred_alloc(new, gfp);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = call_int_hook(cred_prepare, 0, new, old, gfp);
|
||||
if (unlikely(rc))
|
||||
security_cred_free(new);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void security_transfer_creds(struct cred *new, const struct cred *old)
|
||||
@ -1220,22 +1680,40 @@ void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
|
||||
|
||||
int security_msg_msg_alloc(struct msg_msg *msg)
|
||||
{
|
||||
return call_int_hook(msg_msg_alloc_security, 0, msg);
|
||||
int rc = lsm_msg_msg_alloc(msg);
|
||||
|
||||
if (unlikely(rc))
|
||||
return rc;
|
||||
rc = call_int_hook(msg_msg_alloc_security, 0, msg);
|
||||
if (unlikely(rc))
|
||||
security_msg_msg_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void security_msg_msg_free(struct msg_msg *msg)
|
||||
{
|
||||
call_void_hook(msg_msg_free_security, msg);
|
||||
kfree(msg->security);
|
||||
msg->security = NULL;
|
||||
}
|
||||
|
||||
int security_msg_queue_alloc(struct kern_ipc_perm *msq)
|
||||
{
|
||||
return call_int_hook(msg_queue_alloc_security, 0, msq);
|
||||
int rc = lsm_ipc_alloc(msq);
|
||||
|
||||
if (unlikely(rc))
|
||||
return rc;
|
||||
rc = call_int_hook(msg_queue_alloc_security, 0, msq);
|
||||
if (unlikely(rc))
|
||||
security_msg_queue_free(msq);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void security_msg_queue_free(struct kern_ipc_perm *msq)
|
||||
{
|
||||
call_void_hook(msg_queue_free_security, msq);
|
||||
kfree(msq->security);
|
||||
msq->security = NULL;
|
||||
}
|
||||
|
||||
int security_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg)
|
||||
@ -1262,12 +1740,21 @@ int security_msg_queue_msgrcv(struct kern_ipc_perm *msq, struct msg_msg *msg,
|
||||
|
||||
int security_shm_alloc(struct kern_ipc_perm *shp)
|
||||
{
|
||||
return call_int_hook(shm_alloc_security, 0, shp);
|
||||
int rc = lsm_ipc_alloc(shp);
|
||||
|
||||
if (unlikely(rc))
|
||||
return rc;
|
||||
rc = call_int_hook(shm_alloc_security, 0, shp);
|
||||
if (unlikely(rc))
|
||||
security_shm_free(shp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void security_shm_free(struct kern_ipc_perm *shp)
|
||||
{
|
||||
call_void_hook(shm_free_security, shp);
|
||||
kfree(shp->security);
|
||||
shp->security = NULL;
|
||||
}
|
||||
|
||||
int security_shm_associate(struct kern_ipc_perm *shp, int shmflg)
|
||||
@ -1287,12 +1774,21 @@ int security_shm_shmat(struct kern_ipc_perm *shp, char __user *shmaddr, int shmf
|
||||
|
||||
int security_sem_alloc(struct kern_ipc_perm *sma)
|
||||
{
|
||||
return call_int_hook(sem_alloc_security, 0, sma);
|
||||
int rc = lsm_ipc_alloc(sma);
|
||||
|
||||
if (unlikely(rc))
|
||||
return rc;
|
||||
rc = call_int_hook(sem_alloc_security, 0, sma);
|
||||
if (unlikely(rc))
|
||||
security_sem_free(sma);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void security_sem_free(struct kern_ipc_perm *sma)
|
||||
{
|
||||
call_void_hook(sem_free_security, sma);
|
||||
kfree(sma->security);
|
||||
sma->security = NULL;
|
||||
}
|
||||
|
||||
int security_sem_associate(struct kern_ipc_perm *sma, int semflg)
|
||||
@ -1319,14 +1815,30 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode)
|
||||
}
|
||||
EXPORT_SYMBOL(security_d_instantiate);
|
||||
|
||||
int security_getprocattr(struct task_struct *p, char *name, char **value)
|
||||
int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
|
||||
char **value)
|
||||
{
|
||||
return call_int_hook(getprocattr, -EINVAL, p, name, value);
|
||||
struct security_hook_list *hp;
|
||||
|
||||
hlist_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
|
||||
if (lsm != NULL && strcmp(lsm, hp->lsm))
|
||||
continue;
|
||||
return hp->hook.getprocattr(p, name, value);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int security_setprocattr(const char *name, void *value, size_t size)
|
||||
int security_setprocattr(const char *lsm, const char *name, void *value,
|
||||
size_t size)
|
||||
{
|
||||
return call_int_hook(setprocattr, -EINVAL, name, value, size);
|
||||
struct security_hook_list *hp;
|
||||
|
||||
hlist_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
|
||||
if (lsm != NULL && strcmp(lsm, hp->lsm))
|
||||
continue;
|
||||
return hp->hook.setprocattr(name, value, size);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int security_netlink_send(struct sock *sk, struct sk_buff *skb)
|
||||
|
@ -22,21 +22,6 @@ config SECURITY_SELINUX_BOOTPARAM
|
||||
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
config SECURITY_SELINUX_BOOTPARAM_VALUE
|
||||
int "NSA SELinux boot parameter default value"
|
||||
depends on SECURITY_SELINUX_BOOTPARAM
|
||||
range 0 1
|
||||
default 1
|
||||
help
|
||||
This option sets the default value for the kernel parameter
|
||||
'selinux', which allows SELinux to be disabled at boot. If this
|
||||
option is set to 0 (zero), the SELinux kernel parameter will
|
||||
default to 0, disabling SELinux at bootup. If this option is
|
||||
set to 1 (one), the SELinux kernel parameter will default to 1,
|
||||
enabling SELinux at bootup.
|
||||
|
||||
If you are unsure how to answer this question, answer 1.
|
||||
|
||||
config SECURITY_SELINUX_DISABLE
|
||||
bool "NSA SELinux runtime disable"
|
||||
depends on SECURITY_SELINUX
|
||||
|
@ -6,7 +6,7 @@
|
||||
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o
|
||||
|
||||
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \
|
||||
netnode.o netport.o ibpkey.o exports.o \
|
||||
netnode.o netport.o ibpkey.o \
|
||||
ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \
|
||||
ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/status.o
|
||||
|
||||
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* SELinux services exported to the rest of the kernel.
|
||||
*
|
||||
* Author: James Morris <jmorris@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2005 Red Hat, Inc., James Morris <jmorris@redhat.com>
|
||||
* Copyright (C) 2006 Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
|
||||
* Copyright (C) 2006 IBM Corporation, Timothy R. Chavez <tinytim@us.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2,
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/selinux.h>
|
||||
|
||||
#include "security.h"
|
||||
|
||||
bool selinux_is_enabled(void)
|
||||
{
|
||||
return selinux_enabled;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(selinux_is_enabled);
|
@ -79,7 +79,6 @@
|
||||
#include <linux/personality.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/selinux.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/posix-timers.h>
|
||||
#include <linux/syslog.h>
|
||||
@ -121,9 +120,8 @@ __setup("enforcing=", enforcing_setup);
|
||||
#define selinux_enforcing_boot 1
|
||||
#endif
|
||||
|
||||
int selinux_enabled __lsm_ro_after_init = 1;
|
||||
#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
|
||||
int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;
|
||||
|
||||
static int __init selinux_enabled_setup(char *str)
|
||||
{
|
||||
unsigned long enabled;
|
||||
@ -132,8 +130,6 @@ static int __init selinux_enabled_setup(char *str)
|
||||
return 1;
|
||||
}
|
||||
__setup("selinux=", selinux_enabled_setup);
|
||||
#else
|
||||
int selinux_enabled = 1;
|
||||
#endif
|
||||
|
||||
static unsigned int selinux_checkreqprot_boot =
|
||||
@ -149,9 +145,6 @@ static int __init checkreqprot_setup(char *str)
|
||||
}
|
||||
__setup("checkreqprot=", checkreqprot_setup);
|
||||
|
||||
static struct kmem_cache *sel_inode_cache;
|
||||
static struct kmem_cache *file_security_cache;
|
||||
|
||||
/**
|
||||
* selinux_secmark_enabled - Check to see if SECMARK is currently enabled
|
||||
*
|
||||
@ -214,12 +207,8 @@ static void cred_init_security(void)
|
||||
struct cred *cred = (struct cred *) current->real_cred;
|
||||
struct task_security_struct *tsec;
|
||||
|
||||
tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
|
||||
if (!tsec)
|
||||
panic("SELinux: Failed to initialize initial task.\n");
|
||||
|
||||
tsec = selinux_cred(cred);
|
||||
tsec->osid = tsec->sid = SECINITSID_KERNEL;
|
||||
cred->security = tsec;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -229,7 +218,7 @@ static inline u32 cred_sid(const struct cred *cred)
|
||||
{
|
||||
const struct task_security_struct *tsec;
|
||||
|
||||
tsec = cred->security;
|
||||
tsec = selinux_cred(cred);
|
||||
return tsec->sid;
|
||||
}
|
||||
|
||||
@ -250,13 +239,9 @@ static inline u32 task_sid(const struct task_struct *task)
|
||||
|
||||
static int inode_alloc_security(struct inode *inode)
|
||||
{
|
||||
struct inode_security_struct *isec;
|
||||
struct inode_security_struct *isec = selinux_inode(inode);
|
||||
u32 sid = current_sid();
|
||||
|
||||
isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS);
|
||||
if (!isec)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&isec->lock);
|
||||
INIT_LIST_HEAD(&isec->list);
|
||||
isec->inode = inode;
|
||||
@ -264,7 +249,6 @@ static int inode_alloc_security(struct inode *inode)
|
||||
isec->sclass = SECCLASS_FILE;
|
||||
isec->task_sid = sid;
|
||||
isec->initialized = LABEL_INVALID;
|
||||
inode->i_security = isec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -281,7 +265,7 @@ static int __inode_security_revalidate(struct inode *inode,
|
||||
struct dentry *dentry,
|
||||
bool may_sleep)
|
||||
{
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
struct inode_security_struct *isec = selinux_inode(inode);
|
||||
|
||||
might_sleep_if(may_sleep);
|
||||
|
||||
@ -302,7 +286,7 @@ static int __inode_security_revalidate(struct inode *inode,
|
||||
|
||||
static struct inode_security_struct *inode_security_novalidate(struct inode *inode)
|
||||
{
|
||||
return inode->i_security;
|
||||
return selinux_inode(inode);
|
||||
}
|
||||
|
||||
static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu)
|
||||
@ -312,7 +296,7 @@ static struct inode_security_struct *inode_security_rcu(struct inode *inode, boo
|
||||
error = __inode_security_revalidate(inode, NULL, !rcu);
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
return inode->i_security;
|
||||
return selinux_inode(inode);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -321,14 +305,14 @@ static struct inode_security_struct *inode_security_rcu(struct inode *inode, boo
|
||||
static struct inode_security_struct *inode_security(struct inode *inode)
|
||||
{
|
||||
__inode_security_revalidate(inode, NULL, true);
|
||||
return inode->i_security;
|
||||
return selinux_inode(inode);
|
||||
}
|
||||
|
||||
static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = d_backing_inode(dentry);
|
||||
|
||||
return inode->i_security;
|
||||
return selinux_inode(inode);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -339,22 +323,17 @@ static struct inode_security_struct *backing_inode_security(struct dentry *dentr
|
||||
struct inode *inode = d_backing_inode(dentry);
|
||||
|
||||
__inode_security_revalidate(inode, dentry, true);
|
||||
return inode->i_security;
|
||||
}
|
||||
|
||||
static void inode_free_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct inode_security_struct *isec;
|
||||
|
||||
isec = container_of(head, struct inode_security_struct, rcu);
|
||||
kmem_cache_free(sel_inode_cache, isec);
|
||||
return selinux_inode(inode);
|
||||
}
|
||||
|
||||
static void inode_free_security(struct inode *inode)
|
||||
{
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
struct superblock_security_struct *sbsec = inode->i_sb->s_security;
|
||||
struct inode_security_struct *isec = selinux_inode(inode);
|
||||
struct superblock_security_struct *sbsec;
|
||||
|
||||
if (!isec)
|
||||
return;
|
||||
sbsec = inode->i_sb->s_security;
|
||||
/*
|
||||
* As not all inode security structures are in a list, we check for
|
||||
* empty list outside of the lock to make sure that we won't waste
|
||||
@ -370,42 +349,19 @@ static void inode_free_security(struct inode *inode)
|
||||
list_del_init(&isec->list);
|
||||
spin_unlock(&sbsec->isec_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* The inode may still be referenced in a path walk and
|
||||
* a call to selinux_inode_permission() can be made
|
||||
* after inode_free_security() is called. Ideally, the VFS
|
||||
* wouldn't do this, but fixing that is a much harder
|
||||
* job. For now, simply free the i_security via RCU, and
|
||||
* leave the current inode->i_security pointer intact.
|
||||
* The inode will be freed after the RCU grace period too.
|
||||
*/
|
||||
call_rcu(&isec->rcu, inode_free_rcu);
|
||||
}
|
||||
|
||||
static int file_alloc_security(struct file *file)
|
||||
{
|
||||
struct file_security_struct *fsec;
|
||||
struct file_security_struct *fsec = selinux_file(file);
|
||||
u32 sid = current_sid();
|
||||
|
||||
fsec = kmem_cache_zalloc(file_security_cache, GFP_KERNEL);
|
||||
if (!fsec)
|
||||
return -ENOMEM;
|
||||
|
||||
fsec->sid = sid;
|
||||
fsec->fown_sid = sid;
|
||||
file->f_security = fsec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void file_free_security(struct file *file)
|
||||
{
|
||||
struct file_security_struct *fsec = file->f_security;
|
||||
file->f_security = NULL;
|
||||
kmem_cache_free(file_security_cache, fsec);
|
||||
}
|
||||
|
||||
static int superblock_alloc_security(struct super_block *sb)
|
||||
{
|
||||
struct superblock_security_struct *sbsec;
|
||||
@ -501,7 +457,7 @@ static int may_context_mount_sb_relabel(u32 sid,
|
||||
struct superblock_security_struct *sbsec,
|
||||
const struct cred *cred)
|
||||
{
|
||||
const struct task_security_struct *tsec = cred->security;
|
||||
const struct task_security_struct *tsec = selinux_cred(cred);
|
||||
int rc;
|
||||
|
||||
rc = avc_has_perm(&selinux_state,
|
||||
@ -520,7 +476,7 @@ static int may_context_mount_inode_relabel(u32 sid,
|
||||
struct superblock_security_struct *sbsec,
|
||||
const struct cred *cred)
|
||||
{
|
||||
const struct task_security_struct *tsec = cred->security;
|
||||
const struct task_security_struct *tsec = selinux_cred(cred);
|
||||
int rc;
|
||||
rc = avc_has_perm(&selinux_state,
|
||||
tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
|
||||
@ -1374,7 +1330,7 @@ static int selinux_genfs_get_sid(struct dentry *dentry,
|
||||
static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry)
|
||||
{
|
||||
struct superblock_security_struct *sbsec = NULL;
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
struct inode_security_struct *isec = selinux_inode(inode);
|
||||
u32 task_sid, sid = 0;
|
||||
u16 sclass;
|
||||
struct dentry *dentry;
|
||||
@ -1621,7 +1577,7 @@ static inline u32 signal_to_av(int sig)
|
||||
|
||||
/* Check whether a task is allowed to use a capability. */
|
||||
static int cred_has_capability(const struct cred *cred,
|
||||
int cap, int audit, bool initns)
|
||||
int cap, unsigned int opts, bool initns)
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
struct av_decision avd;
|
||||
@ -1648,7 +1604,7 @@ static int cred_has_capability(const struct cred *cred,
|
||||
|
||||
rc = avc_has_perm_noaudit(&selinux_state,
|
||||
sid, sid, sclass, av, 0, &avd);
|
||||
if (audit == SECURITY_CAP_AUDIT) {
|
||||
if (!(opts & CAP_OPT_NOAUDIT)) {
|
||||
int rc2 = avc_audit(&selinux_state,
|
||||
sid, sid, sclass, av, &avd, rc, &ad, 0);
|
||||
if (rc2)
|
||||
@ -1674,7 +1630,7 @@ static int inode_has_perm(const struct cred *cred,
|
||||
return 0;
|
||||
|
||||
sid = cred_sid(cred);
|
||||
isec = inode->i_security;
|
||||
isec = selinux_inode(inode);
|
||||
|
||||
return avc_has_perm(&selinux_state,
|
||||
sid, isec->sid, isec->sclass, perms, adp);
|
||||
@ -1740,7 +1696,7 @@ static int file_has_perm(const struct cred *cred,
|
||||
struct file *file,
|
||||
u32 av)
|
||||
{
|
||||
struct file_security_struct *fsec = file->f_security;
|
||||
struct file_security_struct *fsec = selinux_file(file);
|
||||
struct inode *inode = file_inode(file);
|
||||
struct common_audit_data ad;
|
||||
u32 sid = cred_sid(cred);
|
||||
@ -1806,7 +1762,7 @@ static int may_create(struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
u16 tclass)
|
||||
{
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
const struct task_security_struct *tsec = selinux_cred(current_cred());
|
||||
struct inode_security_struct *dsec;
|
||||
struct superblock_security_struct *sbsec;
|
||||
u32 sid, newsid;
|
||||
@ -1828,7 +1784,7 @@ static int may_create(struct inode *dir,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = selinux_determine_inode_label(current_security(), dir,
|
||||
rc = selinux_determine_inode_label(selinux_cred(current_cred()), dir,
|
||||
&dentry->d_name, tclass, &newsid);
|
||||
if (rc)
|
||||
return rc;
|
||||
@ -2084,7 +2040,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,
|
||||
struct file *file)
|
||||
{
|
||||
u32 sid = task_sid(to);
|
||||
struct file_security_struct *fsec = file->f_security;
|
||||
struct file_security_struct *fsec = selinux_file(file);
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct inode_security_struct *isec;
|
||||
struct common_audit_data ad;
|
||||
@ -2168,9 +2124,9 @@ static int selinux_capset(struct cred *new, const struct cred *old,
|
||||
*/
|
||||
|
||||
static int selinux_capable(const struct cred *cred, struct user_namespace *ns,
|
||||
int cap, int audit)
|
||||
int cap, unsigned int opts)
|
||||
{
|
||||
return cred_has_capability(cred, cap, audit, ns == &init_user_ns);
|
||||
return cred_has_capability(cred, cap, opts, ns == &init_user_ns);
|
||||
}
|
||||
|
||||
static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
|
||||
@ -2244,7 +2200,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
|
||||
int rc, cap_sys_admin = 0;
|
||||
|
||||
rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN,
|
||||
SECURITY_CAP_NOAUDIT, true);
|
||||
CAP_OPT_NOAUDIT, true);
|
||||
if (rc == 0)
|
||||
cap_sys_admin = 1;
|
||||
|
||||
@ -2335,8 +2291,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
|
||||
if (bprm->called_set_creds)
|
||||
return 0;
|
||||
|
||||
old_tsec = current_security();
|
||||
new_tsec = bprm->cred->security;
|
||||
old_tsec = selinux_cred(current_cred());
|
||||
new_tsec = selinux_cred(bprm->cred);
|
||||
isec = inode_security(inode);
|
||||
|
||||
/* Default to the current task SID. */
|
||||
@ -2500,7 +2456,7 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
|
||||
struct rlimit *rlim, *initrlim;
|
||||
int rc, i;
|
||||
|
||||
new_tsec = bprm->cred->security;
|
||||
new_tsec = selinux_cred(bprm->cred);
|
||||
if (new_tsec->sid == new_tsec->osid)
|
||||
return;
|
||||
|
||||
@ -2543,7 +2499,7 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
|
||||
*/
|
||||
static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
|
||||
{
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
const struct task_security_struct *tsec = selinux_cred(current_cred());
|
||||
struct itimerval itimer;
|
||||
u32 osid, sid;
|
||||
int rc, i;
|
||||
@ -2780,7 +2736,7 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode,
|
||||
u32 newsid;
|
||||
int rc;
|
||||
|
||||
rc = selinux_determine_inode_label(current_security(),
|
||||
rc = selinux_determine_inode_label(selinux_cred(current_cred()),
|
||||
d_inode(dentry->d_parent), name,
|
||||
inode_mode_to_security_class(mode),
|
||||
&newsid);
|
||||
@ -2800,14 +2756,14 @@ static int selinux_dentry_create_files_as(struct dentry *dentry, int mode,
|
||||
int rc;
|
||||
struct task_security_struct *tsec;
|
||||
|
||||
rc = selinux_determine_inode_label(old->security,
|
||||
rc = selinux_determine_inode_label(selinux_cred(old),
|
||||
d_inode(dentry->d_parent), name,
|
||||
inode_mode_to_security_class(mode),
|
||||
&newsid);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
tsec = new->security;
|
||||
tsec = selinux_cred(new);
|
||||
tsec->create_sid = newsid;
|
||||
return 0;
|
||||
}
|
||||
@ -2817,7 +2773,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
|
||||
const char **name,
|
||||
void **value, size_t *len)
|
||||
{
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
const struct task_security_struct *tsec = selinux_cred(current_cred());
|
||||
struct superblock_security_struct *sbsec;
|
||||
u32 newsid, clen;
|
||||
int rc;
|
||||
@ -2827,7 +2783,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
|
||||
|
||||
newsid = tsec->create_sid;
|
||||
|
||||
rc = selinux_determine_inode_label(current_security(),
|
||||
rc = selinux_determine_inode_label(selinux_cred(current_cred()),
|
||||
dir, qstr,
|
||||
inode_mode_to_security_class(inode->i_mode),
|
||||
&newsid);
|
||||
@ -2836,7 +2792,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
|
||||
|
||||
/* Possibly defer initialization to selinux_complete_init. */
|
||||
if (sbsec->flags & SE_SBINITIALIZED) {
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
struct inode_security_struct *isec = selinux_inode(inode);
|
||||
isec->sclass = inode_mode_to_security_class(inode->i_mode);
|
||||
isec->sid = newsid;
|
||||
isec->initialized = LABEL_INITIALIZED;
|
||||
@ -2936,7 +2892,7 @@ static noinline int audit_inode_permission(struct inode *inode,
|
||||
unsigned flags)
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
struct inode_security_struct *isec = selinux_inode(inode);
|
||||
int rc;
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_INODE;
|
||||
@ -3031,11 +2987,11 @@ static int selinux_inode_getattr(const struct path *path)
|
||||
static bool has_cap_mac_admin(bool audit)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
int cap_audit = audit ? SECURITY_CAP_AUDIT : SECURITY_CAP_NOAUDIT;
|
||||
unsigned int opts = audit ? CAP_OPT_NONE : CAP_OPT_NOAUDIT;
|
||||
|
||||
if (cap_capable(cred, &init_user_ns, CAP_MAC_ADMIN, cap_audit))
|
||||
if (cap_capable(cred, &init_user_ns, CAP_MAC_ADMIN, opts))
|
||||
return false;
|
||||
if (cred_has_capability(cred, CAP_MAC_ADMIN, cap_audit, true))
|
||||
if (cred_has_capability(cred, CAP_MAC_ADMIN, opts, true))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@ -3289,7 +3245,7 @@ static int selinux_inode_copy_up(struct dentry *src, struct cred **new)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tsec = new_creds->security;
|
||||
tsec = selinux_cred(new_creds);
|
||||
/* Get label from overlay inode and set it in create_sid */
|
||||
selinux_inode_getsecid(d_inode(src), &sid);
|
||||
tsec->create_sid = sid;
|
||||
@ -3330,7 +3286,7 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)
|
||||
static int selinux_file_permission(struct file *file, int mask)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct file_security_struct *fsec = file->f_security;
|
||||
struct file_security_struct *fsec = selinux_file(file);
|
||||
struct inode_security_struct *isec;
|
||||
u32 sid = current_sid();
|
||||
|
||||
@ -3352,11 +3308,6 @@ static int selinux_file_alloc_security(struct file *file)
|
||||
return file_alloc_security(file);
|
||||
}
|
||||
|
||||
static void selinux_file_free_security(struct file *file)
|
||||
{
|
||||
file_free_security(file);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a task has the ioctl permission and cmd
|
||||
* operation to an inode.
|
||||
@ -3365,7 +3316,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
|
||||
u32 requested, u16 cmd)
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
struct file_security_struct *fsec = file->f_security;
|
||||
struct file_security_struct *fsec = selinux_file(file);
|
||||
struct inode *inode = file_inode(file);
|
||||
struct inode_security_struct *isec;
|
||||
struct lsm_ioctlop_audit ioctl;
|
||||
@ -3435,7 +3386,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
|
||||
case KDSKBENT:
|
||||
case KDSKBSENT:
|
||||
error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG,
|
||||
SECURITY_CAP_AUDIT, true);
|
||||
CAP_OPT_NONE, true);
|
||||
break;
|
||||
|
||||
/* default case assumes that the command will go
|
||||
@ -3617,7 +3568,7 @@ static void selinux_file_set_fowner(struct file *file)
|
||||
{
|
||||
struct file_security_struct *fsec;
|
||||
|
||||
fsec = file->f_security;
|
||||
fsec = selinux_file(file);
|
||||
fsec->fown_sid = current_sid();
|
||||
}
|
||||
|
||||
@ -3632,7 +3583,7 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
|
||||
/* struct fown_struct is never outside the context of a struct file */
|
||||
file = container_of(fown, struct file, f_owner);
|
||||
|
||||
fsec = file->f_security;
|
||||
fsec = selinux_file(file);
|
||||
|
||||
if (!signum)
|
||||
perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */
|
||||
@ -3656,7 +3607,7 @@ static int selinux_file_open(struct file *file)
|
||||
struct file_security_struct *fsec;
|
||||
struct inode_security_struct *isec;
|
||||
|
||||
fsec = file->f_security;
|
||||
fsec = selinux_file(file);
|
||||
isec = inode_security(file_inode(file));
|
||||
/*
|
||||
* Save inode label and policy sequence number
|
||||
@ -3689,53 +3640,16 @@ static int selinux_task_alloc(struct task_struct *task,
|
||||
sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate the SELinux part of blank credentials
|
||||
*/
|
||||
static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp)
|
||||
{
|
||||
struct task_security_struct *tsec;
|
||||
|
||||
tsec = kzalloc(sizeof(struct task_security_struct), gfp);
|
||||
if (!tsec)
|
||||
return -ENOMEM;
|
||||
|
||||
cred->security = tsec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* detach and free the LSM part of a set of credentials
|
||||
*/
|
||||
static void selinux_cred_free(struct cred *cred)
|
||||
{
|
||||
struct task_security_struct *tsec = cred->security;
|
||||
|
||||
/*
|
||||
* cred->security == NULL if security_cred_alloc_blank() or
|
||||
* security_prepare_creds() returned an error.
|
||||
*/
|
||||
BUG_ON(cred->security && (unsigned long) cred->security < PAGE_SIZE);
|
||||
cred->security = (void *) 0x7UL;
|
||||
kfree(tsec);
|
||||
}
|
||||
|
||||
/*
|
||||
* prepare a new set of credentials for modification
|
||||
*/
|
||||
static int selinux_cred_prepare(struct cred *new, const struct cred *old,
|
||||
gfp_t gfp)
|
||||
{
|
||||
const struct task_security_struct *old_tsec;
|
||||
struct task_security_struct *tsec;
|
||||
const struct task_security_struct *old_tsec = selinux_cred(old);
|
||||
struct task_security_struct *tsec = selinux_cred(new);
|
||||
|
||||
old_tsec = old->security;
|
||||
|
||||
tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp);
|
||||
if (!tsec)
|
||||
return -ENOMEM;
|
||||
|
||||
new->security = tsec;
|
||||
*tsec = *old_tsec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3744,8 +3658,8 @@ static int selinux_cred_prepare(struct cred *new, const struct cred *old,
|
||||
*/
|
||||
static void selinux_cred_transfer(struct cred *new, const struct cred *old)
|
||||
{
|
||||
const struct task_security_struct *old_tsec = old->security;
|
||||
struct task_security_struct *tsec = new->security;
|
||||
const struct task_security_struct *old_tsec = selinux_cred(old);
|
||||
struct task_security_struct *tsec = selinux_cred(new);
|
||||
|
||||
*tsec = *old_tsec;
|
||||
}
|
||||
@ -3761,7 +3675,7 @@ static void selinux_cred_getsecid(const struct cred *c, u32 *secid)
|
||||
*/
|
||||
static int selinux_kernel_act_as(struct cred *new, u32 secid)
|
||||
{
|
||||
struct task_security_struct *tsec = new->security;
|
||||
struct task_security_struct *tsec = selinux_cred(new);
|
||||
u32 sid = current_sid();
|
||||
int ret;
|
||||
|
||||
@ -3786,7 +3700,7 @@ static int selinux_kernel_act_as(struct cred *new, u32 secid)
|
||||
static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
|
||||
{
|
||||
struct inode_security_struct *isec = inode_security(inode);
|
||||
struct task_security_struct *tsec = new->security;
|
||||
struct task_security_struct *tsec = selinux_cred(new);
|
||||
u32 sid = current_sid();
|
||||
int ret;
|
||||
|
||||
@ -3832,7 +3746,7 @@ static int selinux_kernel_module_from_file(struct file *file)
|
||||
ad.type = LSM_AUDIT_DATA_FILE;
|
||||
ad.u.file = file;
|
||||
|
||||
fsec = file->f_security;
|
||||
fsec = selinux_file(file);
|
||||
if (sid != fsec->sid) {
|
||||
rc = avc_has_perm(&selinux_state,
|
||||
sid, fsec->sid, SECCLASS_FD, FD__USE, &ad);
|
||||
@ -3998,7 +3912,7 @@ static int selinux_task_kill(struct task_struct *p, struct kernel_siginfo *info,
|
||||
static void selinux_task_to_inode(struct task_struct *p,
|
||||
struct inode *inode)
|
||||
{
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
struct inode_security_struct *isec = selinux_inode(inode);
|
||||
u32 sid = task_sid(p);
|
||||
|
||||
spin_lock(&isec->lock);
|
||||
@ -4335,7 +4249,7 @@ static int sock_has_perm(struct sock *sk, u32 perms)
|
||||
static int selinux_socket_create(int family, int type,
|
||||
int protocol, int kern)
|
||||
{
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
const struct task_security_struct *tsec = selinux_cred(current_cred());
|
||||
u32 newsid;
|
||||
u16 secclass;
|
||||
int rc;
|
||||
@ -4355,7 +4269,7 @@ static int selinux_socket_create(int family, int type,
|
||||
static int selinux_socket_post_create(struct socket *sock, int family,
|
||||
int type, int protocol, int kern)
|
||||
{
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
const struct task_security_struct *tsec = selinux_cred(current_cred());
|
||||
struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock));
|
||||
struct sk_security_struct *sksec;
|
||||
u16 sclass = socket_type_to_security_class(family, type, protocol);
|
||||
@ -5236,7 +5150,7 @@ static int selinux_secmark_relabel_packet(u32 sid)
|
||||
const struct task_security_struct *__tsec;
|
||||
u32 tsid;
|
||||
|
||||
__tsec = current_security();
|
||||
__tsec = selinux_cred(current_cred());
|
||||
tsid = __tsec->sid;
|
||||
|
||||
return avc_has_perm(&selinux_state,
|
||||
@ -5711,51 +5625,22 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
|
||||
return selinux_nlmsg_perm(sk, skb);
|
||||
}
|
||||
|
||||
static int ipc_alloc_security(struct kern_ipc_perm *perm,
|
||||
u16 sclass)
|
||||
static void ipc_init_security(struct ipc_security_struct *isec, u16 sclass)
|
||||
{
|
||||
struct ipc_security_struct *isec;
|
||||
|
||||
isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
|
||||
if (!isec)
|
||||
return -ENOMEM;
|
||||
|
||||
isec->sclass = sclass;
|
||||
isec->sid = current_sid();
|
||||
perm->security = isec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipc_free_security(struct kern_ipc_perm *perm)
|
||||
{
|
||||
struct ipc_security_struct *isec = perm->security;
|
||||
perm->security = NULL;
|
||||
kfree(isec);
|
||||
}
|
||||
|
||||
static int msg_msg_alloc_security(struct msg_msg *msg)
|
||||
{
|
||||
struct msg_security_struct *msec;
|
||||
|
||||
msec = kzalloc(sizeof(struct msg_security_struct), GFP_KERNEL);
|
||||
if (!msec)
|
||||
return -ENOMEM;
|
||||
|
||||
msec = selinux_msg_msg(msg);
|
||||
msec->sid = SECINITSID_UNLABELED;
|
||||
msg->security = msec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msg_msg_free_security(struct msg_msg *msg)
|
||||
{
|
||||
struct msg_security_struct *msec = msg->security;
|
||||
|
||||
msg->security = NULL;
|
||||
kfree(msec);
|
||||
}
|
||||
|
||||
static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
|
||||
u32 perms)
|
||||
{
|
||||
@ -5763,7 +5648,7 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
|
||||
struct common_audit_data ad;
|
||||
u32 sid = current_sid();
|
||||
|
||||
isec = ipc_perms->security;
|
||||
isec = selinux_ipc(ipc_perms);
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_IPC;
|
||||
ad.u.ipc_id = ipc_perms->key;
|
||||
@ -5777,11 +5662,6 @@ static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
|
||||
return msg_msg_alloc_security(msg);
|
||||
}
|
||||
|
||||
static void selinux_msg_msg_free_security(struct msg_msg *msg)
|
||||
{
|
||||
msg_msg_free_security(msg);
|
||||
}
|
||||
|
||||
/* message queue security operations */
|
||||
static int selinux_msg_queue_alloc_security(struct kern_ipc_perm *msq)
|
||||
{
|
||||
@ -5790,11 +5670,8 @@ static int selinux_msg_queue_alloc_security(struct kern_ipc_perm *msq)
|
||||
u32 sid = current_sid();
|
||||
int rc;
|
||||
|
||||
rc = ipc_alloc_security(msq, SECCLASS_MSGQ);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
isec = msq->security;
|
||||
isec = selinux_ipc(msq);
|
||||
ipc_init_security(isec, SECCLASS_MSGQ);
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_IPC;
|
||||
ad.u.ipc_id = msq->key;
|
||||
@ -5802,16 +5679,7 @@ static int selinux_msg_queue_alloc_security(struct kern_ipc_perm *msq)
|
||||
rc = avc_has_perm(&selinux_state,
|
||||
sid, isec->sid, SECCLASS_MSGQ,
|
||||
MSGQ__CREATE, &ad);
|
||||
if (rc) {
|
||||
ipc_free_security(msq);
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void selinux_msg_queue_free_security(struct kern_ipc_perm *msq)
|
||||
{
|
||||
ipc_free_security(msq);
|
||||
}
|
||||
|
||||
static int selinux_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg)
|
||||
@ -5820,7 +5688,7 @@ static int selinux_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg)
|
||||
struct common_audit_data ad;
|
||||
u32 sid = current_sid();
|
||||
|
||||
isec = msq->security;
|
||||
isec = selinux_ipc(msq);
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_IPC;
|
||||
ad.u.ipc_id = msq->key;
|
||||
@ -5869,8 +5737,8 @@ static int selinux_msg_queue_msgsnd(struct kern_ipc_perm *msq, struct msg_msg *m
|
||||
u32 sid = current_sid();
|
||||
int rc;
|
||||
|
||||
isec = msq->security;
|
||||
msec = msg->security;
|
||||
isec = selinux_ipc(msq);
|
||||
msec = selinux_msg_msg(msg);
|
||||
|
||||
/*
|
||||
* First time through, need to assign label to the message
|
||||
@ -5917,8 +5785,8 @@ static int selinux_msg_queue_msgrcv(struct kern_ipc_perm *msq, struct msg_msg *m
|
||||
u32 sid = task_sid(target);
|
||||
int rc;
|
||||
|
||||
isec = msq->security;
|
||||
msec = msg->security;
|
||||
isec = selinux_ipc(msq);
|
||||
msec = selinux_msg_msg(msg);
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_IPC;
|
||||
ad.u.ipc_id = msq->key;
|
||||
@ -5941,11 +5809,8 @@ static int selinux_shm_alloc_security(struct kern_ipc_perm *shp)
|
||||
u32 sid = current_sid();
|
||||
int rc;
|
||||
|
||||
rc = ipc_alloc_security(shp, SECCLASS_SHM);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
isec = shp->security;
|
||||
isec = selinux_ipc(shp);
|
||||
ipc_init_security(isec, SECCLASS_SHM);
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_IPC;
|
||||
ad.u.ipc_id = shp->key;
|
||||
@ -5953,16 +5818,7 @@ static int selinux_shm_alloc_security(struct kern_ipc_perm *shp)
|
||||
rc = avc_has_perm(&selinux_state,
|
||||
sid, isec->sid, SECCLASS_SHM,
|
||||
SHM__CREATE, &ad);
|
||||
if (rc) {
|
||||
ipc_free_security(shp);
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void selinux_shm_free_security(struct kern_ipc_perm *shp)
|
||||
{
|
||||
ipc_free_security(shp);
|
||||
}
|
||||
|
||||
static int selinux_shm_associate(struct kern_ipc_perm *shp, int shmflg)
|
||||
@ -5971,7 +5827,7 @@ static int selinux_shm_associate(struct kern_ipc_perm *shp, int shmflg)
|
||||
struct common_audit_data ad;
|
||||
u32 sid = current_sid();
|
||||
|
||||
isec = shp->security;
|
||||
isec = selinux_ipc(shp);
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_IPC;
|
||||
ad.u.ipc_id = shp->key;
|
||||
@ -6038,11 +5894,8 @@ static int selinux_sem_alloc_security(struct kern_ipc_perm *sma)
|
||||
u32 sid = current_sid();
|
||||
int rc;
|
||||
|
||||
rc = ipc_alloc_security(sma, SECCLASS_SEM);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
isec = sma->security;
|
||||
isec = selinux_ipc(sma);
|
||||
ipc_init_security(isec, SECCLASS_SEM);
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_IPC;
|
||||
ad.u.ipc_id = sma->key;
|
||||
@ -6050,16 +5903,7 @@ static int selinux_sem_alloc_security(struct kern_ipc_perm *sma)
|
||||
rc = avc_has_perm(&selinux_state,
|
||||
sid, isec->sid, SECCLASS_SEM,
|
||||
SEM__CREATE, &ad);
|
||||
if (rc) {
|
||||
ipc_free_security(sma);
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void selinux_sem_free_security(struct kern_ipc_perm *sma)
|
||||
{
|
||||
ipc_free_security(sma);
|
||||
}
|
||||
|
||||
static int selinux_sem_associate(struct kern_ipc_perm *sma, int semflg)
|
||||
@ -6068,7 +5912,7 @@ static int selinux_sem_associate(struct kern_ipc_perm *sma, int semflg)
|
||||
struct common_audit_data ad;
|
||||
u32 sid = current_sid();
|
||||
|
||||
isec = sma->security;
|
||||
isec = selinux_ipc(sma);
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_IPC;
|
||||
ad.u.ipc_id = sma->key;
|
||||
@ -6154,7 +5998,7 @@ static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
|
||||
|
||||
static void selinux_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
|
||||
{
|
||||
struct ipc_security_struct *isec = ipcp->security;
|
||||
struct ipc_security_struct *isec = selinux_ipc(ipcp);
|
||||
*secid = isec->sid;
|
||||
}
|
||||
|
||||
@ -6173,7 +6017,7 @@ static int selinux_getprocattr(struct task_struct *p,
|
||||
unsigned len;
|
||||
|
||||
rcu_read_lock();
|
||||
__tsec = __task_cred(p)->security;
|
||||
__tsec = selinux_cred(__task_cred(p));
|
||||
|
||||
if (current != p) {
|
||||
error = avc_has_perm(&selinux_state,
|
||||
@ -6296,7 +6140,7 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
|
||||
operation. See selinux_bprm_set_creds for the execve
|
||||
checks and may_create for the file creation checks. The
|
||||
operation will then fail if the context is not permitted. */
|
||||
tsec = new->security;
|
||||
tsec = selinux_cred(new);
|
||||
if (!strcmp(name, "exec")) {
|
||||
tsec->exec_sid = sid;
|
||||
} else if (!strcmp(name, "fscreate")) {
|
||||
@ -6380,7 +6224,7 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
|
||||
|
||||
static void selinux_inode_invalidate_secctx(struct inode *inode)
|
||||
{
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
struct inode_security_struct *isec = selinux_inode(inode);
|
||||
|
||||
spin_lock(&isec->lock);
|
||||
isec->initialized = LABEL_INVALID;
|
||||
@ -6425,7 +6269,7 @@ static int selinux_key_alloc(struct key *k, const struct cred *cred,
|
||||
if (!ksec)
|
||||
return -ENOMEM;
|
||||
|
||||
tsec = cred->security;
|
||||
tsec = selinux_cred(cred);
|
||||
if (tsec->keycreate_sid)
|
||||
ksec->sid = tsec->keycreate_sid;
|
||||
else
|
||||
@ -6688,6 +6532,14 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
|
||||
}
|
||||
#endif
|
||||
|
||||
struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = {
|
||||
.lbs_cred = sizeof(struct task_security_struct),
|
||||
.lbs_file = sizeof(struct file_security_struct),
|
||||
.lbs_inode = sizeof(struct inode_security_struct),
|
||||
.lbs_ipc = sizeof(struct ipc_security_struct),
|
||||
.lbs_msg_msg = sizeof(struct msg_security_struct),
|
||||
};
|
||||
|
||||
static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
|
||||
LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),
|
||||
@ -6757,7 +6609,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
|
||||
|
||||
LSM_HOOK_INIT(file_permission, selinux_file_permission),
|
||||
LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
|
||||
LSM_HOOK_INIT(file_free_security, selinux_file_free_security),
|
||||
LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
|
||||
LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
|
||||
LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr),
|
||||
@ -6771,8 +6622,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(file_open, selinux_file_open),
|
||||
|
||||
LSM_HOOK_INIT(task_alloc, selinux_task_alloc),
|
||||
LSM_HOOK_INIT(cred_alloc_blank, selinux_cred_alloc_blank),
|
||||
LSM_HOOK_INIT(cred_free, selinux_cred_free),
|
||||
LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare),
|
||||
LSM_HOOK_INIT(cred_transfer, selinux_cred_transfer),
|
||||
LSM_HOOK_INIT(cred_getsecid, selinux_cred_getsecid),
|
||||
@ -6800,24 +6649,20 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(ipc_getsecid, selinux_ipc_getsecid),
|
||||
|
||||
LSM_HOOK_INIT(msg_msg_alloc_security, selinux_msg_msg_alloc_security),
|
||||
LSM_HOOK_INIT(msg_msg_free_security, selinux_msg_msg_free_security),
|
||||
|
||||
LSM_HOOK_INIT(msg_queue_alloc_security,
|
||||
selinux_msg_queue_alloc_security),
|
||||
LSM_HOOK_INIT(msg_queue_free_security, selinux_msg_queue_free_security),
|
||||
LSM_HOOK_INIT(msg_queue_associate, selinux_msg_queue_associate),
|
||||
LSM_HOOK_INIT(msg_queue_msgctl, selinux_msg_queue_msgctl),
|
||||
LSM_HOOK_INIT(msg_queue_msgsnd, selinux_msg_queue_msgsnd),
|
||||
LSM_HOOK_INIT(msg_queue_msgrcv, selinux_msg_queue_msgrcv),
|
||||
|
||||
LSM_HOOK_INIT(shm_alloc_security, selinux_shm_alloc_security),
|
||||
LSM_HOOK_INIT(shm_free_security, selinux_shm_free_security),
|
||||
LSM_HOOK_INIT(shm_associate, selinux_shm_associate),
|
||||
LSM_HOOK_INIT(shm_shmctl, selinux_shm_shmctl),
|
||||
LSM_HOOK_INIT(shm_shmat, selinux_shm_shmat),
|
||||
|
||||
LSM_HOOK_INIT(sem_alloc_security, selinux_sem_alloc_security),
|
||||
LSM_HOOK_INIT(sem_free_security, selinux_sem_free_security),
|
||||
LSM_HOOK_INIT(sem_associate, selinux_sem_associate),
|
||||
LSM_HOOK_INIT(sem_semctl, selinux_sem_semctl),
|
||||
LSM_HOOK_INIT(sem_semop, selinux_sem_semop),
|
||||
@ -6928,16 +6773,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
|
||||
|
||||
static __init int selinux_init(void)
|
||||
{
|
||||
if (!security_module_enable("selinux")) {
|
||||
selinux_enabled = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!selinux_enabled) {
|
||||
pr_info("SELinux: Disabled at boot.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_info("SELinux: Initializing.\n");
|
||||
|
||||
memset(&selinux_state, 0, sizeof(selinux_state));
|
||||
@ -6951,12 +6786,6 @@ static __init int selinux_init(void)
|
||||
|
||||
default_noexec = !(VM_DATA_DEFAULT_FLAGS & VM_EXEC);
|
||||
|
||||
sel_inode_cache = kmem_cache_create("selinux_inode_security",
|
||||
sizeof(struct inode_security_struct),
|
||||
0, SLAB_PANIC, NULL);
|
||||
file_security_cache = kmem_cache_create("selinux_file_security",
|
||||
sizeof(struct file_security_struct),
|
||||
0, SLAB_PANIC, NULL);
|
||||
avc_init();
|
||||
|
||||
avtab_cache_init();
|
||||
@ -6999,6 +6828,9 @@ void selinux_complete_init(void)
|
||||
all processes and objects when they are created. */
|
||||
DEFINE_LSM(selinux) = {
|
||||
.name = "selinux",
|
||||
.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
|
||||
.enabled = &selinux_enabled,
|
||||
.blobs = &selinux_blob_sizes,
|
||||
.init = selinux_init,
|
||||
};
|
||||
|
||||
|
@ -1,9 +1,6 @@
|
||||
/*
|
||||
* SELinux support for the Audit LSM hooks
|
||||
*
|
||||
* Most of below header was moved from include/linux/selinux.h which
|
||||
* is released under below copyrights:
|
||||
*
|
||||
* Author: James Morris <jmorris@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2005 Red Hat, Inc., James Morris <jmorris@redhat.com>
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include <linux/binfmts.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/lsm_hooks.h>
|
||||
#include <linux/msg.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include "flask.h"
|
||||
#include "avc.h"
|
||||
@ -56,10 +58,7 @@ enum label_initialized {
|
||||
|
||||
struct inode_security_struct {
|
||||
struct inode *inode; /* back pointer to inode object */
|
||||
union {
|
||||
struct list_head list; /* list of inode_security_struct */
|
||||
struct rcu_head rcu; /* for freeing the inode_security_struct */
|
||||
};
|
||||
u32 task_sid; /* SID of creating task */
|
||||
u32 sid; /* SID of this object */
|
||||
u16 sclass; /* security class of this object */
|
||||
@ -158,4 +157,35 @@ struct bpf_security_struct {
|
||||
u32 sid; /*SID of bpf obj creater*/
|
||||
};
|
||||
|
||||
extern struct lsm_blob_sizes selinux_blob_sizes;
|
||||
static inline struct task_security_struct *selinux_cred(const struct cred *cred)
|
||||
{
|
||||
return cred->security + selinux_blob_sizes.lbs_cred;
|
||||
}
|
||||
|
||||
static inline struct file_security_struct *selinux_file(const struct file *file)
|
||||
{
|
||||
return file->f_security + selinux_blob_sizes.lbs_file;
|
||||
}
|
||||
|
||||
static inline struct inode_security_struct *selinux_inode(
|
||||
const struct inode *inode)
|
||||
{
|
||||
if (unlikely(!inode->i_security))
|
||||
return NULL;
|
||||
return inode->i_security + selinux_blob_sizes.lbs_inode;
|
||||
}
|
||||
|
||||
static inline struct msg_security_struct *selinux_msg_msg(
|
||||
const struct msg_msg *msg_msg)
|
||||
{
|
||||
return msg_msg->security + selinux_blob_sizes.lbs_msg_msg;
|
||||
}
|
||||
|
||||
static inline struct ipc_security_struct *selinux_ipc(
|
||||
const struct kern_ipc_perm *ipc)
|
||||
{
|
||||
return ipc->security + selinux_blob_sizes.lbs_ipc;
|
||||
}
|
||||
|
||||
#endif /* _SELINUX_OBJSEC_H_ */
|
||||
|
@ -1378,7 +1378,7 @@ static int sel_make_bools(struct selinux_fs_info *fsi)
|
||||
goto out;
|
||||
}
|
||||
|
||||
isec = (struct inode_security_struct *)inode->i_security;
|
||||
isec = selinux_inode(inode);
|
||||
ret = security_genfs_sid(fsi->state, "selinuxfs", page,
|
||||
SECCLASS_FILE, &sid);
|
||||
if (ret) {
|
||||
@ -1953,7 +1953,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
|
||||
}
|
||||
|
||||
inode->i_ino = ++fsi->last_ino;
|
||||
isec = (struct inode_security_struct *)inode->i_security;
|
||||
isec = selinux_inode(inode);
|
||||
isec->sid = SECINITSID_DEVNULL;
|
||||
isec->sclass = SECCLASS_CHR_FILE;
|
||||
isec->initialized = LABEL_INITIALIZED;
|
||||
|
@ -49,7 +49,6 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/selinux.h>
|
||||
#include <linux/flex_array.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <net/netlabel.h>
|
||||
|
@ -79,7 +79,7 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp,
|
||||
gfp_t gfp)
|
||||
{
|
||||
int rc;
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
const struct task_security_struct *tsec = selinux_cred(current_cred());
|
||||
struct xfrm_sec_ctx *ctx = NULL;
|
||||
u32 str_len;
|
||||
|
||||
@ -138,7 +138,7 @@ static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx)
|
||||
*/
|
||||
static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx)
|
||||
{
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
const struct task_security_struct *tsec = selinux_cred(current_cred());
|
||||
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/lsm_audit.h>
|
||||
#include <linux/msg.h>
|
||||
|
||||
/*
|
||||
* Use IPv6 port labeling if IPv6 is enabled and secmarks
|
||||
@ -336,6 +337,7 @@ extern struct smack_known *smack_syslog_label;
|
||||
extern struct smack_known *smack_unconfined;
|
||||
#endif
|
||||
extern int smack_ptrace_rule;
|
||||
extern struct lsm_blob_sizes smack_blob_sizes;
|
||||
|
||||
extern struct smack_known smack_known_floor;
|
||||
extern struct smack_known smack_known_hat;
|
||||
@ -356,12 +358,38 @@ extern struct list_head smack_onlycap_list;
|
||||
#define SMACK_HASH_SLOTS 16
|
||||
extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
|
||||
|
||||
static inline struct task_smack *smack_cred(const struct cred *cred)
|
||||
{
|
||||
return cred->security + smack_blob_sizes.lbs_cred;
|
||||
}
|
||||
|
||||
static inline struct smack_known **smack_file(const struct file *file)
|
||||
{
|
||||
return (struct smack_known **)(file->f_security +
|
||||
smack_blob_sizes.lbs_file);
|
||||
}
|
||||
|
||||
static inline struct inode_smack *smack_inode(const struct inode *inode)
|
||||
{
|
||||
return inode->i_security + smack_blob_sizes.lbs_inode;
|
||||
}
|
||||
|
||||
static inline struct smack_known **smack_msg_msg(const struct msg_msg *msg)
|
||||
{
|
||||
return msg->security + smack_blob_sizes.lbs_msg_msg;
|
||||
}
|
||||
|
||||
static inline struct smack_known **smack_ipc(const struct kern_ipc_perm *ipc)
|
||||
{
|
||||
return ipc->security + smack_blob_sizes.lbs_ipc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is the directory transmuting?
|
||||
*/
|
||||
static inline int smk_inode_transmutable(const struct inode *isp)
|
||||
{
|
||||
struct inode_smack *sip = isp->i_security;
|
||||
struct inode_smack *sip = smack_inode(isp);
|
||||
return (sip->smk_flags & SMK_INODE_TRANSMUTE) != 0;
|
||||
}
|
||||
|
||||
@ -370,7 +398,7 @@ static inline int smk_inode_transmutable(const struct inode *isp)
|
||||
*/
|
||||
static inline struct smack_known *smk_of_inode(const struct inode *isp)
|
||||
{
|
||||
struct inode_smack *sip = isp->i_security;
|
||||
struct inode_smack *sip = smack_inode(isp);
|
||||
return sip->smk_inode;
|
||||
}
|
||||
|
||||
@ -382,13 +410,19 @@ static inline struct smack_known *smk_of_task(const struct task_smack *tsp)
|
||||
return tsp->smk_task;
|
||||
}
|
||||
|
||||
static inline struct smack_known *smk_of_task_struct(const struct task_struct *t)
|
||||
static inline struct smack_known *smk_of_task_struct(
|
||||
const struct task_struct *t)
|
||||
{
|
||||
struct smack_known *skp;
|
||||
const struct cred *cred;
|
||||
|
||||
rcu_read_lock();
|
||||
skp = smk_of_task(__task_cred(t)->security);
|
||||
|
||||
cred = __task_cred(t);
|
||||
skp = smk_of_task(smack_cred(cred));
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return skp;
|
||||
}
|
||||
|
||||
@ -405,7 +439,7 @@ static inline struct smack_known *smk_of_forked(const struct task_smack *tsp)
|
||||
*/
|
||||
static inline struct smack_known *smk_of_current(void)
|
||||
{
|
||||
return smk_of_task(current_security());
|
||||
return smk_of_task(smack_cred(current_cred()));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -275,7 +275,7 @@ out_audit:
|
||||
int smk_curacc(struct smack_known *obj_known,
|
||||
u32 mode, struct smk_audit_info *a)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
|
||||
return smk_tskacc(tsp, obj_known, mode, a);
|
||||
}
|
||||
@ -635,12 +635,12 @@ DEFINE_MUTEX(smack_onlycap_lock);
|
||||
*/
|
||||
bool smack_privileged_cred(int cap, const struct cred *cred)
|
||||
{
|
||||
struct task_smack *tsp = cred->security;
|
||||
struct task_smack *tsp = smack_cred(cred);
|
||||
struct smack_known *skp = tsp->smk_task;
|
||||
struct smack_known_list_elem *sklep;
|
||||
int rc;
|
||||
|
||||
rc = cap_capable(cred, &init_user_ns, cap, SECURITY_CAP_AUDIT);
|
||||
rc = cap_capable(cred, &init_user_ns, cap, CAP_OPT_NONE);
|
||||
if (rc)
|
||||
return false;
|
||||
|
||||
|
@ -139,7 +139,7 @@ static int smk_bu_note(char *note, struct smack_known *sskp,
|
||||
static int smk_bu_current(char *note, struct smack_known *oskp,
|
||||
int mode, int rc)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
char acc[SMK_NUM_ACCESS_TYPE + 1];
|
||||
|
||||
if (rc <= 0)
|
||||
@ -160,7 +160,7 @@ static int smk_bu_current(char *note, struct smack_known *oskp,
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
static int smk_bu_task(struct task_struct *otp, int mode, int rc)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
struct smack_known *smk_task = smk_of_task_struct(otp);
|
||||
char acc[SMK_NUM_ACCESS_TYPE + 1];
|
||||
|
||||
@ -182,8 +182,8 @@ static int smk_bu_task(struct task_struct *otp, int mode, int rc)
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
static int smk_bu_inode(struct inode *inode, int mode, int rc)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct inode_smack *isp = inode->i_security;
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
struct inode_smack *isp = smack_inode(inode);
|
||||
char acc[SMK_NUM_ACCESS_TYPE + 1];
|
||||
|
||||
if (isp->smk_flags & SMK_INODE_IMPURE)
|
||||
@ -212,10 +212,10 @@ static int smk_bu_inode(struct inode *inode, int mode, int rc)
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
static int smk_bu_file(struct file *file, int mode, int rc)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
struct smack_known *sskp = tsp->smk_task;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct inode_smack *isp = inode->i_security;
|
||||
struct inode_smack *isp = smack_inode(inode);
|
||||
char acc[SMK_NUM_ACCESS_TYPE + 1];
|
||||
|
||||
if (isp->smk_flags & SMK_INODE_IMPURE)
|
||||
@ -242,10 +242,10 @@ static int smk_bu_file(struct file *file, int mode, int rc)
|
||||
static int smk_bu_credfile(const struct cred *cred, struct file *file,
|
||||
int mode, int rc)
|
||||
{
|
||||
struct task_smack *tsp = cred->security;
|
||||
struct task_smack *tsp = smack_cred(cred);
|
||||
struct smack_known *sskp = tsp->smk_task;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct inode_smack *isp = inode->i_security;
|
||||
struct inode_smack *isp = smack_inode(inode);
|
||||
char acc[SMK_NUM_ACCESS_TYPE + 1];
|
||||
|
||||
if (isp->smk_flags & SMK_INODE_IMPURE)
|
||||
@ -305,50 +305,35 @@ static struct smack_known *smk_fetch(const char *name, struct inode *ip,
|
||||
}
|
||||
|
||||
/**
|
||||
* new_inode_smack - allocate an inode security blob
|
||||
* init_inode_smack - initialize an inode security blob
|
||||
* @isp: the blob to initialize
|
||||
* @skp: a pointer to the Smack label entry to use in the blob
|
||||
*
|
||||
* Returns the new blob or NULL if there's no memory available
|
||||
*/
|
||||
static struct inode_smack *new_inode_smack(struct smack_known *skp)
|
||||
static void init_inode_smack(struct inode *inode, struct smack_known *skp)
|
||||
{
|
||||
struct inode_smack *isp;
|
||||
|
||||
isp = kmem_cache_zalloc(smack_inode_cache, GFP_NOFS);
|
||||
if (isp == NULL)
|
||||
return NULL;
|
||||
struct inode_smack *isp = smack_inode(inode);
|
||||
|
||||
isp->smk_inode = skp;
|
||||
isp->smk_flags = 0;
|
||||
mutex_init(&isp->smk_lock);
|
||||
|
||||
return isp;
|
||||
}
|
||||
|
||||
/**
|
||||
* new_task_smack - allocate a task security blob
|
||||
* init_task_smack - initialize a task security blob
|
||||
* @tsp: blob to initialize
|
||||
* @task: a pointer to the Smack label for the running task
|
||||
* @forked: a pointer to the Smack label for the forked task
|
||||
* @gfp: type of the memory for the allocation
|
||||
*
|
||||
* Returns the new blob or NULL if there's no memory available
|
||||
*/
|
||||
static struct task_smack *new_task_smack(struct smack_known *task,
|
||||
struct smack_known *forked, gfp_t gfp)
|
||||
static void init_task_smack(struct task_smack *tsp, struct smack_known *task,
|
||||
struct smack_known *forked)
|
||||
{
|
||||
struct task_smack *tsp;
|
||||
|
||||
tsp = kzalloc(sizeof(struct task_smack), gfp);
|
||||
if (tsp == NULL)
|
||||
return NULL;
|
||||
|
||||
tsp->smk_task = task;
|
||||
tsp->smk_forked = forked;
|
||||
INIT_LIST_HEAD(&tsp->smk_rules);
|
||||
INIT_LIST_HEAD(&tsp->smk_relabel);
|
||||
mutex_init(&tsp->smk_rules_lock);
|
||||
|
||||
return tsp;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -448,7 +433,7 @@ static int smk_ptrace_rule_check(struct task_struct *tracer,
|
||||
|
||||
rcu_read_lock();
|
||||
tracercred = __task_cred(tracer);
|
||||
tsp = tracercred->security;
|
||||
tsp = smack_cred(tracercred);
|
||||
tracer_known = smk_of_task(tsp);
|
||||
|
||||
if ((mode & PTRACE_MODE_ATTACH) &&
|
||||
@ -515,7 +500,7 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
|
||||
int rc;
|
||||
struct smack_known *skp;
|
||||
|
||||
skp = smk_of_task(current_security());
|
||||
skp = smk_of_task(smack_cred(current_cred()));
|
||||
|
||||
rc = smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__);
|
||||
return rc;
|
||||
@ -718,6 +703,13 @@ static int smack_set_mnt_opts(struct super_block *sb,
|
||||
if (sp->smk_flags & SMK_SB_INITIALIZED)
|
||||
return 0;
|
||||
|
||||
if (inode->i_security == NULL) {
|
||||
int rc = lsm_inode_alloc(inode);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!smack_privileged(CAP_MAC_ADMIN)) {
|
||||
/*
|
||||
* Unprivileged mounts don't get to specify Smack values.
|
||||
@ -782,17 +774,12 @@ static int smack_set_mnt_opts(struct super_block *sb,
|
||||
/*
|
||||
* Initialize the root inode.
|
||||
*/
|
||||
isp = inode->i_security;
|
||||
if (isp == NULL) {
|
||||
isp = new_inode_smack(sp->smk_root);
|
||||
if (isp == NULL)
|
||||
return -ENOMEM;
|
||||
inode->i_security = isp;
|
||||
} else
|
||||
isp->smk_inode = sp->smk_root;
|
||||
init_inode_smack(inode, sp->smk_root);
|
||||
|
||||
if (transmute)
|
||||
if (transmute) {
|
||||
isp = smack_inode(inode);
|
||||
isp->smk_flags |= SMK_INODE_TRANSMUTE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -831,7 +818,7 @@ static int smack_sb_statfs(struct dentry *dentry)
|
||||
static int smack_bprm_set_creds(struct linux_binprm *bprm)
|
||||
{
|
||||
struct inode *inode = file_inode(bprm->file);
|
||||
struct task_smack *bsp = bprm->cred->security;
|
||||
struct task_smack *bsp = smack_cred(bprm->cred);
|
||||
struct inode_smack *isp;
|
||||
struct superblock_smack *sbsp;
|
||||
int rc;
|
||||
@ -839,7 +826,7 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm)
|
||||
if (bprm->called_set_creds)
|
||||
return 0;
|
||||
|
||||
isp = inode->i_security;
|
||||
isp = smack_inode(inode);
|
||||
if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)
|
||||
return 0;
|
||||
|
||||
@ -890,48 +877,10 @@ static int smack_inode_alloc_security(struct inode *inode)
|
||||
{
|
||||
struct smack_known *skp = smk_of_current();
|
||||
|
||||
inode->i_security = new_inode_smack(skp);
|
||||
if (inode->i_security == NULL)
|
||||
return -ENOMEM;
|
||||
init_inode_smack(inode, skp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_inode_free_rcu - Free inode_smack blob from cache
|
||||
* @head: the rcu_head for getting inode_smack pointer
|
||||
*
|
||||
* Call back function called from call_rcu() to free
|
||||
* the i_security blob pointer in inode
|
||||
*/
|
||||
static void smack_inode_free_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct inode_smack *issp;
|
||||
|
||||
issp = container_of(head, struct inode_smack, smk_rcu);
|
||||
kmem_cache_free(smack_inode_cache, issp);
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_inode_free_security - free an inode blob using call_rcu()
|
||||
* @inode: the inode with a blob
|
||||
*
|
||||
* Clears the blob pointer in inode using RCU
|
||||
*/
|
||||
static void smack_inode_free_security(struct inode *inode)
|
||||
{
|
||||
struct inode_smack *issp = inode->i_security;
|
||||
|
||||
/*
|
||||
* The inode may still be referenced in a path walk and
|
||||
* a call to smack_inode_permission() can be made
|
||||
* after smack_inode_free_security() is called.
|
||||
* To avoid race condition free the i_security via RCU
|
||||
* and leave the current inode->i_security pointer intact.
|
||||
* The inode will be freed after the RCU grace period too.
|
||||
*/
|
||||
call_rcu(&issp->smk_rcu, smack_inode_free_rcu);
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_inode_init_security - copy out the smack from an inode
|
||||
* @inode: the newly created inode
|
||||
@ -947,7 +896,7 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, const char **name,
|
||||
void **value, size_t *len)
|
||||
{
|
||||
struct inode_smack *issp = inode->i_security;
|
||||
struct inode_smack *issp = smack_inode(inode);
|
||||
struct smack_known *skp = smk_of_current();
|
||||
struct smack_known *isp = smk_of_inode(inode);
|
||||
struct smack_known *dsp = smk_of_inode(dir);
|
||||
@ -1285,7 +1234,7 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags)
|
||||
{
|
||||
struct smack_known *skp;
|
||||
struct inode_smack *isp = d_backing_inode(dentry)->i_security;
|
||||
struct inode_smack *isp = smack_inode(d_backing_inode(dentry));
|
||||
|
||||
if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
|
||||
isp->smk_flags |= SMK_INODE_TRANSMUTE;
|
||||
@ -1366,7 +1315,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name)
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
isp = d_backing_inode(dentry)->i_security;
|
||||
isp = smack_inode(d_backing_inode(dentry));
|
||||
/*
|
||||
* Don't do anything special for these.
|
||||
* XATTR_NAME_SMACKIPIN
|
||||
@ -1498,24 +1447,12 @@ static void smack_inode_getsecid(struct inode *inode, u32 *secid)
|
||||
*/
|
||||
static int smack_file_alloc_security(struct file *file)
|
||||
{
|
||||
struct smack_known *skp = smk_of_current();
|
||||
struct smack_known **blob = smack_file(file);
|
||||
|
||||
file->f_security = skp;
|
||||
*blob = smk_of_current();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_file_free_security - clear a file security blob
|
||||
* @file: the object
|
||||
*
|
||||
* The security blob for a file is a pointer to the master
|
||||
* label list, so no memory is freed.
|
||||
*/
|
||||
static void smack_file_free_security(struct file *file)
|
||||
{
|
||||
file->f_security = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_file_ioctl - Smack check on ioctls
|
||||
* @file: the object
|
||||
@ -1653,7 +1590,7 @@ static int smack_mmap_file(struct file *file,
|
||||
if (unlikely(IS_PRIVATE(file_inode(file))))
|
||||
return 0;
|
||||
|
||||
isp = file_inode(file)->i_security;
|
||||
isp = smack_inode(file_inode(file));
|
||||
if (isp->smk_mmap == NULL)
|
||||
return 0;
|
||||
sbsp = file_inode(file)->i_sb->s_security;
|
||||
@ -1662,7 +1599,7 @@ static int smack_mmap_file(struct file *file,
|
||||
return -EACCES;
|
||||
mkp = isp->smk_mmap;
|
||||
|
||||
tsp = current_security();
|
||||
tsp = smack_cred(current_cred());
|
||||
skp = smk_of_current();
|
||||
rc = 0;
|
||||
|
||||
@ -1740,7 +1677,9 @@ static int smack_mmap_file(struct file *file,
|
||||
*/
|
||||
static void smack_file_set_fowner(struct file *file)
|
||||
{
|
||||
file->f_security = smk_of_current();
|
||||
struct smack_known **blob = smack_file(file);
|
||||
|
||||
*blob = smk_of_current();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1757,8 +1696,9 @@ static void smack_file_set_fowner(struct file *file)
|
||||
static int smack_file_send_sigiotask(struct task_struct *tsk,
|
||||
struct fown_struct *fown, int signum)
|
||||
{
|
||||
struct smack_known **blob;
|
||||
struct smack_known *skp;
|
||||
struct smack_known *tkp = smk_of_task(tsk->cred->security);
|
||||
struct smack_known *tkp = smk_of_task(smack_cred(tsk->cred));
|
||||
const struct cred *tcred;
|
||||
struct file *file;
|
||||
int rc;
|
||||
@ -1770,7 +1710,8 @@ static int smack_file_send_sigiotask(struct task_struct *tsk,
|
||||
file = container_of(fown, struct file, f_owner);
|
||||
|
||||
/* we don't log here as rc can be overriden */
|
||||
skp = file->f_security;
|
||||
blob = smack_file(file);
|
||||
skp = *blob;
|
||||
rc = smk_access(skp, tkp, MAY_DELIVER, NULL);
|
||||
rc = smk_bu_note("sigiotask", skp, tkp, MAY_DELIVER, rc);
|
||||
|
||||
@ -1811,7 +1752,7 @@ static int smack_file_receive(struct file *file)
|
||||
if (inode->i_sb->s_magic == SOCKFS_MAGIC) {
|
||||
sock = SOCKET_I(inode);
|
||||
ssp = sock->sk->sk_security;
|
||||
tsp = current_security();
|
||||
tsp = smack_cred(current_cred());
|
||||
/*
|
||||
* If the receiving process can't write to the
|
||||
* passed socket or if the passed socket can't
|
||||
@ -1853,7 +1794,7 @@ static int smack_file_receive(struct file *file)
|
||||
*/
|
||||
static int smack_file_open(struct file *file)
|
||||
{
|
||||
struct task_smack *tsp = file->f_cred->security;
|
||||
struct task_smack *tsp = smack_cred(file->f_cred);
|
||||
struct inode *inode = file_inode(file);
|
||||
struct smk_audit_info ad;
|
||||
int rc;
|
||||
@ -1881,14 +1822,7 @@ static int smack_file_open(struct file *file)
|
||||
*/
|
||||
static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
|
||||
{
|
||||
struct task_smack *tsp;
|
||||
|
||||
tsp = new_task_smack(NULL, NULL, gfp);
|
||||
if (tsp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
cred->security = tsp;
|
||||
|
||||
init_task_smack(smack_cred(cred), NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1900,15 +1834,11 @@ static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
|
||||
*/
|
||||
static void smack_cred_free(struct cred *cred)
|
||||
{
|
||||
struct task_smack *tsp = cred->security;
|
||||
struct task_smack *tsp = smack_cred(cred);
|
||||
struct smack_rule *rp;
|
||||
struct list_head *l;
|
||||
struct list_head *n;
|
||||
|
||||
if (tsp == NULL)
|
||||
return;
|
||||
cred->security = NULL;
|
||||
|
||||
smk_destroy_label_list(&tsp->smk_relabel);
|
||||
|
||||
list_for_each_safe(l, n, &tsp->smk_rules) {
|
||||
@ -1916,7 +1846,6 @@ static void smack_cred_free(struct cred *cred)
|
||||
list_del(&rp->list);
|
||||
kfree(rp);
|
||||
}
|
||||
kfree(tsp);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1930,15 +1859,11 @@ static void smack_cred_free(struct cred *cred)
|
||||
static int smack_cred_prepare(struct cred *new, const struct cred *old,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct task_smack *old_tsp = old->security;
|
||||
struct task_smack *new_tsp;
|
||||
struct task_smack *old_tsp = smack_cred(old);
|
||||
struct task_smack *new_tsp = smack_cred(new);
|
||||
int rc;
|
||||
|
||||
new_tsp = new_task_smack(old_tsp->smk_task, old_tsp->smk_task, gfp);
|
||||
if (new_tsp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
new->security = new_tsp;
|
||||
init_task_smack(new_tsp, old_tsp->smk_task, old_tsp->smk_task);
|
||||
|
||||
rc = smk_copy_rules(&new_tsp->smk_rules, &old_tsp->smk_rules, gfp);
|
||||
if (rc != 0)
|
||||
@ -1946,10 +1871,7 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
|
||||
|
||||
rc = smk_copy_relabel(&new_tsp->smk_relabel, &old_tsp->smk_relabel,
|
||||
gfp);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1961,15 +1883,14 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
|
||||
*/
|
||||
static void smack_cred_transfer(struct cred *new, const struct cred *old)
|
||||
{
|
||||
struct task_smack *old_tsp = old->security;
|
||||
struct task_smack *new_tsp = new->security;
|
||||
struct task_smack *old_tsp = smack_cred(old);
|
||||
struct task_smack *new_tsp = smack_cred(new);
|
||||
|
||||
new_tsp->smk_task = old_tsp->smk_task;
|
||||
new_tsp->smk_forked = old_tsp->smk_task;
|
||||
mutex_init(&new_tsp->smk_rules_lock);
|
||||
INIT_LIST_HEAD(&new_tsp->smk_rules);
|
||||
|
||||
|
||||
/* cbs copy rule list */
|
||||
}
|
||||
|
||||
@ -1980,12 +1901,12 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old)
|
||||
*
|
||||
* Sets the secid to contain a u32 version of the smack label.
|
||||
*/
|
||||
static void smack_cred_getsecid(const struct cred *c, u32 *secid)
|
||||
static void smack_cred_getsecid(const struct cred *cred, u32 *secid)
|
||||
{
|
||||
struct smack_known *skp;
|
||||
|
||||
rcu_read_lock();
|
||||
skp = smk_of_task(c->security);
|
||||
skp = smk_of_task(smack_cred(cred));
|
||||
*secid = skp->smk_secid;
|
||||
rcu_read_unlock();
|
||||
}
|
||||
@ -1999,7 +1920,7 @@ static void smack_cred_getsecid(const struct cred *c, u32 *secid)
|
||||
*/
|
||||
static int smack_kernel_act_as(struct cred *new, u32 secid)
|
||||
{
|
||||
struct task_smack *new_tsp = new->security;
|
||||
struct task_smack *new_tsp = smack_cred(new);
|
||||
|
||||
new_tsp->smk_task = smack_from_secid(secid);
|
||||
return 0;
|
||||
@ -2016,8 +1937,8 @@ static int smack_kernel_act_as(struct cred *new, u32 secid)
|
||||
static int smack_kernel_create_files_as(struct cred *new,
|
||||
struct inode *inode)
|
||||
{
|
||||
struct inode_smack *isp = inode->i_security;
|
||||
struct task_smack *tsp = new->security;
|
||||
struct inode_smack *isp = smack_inode(inode);
|
||||
struct task_smack *tsp = smack_cred(new);
|
||||
|
||||
tsp->smk_forked = isp->smk_inode;
|
||||
tsp->smk_task = tsp->smk_forked;
|
||||
@ -2201,7 +2122,7 @@ static int smack_task_kill(struct task_struct *p, struct kernel_siginfo *info,
|
||||
* specific behavior. This is not clean. For one thing
|
||||
* we can't take privilege into account.
|
||||
*/
|
||||
skp = smk_of_task(cred->security);
|
||||
skp = smk_of_task(smack_cred(cred));
|
||||
rc = smk_access(skp, tkp, MAY_DELIVER, &ad);
|
||||
rc = smk_bu_note("USB signal", skp, tkp, MAY_DELIVER, rc);
|
||||
return rc;
|
||||
@ -2216,7 +2137,7 @@ static int smack_task_kill(struct task_struct *p, struct kernel_siginfo *info,
|
||||
*/
|
||||
static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
|
||||
{
|
||||
struct inode_smack *isp = inode->i_security;
|
||||
struct inode_smack *isp = smack_inode(inode);
|
||||
struct smack_known *skp = smk_of_task_struct(p);
|
||||
|
||||
isp->smk_inode = skp;
|
||||
@ -2679,7 +2600,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
|
||||
const void *value, size_t size, int flags)
|
||||
{
|
||||
struct smack_known *skp;
|
||||
struct inode_smack *nsp = inode->i_security;
|
||||
struct inode_smack *nsp = smack_inode(inode);
|
||||
struct socket_smack *ssp;
|
||||
struct socket *sock;
|
||||
int rc = 0;
|
||||
@ -2888,23 +2809,12 @@ static int smack_flags_to_may(int flags)
|
||||
*/
|
||||
static int smack_msg_msg_alloc_security(struct msg_msg *msg)
|
||||
{
|
||||
struct smack_known *skp = smk_of_current();
|
||||
struct smack_known **blob = smack_msg_msg(msg);
|
||||
|
||||
msg->security = skp;
|
||||
*blob = smk_of_current();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_msg_msg_free_security - Clear the security blob for msg_msg
|
||||
* @msg: the object
|
||||
*
|
||||
* Clears the blob pointer
|
||||
*/
|
||||
static void smack_msg_msg_free_security(struct msg_msg *msg)
|
||||
{
|
||||
msg->security = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_of_ipc - the smack pointer for the ipc
|
||||
* @isp: the object
|
||||
@ -2913,7 +2823,9 @@ static void smack_msg_msg_free_security(struct msg_msg *msg)
|
||||
*/
|
||||
static struct smack_known *smack_of_ipc(struct kern_ipc_perm *isp)
|
||||
{
|
||||
return (struct smack_known *)isp->security;
|
||||
struct smack_known **blob = smack_ipc(isp);
|
||||
|
||||
return *blob;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2924,23 +2836,12 @@ static struct smack_known *smack_of_ipc(struct kern_ipc_perm *isp)
|
||||
*/
|
||||
static int smack_ipc_alloc_security(struct kern_ipc_perm *isp)
|
||||
{
|
||||
struct smack_known *skp = smk_of_current();
|
||||
struct smack_known **blob = smack_ipc(isp);
|
||||
|
||||
isp->security = skp;
|
||||
*blob = smk_of_current();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_ipc_free_security - Clear the security blob for ipc
|
||||
* @isp: the object
|
||||
*
|
||||
* Clears the blob pointer
|
||||
*/
|
||||
static void smack_ipc_free_security(struct kern_ipc_perm *isp)
|
||||
{
|
||||
isp->security = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_curacc_shm : check if current has access on shm
|
||||
* @isp : the object
|
||||
@ -3238,7 +3139,8 @@ static int smack_msg_queue_msgrcv(struct kern_ipc_perm *isp, struct msg_msg *msg
|
||||
*/
|
||||
static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag)
|
||||
{
|
||||
struct smack_known *iskp = ipp->security;
|
||||
struct smack_known **blob = smack_ipc(ipp);
|
||||
struct smack_known *iskp = *blob;
|
||||
int may = smack_flags_to_may(flag);
|
||||
struct smk_audit_info ad;
|
||||
int rc;
|
||||
@ -3259,7 +3161,8 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag)
|
||||
*/
|
||||
static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid)
|
||||
{
|
||||
struct smack_known *iskp = ipp->security;
|
||||
struct smack_known **blob = smack_ipc(ipp);
|
||||
struct smack_known *iskp = *blob;
|
||||
|
||||
*secid = iskp->smk_secid;
|
||||
}
|
||||
@ -3287,7 +3190,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
|
||||
if (inode == NULL)
|
||||
return;
|
||||
|
||||
isp = inode->i_security;
|
||||
isp = smack_inode(inode);
|
||||
|
||||
mutex_lock(&isp->smk_lock);
|
||||
/*
|
||||
@ -3390,13 +3293,12 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
|
||||
*/
|
||||
final = &smack_known_star;
|
||||
/*
|
||||
* Fall through.
|
||||
*
|
||||
* If a smack value has been set we want to use it,
|
||||
* but since tmpfs isn't giving us the opportunity
|
||||
* to set mount options simulate setting the
|
||||
* superblock default.
|
||||
*/
|
||||
/* Fall through */
|
||||
default:
|
||||
/*
|
||||
* This isn't an understood special case.
|
||||
@ -3528,7 +3430,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
|
||||
*/
|
||||
static int smack_setprocattr(const char *name, void *value, size_t size)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
struct cred *new;
|
||||
struct smack_known *skp;
|
||||
struct smack_known_list_elem *sklep;
|
||||
@ -3569,7 +3471,7 @@ static int smack_setprocattr(const char *name, void *value, size_t size)
|
||||
if (new == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
tsp = new->security;
|
||||
tsp = smack_cred(new);
|
||||
tsp->smk_task = skp;
|
||||
/*
|
||||
* process can change its label only once
|
||||
@ -4214,7 +4116,7 @@ static void smack_inet_csk_clone(struct sock *sk,
|
||||
static int smack_key_alloc(struct key *key, const struct cred *cred,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct smack_known *skp = smk_of_task(cred->security);
|
||||
struct smack_known *skp = smk_of_task(smack_cred(cred));
|
||||
|
||||
key->security = skp;
|
||||
return 0;
|
||||
@ -4245,7 +4147,7 @@ static int smack_key_permission(key_ref_t key_ref,
|
||||
{
|
||||
struct key *keyp;
|
||||
struct smk_audit_info ad;
|
||||
struct smack_known *tkp = smk_of_task(cred->security);
|
||||
struct smack_known *tkp = smk_of_task(smack_cred(cred));
|
||||
int request = 0;
|
||||
int rc;
|
||||
|
||||
@ -4520,12 +4422,12 @@ static int smack_inode_copy_up(struct dentry *dentry, struct cred **new)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tsp = new_creds->security;
|
||||
tsp = smack_cred(new_creds);
|
||||
|
||||
/*
|
||||
* Get label from overlay inode and set it in create_sid
|
||||
*/
|
||||
isp = d_inode(dentry->d_parent)->i_security;
|
||||
isp = smack_inode(d_inode(dentry->d_parent));
|
||||
skp = isp->smk_inode;
|
||||
tsp->smk_task = skp;
|
||||
*new = new_creds;
|
||||
@ -4548,8 +4450,8 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
|
||||
const struct cred *old,
|
||||
struct cred *new)
|
||||
{
|
||||
struct task_smack *otsp = old->security;
|
||||
struct task_smack *ntsp = new->security;
|
||||
struct task_smack *otsp = smack_cred(old);
|
||||
struct task_smack *ntsp = smack_cred(new);
|
||||
struct inode_smack *isp;
|
||||
int may;
|
||||
|
||||
@ -4562,7 +4464,7 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
|
||||
/*
|
||||
* the attribute of the containing directory
|
||||
*/
|
||||
isp = d_inode(dentry->d_parent)->i_security;
|
||||
isp = smack_inode(d_inode(dentry->d_parent));
|
||||
|
||||
if (isp->smk_flags & SMK_INODE_TRANSMUTE) {
|
||||
rcu_read_lock();
|
||||
@ -4582,6 +4484,14 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lsm_blob_sizes smack_blob_sizes __lsm_ro_after_init = {
|
||||
.lbs_cred = sizeof(struct task_smack),
|
||||
.lbs_file = sizeof(struct smack_known *),
|
||||
.lbs_inode = sizeof(struct inode_smack),
|
||||
.lbs_ipc = sizeof(struct smack_known *),
|
||||
.lbs_msg_msg = sizeof(struct smack_known *),
|
||||
};
|
||||
|
||||
static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check),
|
||||
LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
|
||||
@ -4597,7 +4507,6 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds),
|
||||
|
||||
LSM_HOOK_INIT(inode_alloc_security, smack_inode_alloc_security),
|
||||
LSM_HOOK_INIT(inode_free_security, smack_inode_free_security),
|
||||
LSM_HOOK_INIT(inode_init_security, smack_inode_init_security),
|
||||
LSM_HOOK_INIT(inode_link, smack_inode_link),
|
||||
LSM_HOOK_INIT(inode_unlink, smack_inode_unlink),
|
||||
@ -4616,7 +4525,6 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(inode_getsecid, smack_inode_getsecid),
|
||||
|
||||
LSM_HOOK_INIT(file_alloc_security, smack_file_alloc_security),
|
||||
LSM_HOOK_INIT(file_free_security, smack_file_free_security),
|
||||
LSM_HOOK_INIT(file_ioctl, smack_file_ioctl),
|
||||
LSM_HOOK_INIT(file_lock, smack_file_lock),
|
||||
LSM_HOOK_INIT(file_fcntl, smack_file_fcntl),
|
||||
@ -4652,23 +4560,19 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(ipc_getsecid, smack_ipc_getsecid),
|
||||
|
||||
LSM_HOOK_INIT(msg_msg_alloc_security, smack_msg_msg_alloc_security),
|
||||
LSM_HOOK_INIT(msg_msg_free_security, smack_msg_msg_free_security),
|
||||
|
||||
LSM_HOOK_INIT(msg_queue_alloc_security, smack_ipc_alloc_security),
|
||||
LSM_HOOK_INIT(msg_queue_free_security, smack_ipc_free_security),
|
||||
LSM_HOOK_INIT(msg_queue_associate, smack_msg_queue_associate),
|
||||
LSM_HOOK_INIT(msg_queue_msgctl, smack_msg_queue_msgctl),
|
||||
LSM_HOOK_INIT(msg_queue_msgsnd, smack_msg_queue_msgsnd),
|
||||
LSM_HOOK_INIT(msg_queue_msgrcv, smack_msg_queue_msgrcv),
|
||||
|
||||
LSM_HOOK_INIT(shm_alloc_security, smack_ipc_alloc_security),
|
||||
LSM_HOOK_INIT(shm_free_security, smack_ipc_free_security),
|
||||
LSM_HOOK_INIT(shm_associate, smack_shm_associate),
|
||||
LSM_HOOK_INIT(shm_shmctl, smack_shm_shmctl),
|
||||
LSM_HOOK_INIT(shm_shmat, smack_shm_shmat),
|
||||
|
||||
LSM_HOOK_INIT(sem_alloc_security, smack_ipc_alloc_security),
|
||||
LSM_HOOK_INIT(sem_free_security, smack_ipc_free_security),
|
||||
LSM_HOOK_INIT(sem_associate, smack_sem_associate),
|
||||
LSM_HOOK_INIT(sem_semctl, smack_sem_semctl),
|
||||
LSM_HOOK_INIT(sem_semop, smack_sem_semop),
|
||||
@ -4759,23 +4663,23 @@ static __init void init_smack_known_list(void)
|
||||
*/
|
||||
static __init int smack_init(void)
|
||||
{
|
||||
struct cred *cred;
|
||||
struct cred *cred = (struct cred *) current->cred;
|
||||
struct task_smack *tsp;
|
||||
|
||||
if (!security_module_enable("smack"))
|
||||
return 0;
|
||||
|
||||
smack_inode_cache = KMEM_CACHE(inode_smack, 0);
|
||||
if (!smack_inode_cache)
|
||||
return -ENOMEM;
|
||||
|
||||
tsp = new_task_smack(&smack_known_floor, &smack_known_floor,
|
||||
GFP_KERNEL);
|
||||
if (tsp == NULL) {
|
||||
kmem_cache_destroy(smack_inode_cache);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/*
|
||||
* Set the security state for the initial task.
|
||||
*/
|
||||
tsp = smack_cred(cred);
|
||||
init_task_smack(tsp, &smack_known_floor, &smack_known_floor);
|
||||
|
||||
/*
|
||||
* Register with LSM
|
||||
*/
|
||||
security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
|
||||
smack_enabled = 1;
|
||||
|
||||
pr_info("Smack: Initializing.\n");
|
||||
@ -4789,20 +4693,9 @@ static __init int smack_init(void)
|
||||
pr_info("Smack: IPv6 Netfilter enabled.\n");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set the security state for the initial task.
|
||||
*/
|
||||
cred = (struct cred *) current->cred;
|
||||
cred->security = tsp;
|
||||
|
||||
/* initialize the smack_known_list */
|
||||
init_smack_known_list();
|
||||
|
||||
/*
|
||||
* Register with LSM
|
||||
*/
|
||||
security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4812,5 +4705,7 @@ static __init int smack_init(void)
|
||||
*/
|
||||
DEFINE_LSM(smack) = {
|
||||
.name = "smack",
|
||||
.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
|
||||
.blobs = &smack_blob_sizes,
|
||||
.init = smack_init,
|
||||
};
|
||||
|
@ -2208,14 +2208,14 @@ static const struct file_operations smk_logging_ops = {
|
||||
|
||||
static void *load_self_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
|
||||
return smk_seq_start(s, pos, &tsp->smk_rules);
|
||||
}
|
||||
|
||||
static void *load_self_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
|
||||
return smk_seq_next(s, v, pos, &tsp->smk_rules);
|
||||
}
|
||||
@ -2262,7 +2262,7 @@ static int smk_open_load_self(struct inode *inode, struct file *file)
|
||||
static ssize_t smk_write_load_self(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
|
||||
return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules,
|
||||
&tsp->smk_rules_lock, SMK_FIXED24_FMT);
|
||||
@ -2414,14 +2414,14 @@ static const struct file_operations smk_load2_ops = {
|
||||
|
||||
static void *load_self2_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
|
||||
return smk_seq_start(s, pos, &tsp->smk_rules);
|
||||
}
|
||||
|
||||
static void *load_self2_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
|
||||
return smk_seq_next(s, v, pos, &tsp->smk_rules);
|
||||
}
|
||||
@ -2467,7 +2467,7 @@ static int smk_open_load_self2(struct inode *inode, struct file *file)
|
||||
static ssize_t smk_write_load_self2(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
|
||||
return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules,
|
||||
&tsp->smk_rules_lock, SMK_LONG_FMT);
|
||||
@ -2681,14 +2681,14 @@ static const struct file_operations smk_syslog_ops = {
|
||||
|
||||
static void *relabel_self_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
|
||||
return smk_seq_start(s, pos, &tsp->smk_relabel);
|
||||
}
|
||||
|
||||
static void *relabel_self_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
|
||||
return smk_seq_next(s, v, pos, &tsp->smk_relabel);
|
||||
}
|
||||
@ -2736,7 +2736,7 @@ static int smk_open_relabel_self(struct inode *inode, struct file *file)
|
||||
static ssize_t smk_write_relabel_self(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct task_smack *tsp = current_security();
|
||||
struct task_smack *tsp = smack_cred(current_cred());
|
||||
char *data;
|
||||
int rc;
|
||||
LIST_HEAD(list_tmp);
|
||||
|
@ -32,6 +32,7 @@ static char *tomoyo_print_bprm(struct linux_binprm *bprm,
|
||||
int argv_count = bprm->argc;
|
||||
int envp_count = bprm->envc;
|
||||
bool truncated = false;
|
||||
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
len = snprintf(buffer, tomoyo_buffer_len - 1, "argv[]={ ");
|
||||
@ -49,6 +50,7 @@ static char *tomoyo_print_bprm(struct linux_binprm *bprm,
|
||||
while (offset < PAGE_SIZE) {
|
||||
const char *kaddr = dump->data;
|
||||
const unsigned char c = kaddr[offset++];
|
||||
|
||||
if (cp == last_start)
|
||||
*cp++ = '"';
|
||||
if (cp >= buffer + tomoyo_buffer_len - 32) {
|
||||
@ -154,19 +156,18 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r)
|
||||
char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
|
||||
int pos;
|
||||
u8 i;
|
||||
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
|
||||
tomoyo_convert_time(ktime_get_real_seconds(), &stamp);
|
||||
|
||||
pos = snprintf(buffer, tomoyo_buffer_len - 1,
|
||||
"#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s "
|
||||
"granted=%s (global-pid=%u) task={ pid=%u ppid=%u "
|
||||
"uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u "
|
||||
"fsuid=%u fsgid=%u }", stamp.year, stamp.month,
|
||||
stamp.day, stamp.hour, stamp.min, stamp.sec, r->profile,
|
||||
tomoyo_mode[r->mode], tomoyo_yesno(r->granted), gpid,
|
||||
tomoyo_sys_getpid(), tomoyo_sys_getppid(),
|
||||
"#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s granted=%s (global-pid=%u) task={ pid=%u ppid=%u uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }",
|
||||
stamp.year, stamp.month, stamp.day, stamp.hour,
|
||||
stamp.min, stamp.sec, r->profile, tomoyo_mode[r->mode],
|
||||
tomoyo_yesno(r->granted), gpid, tomoyo_sys_getpid(),
|
||||
tomoyo_sys_getppid(),
|
||||
from_kuid(&init_user_ns, current_uid()),
|
||||
from_kgid(&init_user_ns, current_gid()),
|
||||
from_kuid(&init_user_ns, current_euid()),
|
||||
@ -185,6 +186,7 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r)
|
||||
struct tomoyo_mini_stat *stat;
|
||||
unsigned int dev;
|
||||
umode_t mode;
|
||||
|
||||
if (!obj->stat_valid[i])
|
||||
continue;
|
||||
stat = &obj->stat[i];
|
||||
@ -193,8 +195,8 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r)
|
||||
if (i & 1) {
|
||||
pos += snprintf(buffer + pos,
|
||||
tomoyo_buffer_len - 1 - pos,
|
||||
" path%u.parent={ uid=%u gid=%u "
|
||||
"ino=%lu perm=0%o }", (i >> 1) + 1,
|
||||
" path%u.parent={ uid=%u gid=%u ino=%lu perm=0%o }",
|
||||
(i >> 1) + 1,
|
||||
from_kuid(&init_user_ns, stat->uid),
|
||||
from_kgid(&init_user_ns, stat->gid),
|
||||
(unsigned long)stat->ino,
|
||||
@ -202,8 +204,8 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r)
|
||||
continue;
|
||||
}
|
||||
pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
|
||||
" path%u={ uid=%u gid=%u ino=%lu major=%u"
|
||||
" minor=%u perm=0%o type=%s", (i >> 1) + 1,
|
||||
" path%u={ uid=%u gid=%u ino=%lu major=%u minor=%u perm=0%o type=%s",
|
||||
(i >> 1) + 1,
|
||||
from_kuid(&init_user_ns, stat->uid),
|
||||
from_kgid(&init_user_ns, stat->gid),
|
||||
(unsigned long)stat->ino,
|
||||
@ -249,6 +251,7 @@ char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
|
||||
const char *symlink = NULL;
|
||||
int pos;
|
||||
const char *domainname = r->domain->domainname->name;
|
||||
|
||||
header = tomoyo_print_header(r);
|
||||
if (!header)
|
||||
return NULL;
|
||||
@ -256,6 +259,7 @@ char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
|
||||
len += strlen(domainname) + strlen(header) + 10;
|
||||
if (r->ee) {
|
||||
struct file *file = r->ee->bprm->file;
|
||||
|
||||
realpath = tomoyo_realpath_from_path(&file->f_path);
|
||||
bprm_info = tomoyo_print_bprm(r->ee->bprm, &r->ee->dump);
|
||||
if (!realpath || !bprm_info)
|
||||
@ -275,6 +279,7 @@ char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
|
||||
pos = snprintf(buf, len, "%s", header);
|
||||
if (realpath) {
|
||||
struct linux_binprm *bprm = r->ee->bprm;
|
||||
|
||||
pos += snprintf(buf + pos, len - pos,
|
||||
" exec={ realpath=\"%s\" argc=%d envc=%d %s }",
|
||||
realpath, bprm->argc, bprm->envc, bprm_info);
|
||||
@ -328,6 +333,7 @@ static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
|
||||
const u8 category = tomoyo_index2category[index] +
|
||||
TOMOYO_MAX_MAC_INDEX;
|
||||
struct tomoyo_profile *p;
|
||||
|
||||
if (!tomoyo_policy_loaded)
|
||||
return false;
|
||||
p = tomoyo_profile(ns, profile);
|
||||
@ -362,6 +368,7 @@ void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
|
||||
char *buf;
|
||||
struct tomoyo_log *entry;
|
||||
bool quota_exceeded = false;
|
||||
|
||||
if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type,
|
||||
r->matched_acl, r->granted))
|
||||
goto out;
|
||||
@ -413,6 +420,7 @@ void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int len;
|
||||
|
||||
va_start(args, fmt);
|
||||
len = vsnprintf((char *) &len, 1, fmt, args) + 1;
|
||||
va_end(args);
|
||||
@ -431,6 +439,7 @@ void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...)
|
||||
void tomoyo_read_log(struct tomoyo_io_buffer *head)
|
||||
{
|
||||
struct tomoyo_log *ptr = NULL;
|
||||
|
||||
if (head->r.w_pos)
|
||||
return;
|
||||
kfree(head->read_buf);
|
||||
|
@ -197,6 +197,7 @@ static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
const int pos = strlen(buffer);
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buffer + pos, len - pos - 1, fmt, args);
|
||||
va_end(args);
|
||||
@ -214,6 +215,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head)
|
||||
while (head->r.w_pos) {
|
||||
const char *w = head->r.w[0];
|
||||
size_t len = strlen(w);
|
||||
|
||||
if (len) {
|
||||
if (len > head->read_user_buf_avail)
|
||||
len = head->read_user_buf_avail;
|
||||
@ -279,6 +281,7 @@ static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt,
|
||||
size_t len;
|
||||
size_t pos = head->r.avail;
|
||||
int size = head->readbuf_size - pos;
|
||||
|
||||
if (size <= 0)
|
||||
return;
|
||||
va_start(args, fmt);
|
||||
@ -344,13 +347,14 @@ static bool tomoyo_namespace_enabled;
|
||||
void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns)
|
||||
{
|
||||
unsigned int idx;
|
||||
|
||||
for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++)
|
||||
INIT_LIST_HEAD(&ns->acl_group[idx]);
|
||||
for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++)
|
||||
INIT_LIST_HEAD(&ns->group_list[idx]);
|
||||
for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
|
||||
INIT_LIST_HEAD(&ns->policy_list[idx]);
|
||||
ns->profile_version = 20110903;
|
||||
ns->profile_version = 20150505;
|
||||
tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list);
|
||||
list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list);
|
||||
}
|
||||
@ -433,6 +437,7 @@ static void tomoyo_print_number_union_nospace
|
||||
u8 min_type = ptr->value_type[0];
|
||||
const u8 max_type = ptr->value_type[1];
|
||||
char buffer[128];
|
||||
|
||||
buffer[0] = '\0';
|
||||
for (i = 0; i < 2; i++) {
|
||||
switch (min_type) {
|
||||
@ -487,6 +492,7 @@ static struct tomoyo_profile *tomoyo_assign_profile
|
||||
{
|
||||
struct tomoyo_profile *ptr;
|
||||
struct tomoyo_profile *entry;
|
||||
|
||||
if (profile >= TOMOYO_MAX_PROFILES)
|
||||
return NULL;
|
||||
ptr = ns->profile_ptr[profile];
|
||||
@ -530,6 +536,7 @@ struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns,
|
||||
{
|
||||
static struct tomoyo_profile tomoyo_null_profile;
|
||||
struct tomoyo_profile *ptr = ns->profile_ptr[profile];
|
||||
|
||||
if (!ptr)
|
||||
ptr = &tomoyo_null_profile;
|
||||
return ptr;
|
||||
@ -546,6 +553,7 @@ struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns,
|
||||
static s8 tomoyo_find_yesno(const char *string, const char *find)
|
||||
{
|
||||
const char *cp = strstr(string, find);
|
||||
|
||||
if (cp) {
|
||||
cp += strlen(find);
|
||||
if (!strncmp(cp, "=yes", 4))
|
||||
@ -569,6 +577,7 @@ static void tomoyo_set_uint(unsigned int *i, const char *string,
|
||||
const char *find)
|
||||
{
|
||||
const char *cp = strstr(string, find);
|
||||
|
||||
if (cp)
|
||||
sscanf(cp + strlen(find), "=%u", i);
|
||||
}
|
||||
@ -587,6 +596,7 @@ static int tomoyo_set_mode(char *name, const char *value,
|
||||
{
|
||||
u8 i;
|
||||
u8 config;
|
||||
|
||||
if (!strcmp(name, "CONFIG")) {
|
||||
i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX;
|
||||
config = profile->default_config;
|
||||
@ -595,10 +605,12 @@ static int tomoyo_set_mode(char *name, const char *value,
|
||||
for (i = 0; i < TOMOYO_MAX_MAC_INDEX
|
||||
+ TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) {
|
||||
int len = 0;
|
||||
|
||||
if (i < TOMOYO_MAX_MAC_INDEX) {
|
||||
const u8 c = tomoyo_index2category[i];
|
||||
const char *category =
|
||||
tomoyo_category_keywords[c];
|
||||
|
||||
len = strlen(category);
|
||||
if (strncmp(name, category, len) ||
|
||||
name[len++] != ':' || name[len++] != ':')
|
||||
@ -618,6 +630,7 @@ static int tomoyo_set_mode(char *name, const char *value,
|
||||
config = TOMOYO_CONFIG_USE_DEFAULT;
|
||||
} else {
|
||||
u8 mode;
|
||||
|
||||
for (mode = 0; mode < 4; mode++)
|
||||
if (strstr(value, tomoyo_mode[mode]))
|
||||
/*
|
||||
@ -664,6 +677,7 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
|
||||
unsigned int i;
|
||||
char *cp;
|
||||
struct tomoyo_profile *profile;
|
||||
|
||||
if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->profile_version)
|
||||
== 1)
|
||||
return 0;
|
||||
@ -683,6 +697,7 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
|
||||
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);
|
||||
@ -732,6 +747,7 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
|
||||
struct tomoyo_policy_namespace *ns =
|
||||
container_of(head->r.ns, typeof(*ns), namespace_list);
|
||||
const struct tomoyo_profile *profile;
|
||||
|
||||
if (head->r.eof)
|
||||
return;
|
||||
next:
|
||||
@ -760,6 +776,7 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
|
||||
u8 i;
|
||||
const struct tomoyo_path_info *comment =
|
||||
profile->comment;
|
||||
|
||||
tomoyo_print_namespace(head);
|
||||
tomoyo_io_printf(head, "%u-COMMENT=", index);
|
||||
tomoyo_set_string(head, comment ? comment->name : "");
|
||||
@ -788,6 +805,7 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
|
||||
+ TOMOYO_MAX_MAC_CATEGORY_INDEX; head->r.bit++) {
|
||||
const u8 i = head->r.bit;
|
||||
const u8 config = profile->config[i];
|
||||
|
||||
if (config == TOMOYO_CONFIG_USE_DEFAULT)
|
||||
continue;
|
||||
tomoyo_print_namespace(head);
|
||||
@ -847,10 +865,10 @@ static int tomoyo_update_manager_entry(const char *manager,
|
||||
struct tomoyo_acl_param param = {
|
||||
/* .ns = &tomoyo_kernel_namespace, */
|
||||
.is_delete = is_delete,
|
||||
.list = &tomoyo_kernel_namespace.
|
||||
policy_list[TOMOYO_ID_MANAGER],
|
||||
.list = &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER],
|
||||
};
|
||||
int error = is_delete ? -ENOENT : -ENOMEM;
|
||||
|
||||
if (!tomoyo_correct_domain(manager) &&
|
||||
!tomoyo_correct_word(manager))
|
||||
return -EINVAL;
|
||||
@ -894,10 +912,10 @@ static void tomoyo_read_manager(struct tomoyo_io_buffer *head)
|
||||
{
|
||||
if (head->r.eof)
|
||||
return;
|
||||
list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace.
|
||||
policy_list[TOMOYO_ID_MANAGER]) {
|
||||
list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER]) {
|
||||
struct tomoyo_manager *ptr =
|
||||
list_entry(head->r.acl, typeof(*ptr), head.list);
|
||||
|
||||
if (ptr->head.is_deleted)
|
||||
continue;
|
||||
if (!tomoyo_flush(head))
|
||||
@ -933,8 +951,7 @@ static bool tomoyo_manager(void)
|
||||
exe = tomoyo_get_exe();
|
||||
if (!exe)
|
||||
return false;
|
||||
list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.
|
||||
policy_list[TOMOYO_ID_MANAGER], head.list) {
|
||||
list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER], head.list) {
|
||||
if (!ptr->head.is_deleted &&
|
||||
(!tomoyo_pathcmp(domainname, ptr->manager) ||
|
||||
!strcmp(exe, ptr->manager->name))) {
|
||||
@ -945,9 +962,10 @@ static bool tomoyo_manager(void)
|
||||
if (!found) { /* Reduce error messages. */
|
||||
static pid_t last_pid;
|
||||
const pid_t pid = current->pid;
|
||||
|
||||
if (last_pid != pid) {
|
||||
printk(KERN_WARNING "%s ( %s ) is not permitted to "
|
||||
"update policies.\n", domainname->name, exe);
|
||||
pr_warn("%s ( %s ) is not permitted to update policies.\n",
|
||||
domainname->name, exe);
|
||||
last_pid = pid;
|
||||
}
|
||||
}
|
||||
@ -974,19 +992,21 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head,
|
||||
unsigned int pid;
|
||||
struct tomoyo_domain_info *domain = NULL;
|
||||
bool global_pid = false;
|
||||
|
||||
if (strncmp(data, "select ", 7))
|
||||
return false;
|
||||
data += 7;
|
||||
if (sscanf(data, "pid=%u", &pid) == 1 ||
|
||||
(global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {
|
||||
struct task_struct *p;
|
||||
|
||||
rcu_read_lock();
|
||||
if (global_pid)
|
||||
p = find_task_by_pid_ns(pid, &init_pid_ns);
|
||||
else
|
||||
p = find_task_by_vpid(pid);
|
||||
if (p)
|
||||
domain = tomoyo_real_domain(p);
|
||||
domain = tomoyo_task(p)->domain_info;
|
||||
rcu_read_unlock();
|
||||
} else if (!strncmp(data, "domain=", 7)) {
|
||||
if (tomoyo_domain_def(data + 7))
|
||||
@ -1024,6 +1044,7 @@ static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a,
|
||||
{
|
||||
const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head);
|
||||
const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head);
|
||||
|
||||
return p1->domainname == p2->domainname;
|
||||
}
|
||||
|
||||
@ -1039,11 +1060,13 @@ static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a,
|
||||
static int tomoyo_write_task(struct tomoyo_acl_param *param)
|
||||
{
|
||||
int error = -EINVAL;
|
||||
|
||||
if (tomoyo_str_starts(¶m->data, "manual_domain_transition ")) {
|
||||
struct tomoyo_task_acl e = {
|
||||
.head.type = TOMOYO_TYPE_MANUAL_TASK_ACL,
|
||||
.domainname = tomoyo_get_domainname(param),
|
||||
};
|
||||
|
||||
if (e.domainname)
|
||||
error = tomoyo_update_domain(&e.head, sizeof(e), param,
|
||||
tomoyo_same_task_acl,
|
||||
@ -1110,7 +1133,7 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns,
|
||||
};
|
||||
static const struct {
|
||||
const char *keyword;
|
||||
int (*write) (struct tomoyo_acl_param *);
|
||||
int (*write)(struct tomoyo_acl_param *param);
|
||||
} tomoyo_callback[5] = {
|
||||
{ "file ", tomoyo_write_file },
|
||||
{ "network inet ", tomoyo_write_inet_network },
|
||||
@ -1151,9 +1174,11 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head)
|
||||
struct tomoyo_domain_info *domain = head->w.domain;
|
||||
const bool is_delete = head->w.is_delete;
|
||||
bool is_select = !is_delete && tomoyo_str_starts(&data, "select ");
|
||||
unsigned int profile;
|
||||
unsigned int idx;
|
||||
|
||||
if (*data == '<') {
|
||||
int ret = 0;
|
||||
|
||||
domain = NULL;
|
||||
if (is_delete)
|
||||
ret = tomoyo_delete_domain(data);
|
||||
@ -1167,23 +1192,27 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head)
|
||||
if (!domain)
|
||||
return -EINVAL;
|
||||
ns = domain->ns;
|
||||
if (sscanf(data, "use_profile %u", &profile) == 1
|
||||
&& profile < TOMOYO_MAX_PROFILES) {
|
||||
if (!tomoyo_policy_loaded || ns->profile_ptr[profile])
|
||||
domain->profile = (u8) profile;
|
||||
return 0;
|
||||
}
|
||||
if (sscanf(data, "use_group %u\n", &profile) == 1
|
||||
&& profile < TOMOYO_MAX_ACL_GROUPS) {
|
||||
if (sscanf(data, "use_profile %u", &idx) == 1
|
||||
&& idx < TOMOYO_MAX_PROFILES) {
|
||||
if (!tomoyo_policy_loaded || ns->profile_ptr[idx])
|
||||
if (!is_delete)
|
||||
domain->group = (u8) profile;
|
||||
domain->profile = (u8) idx;
|
||||
return 0;
|
||||
}
|
||||
for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) {
|
||||
const char *cp = tomoyo_dif[profile];
|
||||
if (sscanf(data, "use_group %u\n", &idx) == 1
|
||||
&& idx < TOMOYO_MAX_ACL_GROUPS) {
|
||||
if (!is_delete)
|
||||
set_bit(idx, domain->group);
|
||||
else
|
||||
clear_bit(idx, domain->group);
|
||||
return 0;
|
||||
}
|
||||
for (idx = 0; idx < TOMOYO_MAX_DOMAIN_INFO_FLAGS; idx++) {
|
||||
const char *cp = tomoyo_dif[idx];
|
||||
|
||||
if (strncmp(data, cp, strlen(cp) - 1))
|
||||
continue;
|
||||
domain->flags[profile] = !is_delete;
|
||||
domain->flags[idx] = !is_delete;
|
||||
return 0;
|
||||
}
|
||||
return tomoyo_write_domain2(ns, &domain->acl_info_list, data,
|
||||
@ -1225,9 +1254,11 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
|
||||
const struct tomoyo_envp *envp =
|
||||
(typeof(envp)) (argv + cond->argc);
|
||||
u16 skip;
|
||||
|
||||
for (skip = 0; skip < head->r.cond_index; skip++) {
|
||||
const u8 left = condp->left;
|
||||
const u8 right = condp->right;
|
||||
|
||||
condp++;
|
||||
switch (left) {
|
||||
case TOMOYO_ARGV_ENTRY:
|
||||
@ -1253,6 +1284,7 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
|
||||
const u8 match = condp->equals;
|
||||
const u8 left = condp->left;
|
||||
const u8 right = condp->right;
|
||||
|
||||
if (!tomoyo_flush(head))
|
||||
return false;
|
||||
condp++;
|
||||
@ -1262,8 +1294,7 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
|
||||
case TOMOYO_ARGV_ENTRY:
|
||||
tomoyo_io_printf(head,
|
||||
"exec.argv[%lu]%s=\"",
|
||||
argv->index, argv->
|
||||
is_not ? "!" : "");
|
||||
argv->index, argv->is_not ? "!" : "");
|
||||
tomoyo_set_string(head,
|
||||
argv->value->name);
|
||||
tomoyo_set_string(head, "\"");
|
||||
@ -1274,12 +1305,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
|
||||
"exec.envp[\"");
|
||||
tomoyo_set_string(head,
|
||||
envp->name->name);
|
||||
tomoyo_io_printf(head, "\"]%s=", envp->
|
||||
is_not ? "!" : "");
|
||||
tomoyo_io_printf(head, "\"]%s=", envp->is_not ? "!" : "");
|
||||
if (envp->value) {
|
||||
tomoyo_set_string(head, "\"");
|
||||
tomoyo_set_string(head, envp->
|
||||
value->name);
|
||||
tomoyo_set_string(head, envp->value->name);
|
||||
tomoyo_set_string(head, "\"");
|
||||
} else {
|
||||
tomoyo_set_string(head,
|
||||
@ -1375,6 +1404,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
|
||||
struct tomoyo_path_acl *ptr =
|
||||
container_of(acl, typeof(*ptr), head);
|
||||
const u16 perm = ptr->perm;
|
||||
|
||||
for (bit = 0; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
|
||||
if (!(perm & (1 << bit)))
|
||||
continue;
|
||||
@ -1395,6 +1425,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
|
||||
} else if (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) {
|
||||
struct tomoyo_task_acl *ptr =
|
||||
container_of(acl, typeof(*ptr), head);
|
||||
|
||||
tomoyo_set_group(head, "task ");
|
||||
tomoyo_set_string(head, "manual_domain_transition ");
|
||||
tomoyo_set_string(head, ptr->domainname->name);
|
||||
@ -1404,6 +1435,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
|
||||
struct tomoyo_path2_acl *ptr =
|
||||
container_of(acl, typeof(*ptr), head);
|
||||
const u8 perm = ptr->perm;
|
||||
|
||||
for (bit = 0; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) {
|
||||
if (!(perm & (1 << bit)))
|
||||
continue;
|
||||
@ -1424,6 +1456,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
|
||||
struct tomoyo_path_number_acl *ptr =
|
||||
container_of(acl, typeof(*ptr), head);
|
||||
const u8 perm = ptr->perm;
|
||||
|
||||
for (bit = 0; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; bit++) {
|
||||
if (!(perm & (1 << bit)))
|
||||
continue;
|
||||
@ -1444,6 +1477,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
|
||||
struct tomoyo_mkdev_acl *ptr =
|
||||
container_of(acl, typeof(*ptr), head);
|
||||
const u8 perm = ptr->perm;
|
||||
|
||||
for (bit = 0; bit < TOMOYO_MAX_MKDEV_OPERATION; bit++) {
|
||||
if (!(perm & (1 << bit)))
|
||||
continue;
|
||||
@ -1490,6 +1524,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
|
||||
->name);
|
||||
} else {
|
||||
char buf[128];
|
||||
|
||||
tomoyo_print_ip(buf, sizeof(buf), &ptr->address);
|
||||
tomoyo_io_printf(head, "%s", buf);
|
||||
}
|
||||
@ -1519,6 +1554,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
|
||||
} else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
|
||||
struct tomoyo_mount_acl *ptr =
|
||||
container_of(acl, typeof(*ptr), head);
|
||||
|
||||
tomoyo_set_group(head, "file mount");
|
||||
tomoyo_print_name_union(head, &ptr->dev_name);
|
||||
tomoyo_print_name_union(head, &ptr->dir_name);
|
||||
@ -1562,6 +1598,7 @@ static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head,
|
||||
list_for_each_cookie(head->r.acl, list) {
|
||||
struct tomoyo_acl_info *ptr =
|
||||
list_entry(head->r.acl, typeof(*ptr), list);
|
||||
|
||||
if (!tomoyo_print_entry(head, ptr))
|
||||
return false;
|
||||
}
|
||||
@ -1583,8 +1620,9 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
|
||||
list_for_each_cookie(head->r.domain, &tomoyo_domain_list) {
|
||||
struct tomoyo_domain_info *domain =
|
||||
list_entry(head->r.domain, typeof(*domain), list);
|
||||
switch (head->r.step) {
|
||||
u8 i;
|
||||
|
||||
switch (head->r.step) {
|
||||
case 0:
|
||||
if (domain->is_deleted &&
|
||||
!head->r.print_this_domain_only)
|
||||
@ -1594,22 +1632,33 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
|
||||
tomoyo_set_lf(head);
|
||||
tomoyo_io_printf(head, "use_profile %u\n",
|
||||
domain->profile);
|
||||
tomoyo_io_printf(head, "use_group %u\n",
|
||||
domain->group);
|
||||
for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++)
|
||||
if (domain->flags[i])
|
||||
tomoyo_set_string(head, tomoyo_dif[i]);
|
||||
head->r.index = 0;
|
||||
head->r.step++;
|
||||
/* fall through */
|
||||
case 1:
|
||||
while (head->r.index < TOMOYO_MAX_ACL_GROUPS) {
|
||||
i = head->r.index++;
|
||||
if (!test_bit(i, domain->group))
|
||||
continue;
|
||||
tomoyo_io_printf(head, "use_group %u\n", i);
|
||||
if (!tomoyo_flush(head))
|
||||
return;
|
||||
}
|
||||
head->r.index = 0;
|
||||
head->r.step++;
|
||||
tomoyo_set_lf(head);
|
||||
/* fall through */
|
||||
case 1:
|
||||
case 2:
|
||||
if (!tomoyo_read_domain2(head, &domain->acl_info_list))
|
||||
return;
|
||||
head->r.step++;
|
||||
if (!tomoyo_set_lf(head))
|
||||
return;
|
||||
/* fall through */
|
||||
case 2:
|
||||
case 3:
|
||||
head->r.step = 0;
|
||||
if (head->r.print_this_domain_only)
|
||||
goto done;
|
||||
@ -1668,7 +1717,7 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head)
|
||||
else
|
||||
p = find_task_by_vpid(pid);
|
||||
if (p)
|
||||
domain = tomoyo_real_domain(p);
|
||||
domain = tomoyo_task(p)->domain_info;
|
||||
rcu_read_unlock();
|
||||
if (!domain)
|
||||
return;
|
||||
@ -1711,6 +1760,7 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
|
||||
.data = head->write_buf,
|
||||
};
|
||||
u8 i;
|
||||
|
||||
if (tomoyo_str_starts(¶m.data, "aggregator "))
|
||||
return tomoyo_write_aggregator(¶m);
|
||||
for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++)
|
||||
@ -1722,6 +1772,7 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
|
||||
if (tomoyo_str_starts(¶m.data, "acl_group ")) {
|
||||
unsigned int group;
|
||||
char *data;
|
||||
|
||||
group = simple_strtoul(param.data, &data, 10);
|
||||
if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ')
|
||||
return tomoyo_write_domain2
|
||||
@ -1746,12 +1797,15 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
|
||||
struct tomoyo_policy_namespace *ns =
|
||||
container_of(head->r.ns, typeof(*ns), namespace_list);
|
||||
struct list_head *list = &ns->group_list[idx];
|
||||
|
||||
list_for_each_cookie(head->r.group, list) {
|
||||
struct tomoyo_group *group =
|
||||
list_entry(head->r.group, typeof(*group), head.list);
|
||||
|
||||
list_for_each_cookie(head->r.acl, &group->member_list) {
|
||||
struct tomoyo_acl_head *ptr =
|
||||
list_entry(head->r.acl, typeof(*ptr), list);
|
||||
|
||||
if (ptr->is_deleted)
|
||||
continue;
|
||||
if (!tomoyo_flush(head))
|
||||
@ -1771,10 +1825,10 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
|
||||
head)->number);
|
||||
} else if (idx == TOMOYO_ADDRESS_GROUP) {
|
||||
char buffer[128];
|
||||
|
||||
struct tomoyo_address_group *member =
|
||||
container_of(ptr, typeof(*member),
|
||||
head);
|
||||
|
||||
tomoyo_print_ip(buffer, sizeof(buffer),
|
||||
&member->address);
|
||||
tomoyo_io_printf(head, " %s", buffer);
|
||||
@ -1802,6 +1856,7 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
|
||||
struct tomoyo_policy_namespace *ns =
|
||||
container_of(head->r.ns, typeof(*ns), namespace_list);
|
||||
struct list_head *list = &ns->policy_list[idx];
|
||||
|
||||
list_for_each_cookie(head->r.acl, list) {
|
||||
struct tomoyo_acl_head *acl =
|
||||
container_of(head->r.acl, typeof(*acl), list);
|
||||
@ -1814,6 +1869,7 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
|
||||
{
|
||||
struct tomoyo_transition_control *ptr =
|
||||
container_of(acl, typeof(*ptr), head);
|
||||
|
||||
tomoyo_print_namespace(head);
|
||||
tomoyo_set_string(head, tomoyo_transition_type
|
||||
[ptr->type]);
|
||||
@ -1829,6 +1885,7 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
|
||||
{
|
||||
struct tomoyo_aggregator *ptr =
|
||||
container_of(acl, typeof(*ptr), head);
|
||||
|
||||
tomoyo_print_namespace(head);
|
||||
tomoyo_set_string(head, "aggregator ");
|
||||
tomoyo_set_string(head,
|
||||
@ -1858,6 +1915,7 @@ static void tomoyo_read_exception(struct tomoyo_io_buffer *head)
|
||||
{
|
||||
struct tomoyo_policy_namespace *ns =
|
||||
container_of(head->r.ns, typeof(*ns), namespace_list);
|
||||
|
||||
if (head->r.eof)
|
||||
return;
|
||||
while (head->r.step < TOMOYO_MAX_POLICY &&
|
||||
@ -1921,6 +1979,7 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
|
||||
static int tomoyo_truncate(char *str)
|
||||
{
|
||||
char *start = str;
|
||||
|
||||
while (*(unsigned char *) str > (unsigned char) ' ')
|
||||
str++;
|
||||
*str = '\0';
|
||||
@ -1943,6 +2002,7 @@ static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
|
||||
char *symlink = NULL;
|
||||
char *cp = strchr(header, '\n');
|
||||
int len;
|
||||
|
||||
if (!cp)
|
||||
return;
|
||||
cp = strchr(cp + 1, '\n');
|
||||
@ -2002,6 +2062,7 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
|
||||
static unsigned int tomoyo_serial;
|
||||
struct tomoyo_query entry = { };
|
||||
bool quota_exceeded = false;
|
||||
|
||||
va_start(args, fmt);
|
||||
len = vsnprintf((char *) &len, 1, fmt, args) + 1;
|
||||
va_end(args);
|
||||
@ -2063,7 +2124,6 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
|
||||
(tomoyo_answer_wait, entry.answer ||
|
||||
!atomic_read(&tomoyo_query_observers), HZ))
|
||||
break;
|
||||
else
|
||||
entry.timer++;
|
||||
}
|
||||
spin_lock(&tomoyo_query_list_lock);
|
||||
@ -2100,6 +2160,7 @@ static struct tomoyo_domain_info *tomoyo_find_domain_by_qid
|
||||
{
|
||||
struct tomoyo_query *ptr;
|
||||
struct tomoyo_domain_info *domain = NULL;
|
||||
|
||||
spin_lock(&tomoyo_query_list_lock);
|
||||
list_for_each_entry(ptr, &tomoyo_query_list, list) {
|
||||
if (ptr->serial != serial)
|
||||
@ -2142,15 +2203,15 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head)
|
||||
unsigned int pos = 0;
|
||||
size_t len = 0;
|
||||
char *buf;
|
||||
|
||||
if (head->r.w_pos)
|
||||
return;
|
||||
if (head->read_buf) {
|
||||
kfree(head->read_buf);
|
||||
head->read_buf = NULL;
|
||||
}
|
||||
spin_lock(&tomoyo_query_list_lock);
|
||||
list_for_each(tmp, &tomoyo_query_list) {
|
||||
struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list);
|
||||
|
||||
if (pos++ != head->r.query_index)
|
||||
continue;
|
||||
len = ptr->query_len;
|
||||
@ -2168,6 +2229,7 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head)
|
||||
spin_lock(&tomoyo_query_list_lock);
|
||||
list_for_each(tmp, &tomoyo_query_list) {
|
||||
struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list);
|
||||
|
||||
if (pos++ != head->r.query_index)
|
||||
continue;
|
||||
/*
|
||||
@ -2202,9 +2264,11 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head)
|
||||
struct list_head *tmp;
|
||||
unsigned int serial;
|
||||
unsigned int answer;
|
||||
|
||||
spin_lock(&tomoyo_query_list_lock);
|
||||
list_for_each(tmp, &tomoyo_query_list) {
|
||||
struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list);
|
||||
|
||||
ptr->timer = 0;
|
||||
}
|
||||
spin_unlock(&tomoyo_query_list_lock);
|
||||
@ -2213,6 +2277,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head)
|
||||
spin_lock(&tomoyo_query_list_lock);
|
||||
list_for_each(tmp, &tomoyo_query_list) {
|
||||
struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list);
|
||||
|
||||
if (ptr->serial != serial)
|
||||
continue;
|
||||
ptr->answer = answer;
|
||||
@ -2235,7 +2300,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head)
|
||||
static void tomoyo_read_version(struct tomoyo_io_buffer *head)
|
||||
{
|
||||
if (!head->r.eof) {
|
||||
tomoyo_io_printf(head, "2.5.0");
|
||||
tomoyo_io_printf(head, "2.6.0");
|
||||
head->r.eof = true;
|
||||
}
|
||||
}
|
||||
@ -2287,6 +2352,7 @@ static void tomoyo_read_stat(struct tomoyo_io_buffer *head)
|
||||
{
|
||||
u8 i;
|
||||
unsigned int total = 0;
|
||||
|
||||
if (head->r.eof)
|
||||
return;
|
||||
for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) {
|
||||
@ -2295,9 +2361,9 @@ static void tomoyo_read_stat(struct tomoyo_io_buffer *head)
|
||||
tomoyo_stat_updated[i]);
|
||||
if (tomoyo_stat_modified[i]) {
|
||||
struct tomoyo_time stamp;
|
||||
|
||||
tomoyo_convert_time(tomoyo_stat_modified[i], &stamp);
|
||||
tomoyo_io_printf(head, " (Last: %04u/%02u/%02u "
|
||||
"%02u:%02u:%02u)",
|
||||
tomoyo_io_printf(head, " (Last: %04u/%02u/%02u %02u:%02u:%02u)",
|
||||
stamp.year, stamp.month, stamp.day,
|
||||
stamp.hour, stamp.min, stamp.sec);
|
||||
}
|
||||
@ -2305,6 +2371,7 @@ static void tomoyo_read_stat(struct tomoyo_io_buffer *head)
|
||||
}
|
||||
for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) {
|
||||
unsigned int used = tomoyo_memory_used[i];
|
||||
|
||||
total += used;
|
||||
tomoyo_io_printf(head, "Memory used by %-22s %10u",
|
||||
tomoyo_memory_headers[i], used);
|
||||
@ -2329,6 +2396,7 @@ static int tomoyo_write_stat(struct tomoyo_io_buffer *head)
|
||||
{
|
||||
char *data = head->write_buf;
|
||||
u8 i;
|
||||
|
||||
if (tomoyo_str_starts(&data, "Memory used by "))
|
||||
for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++)
|
||||
if (tomoyo_str_starts(&data, tomoyo_memory_headers[i]))
|
||||
@ -2457,6 +2525,7 @@ int tomoyo_open_control(const u8 type, struct file *file)
|
||||
__poll_t tomoyo_poll_control(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct tomoyo_io_buffer *head = file->private_data;
|
||||
|
||||
if (head->poll)
|
||||
return head->poll(file, wait) | EPOLLOUT | EPOLLWRNORM;
|
||||
return EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM;
|
||||
@ -2472,6 +2541,7 @@ __poll_t tomoyo_poll_control(struct file *file, poll_table *wait)
|
||||
static inline void tomoyo_set_namespace_cursor(struct tomoyo_io_buffer *head)
|
||||
{
|
||||
struct list_head *ns;
|
||||
|
||||
if (head->type != TOMOYO_EXCEPTIONPOLICY &&
|
||||
head->type != TOMOYO_PROFILE)
|
||||
return;
|
||||
@ -2517,7 +2587,7 @@ ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
|
||||
int idx;
|
||||
|
||||
if (!head->read)
|
||||
return -ENOSYS;
|
||||
return -EINVAL;
|
||||
if (mutex_lock_interruptible(&head->io_sem))
|
||||
return -EINTR;
|
||||
head->read_user_buf = buffer;
|
||||
@ -2557,6 +2627,7 @@ static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line)
|
||||
head->type == TOMOYO_PROFILE) {
|
||||
if (*line == '<') {
|
||||
char *cp = strchr(line, ' ');
|
||||
|
||||
if (cp) {
|
||||
*cp++ = '\0';
|
||||
head->w.ns = tomoyo_assign_namespace(line);
|
||||
@ -2589,8 +2660,9 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
|
||||
size_t avail_len = buffer_len;
|
||||
char *cp0 = head->write_buf;
|
||||
int idx;
|
||||
|
||||
if (!head->write)
|
||||
return -ENOSYS;
|
||||
return -EINVAL;
|
||||
if (!access_ok(buffer, buffer_len))
|
||||
return -EFAULT;
|
||||
if (mutex_lock_interruptible(&head->io_sem))
|
||||
@ -2600,9 +2672,11 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
|
||||
/* Read a line and dispatch it to the policy handler. */
|
||||
while (avail_len > 0) {
|
||||
char c;
|
||||
|
||||
if (head->w.avail >= head->writebuf_size - 1) {
|
||||
const int len = head->writebuf_size * 2;
|
||||
char *cp = kzalloc(len, GFP_NOFS);
|
||||
|
||||
if (!cp) {
|
||||
error = -ENOMEM;
|
||||
break;
|
||||
@ -2701,30 +2775,32 @@ void tomoyo_check_profile(void)
|
||||
{
|
||||
struct tomoyo_domain_info *domain;
|
||||
const int idx = tomoyo_read_lock();
|
||||
|
||||
tomoyo_policy_loaded = true;
|
||||
printk(KERN_INFO "TOMOYO: 2.5.0\n");
|
||||
pr_info("TOMOYO: 2.6.0\n");
|
||||
list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
|
||||
const u8 profile = domain->profile;
|
||||
const struct tomoyo_policy_namespace *ns = domain->ns;
|
||||
if (ns->profile_version != 20110903)
|
||||
printk(KERN_ERR
|
||||
"Profile version %u is not supported.\n",
|
||||
struct tomoyo_policy_namespace *ns = domain->ns;
|
||||
|
||||
if (ns->profile_version == 20110903) {
|
||||
pr_info_once("Converting profile version from %u to %u.\n",
|
||||
20110903, 20150505);
|
||||
ns->profile_version = 20150505;
|
||||
}
|
||||
if (ns->profile_version != 20150505)
|
||||
pr_err("Profile version %u is not supported.\n",
|
||||
ns->profile_version);
|
||||
else if (!ns->profile_ptr[profile])
|
||||
printk(KERN_ERR
|
||||
"Profile %u (used by '%s') is not defined.\n",
|
||||
pr_err("Profile %u (used by '%s') is not defined.\n",
|
||||
profile, domain->domainname->name);
|
||||
else
|
||||
continue;
|
||||
printk(KERN_ERR
|
||||
"Userland tools for TOMOYO 2.5 must be installed and "
|
||||
"policy must be initialized.\n");
|
||||
printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.5/ "
|
||||
"for more information.\n");
|
||||
pr_err("Userland tools for TOMOYO 2.6 must be installed and policy must be initialized.\n");
|
||||
pr_err("Please see https://tomoyo.osdn.jp/2.6/ for more information.\n");
|
||||
panic("STOP!");
|
||||
}
|
||||
tomoyo_read_unlock(idx);
|
||||
printk(KERN_INFO "Mandatory Access Control activated.\n");
|
||||
pr_info("Mandatory Access Control activated.\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2743,9 +2819,11 @@ void __init tomoyo_load_builtin_policy(void)
|
||||
#include "builtin-policy.h"
|
||||
u8 i;
|
||||
const int idx = tomoyo_read_lock();
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
struct tomoyo_io_buffer head = { };
|
||||
char *start = "";
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
start = tomoyo_builtin_profile;
|
||||
@ -2775,6 +2853,7 @@ void __init tomoyo_load_builtin_policy(void)
|
||||
}
|
||||
while (1) {
|
||||
char *end = strchr(start, '\n');
|
||||
|
||||
if (!end)
|
||||
break;
|
||||
*end = '\0';
|
||||
|
@ -10,6 +10,8 @@
|
||||
#ifndef _SECURITY_TOMOYO_COMMON_H
|
||||
#define _SECURITY_TOMOYO_COMMON_H
|
||||
|
||||
#define pr_fmt(fmt) fmt
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mm.h>
|
||||
@ -29,6 +31,7 @@
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/un.h>
|
||||
#include <linux/lsm_hooks.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/af_unix.h>
|
||||
#include <net/ip.h>
|
||||
@ -681,11 +684,12 @@ struct tomoyo_domain_info {
|
||||
const struct tomoyo_path_info *domainname;
|
||||
/* Namespace for this domain. Never NULL. */
|
||||
struct tomoyo_policy_namespace *ns;
|
||||
/* Group numbers to use. */
|
||||
unsigned long group[TOMOYO_MAX_ACL_GROUPS / BITS_PER_LONG];
|
||||
u8 profile; /* Profile number to use. */
|
||||
u8 group; /* Group number to use. */
|
||||
bool is_deleted; /* Delete flag. */
|
||||
bool flags[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
|
||||
atomic_t users; /* Number of referring credentials. */
|
||||
atomic_t users; /* Number of referring tasks. */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -787,9 +791,9 @@ struct tomoyo_acl_param {
|
||||
* interfaces.
|
||||
*/
|
||||
struct tomoyo_io_buffer {
|
||||
void (*read) (struct tomoyo_io_buffer *);
|
||||
int (*write) (struct tomoyo_io_buffer *);
|
||||
__poll_t (*poll) (struct file *file, poll_table *wait);
|
||||
void (*read)(struct tomoyo_io_buffer *head);
|
||||
int (*write)(struct tomoyo_io_buffer *head);
|
||||
__poll_t (*poll)(struct file *file, poll_table *wait);
|
||||
/* Exclusive lock for this structure. */
|
||||
struct mutex io_sem;
|
||||
char __user *read_user_buf;
|
||||
@ -906,12 +910,18 @@ struct tomoyo_policy_namespace {
|
||||
struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS];
|
||||
/* List for connecting to tomoyo_namespace_list list. */
|
||||
struct list_head namespace_list;
|
||||
/* Profile version. Currently only 20110903 is defined. */
|
||||
/* Profile version. Currently only 20150505 is defined. */
|
||||
unsigned int profile_version;
|
||||
/* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */
|
||||
const char *name;
|
||||
};
|
||||
|
||||
/* Structure for "struct task_struct"->security. */
|
||||
struct tomoyo_task {
|
||||
struct tomoyo_domain_info *domain_info;
|
||||
struct tomoyo_domain_info *old_domain_info;
|
||||
};
|
||||
|
||||
/********** Function prototypes. **********/
|
||||
|
||||
bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address,
|
||||
@ -1020,6 +1030,7 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
|
||||
struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param);
|
||||
struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
|
||||
const bool transit);
|
||||
struct tomoyo_domain_info *tomoyo_domain(void);
|
||||
struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
|
||||
struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
|
||||
const u8 idx);
|
||||
@ -1034,7 +1045,7 @@ void *tomoyo_commit_ok(void *data, const unsigned int size);
|
||||
void __init tomoyo_load_builtin_policy(void);
|
||||
void __init tomoyo_mm_init(void);
|
||||
void tomoyo_check_acl(struct tomoyo_request_info *r,
|
||||
bool (*check_entry) (struct tomoyo_request_info *,
|
||||
bool (*check_entry)(struct tomoyo_request_info *,
|
||||
const struct tomoyo_acl_info *));
|
||||
void tomoyo_check_profile(void);
|
||||
void tomoyo_convert_time(time64_t time, struct tomoyo_time *stamp);
|
||||
@ -1062,6 +1073,7 @@ void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
|
||||
/********** External variable definitions. **********/
|
||||
|
||||
extern bool tomoyo_policy_loaded;
|
||||
extern int tomoyo_enabled;
|
||||
extern const char * const tomoyo_condition_keyword
|
||||
[TOMOYO_MAX_CONDITION_KEYWORD];
|
||||
extern const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
|
||||
@ -1085,6 +1097,7 @@ extern struct tomoyo_domain_info tomoyo_kernel_domain;
|
||||
extern struct tomoyo_policy_namespace tomoyo_kernel_namespace;
|
||||
extern unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
|
||||
extern unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
|
||||
extern struct lsm_blob_sizes tomoyo_blob_sizes;
|
||||
|
||||
/********** Inlined functions. **********/
|
||||
|
||||
@ -1121,6 +1134,7 @@ static inline void tomoyo_read_unlock(int idx)
|
||||
static inline pid_t tomoyo_sys_getppid(void)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
rcu_read_lock();
|
||||
pid = task_tgid_vnr(rcu_dereference(current->real_parent));
|
||||
rcu_read_unlock();
|
||||
@ -1197,26 +1211,15 @@ static inline void tomoyo_put_group(struct tomoyo_group *group)
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_domain - Get "struct tomoyo_domain_info" for current thread.
|
||||
* tomoyo_task - Get "struct tomoyo_task" for specified thread.
|
||||
*
|
||||
* Returns pointer to "struct tomoyo_domain_info" for current thread.
|
||||
* @task - Pointer to "struct task_struct".
|
||||
*
|
||||
* Returns pointer to "struct tomoyo_task" for specified thread.
|
||||
*/
|
||||
static inline struct tomoyo_domain_info *tomoyo_domain(void)
|
||||
static inline struct tomoyo_task *tomoyo_task(struct task_struct *task)
|
||||
{
|
||||
return current_cred()->security;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_real_domain - Get "struct tomoyo_domain_info" for specified thread.
|
||||
*
|
||||
* @task: Pointer to "struct task_struct".
|
||||
*
|
||||
* Returns pointer to "struct tomoyo_security" for specified thread.
|
||||
*/
|
||||
static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
|
||||
*task)
|
||||
{
|
||||
return task_cred_xxx(task, security);
|
||||
return task->security + tomoyo_blob_sizes.lbs_task;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,9 +28,11 @@ static bool tomoyo_argv(const unsigned int index, const char *arg_ptr,
|
||||
{
|
||||
int i;
|
||||
struct tomoyo_path_info arg;
|
||||
|
||||
arg.name = arg_ptr;
|
||||
for (i = 0; i < argc; argv++, checked++, i++) {
|
||||
bool result;
|
||||
|
||||
if (index != argv->index)
|
||||
continue;
|
||||
*checked = 1;
|
||||
@ -62,12 +64,14 @@ static bool tomoyo_envp(const char *env_name, const char *env_value,
|
||||
int i;
|
||||
struct tomoyo_path_info name;
|
||||
struct tomoyo_path_info value;
|
||||
|
||||
name.name = env_name;
|
||||
tomoyo_fill_path_info(&name);
|
||||
value.name = env_value;
|
||||
tomoyo_fill_path_info(&value);
|
||||
for (i = 0; i < envc; envp++, checked++, i++) {
|
||||
bool result;
|
||||
|
||||
if (!tomoyo_path_matches_pattern(&name, envp->name))
|
||||
continue;
|
||||
*checked = 1;
|
||||
@ -113,6 +117,7 @@ static bool tomoyo_scan_bprm(struct tomoyo_execve *ee,
|
||||
bool result = true;
|
||||
u8 local_checked[32];
|
||||
u8 *checked;
|
||||
|
||||
if (argc + envc <= sizeof(local_checked)) {
|
||||
checked = local_checked;
|
||||
memset(local_checked, 0, sizeof(local_checked));
|
||||
@ -131,6 +136,7 @@ static bool tomoyo_scan_bprm(struct tomoyo_execve *ee,
|
||||
/* Read. */
|
||||
const char *kaddr = dump->data;
|
||||
const unsigned char c = kaddr[offset++];
|
||||
|
||||
if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
|
||||
if (c == '\\') {
|
||||
arg_ptr[arg_len++] = '\\';
|
||||
@ -160,6 +166,7 @@ static bool tomoyo_scan_bprm(struct tomoyo_execve *ee,
|
||||
argv_count--;
|
||||
} else if (envp_count) {
|
||||
char *cp = strchr(arg_ptr, '=');
|
||||
|
||||
if (cp) {
|
||||
*cp = '\0';
|
||||
if (!tomoyo_envp(arg_ptr, cp + 1,
|
||||
@ -182,6 +189,7 @@ static bool tomoyo_scan_bprm(struct tomoyo_execve *ee,
|
||||
out:
|
||||
if (result) {
|
||||
int i;
|
||||
|
||||
/* Check not-yet-checked entries. */
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (checked[i])
|
||||
@ -229,6 +237,7 @@ static bool tomoyo_scan_exec_realpath(struct file *file,
|
||||
{
|
||||
bool result;
|
||||
struct tomoyo_path_info exe;
|
||||
|
||||
if (!file)
|
||||
return false;
|
||||
exe.name = tomoyo_realpath_from_path(&file->f_path);
|
||||
@ -250,6 +259,7 @@ static bool tomoyo_scan_exec_realpath(struct file *file,
|
||||
static const struct tomoyo_path_info *tomoyo_get_dqword(char *start)
|
||||
{
|
||||
char *cp = start + strlen(start) - 1;
|
||||
|
||||
if (cp == start || *start++ != '"' || *cp != '"')
|
||||
return NULL;
|
||||
*cp = '\0';
|
||||
@ -270,6 +280,7 @@ static bool tomoyo_parse_name_union_quoted(struct tomoyo_acl_param *param,
|
||||
struct tomoyo_name_union *ptr)
|
||||
{
|
||||
char *filename = param->data;
|
||||
|
||||
if (*filename == '@')
|
||||
return tomoyo_parse_name_union(param, ptr);
|
||||
ptr->filename = tomoyo_get_dqword(filename);
|
||||
@ -310,6 +321,7 @@ static bool tomoyo_parse_envp(char *left, char *right,
|
||||
const struct tomoyo_path_info *name;
|
||||
const struct tomoyo_path_info *value;
|
||||
char *cp = left + strlen(left) - 1;
|
||||
|
||||
if (*cp-- != ']' || *cp != '"')
|
||||
goto out;
|
||||
*cp = '\0';
|
||||
@ -364,6 +376,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a,
|
||||
static u8 tomoyo_condition_type(const char *word)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < TOMOYO_MAX_CONDITION_KEYWORD; i++) {
|
||||
if (!strcmp(word, tomoyo_condition_keyword[i]))
|
||||
break;
|
||||
@ -395,6 +408,7 @@ static struct tomoyo_condition *tomoyo_commit_condition
|
||||
{
|
||||
struct tomoyo_condition *ptr;
|
||||
bool found = false;
|
||||
|
||||
if (mutex_lock_interruptible(&tomoyo_policy_lock)) {
|
||||
dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__);
|
||||
ptr = NULL;
|
||||
@ -442,12 +456,14 @@ static char *tomoyo_get_transit_preference(struct tomoyo_acl_param *param,
|
||||
{
|
||||
char * const pos = param->data;
|
||||
bool flag;
|
||||
|
||||
if (*pos == '<') {
|
||||
e->transit = tomoyo_get_domainname(param);
|
||||
goto done;
|
||||
}
|
||||
{
|
||||
char *cp = strchr(pos, ' ');
|
||||
|
||||
if (cp)
|
||||
*cp = '\0';
|
||||
flag = tomoyo_correct_path(pos) || !strcmp(pos, "keep") ||
|
||||
@ -489,6 +505,7 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
|
||||
tomoyo_get_transit_preference(param, &e);
|
||||
char * const end_of_string = start_of_string + strlen(start_of_string);
|
||||
char *pos;
|
||||
|
||||
rerun:
|
||||
pos = start_of_string;
|
||||
while (1) {
|
||||
@ -498,6 +515,7 @@ rerun:
|
||||
char *cp;
|
||||
char *right_word;
|
||||
bool is_not;
|
||||
|
||||
if (!*left_word)
|
||||
break;
|
||||
/*
|
||||
@ -622,8 +640,8 @@ rerun:
|
||||
}
|
||||
store_value:
|
||||
if (!condp) {
|
||||
dprintk(KERN_WARNING "%u: dry_run left=%u right=%u "
|
||||
"match=%u\n", __LINE__, left, right, !is_not);
|
||||
dprintk(KERN_WARNING "%u: dry_run left=%u right=%u match=%u\n",
|
||||
__LINE__, left, right, !is_not);
|
||||
continue;
|
||||
}
|
||||
condp->left = left;
|
||||
@ -660,6 +678,7 @@ store_value:
|
||||
envp = (struct tomoyo_envp *) (argv + e.argc);
|
||||
{
|
||||
bool flag = false;
|
||||
|
||||
for (pos = start_of_string; pos < end_of_string; pos++) {
|
||||
if (*pos)
|
||||
continue;
|
||||
@ -698,6 +717,7 @@ void tomoyo_get_attributes(struct tomoyo_obj_info *obj)
|
||||
|
||||
for (i = 0; i < TOMOYO_MAX_PATH_STAT; i++) {
|
||||
struct inode *inode;
|
||||
|
||||
switch (i) {
|
||||
case TOMOYO_PATH1:
|
||||
dentry = obj->path1.dentry;
|
||||
@ -718,6 +738,7 @@ void tomoyo_get_attributes(struct tomoyo_obj_info *obj)
|
||||
inode = d_backing_inode(dentry);
|
||||
if (inode) {
|
||||
struct tomoyo_mini_stat *stat = &obj->stat[i];
|
||||
|
||||
stat->uid = inode->i_uid;
|
||||
stat->gid = inode->i_gid;
|
||||
stat->ino = inode->i_ino;
|
||||
@ -726,8 +747,7 @@ void tomoyo_get_attributes(struct tomoyo_obj_info *obj)
|
||||
stat->rdev = inode->i_rdev;
|
||||
obj->stat_valid[i] = true;
|
||||
}
|
||||
if (i & 1) /* i == TOMOYO_PATH1_PARENT ||
|
||||
i == TOMOYO_PATH2_PARENT */
|
||||
if (i & 1) /* TOMOYO_PATH1_PARENT or TOMOYO_PATH2_PARENT */
|
||||
dput(dentry);
|
||||
}
|
||||
}
|
||||
@ -758,6 +778,7 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
|
||||
u16 argc;
|
||||
u16 envc;
|
||||
struct linux_binprm *bprm = NULL;
|
||||
|
||||
if (!cond)
|
||||
return true;
|
||||
condc = cond->condc;
|
||||
@ -780,6 +801,7 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
|
||||
const u8 right = condp->right;
|
||||
bool is_bitop[2] = { false, false };
|
||||
u8 j;
|
||||
|
||||
condp++;
|
||||
/* Check argv[] and envp[] later. */
|
||||
if (left == TOMOYO_ARGV_ENTRY || left == TOMOYO_ENVP_ENTRY)
|
||||
@ -787,10 +809,11 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
|
||||
/* Check string expressions. */
|
||||
if (right == TOMOYO_NAME_UNION) {
|
||||
const struct tomoyo_name_union *ptr = names_p++;
|
||||
switch (left) {
|
||||
struct tomoyo_path_info *symlink;
|
||||
struct tomoyo_execve *ee;
|
||||
struct file *file;
|
||||
|
||||
switch (left) {
|
||||
case TOMOYO_SYMLINK_TARGET:
|
||||
symlink = obj ? obj->symlink_target : NULL;
|
||||
if (!symlink ||
|
||||
@ -812,6 +835,7 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
|
||||
for (j = 0; j < 2; j++) {
|
||||
const u8 index = j ? right : left;
|
||||
unsigned long value = 0;
|
||||
|
||||
switch (index) {
|
||||
case TOMOYO_TASK_UID:
|
||||
value = from_kuid(&init_user_ns, current_uid());
|
||||
@ -874,31 +898,31 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
|
||||
value = S_ISVTX;
|
||||
break;
|
||||
case TOMOYO_MODE_OWNER_READ:
|
||||
value = S_IRUSR;
|
||||
value = 0400;
|
||||
break;
|
||||
case TOMOYO_MODE_OWNER_WRITE:
|
||||
value = S_IWUSR;
|
||||
value = 0200;
|
||||
break;
|
||||
case TOMOYO_MODE_OWNER_EXECUTE:
|
||||
value = S_IXUSR;
|
||||
value = 0100;
|
||||
break;
|
||||
case TOMOYO_MODE_GROUP_READ:
|
||||
value = S_IRGRP;
|
||||
value = 0040;
|
||||
break;
|
||||
case TOMOYO_MODE_GROUP_WRITE:
|
||||
value = S_IWGRP;
|
||||
value = 0020;
|
||||
break;
|
||||
case TOMOYO_MODE_GROUP_EXECUTE:
|
||||
value = S_IXGRP;
|
||||
value = 0010;
|
||||
break;
|
||||
case TOMOYO_MODE_OTHERS_READ:
|
||||
value = S_IROTH;
|
||||
value = 0004;
|
||||
break;
|
||||
case TOMOYO_MODE_OTHERS_WRITE:
|
||||
value = S_IWOTH;
|
||||
value = 0002;
|
||||
break;
|
||||
case TOMOYO_MODE_OTHERS_EXECUTE:
|
||||
value = S_IXOTH;
|
||||
value = 0001;
|
||||
break;
|
||||
case TOMOYO_EXEC_ARGC:
|
||||
if (!bprm)
|
||||
@ -923,6 +947,7 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
|
||||
{
|
||||
u8 stat_index;
|
||||
struct tomoyo_mini_stat *stat;
|
||||
|
||||
switch (index) {
|
||||
case TOMOYO_PATH1_UID:
|
||||
case TOMOYO_PATH1_GID:
|
||||
@ -1036,12 +1061,14 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
|
||||
if (left == TOMOYO_NUMBER_UNION) {
|
||||
/* Fetch values now. */
|
||||
const struct tomoyo_number_union *ptr = numbers_p++;
|
||||
|
||||
min_v[0] = ptr->values[0];
|
||||
max_v[0] = ptr->values[1];
|
||||
}
|
||||
if (right == TOMOYO_NUMBER_UNION) {
|
||||
/* Fetch values now. */
|
||||
const struct tomoyo_number_union *ptr = numbers_p++;
|
||||
|
||||
if (ptr->group) {
|
||||
if (tomoyo_number_matches_group(min_v[0],
|
||||
max_v[0],
|
||||
|
@ -30,7 +30,7 @@ struct tomoyo_domain_info tomoyo_kernel_domain;
|
||||
*/
|
||||
int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
|
||||
struct tomoyo_acl_param *param,
|
||||
bool (*check_duplicate) (const struct tomoyo_acl_head
|
||||
bool (*check_duplicate)(const struct tomoyo_acl_head
|
||||
*,
|
||||
const struct tomoyo_acl_head
|
||||
*))
|
||||
@ -90,11 +90,11 @@ static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *a,
|
||||
*/
|
||||
int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
|
||||
struct tomoyo_acl_param *param,
|
||||
bool (*check_duplicate) (const struct tomoyo_acl_info
|
||||
bool (*check_duplicate)(const struct tomoyo_acl_info
|
||||
*,
|
||||
const struct tomoyo_acl_info
|
||||
*),
|
||||
bool (*merge_duplicate) (struct tomoyo_acl_info *,
|
||||
bool (*merge_duplicate)(struct tomoyo_acl_info *,
|
||||
struct tomoyo_acl_info *,
|
||||
const bool))
|
||||
{
|
||||
@ -157,13 +157,13 @@ out:
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
void tomoyo_check_acl(struct tomoyo_request_info *r,
|
||||
bool (*check_entry) (struct tomoyo_request_info *,
|
||||
bool (*check_entry)(struct tomoyo_request_info *,
|
||||
const struct tomoyo_acl_info *))
|
||||
{
|
||||
const struct tomoyo_domain_info *domain = r->domain;
|
||||
struct tomoyo_acl_info *ptr;
|
||||
bool retried = false;
|
||||
const struct list_head *list = &domain->acl_info_list;
|
||||
u16 i = 0;
|
||||
|
||||
retry:
|
||||
list_for_each_entry_rcu(ptr, list, list) {
|
||||
@ -177,9 +177,10 @@ retry:
|
||||
r->granted = true;
|
||||
return;
|
||||
}
|
||||
if (!retried) {
|
||||
retried = true;
|
||||
list = &domain->ns->acl_group[domain->group];
|
||||
for (; i < TOMOYO_MAX_ACL_GROUPS; i++) {
|
||||
if (!test_bit(i, domain->group))
|
||||
continue;
|
||||
list = &domain->ns->acl_group[i++];
|
||||
goto retry;
|
||||
}
|
||||
r->granted = false;
|
||||
@ -198,6 +199,7 @@ LIST_HEAD(tomoyo_domain_list);
|
||||
static const char *tomoyo_last_word(const char *name)
|
||||
{
|
||||
const char *cp = strrchr(name, ' ');
|
||||
|
||||
if (cp)
|
||||
return cp + 1;
|
||||
return name;
|
||||
@ -220,6 +222,7 @@ static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a,
|
||||
const struct tomoyo_transition_control *p2 = container_of(b,
|
||||
typeof(*p2),
|
||||
head);
|
||||
|
||||
return p1->type == p2->type && p1->is_last_name == p2->is_last_name
|
||||
&& p1->domainname == p2->domainname
|
||||
&& p1->program == p2->program;
|
||||
@ -240,6 +243,7 @@ int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
|
||||
int error = param->is_delete ? -ENOENT : -ENOMEM;
|
||||
char *program = param->data;
|
||||
char *domainname = strstr(program, " from ");
|
||||
|
||||
if (domainname) {
|
||||
*domainname = '\0';
|
||||
domainname += 6;
|
||||
@ -293,6 +297,7 @@ static inline bool tomoyo_scan_transition
|
||||
const enum tomoyo_transition_type type)
|
||||
{
|
||||
const struct tomoyo_transition_control *ptr;
|
||||
|
||||
list_for_each_entry_rcu(ptr, list, head.list) {
|
||||
if (ptr->head.is_deleted || ptr->type != type)
|
||||
continue;
|
||||
@ -338,9 +343,11 @@ static enum tomoyo_transition_type tomoyo_transition_type
|
||||
{
|
||||
const char *last_name = tomoyo_last_word(domainname->name);
|
||||
enum tomoyo_transition_type type = TOMOYO_TRANSITION_CONTROL_NO_RESET;
|
||||
|
||||
while (type < TOMOYO_MAX_TRANSITION_TYPE) {
|
||||
const struct list_head * const list =
|
||||
&ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL];
|
||||
|
||||
if (!tomoyo_scan_transition(list, domainname, program,
|
||||
last_name, type)) {
|
||||
type++;
|
||||
@ -375,6 +382,7 @@ static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a,
|
||||
head);
|
||||
const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2),
|
||||
head);
|
||||
|
||||
return p1->original_name == p2->original_name &&
|
||||
p1->aggregated_name == p2->aggregated_name;
|
||||
}
|
||||
@ -394,6 +402,7 @@ int tomoyo_write_aggregator(struct tomoyo_acl_param *param)
|
||||
int error = param->is_delete ? -ENOENT : -ENOMEM;
|
||||
const char *original_name = tomoyo_read_token(param);
|
||||
const char *aggregated_name = tomoyo_read_token(param);
|
||||
|
||||
if (!tomoyo_correct_word(original_name) ||
|
||||
!tomoyo_correct_path(aggregated_name))
|
||||
return -EINVAL;
|
||||
@ -426,6 +435,7 @@ static struct tomoyo_policy_namespace *tomoyo_find_namespace
|
||||
(const char *name, const unsigned int len)
|
||||
{
|
||||
struct tomoyo_policy_namespace *ns;
|
||||
|
||||
list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) {
|
||||
if (strncmp(name, ns->name, len) ||
|
||||
(name[len] && name[len] != ' '))
|
||||
@ -451,6 +461,7 @@ struct tomoyo_policy_namespace *tomoyo_assign_namespace(const char *domainname)
|
||||
struct tomoyo_policy_namespace *entry;
|
||||
const char *cp = domainname;
|
||||
unsigned int len = 0;
|
||||
|
||||
while (*cp && *cp++ != ' ')
|
||||
len++;
|
||||
ptr = tomoyo_find_namespace(domainname, len);
|
||||
@ -466,6 +477,7 @@ struct tomoyo_policy_namespace *tomoyo_assign_namespace(const char *domainname)
|
||||
ptr = tomoyo_find_namespace(domainname, len);
|
||||
if (!ptr && tomoyo_memory_ok(entry)) {
|
||||
char *name = (char *) (entry + 1);
|
||||
|
||||
ptr = entry;
|
||||
memmove(name, domainname, len);
|
||||
name[len] = '\0';
|
||||
@ -490,6 +502,7 @@ static bool tomoyo_namespace_jump(const char *domainname)
|
||||
{
|
||||
const char *namespace = tomoyo_current_namespace()->name;
|
||||
const int len = strlen(namespace);
|
||||
|
||||
return strncmp(domainname, namespace, len) ||
|
||||
(domainname[len] && domainname[len] != ' ');
|
||||
}
|
||||
@ -510,6 +523,7 @@ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
|
||||
struct tomoyo_domain_info e = { };
|
||||
struct tomoyo_domain_info *entry = tomoyo_find_domain(domainname);
|
||||
bool created = false;
|
||||
|
||||
if (entry) {
|
||||
if (transit) {
|
||||
/*
|
||||
@ -546,8 +560,9 @@ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
|
||||
*/
|
||||
if (transit) {
|
||||
const struct tomoyo_domain_info *domain = tomoyo_domain();
|
||||
|
||||
e.profile = domain->profile;
|
||||
e.group = domain->group;
|
||||
memcpy(e.group, domain->group, sizeof(e.group));
|
||||
}
|
||||
e.domainname = tomoyo_get_name(domainname);
|
||||
if (!e.domainname)
|
||||
@ -569,12 +584,17 @@ out:
|
||||
if (entry && transit) {
|
||||
if (created) {
|
||||
struct tomoyo_request_info r;
|
||||
int i;
|
||||
|
||||
tomoyo_init_request_info(&r, entry,
|
||||
TOMOYO_MAC_FILE_EXECUTE);
|
||||
r.granted = false;
|
||||
tomoyo_write_log(&r, "use_profile %u\n",
|
||||
entry->profile);
|
||||
tomoyo_write_log(&r, "use_group %u\n", entry->group);
|
||||
for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++)
|
||||
if (test_bit(i, entry->group))
|
||||
tomoyo_write_log(&r, "use_group %u\n",
|
||||
i);
|
||||
tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
|
||||
}
|
||||
}
|
||||
@ -712,6 +732,7 @@ retry:
|
||||
struct tomoyo_aggregator *ptr;
|
||||
struct list_head *list =
|
||||
&old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR];
|
||||
|
||||
/* Check 'aggregator' directive. */
|
||||
candidate = &exename;
|
||||
list_for_each_entry_rcu(ptr, list, head.list) {
|
||||
@ -747,6 +768,7 @@ retry:
|
||||
*/
|
||||
if (ee->transition) {
|
||||
const char *domainname = ee->transition->name;
|
||||
|
||||
reject_on_transition_failure = true;
|
||||
if (!strcmp(domainname, "keep"))
|
||||
goto force_keep_domain;
|
||||
@ -758,6 +780,7 @@ retry:
|
||||
goto force_initialize_domain;
|
||||
if (!strcmp(domainname, "parent")) {
|
||||
char *cp;
|
||||
|
||||
strncpy(ee->tmp, old_domain->domainname->name,
|
||||
TOMOYO_EXEC_TMPSIZE - 1);
|
||||
cp = strrchr(ee->tmp, ' ');
|
||||
@ -822,8 +845,7 @@ force_jump_domain:
|
||||
if (domain)
|
||||
retval = 0;
|
||||
else if (reject_on_transition_failure) {
|
||||
printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n",
|
||||
ee->tmp);
|
||||
pr_warn("ERROR: Domain '%s' not ready.\n", ee->tmp);
|
||||
retval = -ENOMEM;
|
||||
} else if (ee->r.mode == TOMOYO_CONFIG_ENFORCING)
|
||||
retval = -ENOMEM;
|
||||
@ -834,16 +856,20 @@ force_jump_domain:
|
||||
ee->r.granted = false;
|
||||
tomoyo_write_log(&ee->r, "%s", tomoyo_dif
|
||||
[TOMOYO_DIF_TRANSITION_FAILED]);
|
||||
printk(KERN_WARNING
|
||||
"ERROR: Domain '%s' not defined.\n", ee->tmp);
|
||||
pr_warn("ERROR: Domain '%s' not defined.\n", ee->tmp);
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (!domain)
|
||||
domain = old_domain;
|
||||
/* Update reference count on "struct tomoyo_domain_info". */
|
||||
{
|
||||
struct tomoyo_task *s = tomoyo_task(current);
|
||||
|
||||
s->old_domain_info = s->domain_info;
|
||||
s->domain_info = domain;
|
||||
atomic_inc(&domain->users);
|
||||
bprm->cred->security = domain;
|
||||
}
|
||||
kfree(exename.name);
|
||||
if (!retval) {
|
||||
ee->r.domain = domain;
|
||||
|
@ -214,6 +214,7 @@ static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r)
|
||||
const u8 type = r->param.path_number.operation;
|
||||
u8 radix;
|
||||
char buffer[64];
|
||||
|
||||
switch (type) {
|
||||
case TOMOYO_TYPE_CREATE:
|
||||
case TOMOYO_TYPE_MKDIR:
|
||||
@ -253,6 +254,7 @@ static bool tomoyo_check_path_acl(struct tomoyo_request_info *r,
|
||||
{
|
||||
const struct tomoyo_path_acl *acl = container_of(ptr, typeof(*acl),
|
||||
head);
|
||||
|
||||
if (acl->perm & (1 << r->param.path.operation)) {
|
||||
r->param.path.matched_path =
|
||||
tomoyo_compare_name_union(r->param.path.filename,
|
||||
@ -275,6 +277,7 @@ static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r,
|
||||
{
|
||||
const struct tomoyo_path_number_acl *acl =
|
||||
container_of(ptr, typeof(*acl), head);
|
||||
|
||||
return (acl->perm & (1 << r->param.path_number.operation)) &&
|
||||
tomoyo_compare_number_union(r->param.path_number.number,
|
||||
&acl->number) &&
|
||||
@ -295,6 +298,7 @@ static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r,
|
||||
{
|
||||
const struct tomoyo_path2_acl *acl =
|
||||
container_of(ptr, typeof(*acl), head);
|
||||
|
||||
return (acl->perm & (1 << r->param.path2.operation)) &&
|
||||
tomoyo_compare_name_union(r->param.path2.filename1, &acl->name1)
|
||||
&& tomoyo_compare_name_union(r->param.path2.filename2,
|
||||
@ -314,6 +318,7 @@ static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r,
|
||||
{
|
||||
const struct tomoyo_mkdev_acl *acl =
|
||||
container_of(ptr, typeof(*acl), head);
|
||||
|
||||
return (acl->perm & (1 << r->param.mkdev.operation)) &&
|
||||
tomoyo_compare_number_union(r->param.mkdev.mode,
|
||||
&acl->mode) &&
|
||||
@ -338,6 +343,7 @@ static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a,
|
||||
{
|
||||
const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head);
|
||||
const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head);
|
||||
|
||||
return tomoyo_same_name_union(&p1->name, &p2->name);
|
||||
}
|
||||
|
||||
@ -358,6 +364,7 @@ static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a,
|
||||
->perm;
|
||||
u16 perm = *a_perm;
|
||||
const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm;
|
||||
|
||||
if (is_delete)
|
||||
perm &= ~b_perm;
|
||||
else
|
||||
@ -384,6 +391,7 @@ static int tomoyo_update_path_acl(const u16 perm,
|
||||
.perm = perm
|
||||
};
|
||||
int error;
|
||||
|
||||
if (!tomoyo_parse_name_union(param, &e.name))
|
||||
error = -EINVAL;
|
||||
else
|
||||
@ -407,6 +415,7 @@ static bool tomoyo_same_mkdev_acl(const struct tomoyo_acl_info *a,
|
||||
{
|
||||
const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1), head);
|
||||
const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2), head);
|
||||
|
||||
return tomoyo_same_name_union(&p1->name, &p2->name) &&
|
||||
tomoyo_same_number_union(&p1->mode, &p2->mode) &&
|
||||
tomoyo_same_number_union(&p1->major, &p2->major) &&
|
||||
@ -431,6 +440,7 @@ static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a,
|
||||
u8 perm = *a_perm;
|
||||
const u8 b_perm = container_of(b, struct tomoyo_mkdev_acl, head)
|
||||
->perm;
|
||||
|
||||
if (is_delete)
|
||||
perm &= ~b_perm;
|
||||
else
|
||||
@ -457,6 +467,7 @@ static int tomoyo_update_mkdev_acl(const u8 perm,
|
||||
.perm = perm
|
||||
};
|
||||
int error;
|
||||
|
||||
if (!tomoyo_parse_name_union(param, &e.name) ||
|
||||
!tomoyo_parse_number_union(param, &e.mode) ||
|
||||
!tomoyo_parse_number_union(param, &e.major) ||
|
||||
@ -486,6 +497,7 @@ static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a,
|
||||
{
|
||||
const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head);
|
||||
const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head);
|
||||
|
||||
return tomoyo_same_name_union(&p1->name1, &p2->name1) &&
|
||||
tomoyo_same_name_union(&p1->name2, &p2->name2);
|
||||
}
|
||||
@ -507,6 +519,7 @@ static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a,
|
||||
->perm;
|
||||
u8 perm = *a_perm;
|
||||
const u8 b_perm = container_of(b, struct tomoyo_path2_acl, head)->perm;
|
||||
|
||||
if (is_delete)
|
||||
perm &= ~b_perm;
|
||||
else
|
||||
@ -533,6 +546,7 @@ static int tomoyo_update_path2_acl(const u8 perm,
|
||||
.perm = perm
|
||||
};
|
||||
int error;
|
||||
|
||||
if (!tomoyo_parse_name_union(param, &e.name1) ||
|
||||
!tomoyo_parse_name_union(param, &e.name2))
|
||||
error = -EINVAL;
|
||||
@ -621,6 +635,7 @@ static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a,
|
||||
head);
|
||||
const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2),
|
||||
head);
|
||||
|
||||
return tomoyo_same_name_union(&p1->name, &p2->name) &&
|
||||
tomoyo_same_number_union(&p1->number, &p2->number);
|
||||
}
|
||||
@ -643,6 +658,7 @@ static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a,
|
||||
u8 perm = *a_perm;
|
||||
const u8 b_perm = container_of(b, struct tomoyo_path_number_acl, head)
|
||||
->perm;
|
||||
|
||||
if (is_delete)
|
||||
perm &= ~b_perm;
|
||||
else
|
||||
@ -667,6 +683,7 @@ static int tomoyo_update_path_number_acl(const u8 perm,
|
||||
.perm = perm
|
||||
};
|
||||
int error;
|
||||
|
||||
if (!tomoyo_parse_name_union(param, &e.name) ||
|
||||
!tomoyo_parse_number_union(param, &e.number))
|
||||
error = -EINVAL;
|
||||
@ -947,6 +964,7 @@ static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a,
|
||||
{
|
||||
const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head);
|
||||
const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head);
|
||||
|
||||
return tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) &&
|
||||
tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) &&
|
||||
tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) &&
|
||||
@ -966,6 +984,7 @@ static int tomoyo_update_mount_acl(struct tomoyo_acl_param *param)
|
||||
{
|
||||
struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL };
|
||||
int error;
|
||||
|
||||
if (!tomoyo_parse_name_union(param, &e.dev_name) ||
|
||||
!tomoyo_parse_name_union(param, &e.dir_name) ||
|
||||
!tomoyo_parse_name_union(param, &e.fs_type) ||
|
||||
@ -995,6 +1014,7 @@ int tomoyo_write_file(struct tomoyo_acl_param *param)
|
||||
u16 perm = 0;
|
||||
u8 type;
|
||||
const char *operation = tomoyo_read_token(param);
|
||||
|
||||
for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++)
|
||||
if (tomoyo_permstr(operation, tomoyo_path_keyword[type]))
|
||||
perm |= 1 << type;
|
||||
|
@ -77,11 +77,13 @@ static bool tomoyo_name_used_by_io_buffer(const char *string)
|
||||
spin_lock(&tomoyo_io_buffer_list_lock);
|
||||
list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
|
||||
int i;
|
||||
|
||||
head->users++;
|
||||
spin_unlock(&tomoyo_io_buffer_list_lock);
|
||||
mutex_lock(&head->io_sem);
|
||||
for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) {
|
||||
const char *w = head->r.w[i];
|
||||
|
||||
if (w < string || w > string + size)
|
||||
continue;
|
||||
in_use = true;
|
||||
@ -108,6 +110,7 @@ static inline void tomoyo_del_transition_control(struct list_head *element)
|
||||
{
|
||||
struct tomoyo_transition_control *ptr =
|
||||
container_of(element, typeof(*ptr), head.list);
|
||||
|
||||
tomoyo_put_name(ptr->domainname);
|
||||
tomoyo_put_name(ptr->program);
|
||||
}
|
||||
@ -123,6 +126,7 @@ static inline void tomoyo_del_aggregator(struct list_head *element)
|
||||
{
|
||||
struct tomoyo_aggregator *ptr =
|
||||
container_of(element, typeof(*ptr), head.list);
|
||||
|
||||
tomoyo_put_name(ptr->original_name);
|
||||
tomoyo_put_name(ptr->aggregated_name);
|
||||
}
|
||||
@ -138,6 +142,7 @@ static inline void tomoyo_del_manager(struct list_head *element)
|
||||
{
|
||||
struct tomoyo_manager *ptr =
|
||||
container_of(element, typeof(*ptr), head.list);
|
||||
|
||||
tomoyo_put_name(ptr->manager);
|
||||
}
|
||||
|
||||
@ -152,6 +157,7 @@ static void tomoyo_del_acl(struct list_head *element)
|
||||
{
|
||||
struct tomoyo_acl_info *acl =
|
||||
container_of(element, typeof(*acl), list);
|
||||
|
||||
tomoyo_put_condition(acl->cond);
|
||||
switch (acl->type) {
|
||||
case TOMOYO_TYPE_PATH_ACL:
|
||||
@ -226,6 +232,7 @@ static void tomoyo_del_acl(struct list_head *element)
|
||||
{
|
||||
struct tomoyo_task_acl *entry =
|
||||
container_of(acl, typeof(*entry), head);
|
||||
|
||||
tomoyo_put_name(entry->domainname);
|
||||
}
|
||||
break;
|
||||
@ -247,6 +254,7 @@ static inline void tomoyo_del_domain(struct list_head *element)
|
||||
container_of(element, typeof(*domain), list);
|
||||
struct tomoyo_acl_info *acl;
|
||||
struct tomoyo_acl_info *tmp;
|
||||
|
||||
/*
|
||||
* Since this domain is referenced from neither
|
||||
* "struct tomoyo_io_buffer" nor "struct cred"->security, we can delete
|
||||
@ -286,6 +294,7 @@ void tomoyo_del_condition(struct list_head *element)
|
||||
= (const struct tomoyo_argv *) (names_p + names_count);
|
||||
const struct tomoyo_envp *envp
|
||||
= (const struct tomoyo_envp *) (argv + argc);
|
||||
|
||||
for (i = 0; i < numbers_count; i++)
|
||||
tomoyo_put_number_union(numbers_p++);
|
||||
for (i = 0; i < names_count; i++)
|
||||
@ -321,6 +330,7 @@ static inline void tomoyo_del_path_group(struct list_head *element)
|
||||
{
|
||||
struct tomoyo_path_group *member =
|
||||
container_of(element, typeof(*member), head.list);
|
||||
|
||||
tomoyo_put_name(member->member_name);
|
||||
}
|
||||
|
||||
@ -335,6 +345,7 @@ static inline void tomoyo_del_group(struct list_head *element)
|
||||
{
|
||||
struct tomoyo_group *group =
|
||||
container_of(element, typeof(*group), head.list);
|
||||
|
||||
tomoyo_put_name(group->group_name);
|
||||
}
|
||||
|
||||
@ -476,6 +487,7 @@ static void tomoyo_collect_member(const enum tomoyo_policy_id id,
|
||||
{
|
||||
struct tomoyo_acl_head *member;
|
||||
struct tomoyo_acl_head *tmp;
|
||||
|
||||
list_for_each_entry_safe(member, tmp, member_list, list) {
|
||||
if (!member->is_deleted)
|
||||
continue;
|
||||
@ -495,6 +507,7 @@ static void tomoyo_collect_acl(struct list_head *list)
|
||||
{
|
||||
struct tomoyo_acl_info *acl;
|
||||
struct tomoyo_acl_info *tmp;
|
||||
|
||||
list_for_each_entry_safe(acl, tmp, list, list) {
|
||||
if (!acl->is_deleted)
|
||||
continue;
|
||||
@ -513,10 +526,12 @@ static void tomoyo_collect_entry(void)
|
||||
int i;
|
||||
enum tomoyo_policy_id id;
|
||||
struct tomoyo_policy_namespace *ns;
|
||||
|
||||
mutex_lock(&tomoyo_policy_lock);
|
||||
{
|
||||
struct tomoyo_domain_info *domain;
|
||||
struct tomoyo_domain_info *tmp;
|
||||
|
||||
list_for_each_entry_safe(domain, tmp, &tomoyo_domain_list,
|
||||
list) {
|
||||
tomoyo_collect_acl(&domain->acl_info_list);
|
||||
@ -534,6 +549,7 @@ static void tomoyo_collect_entry(void)
|
||||
{
|
||||
struct tomoyo_shared_acl_head *ptr;
|
||||
struct tomoyo_shared_acl_head *tmp;
|
||||
|
||||
list_for_each_entry_safe(ptr, tmp, &tomoyo_condition_list,
|
||||
list) {
|
||||
if (atomic_read(&ptr->users) > 0)
|
||||
@ -547,6 +563,7 @@ static void tomoyo_collect_entry(void)
|
||||
struct list_head *list = &ns->group_list[i];
|
||||
struct tomoyo_group *group;
|
||||
struct tomoyo_group *tmp;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
id = TOMOYO_ID_PATH_GROUP;
|
||||
@ -574,6 +591,7 @@ static void tomoyo_collect_entry(void)
|
||||
struct list_head *list = &tomoyo_name_list[i];
|
||||
struct tomoyo_shared_acl_head *ptr;
|
||||
struct tomoyo_shared_acl_head *tmp;
|
||||
|
||||
list_for_each_entry_safe(ptr, tmp, list, list) {
|
||||
if (atomic_read(&ptr->users) > 0)
|
||||
continue;
|
||||
@ -595,6 +613,7 @@ static int tomoyo_gc_thread(void *unused)
|
||||
{
|
||||
/* Garbage collector thread is exclusive. */
|
||||
static DEFINE_MUTEX(tomoyo_gc_mutex);
|
||||
|
||||
if (!mutex_trylock(&tomoyo_gc_mutex))
|
||||
goto out;
|
||||
tomoyo_collect_entry();
|
||||
|
@ -75,11 +75,13 @@ int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type)
|
||||
{
|
||||
struct tomoyo_group *group = tomoyo_get_group(param, type);
|
||||
int error = -EINVAL;
|
||||
|
||||
if (!group)
|
||||
return -ENOMEM;
|
||||
param->list = &group->member_list;
|
||||
if (type == TOMOYO_PATH_GROUP) {
|
||||
struct tomoyo_path_group e = { };
|
||||
|
||||
e.member_name = tomoyo_get_name(tomoyo_read_token(param));
|
||||
if (!e.member_name) {
|
||||
error = -ENOMEM;
|
||||
@ -90,6 +92,7 @@ int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type)
|
||||
tomoyo_put_name(e.member_name);
|
||||
} else if (type == TOMOYO_NUMBER_GROUP) {
|
||||
struct tomoyo_number_group e = { };
|
||||
|
||||
if (param->data[0] == '@' ||
|
||||
!tomoyo_parse_number_union(param, &e.number))
|
||||
goto out;
|
||||
@ -129,6 +132,7 @@ tomoyo_path_matches_group(const struct tomoyo_path_info *pathname,
|
||||
const struct tomoyo_group *group)
|
||||
{
|
||||
struct tomoyo_path_group *member;
|
||||
|
||||
list_for_each_entry_rcu(member, &group->member_list, head.list) {
|
||||
if (member->head.is_deleted)
|
||||
continue;
|
||||
@ -156,6 +160,7 @@ bool tomoyo_number_matches_group(const unsigned long min,
|
||||
{
|
||||
struct tomoyo_number_group *member;
|
||||
bool matched = false;
|
||||
|
||||
list_for_each_entry_rcu(member, &group->member_list, head.list) {
|
||||
if (member->head.is_deleted)
|
||||
continue;
|
||||
|
@ -37,11 +37,12 @@ __setup("TOMOYO_loader=", tomoyo_loader_setup);
|
||||
static bool tomoyo_policy_loader_exists(void)
|
||||
{
|
||||
struct path path;
|
||||
|
||||
if (!tomoyo_loader)
|
||||
tomoyo_loader = CONFIG_SECURITY_TOMOYO_POLICY_LOADER;
|
||||
if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) {
|
||||
printk(KERN_INFO "Not activating Mandatory Access Control "
|
||||
"as %s does not exist.\n", tomoyo_loader);
|
||||
pr_info("Not activating Mandatory Access Control as %s does not exist.\n",
|
||||
tomoyo_loader);
|
||||
return false;
|
||||
}
|
||||
path_put(&path);
|
||||
@ -96,8 +97,7 @@ void tomoyo_load_policy(const char *filename)
|
||||
if (!tomoyo_policy_loader_exists())
|
||||
return;
|
||||
done = true;
|
||||
printk(KERN_INFO "Calling %s to load policy. Please wait.\n",
|
||||
tomoyo_loader);
|
||||
pr_info("Calling %s to load policy. Please wait.\n", tomoyo_loader);
|
||||
argv[0] = (char *) tomoyo_loader;
|
||||
argv[1] = NULL;
|
||||
envp[0] = "HOME=/";
|
||||
|
@ -19,9 +19,9 @@ void tomoyo_warn_oom(const char *function)
|
||||
/* Reduce error messages. */
|
||||
static pid_t tomoyo_last_pid;
|
||||
const pid_t pid = current->pid;
|
||||
|
||||
if (tomoyo_last_pid != pid) {
|
||||
printk(KERN_WARNING "ERROR: Out of memory at %s.\n",
|
||||
function);
|
||||
pr_warn("ERROR: Out of memory at %s.\n", function);
|
||||
tomoyo_last_pid = pid;
|
||||
}
|
||||
if (!tomoyo_policy_loaded)
|
||||
@ -48,6 +48,7 @@ bool tomoyo_memory_ok(void *ptr)
|
||||
{
|
||||
if (ptr) {
|
||||
const size_t s = ksize(ptr);
|
||||
|
||||
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s;
|
||||
if (!tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] ||
|
||||
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <=
|
||||
@ -73,6 +74,7 @@ bool tomoyo_memory_ok(void *ptr)
|
||||
void *tomoyo_commit_ok(void *data, const unsigned int size)
|
||||
{
|
||||
void *ptr = kzalloc(size, GFP_NOFS);
|
||||
|
||||
if (tomoyo_memory_ok(ptr)) {
|
||||
memmove(ptr, data, size);
|
||||
memset(data, 0, size);
|
||||
@ -98,6 +100,7 @@ struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
|
||||
struct list_head *list;
|
||||
const char *group_name = tomoyo_read_token(param);
|
||||
bool found = false;
|
||||
|
||||
if (!tomoyo_correct_word(group_name) || idx >= TOMOYO_MAX_GROUP)
|
||||
return NULL;
|
||||
e.group_name = tomoyo_get_name(group_name);
|
||||
@ -116,6 +119,7 @@ struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
|
||||
}
|
||||
if (!found) {
|
||||
struct tomoyo_group *entry = tomoyo_commit_ok(&e, sizeof(e));
|
||||
|
||||
if (entry) {
|
||||
INIT_LIST_HEAD(&entry->member_list);
|
||||
atomic_set(&entry->head.users, 1);
|
||||
@ -191,6 +195,7 @@ struct tomoyo_policy_namespace tomoyo_kernel_namespace;
|
||||
void __init tomoyo_mm_init(void)
|
||||
{
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < TOMOYO_MAX_HASH; idx++)
|
||||
INIT_LIST_HEAD(&tomoyo_name_list[idx]);
|
||||
tomoyo_kernel_namespace.name = "<kernel>";
|
||||
|
@ -49,6 +49,7 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
|
||||
{
|
||||
const struct tomoyo_mount_acl *acl =
|
||||
container_of(ptr, typeof(*acl), head);
|
||||
|
||||
return tomoyo_compare_number_union(r->param.mount.flags,
|
||||
&acl->flags) &&
|
||||
tomoyo_compare_name_union(r->param.mount.type,
|
||||
@ -89,6 +90,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r,
|
||||
struct tomoyo_path_info rdir;
|
||||
int need_dev = 0;
|
||||
int error = -ENOMEM;
|
||||
|
||||
r->obj = &obj;
|
||||
|
||||
/* Get fstype. */
|
||||
|
@ -94,11 +94,13 @@ static char *tomoyo_get_absolute_path(const struct path *path, char * const buff
|
||||
const int buflen)
|
||||
{
|
||||
char *pos = ERR_PTR(-ENOMEM);
|
||||
|
||||
if (buflen >= 256) {
|
||||
/* go to whatever namespace root we are under */
|
||||
pos = d_absolute_path(path, buffer, buflen - 1);
|
||||
if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
|
||||
struct inode *inode = d_backing_inode(path->dentry);
|
||||
|
||||
if (inode && S_ISDIR(inode->i_mode)) {
|
||||
buffer[buflen - 2] = '/';
|
||||
buffer[buflen - 1] = '\0';
|
||||
@ -123,10 +125,12 @@ static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer,
|
||||
const int buflen)
|
||||
{
|
||||
char *pos = ERR_PTR(-ENOMEM);
|
||||
|
||||
if (buflen >= 256) {
|
||||
pos = dentry_path_raw(dentry, buffer, buflen - 1);
|
||||
if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
|
||||
struct inode *inode = d_backing_inode(dentry);
|
||||
|
||||
if (inode && S_ISDIR(inode->i_mode)) {
|
||||
buffer[buflen - 2] = '/';
|
||||
buffer[buflen - 1] = '\0';
|
||||
@ -150,12 +154,14 @@ static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,
|
||||
{
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen);
|
||||
|
||||
if (IS_ERR(pos))
|
||||
return pos;
|
||||
/* Convert from $PID to self if $PID is current thread. */
|
||||
if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') {
|
||||
char *ep;
|
||||
const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10);
|
||||
|
||||
if (*ep == '/' && pid && pid ==
|
||||
task_tgid_nr_ns(current, sb->s_fs_info)) {
|
||||
pos = ep - 5;
|
||||
@ -170,6 +176,7 @@ static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,
|
||||
goto prepend_filesystem_name;
|
||||
{
|
||||
struct inode *inode = d_backing_inode(sb->s_root);
|
||||
|
||||
/*
|
||||
* Use filesystem name if filesystem does not support rename()
|
||||
* operation.
|
||||
@ -182,6 +189,7 @@ static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,
|
||||
char name[64];
|
||||
int name_len;
|
||||
const dev_t dev = sb->s_dev;
|
||||
|
||||
name[sizeof(name) - 1] = '\0';
|
||||
snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev),
|
||||
MINOR(dev));
|
||||
@ -197,6 +205,7 @@ prepend_filesystem_name:
|
||||
{
|
||||
const char *name = sb->s_type->name;
|
||||
const int name_len = strlen(name);
|
||||
|
||||
pos -= name_len + 1;
|
||||
if (pos < buffer)
|
||||
goto out;
|
||||
@ -223,10 +232,10 @@ static char *tomoyo_get_socket_name(const struct path *path, char * const buffer
|
||||
struct inode *inode = d_backing_inode(path->dentry);
|
||||
struct socket *sock = inode ? SOCKET_I(inode) : NULL;
|
||||
struct sock *sk = sock ? sock->sk : NULL;
|
||||
|
||||
if (sk) {
|
||||
snprintf(buffer, buflen, "socket:[family=%u:type=%u:"
|
||||
"protocol=%u]", sk->sk_family, sk->sk_type,
|
||||
sk->sk_protocol);
|
||||
snprintf(buffer, buflen, "socket:[family=%u:type=%u:protocol=%u]",
|
||||
sk->sk_family, sk->sk_type, sk->sk_protocol);
|
||||
} else {
|
||||
snprintf(buffer, buflen, "socket:[unknown]");
|
||||
}
|
||||
@ -255,12 +264,14 @@ char *tomoyo_realpath_from_path(const struct path *path)
|
||||
unsigned int buf_len = PAGE_SIZE / 2;
|
||||
struct dentry *dentry = path->dentry;
|
||||
struct super_block *sb;
|
||||
|
||||
if (!dentry)
|
||||
return NULL;
|
||||
sb = dentry->d_sb;
|
||||
while (1) {
|
||||
char *pos;
|
||||
struct inode *inode;
|
||||
|
||||
buf_len <<= 1;
|
||||
kfree(buf);
|
||||
buf = kmalloc(buf_len, GFP_NOFS);
|
||||
@ -323,6 +334,7 @@ char *tomoyo_realpath_nofollow(const char *pathname)
|
||||
|
||||
if (pathname && kern_path(pathname, 0, &path) == 0) {
|
||||
char *buf = tomoyo_realpath_from_path(&path);
|
||||
|
||||
path_put(&path);
|
||||
return buf;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ static bool tomoyo_check_task_acl(struct tomoyo_request_info *r,
|
||||
{
|
||||
const struct tomoyo_task_acl *acl = container_of(ptr, typeof(*acl),
|
||||
head);
|
||||
|
||||
return !tomoyo_pathcmp(r->param.task.domainname, acl->domainname);
|
||||
}
|
||||
|
||||
@ -42,6 +43,7 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
|
||||
{
|
||||
char *data;
|
||||
int error;
|
||||
|
||||
if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10)
|
||||
return -ENOMEM;
|
||||
data = memdup_user_nul(buf, count);
|
||||
@ -52,6 +54,7 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
|
||||
const int idx = tomoyo_read_lock();
|
||||
struct tomoyo_path_info name;
|
||||
struct tomoyo_request_info r;
|
||||
|
||||
name.name = data;
|
||||
tomoyo_fill_path_info(&name);
|
||||
/* Check "task manual_domain_transition" permission. */
|
||||
@ -67,20 +70,16 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
|
||||
if (!new_domain) {
|
||||
error = -ENOENT;
|
||||
} else {
|
||||
struct cred *cred = prepare_creds();
|
||||
if (!cred) {
|
||||
error = -ENOMEM;
|
||||
} else {
|
||||
struct tomoyo_task *s = tomoyo_task(current);
|
||||
struct tomoyo_domain_info *old_domain =
|
||||
cred->security;
|
||||
cred->security = new_domain;
|
||||
s->domain_info;
|
||||
|
||||
s->domain_info = new_domain;
|
||||
atomic_inc(&new_domain->users);
|
||||
atomic_dec(&old_domain->users);
|
||||
commit_creds(cred);
|
||||
error = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
tomoyo_read_unlock(idx);
|
||||
} else
|
||||
error = -EINVAL;
|
||||
@ -104,6 +103,7 @@ static ssize_t tomoyo_read_self(struct file *file, char __user *buf,
|
||||
const char *domain = tomoyo_domain()->domainname->name;
|
||||
loff_t len = strlen(domain);
|
||||
loff_t pos = *ppos;
|
||||
|
||||
if (pos >= len || !count)
|
||||
return 0;
|
||||
len -= pos;
|
||||
@ -234,10 +234,14 @@ static void __init tomoyo_create_entry(const char *name, const umode_t mode,
|
||||
*/
|
||||
static int __init tomoyo_initerface_init(void)
|
||||
{
|
||||
struct tomoyo_domain_info *domain;
|
||||
struct dentry *tomoyo_dir;
|
||||
|
||||
if (!tomoyo_enabled)
|
||||
return 0;
|
||||
domain = tomoyo_domain();
|
||||
/* Don't create securityfs entries unless registered. */
|
||||
if (current_cred()->security != &tomoyo_kernel_domain)
|
||||
if (domain != &tomoyo_kernel_domain)
|
||||
return 0;
|
||||
|
||||
tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
|
||||
|
@ -9,17 +9,19 @@
|
||||
#include "common.h"
|
||||
|
||||
/**
|
||||
* tomoyo_cred_alloc_blank - Target for security_cred_alloc_blank().
|
||||
* tomoyo_domain - Get "struct tomoyo_domain_info" for current thread.
|
||||
*
|
||||
* @new: Pointer to "struct cred".
|
||||
* @gfp: Memory allocation flags.
|
||||
*
|
||||
* Returns 0.
|
||||
* Returns pointer to "struct tomoyo_domain_info" for current thread.
|
||||
*/
|
||||
static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
|
||||
struct tomoyo_domain_info *tomoyo_domain(void)
|
||||
{
|
||||
new->security = NULL;
|
||||
return 0;
|
||||
struct tomoyo_task *s = tomoyo_task(current);
|
||||
|
||||
if (s->old_domain_info && !current->in_execve) {
|
||||
atomic_dec(&s->old_domain_info->users);
|
||||
s->old_domain_info = NULL;
|
||||
}
|
||||
return s->domain_info;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -34,42 +36,38 @@ static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
|
||||
static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct tomoyo_domain_info *domain = old->security;
|
||||
new->security = domain;
|
||||
if (domain)
|
||||
atomic_inc(&domain->users);
|
||||
/* Restore old_domain_info saved by previous execve() request. */
|
||||
struct tomoyo_task *s = tomoyo_task(current);
|
||||
|
||||
if (s->old_domain_info && !current->in_execve) {
|
||||
atomic_dec(&s->domain_info->users);
|
||||
s->domain_info = s->old_domain_info;
|
||||
s->old_domain_info = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_cred_transfer - Target for security_transfer_creds().
|
||||
* tomoyo_bprm_committed_creds - Target for security_bprm_committed_creds().
|
||||
*
|
||||
* @new: Pointer to "struct cred".
|
||||
* @old: Pointer to "struct cred".
|
||||
* @bprm: Pointer to "struct linux_binprm".
|
||||
*/
|
||||
static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
|
||||
static void tomoyo_bprm_committed_creds(struct linux_binprm *bprm)
|
||||
{
|
||||
tomoyo_cred_prepare(new, old, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_cred_free - Target for security_cred_free().
|
||||
*
|
||||
* @cred: Pointer to "struct cred".
|
||||
*/
|
||||
static void tomoyo_cred_free(struct cred *cred)
|
||||
{
|
||||
struct tomoyo_domain_info *domain = cred->security;
|
||||
if (domain)
|
||||
atomic_dec(&domain->users);
|
||||
/* Clear old_domain_info saved by execve() request. */
|
||||
struct tomoyo_task *s = tomoyo_task(current);
|
||||
|
||||
atomic_dec(&s->old_domain_info->users);
|
||||
s->old_domain_info = NULL;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
||||
/**
|
||||
* tomoyo_bprm_set_creds - Target for security_bprm_set_creds().
|
||||
*
|
||||
* @bprm: Pointer to "struct linux_binprm".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
* Returns 0.
|
||||
*/
|
||||
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
|
||||
{
|
||||
@ -79,29 +77,15 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
|
||||
*/
|
||||
if (bprm->called_set_creds)
|
||||
return 0;
|
||||
#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
||||
/*
|
||||
* Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
|
||||
* for the first time.
|
||||
*/
|
||||
if (!tomoyo_policy_loaded)
|
||||
tomoyo_load_policy(bprm->filename);
|
||||
#endif
|
||||
/*
|
||||
* Release reference to "struct tomoyo_domain_info" stored inside
|
||||
* "bprm->cred->security". New reference to "struct tomoyo_domain_info"
|
||||
* stored inside "bprm->cred->security" will be acquired later inside
|
||||
* tomoyo_find_next_domain().
|
||||
*/
|
||||
atomic_dec(&((struct tomoyo_domain_info *)
|
||||
bprm->cred->security)->users);
|
||||
/*
|
||||
* Tell tomoyo_bprm_check_security() is called for the first time of an
|
||||
* execve operation.
|
||||
*/
|
||||
bprm->cred->security = NULL;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* tomoyo_bprm_check_security - Target for security_bprm_check().
|
||||
@ -112,23 +96,24 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
|
||||
*/
|
||||
static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
|
||||
{
|
||||
struct tomoyo_domain_info *domain = bprm->cred->security;
|
||||
struct tomoyo_task *s = tomoyo_task(current);
|
||||
|
||||
/*
|
||||
* Execute permission is checked against pathname passed to do_execve()
|
||||
* using current domain.
|
||||
*/
|
||||
if (!domain) {
|
||||
if (!s->old_domain_info) {
|
||||
const int idx = tomoyo_read_lock();
|
||||
const int err = tomoyo_find_next_domain(bprm);
|
||||
|
||||
tomoyo_read_unlock(idx);
|
||||
return err;
|
||||
}
|
||||
/*
|
||||
* Read permission is checked against interpreters using next domain.
|
||||
*/
|
||||
return tomoyo_check_open_permission(domain, &bprm->file->f_path,
|
||||
O_RDONLY);
|
||||
return tomoyo_check_open_permission(s->domain_info,
|
||||
&bprm->file->f_path, O_RDONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -167,6 +152,7 @@ static int tomoyo_path_truncate(const struct path *path)
|
||||
static int tomoyo_path_unlink(const struct path *parent, struct dentry *dentry)
|
||||
{
|
||||
struct path path = { .mnt = parent->mnt, .dentry = dentry };
|
||||
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL);
|
||||
}
|
||||
|
||||
@ -183,6 +169,7 @@ static int tomoyo_path_mkdir(const struct path *parent, struct dentry *dentry,
|
||||
umode_t mode)
|
||||
{
|
||||
struct path path = { .mnt = parent->mnt, .dentry = dentry };
|
||||
|
||||
return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path,
|
||||
mode & S_IALLUGO);
|
||||
}
|
||||
@ -198,6 +185,7 @@ static int tomoyo_path_mkdir(const struct path *parent, struct dentry *dentry,
|
||||
static int tomoyo_path_rmdir(const struct path *parent, struct dentry *dentry)
|
||||
{
|
||||
struct path path = { .mnt = parent->mnt, .dentry = dentry };
|
||||
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL);
|
||||
}
|
||||
|
||||
@ -214,6 +202,7 @@ static int tomoyo_path_symlink(const struct path *parent, struct dentry *dentry,
|
||||
const char *old_name)
|
||||
{
|
||||
struct path path = { .mnt = parent->mnt, .dentry = dentry };
|
||||
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path, old_name);
|
||||
}
|
||||
|
||||
@ -271,6 +260,7 @@ static int tomoyo_path_link(struct dentry *old_dentry, const struct path *new_di
|
||||
{
|
||||
struct path path1 = { .mnt = new_dir->mnt, .dentry = old_dentry };
|
||||
struct path path2 = { .mnt = new_dir->mnt, .dentry = new_dentry };
|
||||
|
||||
return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2);
|
||||
}
|
||||
|
||||
@ -291,6 +281,7 @@ static int tomoyo_path_rename(const struct path *old_parent,
|
||||
{
|
||||
struct path path1 = { .mnt = old_parent->mnt, .dentry = old_dentry };
|
||||
struct path path2 = { .mnt = new_parent->mnt, .dentry = new_dentry };
|
||||
|
||||
return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2);
|
||||
}
|
||||
|
||||
@ -322,11 +313,11 @@ static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
|
||||
*/
|
||||
static int tomoyo_file_open(struct file *f)
|
||||
{
|
||||
int flags = f->f_flags;
|
||||
/* Don't check read permission here if called from do_execve(). */
|
||||
if (current->in_execve)
|
||||
return 0;
|
||||
return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
|
||||
return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path,
|
||||
f->f_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -370,6 +361,7 @@ static int tomoyo_path_chmod(const struct path *path, umode_t mode)
|
||||
static int tomoyo_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (uid_valid(uid))
|
||||
error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path,
|
||||
from_kuid(&init_user_ns, uid));
|
||||
@ -419,6 +411,7 @@ static int tomoyo_sb_mount(const char *dev_name, const struct path *path,
|
||||
static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)
|
||||
{
|
||||
struct path path = { .mnt = mnt, .dentry = mnt->mnt_root };
|
||||
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path, NULL);
|
||||
}
|
||||
|
||||
@ -493,16 +486,61 @@ static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
return tomoyo_socket_sendmsg_permission(sock, msg, size);
|
||||
}
|
||||
|
||||
struct lsm_blob_sizes tomoyo_blob_sizes __lsm_ro_after_init = {
|
||||
.lbs_task = sizeof(struct tomoyo_task),
|
||||
};
|
||||
|
||||
/**
|
||||
* tomoyo_task_alloc - Target for security_task_alloc().
|
||||
*
|
||||
* @task: Pointer to "struct task_struct".
|
||||
* @flags: clone() flags.
|
||||
*
|
||||
* Returns 0.
|
||||
*/
|
||||
static int tomoyo_task_alloc(struct task_struct *task,
|
||||
unsigned long clone_flags)
|
||||
{
|
||||
struct tomoyo_task *old = tomoyo_task(current);
|
||||
struct tomoyo_task *new = tomoyo_task(task);
|
||||
|
||||
new->domain_info = old->domain_info;
|
||||
atomic_inc(&new->domain_info->users);
|
||||
new->old_domain_info = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_task_free - Target for security_task_free().
|
||||
*
|
||||
* @task: Pointer to "struct task_struct".
|
||||
*/
|
||||
static void tomoyo_task_free(struct task_struct *task)
|
||||
{
|
||||
struct tomoyo_task *s = tomoyo_task(task);
|
||||
|
||||
if (s->domain_info) {
|
||||
atomic_dec(&s->domain_info->users);
|
||||
s->domain_info = NULL;
|
||||
}
|
||||
if (s->old_domain_info) {
|
||||
atomic_dec(&s->old_domain_info->users);
|
||||
s->old_domain_info = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* tomoyo_security_ops is a "struct security_operations" which is used for
|
||||
* registering TOMOYO.
|
||||
*/
|
||||
static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = {
|
||||
LSM_HOOK_INIT(cred_alloc_blank, tomoyo_cred_alloc_blank),
|
||||
LSM_HOOK_INIT(cred_prepare, tomoyo_cred_prepare),
|
||||
LSM_HOOK_INIT(cred_transfer, tomoyo_cred_transfer),
|
||||
LSM_HOOK_INIT(cred_free, tomoyo_cred_free),
|
||||
LSM_HOOK_INIT(bprm_committed_creds, tomoyo_bprm_committed_creds),
|
||||
LSM_HOOK_INIT(task_alloc, tomoyo_task_alloc),
|
||||
LSM_HOOK_INIT(task_free, tomoyo_task_free),
|
||||
#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
||||
LSM_HOOK_INIT(bprm_set_creds, tomoyo_bprm_set_creds),
|
||||
#endif
|
||||
LSM_HOOK_INIT(bprm_check_security, tomoyo_bprm_check_security),
|
||||
LSM_HOOK_INIT(file_fcntl, tomoyo_file_fcntl),
|
||||
LSM_HOOK_INIT(file_open, tomoyo_file_open),
|
||||
@ -531,6 +569,8 @@ static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = {
|
||||
/* Lock for GC. */
|
||||
DEFINE_SRCU(tomoyo_ss);
|
||||
|
||||
int tomoyo_enabled __lsm_ro_after_init = 1;
|
||||
|
||||
/**
|
||||
* tomoyo_init - Register TOMOYO Linux as a LSM module.
|
||||
*
|
||||
@ -538,19 +578,23 @@ DEFINE_SRCU(tomoyo_ss);
|
||||
*/
|
||||
static int __init tomoyo_init(void)
|
||||
{
|
||||
struct cred *cred = (struct cred *) current_cred();
|
||||
struct tomoyo_task *s = tomoyo_task(current);
|
||||
|
||||
if (!security_module_enable("tomoyo"))
|
||||
return 0;
|
||||
/* register ourselves with the security framework */
|
||||
security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
|
||||
printk(KERN_INFO "TOMOYO Linux initialized\n");
|
||||
cred->security = &tomoyo_kernel_domain;
|
||||
pr_info("TOMOYO Linux initialized\n");
|
||||
s->domain_info = &tomoyo_kernel_domain;
|
||||
atomic_inc(&tomoyo_kernel_domain.users);
|
||||
s->old_domain_info = NULL;
|
||||
tomoyo_mm_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_LSM(tomoyo) = {
|
||||
.name = "tomoyo",
|
||||
.enabled = &tomoyo_enabled,
|
||||
.flags = LSM_FLAG_LEGACY_MAJOR,
|
||||
.blobs = &tomoyo_blob_sizes,
|
||||
.init = tomoyo_init,
|
||||
};
|
||||
|
@ -91,6 +91,7 @@ const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX] = {
|
||||
void tomoyo_convert_time(time64_t time64, struct tomoyo_time *stamp)
|
||||
{
|
||||
struct tm tm;
|
||||
|
||||
time64_to_tm(time64, 0, &tm);
|
||||
stamp->sec = tm.tm_sec;
|
||||
stamp->min = tm.tm_min;
|
||||
@ -113,6 +114,7 @@ void tomoyo_convert_time(time64_t time64, struct tomoyo_time *stamp)
|
||||
bool tomoyo_permstr(const char *string, const char *keyword)
|
||||
{
|
||||
const char *cp = strstr(string, keyword);
|
||||
|
||||
if (cp)
|
||||
return cp == string || *(cp - 1) == '/';
|
||||
return false;
|
||||
@ -132,6 +134,7 @@ char *tomoyo_read_token(struct tomoyo_acl_param *param)
|
||||
{
|
||||
char *pos = param->data;
|
||||
char *del = strchr(pos, ' ');
|
||||
|
||||
if (del)
|
||||
*del++ = '\0';
|
||||
else
|
||||
@ -152,6 +155,7 @@ const struct tomoyo_path_info *tomoyo_get_domainname
|
||||
{
|
||||
char *start = param->data;
|
||||
char *pos = start;
|
||||
|
||||
while (*pos) {
|
||||
if (*pos++ != ' ' || *pos++ == '/')
|
||||
continue;
|
||||
@ -181,8 +185,10 @@ u8 tomoyo_parse_ulong(unsigned long *result, char **str)
|
||||
const char *cp = *str;
|
||||
char *ep;
|
||||
int base = 10;
|
||||
|
||||
if (*cp == '0') {
|
||||
char c = *(cp + 1);
|
||||
|
||||
if (c == 'x' || c == 'X') {
|
||||
base = 16;
|
||||
cp += 2;
|
||||
@ -240,6 +246,7 @@ bool tomoyo_parse_name_union(struct tomoyo_acl_param *param,
|
||||
struct tomoyo_name_union *ptr)
|
||||
{
|
||||
char *filename;
|
||||
|
||||
if (param->data[0] == '@') {
|
||||
param->data++;
|
||||
ptr->group = tomoyo_get_group(param, TOMOYO_PATH_GROUP);
|
||||
@ -266,6 +273,7 @@ bool tomoyo_parse_number_union(struct tomoyo_acl_param *param,
|
||||
char *data;
|
||||
u8 type;
|
||||
unsigned long v;
|
||||
|
||||
memset(ptr, 0, sizeof(*ptr));
|
||||
if (param->data[0] == '@') {
|
||||
param->data++;
|
||||
@ -429,6 +437,7 @@ static bool tomoyo_correct_word2(const char *string, size_t len)
|
||||
unsigned char c;
|
||||
unsigned char d;
|
||||
unsigned char e;
|
||||
|
||||
if (!len)
|
||||
goto out;
|
||||
while (len--) {
|
||||
@ -533,6 +542,7 @@ bool tomoyo_correct_domain(const unsigned char *domainname)
|
||||
return true;
|
||||
while (1) {
|
||||
const unsigned char *cp = strchr(domainname, ' ');
|
||||
|
||||
if (!cp)
|
||||
break;
|
||||
if (*domainname != '/' ||
|
||||
@ -554,6 +564,7 @@ bool tomoyo_domain_def(const unsigned char *buffer)
|
||||
{
|
||||
const unsigned char *cp;
|
||||
int len;
|
||||
|
||||
if (*buffer != '<')
|
||||
return false;
|
||||
cp = strchr(buffer, ' ');
|
||||
@ -668,6 +679,9 @@ static bool tomoyo_file_matches_pattern2(const char *filename,
|
||||
{
|
||||
while (filename < filename_end && pattern < pattern_end) {
|
||||
char c;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
if (*pattern != '\\') {
|
||||
if (*filename++ != *pattern++)
|
||||
return false;
|
||||
@ -676,8 +690,6 @@ static bool tomoyo_file_matches_pattern2(const char *filename,
|
||||
c = *filename;
|
||||
pattern++;
|
||||
switch (*pattern) {
|
||||
int i;
|
||||
int j;
|
||||
case '?':
|
||||
if (c == '/') {
|
||||
return false;
|
||||
@ -985,6 +997,7 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r,
|
||||
struct tomoyo_domain_info *domain, const u8 index)
|
||||
{
|
||||
u8 profile;
|
||||
|
||||
memset(r, 0, sizeof(*r));
|
||||
if (!domain)
|
||||
domain = tomoyo_domain();
|
||||
@ -1018,6 +1031,7 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r)
|
||||
list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
|
||||
u16 perm;
|
||||
u8 i;
|
||||
|
||||
if (ptr->is_deleted)
|
||||
continue;
|
||||
switch (ptr->type) {
|
||||
@ -1062,9 +1076,8 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r)
|
||||
domain->flags[TOMOYO_DIF_QUOTA_WARNED] = true;
|
||||
/* r->granted = false; */
|
||||
tomoyo_write_log(r, "%s", tomoyo_dif[TOMOYO_DIF_QUOTA_WARNED]);
|
||||
printk(KERN_WARNING "WARNING: "
|
||||
"Domain '%s' has too many ACLs to hold. "
|
||||
"Stopped learning mode.\n", domain->domainname->name);
|
||||
pr_warn("WARNING: Domain '%s' has too many ACLs to hold. Stopped learning mode.\n",
|
||||
domain->domainname->name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -479,9 +479,15 @@ static void __init yama_init_sysctl(void)
|
||||
static inline void yama_init_sysctl(void) { }
|
||||
#endif /* CONFIG_SYSCTL */
|
||||
|
||||
void __init yama_add_hooks(void)
|
||||
static int __init yama_init(void)
|
||||
{
|
||||
pr_info("Yama: becoming mindful.\n");
|
||||
security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama");
|
||||
yama_init_sysctl();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_LSM(yama) = {
|
||||
.name = "yama",
|
||||
.init = yama_init,
|
||||
};
|
||||
|
1
tools/testing/selftests/safesetid/.gitignore
vendored
Normal file
1
tools/testing/selftests/safesetid/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
safesetid-test
|
8
tools/testing/selftests/safesetid/Makefile
Normal file
8
tools/testing/selftests/safesetid/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Makefile for mount selftests.
|
||||
CFLAGS = -Wall -lcap -O2
|
||||
|
||||
TEST_PROGS := run_tests.sh
|
||||
TEST_GEN_FILES := safesetid-test
|
||||
|
||||
include ../lib.mk
|
2
tools/testing/selftests/safesetid/config
Normal file
2
tools/testing/selftests/safesetid/config
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_SECURITY=y
|
||||
CONFIG_SECURITYFS=y
|
334
tools/testing/selftests/safesetid/safesetid-test.c
Normal file
334
tools/testing/selftests/safesetid/safesetid-test.c
Normal file
@ -0,0 +1,334 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
#include <sys/capability.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifndef CLONE_NEWUSER
|
||||
# define CLONE_NEWUSER 0x10000000
|
||||
#endif
|
||||
|
||||
#define ROOT_USER 0
|
||||
#define RESTRICTED_PARENT 1
|
||||
#define ALLOWED_CHILD1 2
|
||||
#define ALLOWED_CHILD2 3
|
||||
#define NO_POLICY_USER 4
|
||||
|
||||
char* add_whitelist_policy_file = "/sys/kernel/security/safesetid/add_whitelist_policy";
|
||||
|
||||
static void die(char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static bool vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap)
|
||||
{
|
||||
char buf[4096];
|
||||
int fd;
|
||||
ssize_t written;
|
||||
int buf_len;
|
||||
|
||||
buf_len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
if (buf_len < 0) {
|
||||
printf("vsnprintf failed: %s\n",
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (buf_len >= sizeof(buf)) {
|
||||
printf("vsnprintf output truncated\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fd = open(filename, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
if ((errno == ENOENT) && enoent_ok)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
written = write(fd, buf, buf_len);
|
||||
if (written != buf_len) {
|
||||
if (written >= 0) {
|
||||
printf("short write to %s\n", filename);
|
||||
return false;
|
||||
} else {
|
||||
printf("write to %s failed: %s\n",
|
||||
filename, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (close(fd) != 0) {
|
||||
printf("close of %s failed: %s\n",
|
||||
filename, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write_file(char *filename, char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
bool ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = vmaybe_write_file(false, filename, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ensure_user_exists(uid_t uid)
|
||||
{
|
||||
struct passwd p;
|
||||
|
||||
FILE *fd;
|
||||
char name_str[10];
|
||||
|
||||
if (getpwuid(uid) == NULL) {
|
||||
memset(&p,0x00,sizeof(p));
|
||||
fd=fopen("/etc/passwd","a");
|
||||
if (fd == NULL)
|
||||
die("couldn't open file\n");
|
||||
if (fseek(fd, 0, SEEK_END))
|
||||
die("couldn't fseek\n");
|
||||
snprintf(name_str, 10, "%d", uid);
|
||||
p.pw_name=name_str;
|
||||
p.pw_uid=uid;
|
||||
p.pw_gecos="Test account";
|
||||
p.pw_dir="/dev/null";
|
||||
p.pw_shell="/bin/false";
|
||||
int value = putpwent(&p,fd);
|
||||
if (value != 0)
|
||||
die("putpwent failed\n");
|
||||
if (fclose(fd))
|
||||
die("fclose failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void ensure_securityfs_mounted(void)
|
||||
{
|
||||
int fd = open(add_whitelist_policy_file, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
if (errno == ENOENT) {
|
||||
// Need to mount securityfs
|
||||
if (mount("securityfs", "/sys/kernel/security",
|
||||
"securityfs", 0, NULL) < 0)
|
||||
die("mounting securityfs failed\n");
|
||||
} else {
|
||||
die("couldn't find securityfs for unknown reason\n");
|
||||
}
|
||||
} else {
|
||||
if (close(fd) != 0) {
|
||||
die("close of %s failed: %s\n",
|
||||
add_whitelist_policy_file, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write_policies(void)
|
||||
{
|
||||
ssize_t written;
|
||||
int fd;
|
||||
|
||||
fd = open(add_whitelist_policy_file, O_WRONLY);
|
||||
if (fd < 0)
|
||||
die("cant open add_whitelist_policy file\n");
|
||||
written = write(fd, "1:2", strlen("1:2"));
|
||||
if (written != strlen("1:2")) {
|
||||
if (written >= 0) {
|
||||
die("short write to %s\n", add_whitelist_policy_file);
|
||||
} else {
|
||||
die("write to %s failed: %s\n",
|
||||
add_whitelist_policy_file, strerror(errno));
|
||||
}
|
||||
}
|
||||
written = write(fd, "1:3", strlen("1:3"));
|
||||
if (written != strlen("1:3")) {
|
||||
if (written >= 0) {
|
||||
die("short write to %s\n", add_whitelist_policy_file);
|
||||
} else {
|
||||
die("write to %s failed: %s\n",
|
||||
add_whitelist_policy_file, strerror(errno));
|
||||
}
|
||||
}
|
||||
if (close(fd) != 0) {
|
||||
die("close of %s failed: %s\n",
|
||||
add_whitelist_policy_file, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static bool test_userns(bool expect_success)
|
||||
{
|
||||
uid_t uid;
|
||||
char map_file_name[32];
|
||||
size_t sz = sizeof(map_file_name);
|
||||
pid_t cpid;
|
||||
bool success;
|
||||
|
||||
uid = getuid();
|
||||
|
||||
int clone_flags = CLONE_NEWUSER;
|
||||
cpid = syscall(SYS_clone, clone_flags, NULL);
|
||||
if (cpid == -1) {
|
||||
printf("clone failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cpid == 0) { /* Code executed by child */
|
||||
// Give parent 1 second to write map file
|
||||
sleep(1);
|
||||
exit(EXIT_SUCCESS);
|
||||
} else { /* Code executed by parent */
|
||||
if(snprintf(map_file_name, sz, "/proc/%d/uid_map", cpid) < 0) {
|
||||
printf("preparing file name string failed");
|
||||
return false;
|
||||
}
|
||||
success = write_file(map_file_name, "0 0 1", uid);
|
||||
return success == expect_success;
|
||||
}
|
||||
|
||||
printf("should not reach here");
|
||||
return false;
|
||||
}
|
||||
|
||||
static void test_setuid(uid_t child_uid, bool expect_success)
|
||||
{
|
||||
pid_t cpid, w;
|
||||
int wstatus;
|
||||
|
||||
cpid = fork();
|
||||
if (cpid == -1) {
|
||||
die("fork\n");
|
||||
}
|
||||
|
||||
if (cpid == 0) { /* Code executed by child */
|
||||
setuid(child_uid);
|
||||
if (getuid() == child_uid)
|
||||
exit(EXIT_SUCCESS);
|
||||
else
|
||||
exit(EXIT_FAILURE);
|
||||
} else { /* Code executed by parent */
|
||||
do {
|
||||
w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED);
|
||||
if (w == -1) {
|
||||
die("waitpid\n");
|
||||
}
|
||||
|
||||
if (WIFEXITED(wstatus)) {
|
||||
if (WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
|
||||
if (expect_success) {
|
||||
return;
|
||||
} else {
|
||||
die("unexpected success\n");
|
||||
}
|
||||
} else {
|
||||
if (expect_success) {
|
||||
die("unexpected failure\n");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (WIFSIGNALED(wstatus)) {
|
||||
if (WTERMSIG(wstatus) == 9) {
|
||||
if (expect_success)
|
||||
die("killed unexpectedly\n");
|
||||
else
|
||||
return;
|
||||
} else {
|
||||
die("unexpected signal: %d\n", wstatus);
|
||||
}
|
||||
} else {
|
||||
die("unexpected status: %d\n", wstatus);
|
||||
}
|
||||
} while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
|
||||
}
|
||||
|
||||
die("should not reach here\n");
|
||||
}
|
||||
|
||||
static void ensure_users_exist(void)
|
||||
{
|
||||
ensure_user_exists(ROOT_USER);
|
||||
ensure_user_exists(RESTRICTED_PARENT);
|
||||
ensure_user_exists(ALLOWED_CHILD1);
|
||||
ensure_user_exists(ALLOWED_CHILD2);
|
||||
ensure_user_exists(NO_POLICY_USER);
|
||||
}
|
||||
|
||||
static void drop_caps(bool setid_retained)
|
||||
{
|
||||
cap_value_t cap_values[] = {CAP_SETUID, CAP_SETGID};
|
||||
cap_t caps;
|
||||
|
||||
caps = cap_get_proc();
|
||||
if (setid_retained)
|
||||
cap_set_flag(caps, CAP_EFFECTIVE, 2, cap_values, CAP_SET);
|
||||
else
|
||||
cap_clear(caps);
|
||||
cap_set_proc(caps);
|
||||
cap_free(caps);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ensure_users_exist();
|
||||
ensure_securityfs_mounted();
|
||||
write_policies();
|
||||
|
||||
if (prctl(PR_SET_KEEPCAPS, 1L))
|
||||
die("Error with set keepcaps\n");
|
||||
|
||||
// First test to make sure we can write userns mappings from a user
|
||||
// that doesn't have any restrictions (as long as it has CAP_SETUID);
|
||||
setuid(NO_POLICY_USER);
|
||||
setgid(NO_POLICY_USER);
|
||||
|
||||
// Take away all but setid caps
|
||||
drop_caps(true);
|
||||
|
||||
// Need PR_SET_DUMPABLE flag set so we can write /proc/[pid]/uid_map
|
||||
// from non-root parent process.
|
||||
if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0))
|
||||
die("Error with set dumpable\n");
|
||||
|
||||
if (!test_userns(true)) {
|
||||
die("test_userns failed when it should work\n");
|
||||
}
|
||||
|
||||
setuid(RESTRICTED_PARENT);
|
||||
setgid(RESTRICTED_PARENT);
|
||||
|
||||
test_setuid(ROOT_USER, false);
|
||||
test_setuid(ALLOWED_CHILD1, true);
|
||||
test_setuid(ALLOWED_CHILD2, true);
|
||||
test_setuid(NO_POLICY_USER, false);
|
||||
|
||||
if (!test_userns(false)) {
|
||||
die("test_userns worked when it should fail\n");
|
||||
}
|
||||
|
||||
// Now take away all caps
|
||||
drop_caps(false);
|
||||
test_setuid(2, false);
|
||||
test_setuid(3, false);
|
||||
test_setuid(4, false);
|
||||
|
||||
// NOTE: this test doesn't clean up users that were created in
|
||||
// /etc/passwd or flush policies that were added to the LSM.
|
||||
return EXIT_SUCCESS;
|
||||
}
|
26
tools/testing/selftests/safesetid/safesetid-test.sh
Executable file
26
tools/testing/selftests/safesetid/safesetid-test.sh
Executable file
@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
TCID="safesetid-test.sh"
|
||||
errcode=0
|
||||
|
||||
# Kselftest framework requirement - SKIP code is 4.
|
||||
ksft_skip=4
|
||||
|
||||
check_root()
|
||||
{
|
||||
uid=$(id -u)
|
||||
if [ $uid -ne 0 ]; then
|
||||
echo $TCID: must be run as root >&2
|
||||
exit $ksft_skip
|
||||
fi
|
||||
}
|
||||
|
||||
main_function()
|
||||
{
|
||||
check_root
|
||||
./safesetid-test
|
||||
}
|
||||
|
||||
main_function
|
||||
echo "$TCID: done"
|
||||
exit $errcode
|
Loading…
Reference in New Issue
Block a user