forked from Minki/linux
Merge branch 'next' into for-linus
This commit is contained in:
commit
73fbad283c
@ -184,8 +184,9 @@ length. Single character labels using special characters, that being anything
|
||||
other than a letter or digit, are reserved for use by the Smack development
|
||||
team. Smack labels are unstructured, case sensitive, and the only operation
|
||||
ever performed on them is comparison for equality. Smack labels cannot
|
||||
contain unprintable characters or the "/" (slash) character. Smack labels
|
||||
cannot begin with a '-', which is reserved for special options.
|
||||
contain unprintable characters, the "/" (slash), the "\" (backslash), the "'"
|
||||
(quote) and '"' (double-quote) characters.
|
||||
Smack labels cannot begin with a '-', which is reserved for special options.
|
||||
|
||||
There are some predefined labels:
|
||||
|
||||
@ -523,3 +524,18 @@ Smack supports some mount options:
|
||||
|
||||
These mount options apply to all file system types.
|
||||
|
||||
Smack auditing
|
||||
|
||||
If you want Smack auditing of security events, you need to set CONFIG_AUDIT
|
||||
in your kernel configuration.
|
||||
By default, all denied events will be audited. You can change this behavior by
|
||||
writing a single character to the /smack/logging file :
|
||||
0 : no logging
|
||||
1 : log denied (default)
|
||||
2 : log accepted
|
||||
3 : log denied & accepted
|
||||
|
||||
Events are logged as 'key=value' pairs, for each event you at least will get
|
||||
the subjet, the object, the rights requested, the action, the kernel function
|
||||
that triggered the event, plus other pairs depending on the type of event
|
||||
audited.
|
||||
|
@ -916,6 +916,12 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||
Formt: { "sha1" | "md5" }
|
||||
default: "sha1"
|
||||
|
||||
ima_tcb [IMA]
|
||||
Load a policy which meets the needs of the Trusted
|
||||
Computing Base. This means IMA will measure all
|
||||
programs exec'd, files mmap'd for exec, and all files
|
||||
opened for read by uid=0.
|
||||
|
||||
in2000= [HW,SCSI]
|
||||
See header of drivers/scsi/in2000.c.
|
||||
|
||||
|
@ -32,6 +32,7 @@ show up in /proc/sys/kernel:
|
||||
- kstack_depth_to_print [ X86 only ]
|
||||
- l2cr [ PPC only ]
|
||||
- modprobe ==> Documentation/debugging-modules.txt
|
||||
- modules_disabled
|
||||
- msgmax
|
||||
- msgmnb
|
||||
- msgmni
|
||||
@ -184,6 +185,16 @@ kernel stack.
|
||||
|
||||
==============================================================
|
||||
|
||||
modules_disabled:
|
||||
|
||||
A toggle value indicating if modules are allowed to be loaded
|
||||
in an otherwise modular kernel. This toggle defaults to off
|
||||
(0), but can be set true (1). Once true, modules can be
|
||||
neither loaded nor unloaded, and the toggle cannot be set back
|
||||
to false.
|
||||
|
||||
==============================================================
|
||||
|
||||
osrelease, ostype & version:
|
||||
|
||||
# cat osrelease
|
||||
|
@ -1488,7 +1488,7 @@ int compat_do_execve(char * filename,
|
||||
if (!bprm)
|
||||
goto out_files;
|
||||
|
||||
retval = mutex_lock_interruptible(¤t->cred_exec_mutex);
|
||||
retval = mutex_lock_interruptible(¤t->cred_guard_mutex);
|
||||
if (retval < 0)
|
||||
goto out_free;
|
||||
current->in_execve = 1;
|
||||
@ -1550,7 +1550,7 @@ int compat_do_execve(char * filename,
|
||||
/* execve succeeded */
|
||||
current->fs->in_exec = 0;
|
||||
current->in_execve = 0;
|
||||
mutex_unlock(¤t->cred_exec_mutex);
|
||||
mutex_unlock(¤t->cred_guard_mutex);
|
||||
acct_update_integrals(current);
|
||||
free_bprm(bprm);
|
||||
if (displaced)
|
||||
@ -1573,7 +1573,7 @@ out_unmark:
|
||||
|
||||
out_unlock:
|
||||
current->in_execve = 0;
|
||||
mutex_unlock(¤t->cred_exec_mutex);
|
||||
mutex_unlock(¤t->cred_guard_mutex);
|
||||
|
||||
out_free:
|
||||
free_bprm(bprm);
|
||||
|
10
fs/exec.c
10
fs/exec.c
@ -1016,7 +1016,7 @@ void install_exec_creds(struct linux_binprm *bprm)
|
||||
commit_creds(bprm->cred);
|
||||
bprm->cred = NULL;
|
||||
|
||||
/* cred_exec_mutex must be held at least to this point to prevent
|
||||
/* cred_guard_mutex must be held at least to this point to prevent
|
||||
* ptrace_attach() from altering our determination of the task's
|
||||
* credentials; any time after this it may be unlocked */
|
||||
|
||||
@ -1026,7 +1026,7 @@ EXPORT_SYMBOL(install_exec_creds);
|
||||
|
||||
/*
|
||||
* determine how safe it is to execute the proposed program
|
||||
* - the caller must hold current->cred_exec_mutex to protect against
|
||||
* - the caller must hold current->cred_guard_mutex to protect against
|
||||
* PTRACE_ATTACH
|
||||
*/
|
||||
int check_unsafe_exec(struct linux_binprm *bprm)
|
||||
@ -1268,7 +1268,7 @@ int do_execve(char * filename,
|
||||
if (!bprm)
|
||||
goto out_files;
|
||||
|
||||
retval = mutex_lock_interruptible(¤t->cred_exec_mutex);
|
||||
retval = mutex_lock_interruptible(¤t->cred_guard_mutex);
|
||||
if (retval < 0)
|
||||
goto out_free;
|
||||
current->in_execve = 1;
|
||||
@ -1331,7 +1331,7 @@ int do_execve(char * filename,
|
||||
/* execve succeeded */
|
||||
current->fs->in_exec = 0;
|
||||
current->in_execve = 0;
|
||||
mutex_unlock(¤t->cred_exec_mutex);
|
||||
mutex_unlock(¤t->cred_guard_mutex);
|
||||
acct_update_integrals(current);
|
||||
free_bprm(bprm);
|
||||
if (displaced)
|
||||
@ -1354,7 +1354,7 @@ out_unmark:
|
||||
|
||||
out_unlock:
|
||||
current->in_execve = 0;
|
||||
mutex_unlock(¤t->cred_exec_mutex);
|
||||
mutex_unlock(¤t->cred_guard_mutex);
|
||||
|
||||
out_free:
|
||||
free_bprm(bprm);
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <linux/dnotify.h>
|
||||
#include <linux/statfs.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/ima.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
@ -986,6 +987,7 @@ struct file *hugetlb_file_setup(const char *name, size_t size, int acctflag)
|
||||
&hugetlbfs_file_operations);
|
||||
if (!file)
|
||||
goto out_dentry; /* inode is already attached */
|
||||
ima_counts_get(file);
|
||||
|
||||
return file;
|
||||
|
||||
|
@ -853,7 +853,8 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
|
||||
err = inode_permission(nd->path.dentry->d_inode,
|
||||
MAY_EXEC);
|
||||
if (!err)
|
||||
err = ima_path_check(&nd->path, MAY_EXEC);
|
||||
err = ima_path_check(&nd->path, MAY_EXEC,
|
||||
IMA_COUNT_UPDATE);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
@ -1515,7 +1516,8 @@ int may_open(struct path *path, int acc_mode, int flag)
|
||||
return error;
|
||||
|
||||
error = ima_path_check(path,
|
||||
acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));
|
||||
acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC),
|
||||
IMA_COUNT_UPDATE);
|
||||
if (error)
|
||||
return error;
|
||||
/*
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include <linux/security.h>
|
||||
#endif /* CONFIG_NFSD_V4 */
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/ima.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
@ -735,6 +736,8 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
|
||||
flags, cred);
|
||||
if (IS_ERR(*filp))
|
||||
host_err = PTR_ERR(*filp);
|
||||
else
|
||||
ima_counts_get(*filp);
|
||||
out_nfserr:
|
||||
err = nfserrno(host_err);
|
||||
out:
|
||||
@ -2024,6 +2027,7 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
|
||||
struct dentry *dentry, int acc)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct path path;
|
||||
int err;
|
||||
|
||||
if (acc == NFSD_MAY_NOP)
|
||||
@ -2096,7 +2100,17 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
|
||||
if (err == -EACCES && S_ISREG(inode->i_mode) &&
|
||||
acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE))
|
||||
err = inode_permission(inode, MAY_EXEC);
|
||||
if (err)
|
||||
goto nfsd_out;
|
||||
|
||||
/* Do integrity (permission) checking now, but defer incrementing
|
||||
* IMA counts to the actual file open.
|
||||
*/
|
||||
path.mnt = exp->ex_path.mnt;
|
||||
path.dentry = dentry;
|
||||
err = ima_path_check(&path, acc & (MAY_READ | MAY_WRITE | MAY_EXEC),
|
||||
IMA_COUNT_LEAVE);
|
||||
nfsd_out:
|
||||
return err? nfserrno(err) : 0;
|
||||
}
|
||||
|
||||
|
@ -2128,9 +2128,15 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
|
||||
if (copy_from_user(page, buf, count))
|
||||
goto out_free;
|
||||
|
||||
/* Guard against adverse ptrace interaction */
|
||||
length = mutex_lock_interruptible(&task->cred_guard_mutex);
|
||||
if (length < 0)
|
||||
goto out_free;
|
||||
|
||||
length = security_setprocattr(task,
|
||||
(char*)file->f_path.dentry->d_name.name,
|
||||
(void*)page, count);
|
||||
mutex_unlock(&task->cred_guard_mutex);
|
||||
out_free:
|
||||
free_page((unsigned long) page);
|
||||
out:
|
||||
|
@ -13,14 +13,17 @@
|
||||
#include <linux/fs.h>
|
||||
struct linux_binprm;
|
||||
|
||||
#define IMA_COUNT_UPDATE 1
|
||||
#define IMA_COUNT_LEAVE 0
|
||||
|
||||
#ifdef CONFIG_IMA
|
||||
extern int ima_bprm_check(struct linux_binprm *bprm);
|
||||
extern int ima_inode_alloc(struct inode *inode);
|
||||
extern void ima_inode_free(struct inode *inode);
|
||||
extern int ima_path_check(struct path *path, int mask);
|
||||
extern int ima_path_check(struct path *path, int mask, int update_counts);
|
||||
extern void ima_file_free(struct file *file);
|
||||
extern int ima_file_mmap(struct file *file, unsigned long prot);
|
||||
extern void ima_shm_check(struct file *file);
|
||||
extern void ima_counts_get(struct file *file);
|
||||
|
||||
#else
|
||||
static inline int ima_bprm_check(struct linux_binprm *bprm)
|
||||
@ -38,7 +41,7 @@ static inline void ima_inode_free(struct inode *inode)
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int ima_path_check(struct path *path, int mask)
|
||||
static inline int ima_path_check(struct path *path, int mask, int update_counts)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -53,7 +56,7 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ima_shm_check(struct file *file)
|
||||
static inline void ima_counts_get(struct file *file)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -145,8 +145,8 @@ extern struct cred init_cred;
|
||||
.group_leader = &tsk, \
|
||||
.real_cred = &init_cred, \
|
||||
.cred = &init_cred, \
|
||||
.cred_exec_mutex = \
|
||||
__MUTEX_INITIALIZER(tsk.cred_exec_mutex), \
|
||||
.cred_guard_mutex = \
|
||||
__MUTEX_INITIALIZER(tsk.cred_guard_mutex), \
|
||||
.comm = "swapper", \
|
||||
.thread = INIT_THREAD, \
|
||||
.fs = &init_fs, \
|
||||
|
111
include/linux/lsm_audit.h
Normal file
111
include/linux/lsm_audit.h
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Common LSM logging functions
|
||||
* Heavily borrowed from selinux/avc.h
|
||||
*
|
||||
* Author : Etienne BASSET <etienne.basset@ensta.org>
|
||||
*
|
||||
* All credits to : Stephen Smalley, <sds@epoch.ncsc.mil>
|
||||
* All BUGS to : Etienne BASSET <etienne.basset@ensta.org>
|
||||
*/
|
||||
#ifndef _LSM_COMMON_LOGGING_
|
||||
#define _LSM_COMMON_LOGGING_
|
||||
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/path.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
|
||||
/* Auxiliary data to use in generating the audit record. */
|
||||
struct common_audit_data {
|
||||
char type;
|
||||
#define LSM_AUDIT_DATA_FS 1
|
||||
#define LSM_AUDIT_DATA_NET 2
|
||||
#define LSM_AUDIT_DATA_CAP 3
|
||||
#define LSM_AUDIT_DATA_IPC 4
|
||||
#define LSM_AUDIT_DATA_TASK 5
|
||||
#define LSM_AUDIT_DATA_KEY 6
|
||||
struct task_struct *tsk;
|
||||
union {
|
||||
struct {
|
||||
struct path path;
|
||||
struct inode *inode;
|
||||
} fs;
|
||||
struct {
|
||||
int netif;
|
||||
struct sock *sk;
|
||||
u16 family;
|
||||
__be16 dport;
|
||||
__be16 sport;
|
||||
union {
|
||||
struct {
|
||||
__be32 daddr;
|
||||
__be32 saddr;
|
||||
} v4;
|
||||
struct {
|
||||
struct in6_addr daddr;
|
||||
struct in6_addr saddr;
|
||||
} v6;
|
||||
} fam;
|
||||
} net;
|
||||
int cap;
|
||||
int ipc_id;
|
||||
struct task_struct *tsk;
|
||||
#ifdef CONFIG_KEYS
|
||||
struct {
|
||||
key_serial_t key;
|
||||
char *key_desc;
|
||||
} key_struct;
|
||||
#endif
|
||||
} u;
|
||||
const char *function;
|
||||
/* this union contains LSM specific data */
|
||||
union {
|
||||
/* SMACK data */
|
||||
struct smack_audit_data {
|
||||
char *subject;
|
||||
char *object;
|
||||
char *request;
|
||||
int result;
|
||||
} smack_audit_data;
|
||||
/* SELinux data */
|
||||
struct {
|
||||
u32 ssid;
|
||||
u32 tsid;
|
||||
u16 tclass;
|
||||
u32 requested;
|
||||
u32 audited;
|
||||
struct av_decision *avd;
|
||||
int result;
|
||||
} selinux_audit_data;
|
||||
} lsm_priv;
|
||||
/* these callback will be implemented by a specific LSM */
|
||||
void (*lsm_pre_audit)(struct audit_buffer *, void *);
|
||||
void (*lsm_post_audit)(struct audit_buffer *, void *);
|
||||
};
|
||||
|
||||
#define v4info fam.v4
|
||||
#define v6info fam.v6
|
||||
|
||||
int ipv4_skb_to_auditdata(struct sk_buff *skb,
|
||||
struct common_audit_data *ad, u8 *proto);
|
||||
|
||||
int ipv6_skb_to_auditdata(struct sk_buff *skb,
|
||||
struct common_audit_data *ad, u8 *proto);
|
||||
|
||||
/* Initialize an LSM audit data structure. */
|
||||
#define COMMON_AUDIT_DATA_INIT(_d, _t) \
|
||||
{ memset((_d), 0, sizeof(struct common_audit_data)); \
|
||||
(_d)->type = LSM_AUDIT_DATA_##_t; (_d)->function = __func__; }
|
||||
|
||||
void common_lsm_audit(struct common_audit_data *a);
|
||||
|
||||
#endif
|
@ -9,6 +9,7 @@
|
||||
#define DEBUGFS_MAGIC 0x64626720
|
||||
#define SYSFS_MAGIC 0x62656572
|
||||
#define SECURITYFS_MAGIC 0x73636673
|
||||
#define SELINUX_MAGIC 0xf97cff8c
|
||||
#define TMPFS_MAGIC 0x01021994
|
||||
#define SQUASHFS_MAGIC 0x73717368
|
||||
#define EFS_SUPER_MAGIC 0x414A53
|
||||
|
@ -580,12 +580,10 @@ static inline void set_page_links(struct page *page, enum zone_type zone,
|
||||
*/
|
||||
static inline unsigned long round_hint_to_min(unsigned long hint)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY
|
||||
hint &= PAGE_MASK;
|
||||
if (((void *)hint != NULL) &&
|
||||
(hint < mmap_min_addr))
|
||||
return PAGE_ALIGN(mmap_min_addr);
|
||||
#endif
|
||||
return hint;
|
||||
}
|
||||
|
||||
|
@ -1267,7 +1267,9 @@ struct task_struct {
|
||||
* credentials (COW) */
|
||||
const struct cred *cred; /* effective (overridable) subjective task
|
||||
* credentials (COW) */
|
||||
struct mutex cred_exec_mutex; /* execve vs ptrace cred calculation mutex */
|
||||
struct mutex cred_guard_mutex; /* guard against foreign influences on
|
||||
* credential calculations
|
||||
* (notably. ptrace) */
|
||||
|
||||
char comm[TASK_COMM_LEN]; /* executable name excluding path
|
||||
- access with [gs]et_task_comm (which lock
|
||||
@ -1905,6 +1907,7 @@ extern void sched_dead(struct task_struct *p);
|
||||
|
||||
extern void proc_caches_init(void);
|
||||
extern void flush_signals(struct task_struct *);
|
||||
extern void __flush_signals(struct task_struct *);
|
||||
extern void ignore_signals(struct task_struct *);
|
||||
extern void flush_signal_handlers(struct task_struct *, int force_default);
|
||||
extern int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info);
|
||||
|
@ -2197,6 +2197,8 @@ static inline int security_file_mmap(struct file *file, unsigned long reqprot,
|
||||
unsigned long addr,
|
||||
unsigned long addr_only)
|
||||
{
|
||||
if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO))
|
||||
return -EACCES;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -384,7 +384,6 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
|
||||
error = PTR_ERR(file);
|
||||
if (IS_ERR(file))
|
||||
goto no_file;
|
||||
ima_shm_check(file);
|
||||
|
||||
id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
|
||||
if (id < 0) {
|
||||
@ -891,7 +890,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
|
||||
file = alloc_file(path.mnt, path.dentry, f_mode, &shm_file_operations);
|
||||
if (!file)
|
||||
goto out_free;
|
||||
ima_shm_check(file);
|
||||
ima_counts_get(file);
|
||||
|
||||
file->private_data = sfd;
|
||||
file->f_mapping = shp->shm_file->f_mapping;
|
||||
|
@ -167,7 +167,7 @@ EXPORT_SYMBOL(prepare_creds);
|
||||
|
||||
/*
|
||||
* Prepare credentials for current to perform an execve()
|
||||
* - The caller must hold current->cred_exec_mutex
|
||||
* - The caller must hold current->cred_guard_mutex
|
||||
*/
|
||||
struct cred *prepare_exec_creds(void)
|
||||
{
|
||||
@ -276,7 +276,7 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
|
||||
struct cred *new;
|
||||
int ret;
|
||||
|
||||
mutex_init(&p->cred_exec_mutex);
|
||||
mutex_init(&p->cred_guard_mutex);
|
||||
|
||||
if (
|
||||
#ifdef CONFIG_KEYS
|
||||
|
@ -1476,6 +1476,7 @@ static int wait_consider_task(struct task_struct *parent, int ptrace,
|
||||
*/
|
||||
if (*notask_error)
|
||||
*notask_error = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (likely(!ptrace) && unlikely(p->ptrace)) {
|
||||
|
@ -72,6 +72,9 @@ DEFINE_MUTEX(module_mutex);
|
||||
EXPORT_SYMBOL_GPL(module_mutex);
|
||||
static LIST_HEAD(modules);
|
||||
|
||||
/* Block module loading/unloading? */
|
||||
int modules_disabled = 0;
|
||||
|
||||
/* Waiting for a module to finish initializing? */
|
||||
static DECLARE_WAIT_QUEUE_HEAD(module_wq);
|
||||
|
||||
@ -777,7 +780,7 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
|
||||
char name[MODULE_NAME_LEN];
|
||||
int ret, forced = 0;
|
||||
|
||||
if (!capable(CAP_SYS_MODULE))
|
||||
if (!capable(CAP_SYS_MODULE) || modules_disabled)
|
||||
return -EPERM;
|
||||
|
||||
if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
|
||||
@ -2336,7 +2339,7 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
|
||||
int ret = 0;
|
||||
|
||||
/* Must have permission */
|
||||
if (!capable(CAP_SYS_MODULE))
|
||||
if (!capable(CAP_SYS_MODULE) || modules_disabled)
|
||||
return -EPERM;
|
||||
|
||||
/* Only one module load at a time, please */
|
||||
|
@ -185,10 +185,11 @@ int ptrace_attach(struct task_struct *task)
|
||||
if (same_thread_group(task, current))
|
||||
goto out;
|
||||
|
||||
/* Protect exec's credential calculations against our interference;
|
||||
* SUID, SGID and LSM creds get determined differently under ptrace.
|
||||
/* Protect the target's credential calculations against our
|
||||
* interference; SUID, SGID and LSM creds get determined differently
|
||||
* under ptrace.
|
||||
*/
|
||||
retval = mutex_lock_interruptible(&task->cred_exec_mutex);
|
||||
retval = mutex_lock_interruptible(&task->cred_guard_mutex);
|
||||
if (retval < 0)
|
||||
goto out;
|
||||
|
||||
@ -232,7 +233,7 @@ repeat:
|
||||
bad:
|
||||
write_unlock_irqrestore(&tasklist_lock, flags);
|
||||
task_unlock(task);
|
||||
mutex_unlock(&task->cred_exec_mutex);
|
||||
mutex_unlock(&task->cred_guard_mutex);
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
@ -249,14 +249,19 @@ void flush_sigqueue(struct sigpending *queue)
|
||||
/*
|
||||
* Flush all pending signals for a task.
|
||||
*/
|
||||
void __flush_signals(struct task_struct *t)
|
||||
{
|
||||
clear_tsk_thread_flag(t, TIF_SIGPENDING);
|
||||
flush_sigqueue(&t->pending);
|
||||
flush_sigqueue(&t->signal->shared_pending);
|
||||
}
|
||||
|
||||
void flush_signals(struct task_struct *t)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&t->sighand->siglock, flags);
|
||||
clear_tsk_thread_flag(t, TIF_SIGPENDING);
|
||||
flush_sigqueue(&t->pending);
|
||||
flush_sigqueue(&t->signal->shared_pending);
|
||||
__flush_signals(t);
|
||||
spin_unlock_irqrestore(&t->sighand->siglock, flags);
|
||||
}
|
||||
|
||||
|
@ -114,6 +114,7 @@ static int ngroups_max = NGROUPS_MAX;
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
extern char modprobe_path[];
|
||||
extern int modules_disabled;
|
||||
#endif
|
||||
#ifdef CONFIG_CHR_DEV_SG
|
||||
extern int sg_big_buff;
|
||||
@ -534,6 +535,17 @@ static struct ctl_table kern_table[] = {
|
||||
.proc_handler = &proc_dostring,
|
||||
.strategy = &sysctl_string,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "modules_disabled",
|
||||
.data = &modules_disabled,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
/* only handle a transition from default "0" to "1" */
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.extra1 = &one,
|
||||
.extra2 = &one,
|
||||
},
|
||||
#endif
|
||||
#if defined(CONFIG_HOTPLUG) && defined(CONFIG_NET)
|
||||
{
|
||||
@ -1233,7 +1245,6 @@ static struct ctl_table vm_table[] = {
|
||||
.strategy = &sysctl_jiffies,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_SECURITY
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "mmap_min_addr",
|
||||
@ -1242,7 +1253,6 @@ static struct ctl_table vm_table[] = {
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_doulongvec_minmax,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_NUMA
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
|
19
mm/Kconfig
19
mm/Kconfig
@ -226,6 +226,25 @@ config HAVE_MLOCKED_PAGE_BIT
|
||||
config MMU_NOTIFIER
|
||||
bool
|
||||
|
||||
config DEFAULT_MMAP_MIN_ADDR
|
||||
int "Low address space to protect from user allocation"
|
||||
default 4096
|
||||
help
|
||||
This is the portion of low virtual memory which should be protected
|
||||
from userspace allocation. Keeping a user from writing to low pages
|
||||
can help reduce the impact of kernel NULL pointer bugs.
|
||||
|
||||
For most ia64, ppc64 and x86 users with lots of address space
|
||||
a value of 65536 is reasonable and should cause no problems.
|
||||
On arm and other archs it should not be higher than 32768.
|
||||
Programs which use vm86 functionality would either need additional
|
||||
permissions from either the LSM or the capabilities module or have
|
||||
this protection disabled.
|
||||
|
||||
This value can be changed after boot using the
|
||||
/proc/sys/vm/mmap_min_addr tunable.
|
||||
|
||||
|
||||
config NOMMU_INITIAL_TRIM_EXCESS
|
||||
int "Turn on mmap() excess space trimming before booting"
|
||||
depends on !MMU
|
||||
|
@ -87,6 +87,9 @@ int sysctl_overcommit_ratio = 50; /* default is 50% */
|
||||
int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT;
|
||||
struct percpu_counter vm_committed_as;
|
||||
|
||||
/* amount of vm to protect from userspace access */
|
||||
unsigned long mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
|
||||
|
||||
/*
|
||||
* Check that a process has enough memory to allocate a new virtual
|
||||
* mapping. 0 means there is enough memory for the allocation to
|
||||
|
@ -69,6 +69,9 @@ int sysctl_max_map_count = DEFAULT_MAX_MAP_COUNT;
|
||||
int sysctl_nr_trim_pages = CONFIG_NOMMU_INITIAL_TRIM_EXCESS;
|
||||
int heap_stack_gap = 0;
|
||||
|
||||
/* amount of vm to protect from userspace access */
|
||||
unsigned long mmap_min_addr = CONFIG_DEFAULT_MMAP_MIN_ADDR;
|
||||
|
||||
atomic_long_t mmap_pages_allocated;
|
||||
|
||||
EXPORT_SYMBOL(mem_map);
|
||||
|
@ -2659,6 +2659,7 @@ struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags)
|
||||
if (error)
|
||||
goto close_file;
|
||||
#endif
|
||||
ima_counts_get(file);
|
||||
return file;
|
||||
|
||||
close_file:
|
||||
@ -2684,7 +2685,6 @@ int shmem_zero_setup(struct vm_area_struct *vma)
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
|
||||
ima_shm_check(file);
|
||||
if (vma->vm_file)
|
||||
fput(vma->vm_file);
|
||||
vma->vm_file = file;
|
||||
|
@ -110,29 +110,9 @@ config SECURITY_ROOTPLUG
|
||||
|
||||
See <http://www.linuxjournal.com/article.php?sid=6279> for
|
||||
more information about this module.
|
||||
|
||||
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
config SECURITY_DEFAULT_MMAP_MIN_ADDR
|
||||
int "Low address space to protect from user allocation"
|
||||
depends on SECURITY
|
||||
default 0
|
||||
help
|
||||
This is the portion of low virtual memory which should be protected
|
||||
from userspace allocation. Keeping a user from writing to low pages
|
||||
can help reduce the impact of kernel NULL pointer bugs.
|
||||
|
||||
For most ia64, ppc64 and x86 users with lots of address space
|
||||
a value of 65536 is reasonable and should cause no problems.
|
||||
On arm and other archs it should not be higher than 32768.
|
||||
Programs which use vm86 functionality would either need additional
|
||||
permissions from either the LSM or the capabilities module or have
|
||||
this protection disabled.
|
||||
|
||||
This value can be changed after boot using the
|
||||
/proc/sys/vm/mmap_min_addr tunable.
|
||||
|
||||
|
||||
source security/selinux/Kconfig
|
||||
source security/smack/Kconfig
|
||||
source security/tomoyo/Kconfig
|
||||
|
@ -16,6 +16,9 @@ obj-$(CONFIG_SECURITYFS) += inode.o
|
||||
# Must precede capability.o in order to stack properly.
|
||||
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
|
||||
obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o
|
||||
ifeq ($(CONFIG_AUDIT),y)
|
||||
obj-$(CONFIG_SECURITY_SMACK) += lsm_audit.o
|
||||
endif
|
||||
obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o
|
||||
obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o
|
||||
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
|
||||
|
@ -28,6 +28,28 @@
|
||||
#include <linux/prctl.h>
|
||||
#include <linux/securebits.h>
|
||||
|
||||
/*
|
||||
* If a non-root user executes a setuid-root binary in
|
||||
* !secure(SECURE_NOROOT) mode, then we raise capabilities.
|
||||
* However if fE is also set, then the intent is for only
|
||||
* the file capabilities to be applied, and the setuid-root
|
||||
* bit is left on either to change the uid (plausible) or
|
||||
* to get full privilege on a kernel without file capabilities
|
||||
* support. So in that case we do not raise capabilities.
|
||||
*
|
||||
* Warn if that happens, once per boot.
|
||||
*/
|
||||
static void warn_setuid_and_fcaps_mixed(char *fname)
|
||||
{
|
||||
static int warned;
|
||||
if (!warned) {
|
||||
printk(KERN_INFO "warning: `%s' has both setuid-root and"
|
||||
" effective capabilities. Therefore not raising all"
|
||||
" capabilities.\n", fname);
|
||||
warned = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
NETLINK_CB(skb).eff_cap = current_cap();
|
||||
@ -463,6 +485,15 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
|
||||
return ret;
|
||||
|
||||
if (!issecure(SECURE_NOROOT)) {
|
||||
/*
|
||||
* If the legacy file capability is set, then don't set privs
|
||||
* for a setuid root binary run by a non-root user. Do set it
|
||||
* for a root user just to cause least surprise to an admin.
|
||||
*/
|
||||
if (effective && new->uid != 0 && new->euid == 0) {
|
||||
warn_setuid_and_fcaps_mixed(bprm->filename);
|
||||
goto skip;
|
||||
}
|
||||
/*
|
||||
* To support inheritance of root-permissions and suid-root
|
||||
* executables under compatibility mode, we override the
|
||||
@ -478,6 +509,7 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
|
||||
if (new->euid == 0)
|
||||
effective = true;
|
||||
}
|
||||
skip:
|
||||
|
||||
/* Don't let someone trace a set[ug]id/setpcap binary with the revised
|
||||
* credentials unless they have the appropriate permit
|
||||
|
@ -287,7 +287,7 @@ void securityfs_remove(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *parent;
|
||||
|
||||
if (!dentry)
|
||||
if (!dentry || IS_ERR(dentry))
|
||||
return;
|
||||
|
||||
parent = dentry->d_parent;
|
||||
|
@ -22,18 +22,9 @@ static int ima_audit;
|
||||
static int __init ima_audit_setup(char *str)
|
||||
{
|
||||
unsigned long audit;
|
||||
int rc, result = 0;
|
||||
char *op = "ima_audit";
|
||||
char *cause;
|
||||
|
||||
rc = strict_strtoul(str, 0, &audit);
|
||||
if (rc || audit > 1)
|
||||
result = 1;
|
||||
else
|
||||
ima_audit = audit;
|
||||
cause = ima_audit ? "enabled" : "not_enabled";
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
|
||||
op, cause, result, 0);
|
||||
if (!strict_strtoul(str, 0, &audit))
|
||||
ima_audit = audit ? 1 : 0;
|
||||
return 1;
|
||||
}
|
||||
__setup("ima_audit=", ima_audit_setup);
|
||||
@ -50,23 +41,14 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,
|
||||
|
||||
ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno);
|
||||
audit_log_format(ab, "integrity: pid=%d uid=%u auid=%u ses=%u",
|
||||
current->pid, current->cred->uid,
|
||||
current->pid, current_cred()->uid,
|
||||
audit_get_loginuid(current),
|
||||
audit_get_sessionid(current));
|
||||
audit_log_task_context(ab);
|
||||
switch (audit_msgno) {
|
||||
case AUDIT_INTEGRITY_DATA:
|
||||
case AUDIT_INTEGRITY_METADATA:
|
||||
case AUDIT_INTEGRITY_PCR:
|
||||
case AUDIT_INTEGRITY_STATUS:
|
||||
audit_log_format(ab, " op=%s cause=%s", op, cause);
|
||||
break;
|
||||
case AUDIT_INTEGRITY_HASH:
|
||||
audit_log_format(ab, " op=%s hash=%s", op, cause);
|
||||
break;
|
||||
default:
|
||||
audit_log_format(ab, " op=%s", op);
|
||||
}
|
||||
audit_log_format(ab, " op=");
|
||||
audit_log_string(ab, op);
|
||||
audit_log_format(ab, " cause=");
|
||||
audit_log_string(ab, cause);
|
||||
audit_log_format(ab, " comm=");
|
||||
audit_log_untrustedstring(ab, current->comm);
|
||||
if (fname) {
|
||||
|
@ -103,7 +103,7 @@ int ima_calc_template_hash(int template_len, void *template, char *digest)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void ima_pcrread(int idx, u8 *pcr)
|
||||
static void __init ima_pcrread(int idx, u8 *pcr)
|
||||
{
|
||||
if (!ima_used_chip)
|
||||
return;
|
||||
@ -115,7 +115,7 @@ static void ima_pcrread(int idx, u8 *pcr)
|
||||
/*
|
||||
* Calculate the boot aggregate hash
|
||||
*/
|
||||
int ima_calc_boot_aggregate(char *digest)
|
||||
int __init ima_calc_boot_aggregate(char *digest)
|
||||
{
|
||||
struct hash_desc desc;
|
||||
struct scatterlist sg;
|
||||
|
@ -15,6 +15,7 @@
|
||||
* implemenents security file system for reporting
|
||||
* current measurement list and IMA statistics
|
||||
*/
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/rculist.h>
|
||||
@ -283,6 +284,9 @@ static atomic_t policy_opencount = ATOMIC_INIT(1);
|
||||
*/
|
||||
int ima_open_policy(struct inode * inode, struct file * filp)
|
||||
{
|
||||
/* No point in being allowed to open it if you aren't going to write */
|
||||
if (!(filp->f_flags & O_WRONLY))
|
||||
return -EACCES;
|
||||
if (atomic_dec_and_test(&policy_opencount))
|
||||
return 0;
|
||||
return -EBUSY;
|
||||
@ -315,7 +319,7 @@ static struct file_operations ima_measure_policy_ops = {
|
||||
.release = ima_release_policy
|
||||
};
|
||||
|
||||
int ima_fs_init(void)
|
||||
int __init ima_fs_init(void)
|
||||
{
|
||||
ima_dir = securityfs_create_dir("ima", NULL);
|
||||
if (IS_ERR(ima_dir))
|
||||
@ -349,7 +353,7 @@ int ima_fs_init(void)
|
||||
goto out;
|
||||
|
||||
ima_policy = securityfs_create_file("policy",
|
||||
S_IRUSR | S_IRGRP | S_IWUSR,
|
||||
S_IWUSR,
|
||||
ima_dir, NULL,
|
||||
&ima_measure_policy_ops);
|
||||
if (IS_ERR(ima_policy))
|
||||
|
@ -196,7 +196,7 @@ static void init_once(void *foo)
|
||||
kref_set(&iint->refcount, 1);
|
||||
}
|
||||
|
||||
void ima_iintcache_init(void)
|
||||
void __init ima_iintcache_init(void)
|
||||
{
|
||||
iint_cache =
|
||||
kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0,
|
||||
|
@ -38,7 +38,7 @@ int ima_used_chip;
|
||||
* a different value.) Violations add a zero entry to the measurement
|
||||
* list and extend the aggregate PCR value with ff...ff's.
|
||||
*/
|
||||
static void ima_add_boot_aggregate(void)
|
||||
static void __init ima_add_boot_aggregate(void)
|
||||
{
|
||||
struct ima_template_entry *entry;
|
||||
const char *op = "add_boot_aggregate";
|
||||
@ -71,7 +71,7 @@ err_out:
|
||||
audit_cause, result, 0);
|
||||
}
|
||||
|
||||
int ima_init(void)
|
||||
int __init ima_init(void)
|
||||
{
|
||||
u8 pcr_i[IMA_DIGEST_SIZE];
|
||||
int rc;
|
||||
|
@ -29,20 +29,8 @@ int ima_initialized;
|
||||
char *ima_hash = "sha1";
|
||||
static int __init hash_setup(char *str)
|
||||
{
|
||||
const char *op = "hash_setup";
|
||||
const char *hash = "sha1";
|
||||
int result = 0;
|
||||
int audit_info = 0;
|
||||
|
||||
if (strncmp(str, "md5", 3) == 0) {
|
||||
hash = "md5";
|
||||
ima_hash = str;
|
||||
} else if (strncmp(str, "sha1", 4) != 0) {
|
||||
hash = "invalid_hash_type";
|
||||
result = 1;
|
||||
}
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_HASH, NULL, NULL, op, hash,
|
||||
result, audit_info);
|
||||
if (strncmp(str, "md5", 3) == 0)
|
||||
ima_hash = "md5";
|
||||
return 1;
|
||||
}
|
||||
__setup("ima_hash=", hash_setup);
|
||||
@ -128,10 +116,6 @@ static int get_path_measurement(struct ima_iint_cache *iint, struct file *file,
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (IS_ERR(file)) {
|
||||
pr_info("%s dentry_open failed\n", filename);
|
||||
return rc;
|
||||
}
|
||||
iint->opencount++;
|
||||
iint->readcount++;
|
||||
|
||||
@ -141,6 +125,15 @@ static int get_path_measurement(struct ima_iint_cache *iint, struct file *file,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void ima_update_counts(struct ima_iint_cache *iint, int mask)
|
||||
{
|
||||
iint->opencount++;
|
||||
if ((mask & MAY_WRITE) || (mask == 0))
|
||||
iint->writecount++;
|
||||
else if (mask & (MAY_READ | MAY_EXEC))
|
||||
iint->readcount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* ima_path_check - based on policy, collect/store measurement.
|
||||
* @path: contains a pointer to the path to be measured
|
||||
@ -156,10 +149,10 @@ static int get_path_measurement(struct ima_iint_cache *iint, struct file *file,
|
||||
* - Opening a file for read when already open for write,
|
||||
* could result in a file measurement error.
|
||||
*
|
||||
* Return 0 on success, an error code on failure.
|
||||
* (Based on the results of appraise_measurement().)
|
||||
* Always return 0 and audit dentry_open failures.
|
||||
* (Return code will be based upon measurement appraisal.)
|
||||
*/
|
||||
int ima_path_check(struct path *path, int mask)
|
||||
int ima_path_check(struct path *path, int mask, int update_counts)
|
||||
{
|
||||
struct inode *inode = path->dentry->d_inode;
|
||||
struct ima_iint_cache *iint;
|
||||
@ -173,11 +166,8 @@ int ima_path_check(struct path *path, int mask)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&iint->mutex);
|
||||
iint->opencount++;
|
||||
if ((mask & MAY_WRITE) || (mask == 0))
|
||||
iint->writecount++;
|
||||
else if (mask & (MAY_READ | MAY_EXEC))
|
||||
iint->readcount++;
|
||||
if (update_counts)
|
||||
ima_update_counts(iint, mask);
|
||||
|
||||
rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK);
|
||||
if (rc < 0)
|
||||
@ -196,7 +186,19 @@ int ima_path_check(struct path *path, int mask)
|
||||
struct dentry *dentry = dget(path->dentry);
|
||||
struct vfsmount *mnt = mntget(path->mnt);
|
||||
|
||||
file = dentry_open(dentry, mnt, O_RDONLY, current->cred);
|
||||
file = dentry_open(dentry, mnt, O_RDONLY | O_LARGEFILE,
|
||||
current_cred());
|
||||
if (IS_ERR(file)) {
|
||||
int audit_info = 0;
|
||||
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
|
||||
dentry->d_name.name,
|
||||
"add_measurement",
|
||||
"dentry_open failed",
|
||||
1, audit_info);
|
||||
file = NULL;
|
||||
goto out;
|
||||
}
|
||||
rc = get_path_measurement(iint, file, dentry->d_name.name);
|
||||
}
|
||||
out:
|
||||
@ -206,6 +208,7 @@ out:
|
||||
kref_put(&iint->refcount, iint_free);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ima_path_check);
|
||||
|
||||
static int process_measurement(struct file *file, const unsigned char *filename,
|
||||
int mask, int function)
|
||||
@ -234,7 +237,16 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void opencount_get(struct file *file)
|
||||
/*
|
||||
* ima_opens_get - increment file counts
|
||||
*
|
||||
* - for IPC shm and shmat file.
|
||||
* - for nfsd exported files.
|
||||
*
|
||||
* Increment the counts for these files to prevent unnecessary
|
||||
* imbalance messages.
|
||||
*/
|
||||
void ima_counts_get(struct file *file)
|
||||
{
|
||||
struct inode *inode = file->f_dentry->d_inode;
|
||||
struct ima_iint_cache *iint;
|
||||
@ -246,8 +258,14 @@ static void opencount_get(struct file *file)
|
||||
return;
|
||||
mutex_lock(&iint->mutex);
|
||||
iint->opencount++;
|
||||
if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
|
||||
iint->readcount++;
|
||||
|
||||
if (file->f_mode & FMODE_WRITE)
|
||||
iint->writecount++;
|
||||
mutex_unlock(&iint->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ima_counts_get);
|
||||
|
||||
/**
|
||||
* ima_file_mmap - based on policy, collect/store measurement.
|
||||
@ -272,18 +290,6 @@ int ima_file_mmap(struct file *file, unsigned long prot)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_shm_check - IPC shm and shmat create/fput a file
|
||||
*
|
||||
* Maintain the opencount for these files to prevent unnecessary
|
||||
* imbalance messages.
|
||||
*/
|
||||
void ima_shm_check(struct file *file)
|
||||
{
|
||||
opencount_get(file);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* ima_bprm_check - based on policy, collect/store measurement.
|
||||
* @bprm: contains the linux_binprm structure
|
||||
|
@ -45,24 +45,30 @@ struct ima_measure_rule_entry {
|
||||
} lsm[MAX_LSM_RULES];
|
||||
};
|
||||
|
||||
/* Without LSM specific knowledge, the default policy can only be
|
||||
/*
|
||||
* Without LSM specific knowledge, the default policy can only be
|
||||
* written in terms of .action, .func, .mask, .fsmagic, and .uid
|
||||
*/
|
||||
|
||||
/*
|
||||
* The minimum rule set to allow for full TCB coverage. Measures all files
|
||||
* opened or mmap for exec and everything read by root. Dangerous because
|
||||
* normal users can easily run the machine out of memory simply building
|
||||
* and running executables.
|
||||
*/
|
||||
static struct ima_measure_rule_entry default_rules[] = {
|
||||
{.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,
|
||||
.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,
|
||||
.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = 0xF97CFF8C,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC,
|
||||
.flags = IMA_FUNC | IMA_MASK},
|
||||
{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
|
||||
.flags = IMA_FUNC | IMA_MASK},
|
||||
{.action = MEASURE,.func = PATH_CHECK,.mask = MAY_READ,.uid = 0,
|
||||
.flags = IMA_FUNC | IMA_MASK | IMA_UID}
|
||||
.flags = IMA_FUNC | IMA_MASK | IMA_UID},
|
||||
};
|
||||
|
||||
static LIST_HEAD(measure_default_rules);
|
||||
@ -71,6 +77,14 @@ static struct list_head *ima_measure;
|
||||
|
||||
static DEFINE_MUTEX(ima_measure_mutex);
|
||||
|
||||
static bool ima_use_tcb __initdata;
|
||||
static int __init default_policy_setup(char *str)
|
||||
{
|
||||
ima_use_tcb = 1;
|
||||
return 1;
|
||||
}
|
||||
__setup("ima_tcb", default_policy_setup);
|
||||
|
||||
/**
|
||||
* ima_match_rules - determine whether an inode matches the measure rule.
|
||||
* @rule: a pointer to a rule
|
||||
@ -96,7 +110,7 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
|
||||
if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid)
|
||||
return false;
|
||||
for (i = 0; i < MAX_LSM_RULES; i++) {
|
||||
int rc;
|
||||
int rc = 0;
|
||||
u32 osid, sid;
|
||||
|
||||
if (!rule->lsm[i].rule)
|
||||
@ -109,7 +123,7 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
|
||||
security_inode_getsecid(inode, &osid);
|
||||
rc = security_filter_rule_match(osid,
|
||||
rule->lsm[i].type,
|
||||
AUDIT_EQUAL,
|
||||
Audit_equal,
|
||||
rule->lsm[i].rule,
|
||||
NULL);
|
||||
break;
|
||||
@ -119,7 +133,7 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
|
||||
security_task_getsecid(tsk, &sid);
|
||||
rc = security_filter_rule_match(sid,
|
||||
rule->lsm[i].type,
|
||||
AUDIT_EQUAL,
|
||||
Audit_equal,
|
||||
rule->lsm[i].rule,
|
||||
NULL);
|
||||
default:
|
||||
@ -164,11 +178,17 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask)
|
||||
* ima_measure points to either the measure_default_rules or the
|
||||
* the new measure_policy_rules.
|
||||
*/
|
||||
void ima_init_policy(void)
|
||||
void __init ima_init_policy(void)
|
||||
{
|
||||
int i;
|
||||
int i, entries;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(default_rules); i++)
|
||||
/* if !ima_use_tcb set entries = 0 so we load NO default rules */
|
||||
if (ima_use_tcb)
|
||||
entries = ARRAY_SIZE(default_rules);
|
||||
else
|
||||
entries = 0;
|
||||
|
||||
for (i = 0; i < entries; i++)
|
||||
list_add_tail(&default_rules[i].list, &measure_default_rules);
|
||||
ima_measure = &measure_default_rules;
|
||||
}
|
||||
@ -227,7 +247,7 @@ static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry,
|
||||
|
||||
entry->lsm[lsm_rule].type = audit_type;
|
||||
result = security_filter_rule_init(entry->lsm[lsm_rule].type,
|
||||
AUDIT_EQUAL, args,
|
||||
Audit_equal, args,
|
||||
&entry->lsm[lsm_rule].rule);
|
||||
return result;
|
||||
}
|
||||
|
386
security/lsm_audit.c
Normal file
386
security/lsm_audit.c
Normal file
@ -0,0 +1,386 @@
|
||||
/*
|
||||
* common LSM auditing functions
|
||||
*
|
||||
* Based on code written for SELinux by :
|
||||
* Stephen Smalley, <sds@epoch.ncsc.mil>
|
||||
* James Morris <jmorris@redhat.com>
|
||||
* Author : Etienne Basset, <etienne.basset@ensta.org>
|
||||
*
|
||||
* 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/types.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/un.h>
|
||||
#include <net/af_unix.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/ip.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/dccp.h>
|
||||
#include <linux/sctp.h>
|
||||
#include <linux/lsm_audit.h>
|
||||
|
||||
/**
|
||||
* ipv4_skb_to_auditdata : fill auditdata from skb
|
||||
* @skb : the skb
|
||||
* @ad : the audit data to fill
|
||||
* @proto : the layer 4 protocol
|
||||
*
|
||||
* return 0 on success
|
||||
*/
|
||||
int ipv4_skb_to_auditdata(struct sk_buff *skb,
|
||||
struct common_audit_data *ad, u8 *proto)
|
||||
{
|
||||
int ret = 0;
|
||||
struct iphdr *ih;
|
||||
|
||||
ih = ip_hdr(skb);
|
||||
if (ih == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
ad->u.net.v4info.saddr = ih->saddr;
|
||||
ad->u.net.v4info.daddr = ih->daddr;
|
||||
|
||||
if (proto)
|
||||
*proto = ih->protocol;
|
||||
/* non initial fragment */
|
||||
if (ntohs(ih->frag_off) & IP_OFFSET)
|
||||
return 0;
|
||||
|
||||
switch (ih->protocol) {
|
||||
case IPPROTO_TCP: {
|
||||
struct tcphdr *th = tcp_hdr(skb);
|
||||
if (th == NULL)
|
||||
break;
|
||||
|
||||
ad->u.net.sport = th->source;
|
||||
ad->u.net.dport = th->dest;
|
||||
break;
|
||||
}
|
||||
case IPPROTO_UDP: {
|
||||
struct udphdr *uh = udp_hdr(skb);
|
||||
if (uh == NULL)
|
||||
break;
|
||||
|
||||
ad->u.net.sport = uh->source;
|
||||
ad->u.net.dport = uh->dest;
|
||||
break;
|
||||
}
|
||||
case IPPROTO_DCCP: {
|
||||
struct dccp_hdr *dh = dccp_hdr(skb);
|
||||
if (dh == NULL)
|
||||
break;
|
||||
|
||||
ad->u.net.sport = dh->dccph_sport;
|
||||
ad->u.net.dport = dh->dccph_dport;
|
||||
break;
|
||||
}
|
||||
case IPPROTO_SCTP: {
|
||||
struct sctphdr *sh = sctp_hdr(skb);
|
||||
if (sh == NULL)
|
||||
break;
|
||||
ad->u.net.sport = sh->source;
|
||||
ad->u.net.dport = sh->dest;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
/**
|
||||
* ipv6_skb_to_auditdata : fill auditdata from skb
|
||||
* @skb : the skb
|
||||
* @ad : the audit data to fill
|
||||
* @proto : the layer 4 protocol
|
||||
*
|
||||
* return 0 on success
|
||||
*/
|
||||
int ipv6_skb_to_auditdata(struct sk_buff *skb,
|
||||
struct common_audit_data *ad, u8 *proto)
|
||||
{
|
||||
int offset, ret = 0;
|
||||
struct ipv6hdr *ip6;
|
||||
u8 nexthdr;
|
||||
|
||||
ip6 = ipv6_hdr(skb);
|
||||
if (ip6 == NULL)
|
||||
return -EINVAL;
|
||||
ipv6_addr_copy(&ad->u.net.v6info.saddr, &ip6->saddr);
|
||||
ipv6_addr_copy(&ad->u.net.v6info.daddr, &ip6->daddr);
|
||||
ret = 0;
|
||||
/* IPv6 can have several extension header before the Transport header
|
||||
* skip them */
|
||||
offset = skb_network_offset(skb);
|
||||
offset += sizeof(*ip6);
|
||||
nexthdr = ip6->nexthdr;
|
||||
offset = ipv6_skip_exthdr(skb, offset, &nexthdr);
|
||||
if (offset < 0)
|
||||
return 0;
|
||||
if (proto)
|
||||
*proto = nexthdr;
|
||||
switch (nexthdr) {
|
||||
case IPPROTO_TCP: {
|
||||
struct tcphdr _tcph, *th;
|
||||
|
||||
th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
|
||||
if (th == NULL)
|
||||
break;
|
||||
|
||||
ad->u.net.sport = th->source;
|
||||
ad->u.net.dport = th->dest;
|
||||
break;
|
||||
}
|
||||
case IPPROTO_UDP: {
|
||||
struct udphdr _udph, *uh;
|
||||
|
||||
uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
|
||||
if (uh == NULL)
|
||||
break;
|
||||
|
||||
ad->u.net.sport = uh->source;
|
||||
ad->u.net.dport = uh->dest;
|
||||
break;
|
||||
}
|
||||
case IPPROTO_DCCP: {
|
||||
struct dccp_hdr _dccph, *dh;
|
||||
|
||||
dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph);
|
||||
if (dh == NULL)
|
||||
break;
|
||||
|
||||
ad->u.net.sport = dh->dccph_sport;
|
||||
ad->u.net.dport = dh->dccph_dport;
|
||||
break;
|
||||
}
|
||||
case IPPROTO_SCTP: {
|
||||
struct sctphdr _sctph, *sh;
|
||||
|
||||
sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph);
|
||||
if (sh == NULL)
|
||||
break;
|
||||
ad->u.net.sport = sh->source;
|
||||
ad->u.net.dport = sh->dest;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static inline void print_ipv6_addr(struct audit_buffer *ab,
|
||||
struct in6_addr *addr, __be16 port,
|
||||
char *name1, char *name2)
|
||||
{
|
||||
if (!ipv6_addr_any(addr))
|
||||
audit_log_format(ab, " %s=%pI6", name1, addr);
|
||||
if (port)
|
||||
audit_log_format(ab, " %s=%d", name2, ntohs(port));
|
||||
}
|
||||
|
||||
static inline void print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
|
||||
__be16 port, char *name1, char *name2)
|
||||
{
|
||||
if (addr)
|
||||
audit_log_format(ab, " %s=%pI4", name1, &addr);
|
||||
if (port)
|
||||
audit_log_format(ab, " %s=%d", name2, ntohs(port));
|
||||
}
|
||||
|
||||
/**
|
||||
* dump_common_audit_data - helper to dump common audit data
|
||||
* @a : common audit data
|
||||
*
|
||||
*/
|
||||
static void dump_common_audit_data(struct audit_buffer *ab,
|
||||
struct common_audit_data *a)
|
||||
{
|
||||
struct inode *inode = NULL;
|
||||
struct task_struct *tsk = current;
|
||||
|
||||
if (a->tsk)
|
||||
tsk = a->tsk;
|
||||
if (tsk && tsk->pid) {
|
||||
audit_log_format(ab, " pid=%d comm=", tsk->pid);
|
||||
audit_log_untrustedstring(ab, tsk->comm);
|
||||
}
|
||||
|
||||
switch (a->type) {
|
||||
case LSM_AUDIT_DATA_IPC:
|
||||
audit_log_format(ab, " key=%d ", a->u.ipc_id);
|
||||
break;
|
||||
case LSM_AUDIT_DATA_CAP:
|
||||
audit_log_format(ab, " capability=%d ", a->u.cap);
|
||||
break;
|
||||
case LSM_AUDIT_DATA_FS:
|
||||
if (a->u.fs.path.dentry) {
|
||||
struct dentry *dentry = a->u.fs.path.dentry;
|
||||
if (a->u.fs.path.mnt) {
|
||||
audit_log_d_path(ab, "path=", &a->u.fs.path);
|
||||
} else {
|
||||
audit_log_format(ab, " name=");
|
||||
audit_log_untrustedstring(ab,
|
||||
dentry->d_name.name);
|
||||
}
|
||||
inode = dentry->d_inode;
|
||||
} else if (a->u.fs.inode) {
|
||||
struct dentry *dentry;
|
||||
inode = a->u.fs.inode;
|
||||
dentry = d_find_alias(inode);
|
||||
if (dentry) {
|
||||
audit_log_format(ab, " name=");
|
||||
audit_log_untrustedstring(ab,
|
||||
dentry->d_name.name);
|
||||
dput(dentry);
|
||||
}
|
||||
}
|
||||
if (inode)
|
||||
audit_log_format(ab, " dev=%s ino=%lu",
|
||||
inode->i_sb->s_id,
|
||||
inode->i_ino);
|
||||
break;
|
||||
case LSM_AUDIT_DATA_TASK:
|
||||
tsk = a->u.tsk;
|
||||
if (tsk && tsk->pid) {
|
||||
audit_log_format(ab, " pid=%d comm=", tsk->pid);
|
||||
audit_log_untrustedstring(ab, tsk->comm);
|
||||
}
|
||||
break;
|
||||
case LSM_AUDIT_DATA_NET:
|
||||
if (a->u.net.sk) {
|
||||
struct sock *sk = a->u.net.sk;
|
||||
struct unix_sock *u;
|
||||
int len = 0;
|
||||
char *p = NULL;
|
||||
|
||||
switch (sk->sk_family) {
|
||||
case AF_INET: {
|
||||
struct inet_sock *inet = inet_sk(sk);
|
||||
|
||||
print_ipv4_addr(ab, inet->rcv_saddr,
|
||||
inet->sport,
|
||||
"laddr", "lport");
|
||||
print_ipv4_addr(ab, inet->daddr,
|
||||
inet->dport,
|
||||
"faddr", "fport");
|
||||
break;
|
||||
}
|
||||
case AF_INET6: {
|
||||
struct inet_sock *inet = inet_sk(sk);
|
||||
struct ipv6_pinfo *inet6 = inet6_sk(sk);
|
||||
|
||||
print_ipv6_addr(ab, &inet6->rcv_saddr,
|
||||
inet->sport,
|
||||
"laddr", "lport");
|
||||
print_ipv6_addr(ab, &inet6->daddr,
|
||||
inet->dport,
|
||||
"faddr", "fport");
|
||||
break;
|
||||
}
|
||||
case AF_UNIX:
|
||||
u = unix_sk(sk);
|
||||
if (u->dentry) {
|
||||
struct path path = {
|
||||
.dentry = u->dentry,
|
||||
.mnt = u->mnt
|
||||
};
|
||||
audit_log_d_path(ab, "path=", &path);
|
||||
break;
|
||||
}
|
||||
if (!u->addr)
|
||||
break;
|
||||
len = u->addr->len-sizeof(short);
|
||||
p = &u->addr->name->sun_path[0];
|
||||
audit_log_format(ab, " path=");
|
||||
if (*p)
|
||||
audit_log_untrustedstring(ab, p);
|
||||
else
|
||||
audit_log_n_hex(ab, p, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (a->u.net.family) {
|
||||
case AF_INET:
|
||||
print_ipv4_addr(ab, a->u.net.v4info.saddr,
|
||||
a->u.net.sport,
|
||||
"saddr", "src");
|
||||
print_ipv4_addr(ab, a->u.net.v4info.daddr,
|
||||
a->u.net.dport,
|
||||
"daddr", "dest");
|
||||
break;
|
||||
case AF_INET6:
|
||||
print_ipv6_addr(ab, &a->u.net.v6info.saddr,
|
||||
a->u.net.sport,
|
||||
"saddr", "src");
|
||||
print_ipv6_addr(ab, &a->u.net.v6info.daddr,
|
||||
a->u.net.dport,
|
||||
"daddr", "dest");
|
||||
break;
|
||||
}
|
||||
if (a->u.net.netif > 0) {
|
||||
struct net_device *dev;
|
||||
|
||||
/* NOTE: we always use init's namespace */
|
||||
dev = dev_get_by_index(&init_net, a->u.net.netif);
|
||||
if (dev) {
|
||||
audit_log_format(ab, " netif=%s", dev->name);
|
||||
dev_put(dev);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_KEYS
|
||||
case LSM_AUDIT_DATA_KEY:
|
||||
audit_log_format(ab, " key_serial=%u", a->u.key_struct.key);
|
||||
if (a->u.key_struct.key_desc) {
|
||||
audit_log_format(ab, " key_desc=");
|
||||
audit_log_untrustedstring(ab, a->u.key_struct.key_desc);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
} /* switch (a->type) */
|
||||
}
|
||||
|
||||
/**
|
||||
* common_lsm_audit - generic LSM auditing function
|
||||
* @a: auxiliary audit data
|
||||
*
|
||||
* setup the audit buffer for common security information
|
||||
* uses callback to print LSM specific information
|
||||
*/
|
||||
void common_lsm_audit(struct common_audit_data *a)
|
||||
{
|
||||
struct audit_buffer *ab;
|
||||
|
||||
if (a == NULL)
|
||||
return;
|
||||
/* we use GFP_ATOMIC so we won't sleep */
|
||||
ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_AVC);
|
||||
|
||||
if (ab == NULL)
|
||||
return;
|
||||
|
||||
if (a->lsm_pre_audit)
|
||||
a->lsm_pre_audit(ab, a);
|
||||
|
||||
dump_common_audit_data(ab, a);
|
||||
|
||||
if (a->lsm_post_audit)
|
||||
a->lsm_post_audit(ab, a);
|
||||
|
||||
audit_log_end(ab);
|
||||
}
|
@ -71,18 +71,6 @@ static int rootplug_bprm_check_security (struct linux_binprm *bprm)
|
||||
}
|
||||
|
||||
static struct security_operations rootplug_security_ops = {
|
||||
/* Use the capability functions for some of the hooks */
|
||||
.ptrace_may_access = cap_ptrace_may_access,
|
||||
.ptrace_traceme = cap_ptrace_traceme,
|
||||
.capget = cap_capget,
|
||||
.capset = cap_capset,
|
||||
.capable = cap_capable,
|
||||
|
||||
.bprm_set_creds = cap_bprm_set_creds,
|
||||
|
||||
.task_fix_setuid = cap_task_fix_setuid,
|
||||
.task_prctl = cap_task_prctl,
|
||||
|
||||
.bprm_check_security = rootplug_bprm_check_security,
|
||||
};
|
||||
|
||||
|
@ -26,9 +26,6 @@ extern void security_fixup_ops(struct security_operations *ops);
|
||||
|
||||
struct security_operations *security_ops; /* Initialized to NULL */
|
||||
|
||||
/* amount of vm to protect from userspace access */
|
||||
unsigned long mmap_min_addr = CONFIG_SECURITY_DEFAULT_MMAP_MIN_ADDR;
|
||||
|
||||
static inline int verify(struct security_operations *ops)
|
||||
{
|
||||
/* verify the security_operations structure exists */
|
||||
|
@ -927,7 +927,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
|
||||
if (denied) {
|
||||
if (flags & AVC_STRICT)
|
||||
rc = -EACCES;
|
||||
else if (!selinux_enforcing || security_permissive_sid(ssid))
|
||||
else if (!selinux_enforcing || (avd->flags & AVD_FLAGS_PERMISSIVE))
|
||||
avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
|
||||
tsid, tclass, avd->seqno);
|
||||
else
|
||||
|
@ -1980,10 +1980,6 @@ static int selinux_sysctl(ctl_table *table, int op)
|
||||
u32 tsid, sid;
|
||||
int rc;
|
||||
|
||||
rc = secondary_ops->sysctl(table, op);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sid = current_sid();
|
||||
|
||||
rc = selinux_sysctl_get_sid(table, (op == 0001) ?
|
||||
@ -2375,10 +2371,8 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
|
||||
{
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
struct itimerval itimer;
|
||||
struct sighand_struct *psig;
|
||||
u32 osid, sid;
|
||||
int rc, i;
|
||||
unsigned long flags;
|
||||
|
||||
osid = tsec->osid;
|
||||
sid = tsec->sid;
|
||||
@ -2398,22 +2392,20 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
|
||||
memset(&itimer, 0, sizeof itimer);
|
||||
for (i = 0; i < 3; i++)
|
||||
do_setitimer(i, &itimer, NULL);
|
||||
flush_signals(current);
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
flush_signal_handlers(current, 1);
|
||||
sigemptyset(¤t->blocked);
|
||||
recalc_sigpending();
|
||||
if (!(current->signal->flags & SIGNAL_GROUP_EXIT)) {
|
||||
__flush_signals(current);
|
||||
flush_signal_handlers(current, 1);
|
||||
sigemptyset(¤t->blocked);
|
||||
}
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
}
|
||||
|
||||
/* Wake up the parent if it is waiting so that it can recheck
|
||||
* wait permission to the new task SID. */
|
||||
read_lock_irq(&tasklist_lock);
|
||||
psig = current->parent->sighand;
|
||||
spin_lock_irqsave(&psig->siglock, flags);
|
||||
wake_up_interruptible(¤t->parent->signal->wait_chldexit);
|
||||
spin_unlock_irqrestore(&psig->siglock, flags);
|
||||
read_unlock_irq(&tasklist_lock);
|
||||
read_lock(&tasklist_lock);
|
||||
wake_up_interruptible(¤t->real_parent->signal->wait_chldexit);
|
||||
read_unlock(&tasklist_lock);
|
||||
}
|
||||
|
||||
/* superblock security operations */
|
||||
|
@ -8,14 +8,13 @@
|
||||
#ifndef _SELINUX_SECURITY_H_
|
||||
#define _SELINUX_SECURITY_H_
|
||||
|
||||
#include <linux/magic.h>
|
||||
#include "flask.h"
|
||||
|
||||
#define SECSID_NULL 0x00000000 /* unspecified SID */
|
||||
#define SECSID_WILD 0xffffffff /* wildcard SID */
|
||||
#define SECCLASS_NULL 0x0000 /* no class */
|
||||
|
||||
#define SELINUX_MAGIC 0xf97cff8c
|
||||
|
||||
/* Identify specific policy version changes */
|
||||
#define POLICYDB_VERSION_BASE 15
|
||||
#define POLICYDB_VERSION_BOOL 16
|
||||
@ -91,9 +90,11 @@ struct av_decision {
|
||||
u32 auditallow;
|
||||
u32 auditdeny;
|
||||
u32 seqno;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
int security_permissive_sid(u32 sid);
|
||||
/* definitions of av_decision.flags */
|
||||
#define AVD_FLAGS_PERMISSIVE 0x0001
|
||||
|
||||
int security_compute_av(u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 requested,
|
||||
|
@ -112,6 +112,8 @@ static struct nlmsg_perm nlmsg_audit_perms[] =
|
||||
{ AUDIT_DEL_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
|
||||
{ AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY },
|
||||
{ AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ },
|
||||
{ AUDIT_TRIM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
|
||||
{ AUDIT_MAKE_EQUIV, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
|
||||
{ AUDIT_TTY_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ },
|
||||
{ AUDIT_TTY_SET, NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT },
|
||||
};
|
||||
|
@ -527,10 +527,10 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
|
||||
goto out2;
|
||||
|
||||
length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
|
||||
"%x %x %x %x %u",
|
||||
"%x %x %x %x %u %x",
|
||||
avd.allowed, 0xffffffff,
|
||||
avd.auditallow, avd.auditdeny,
|
||||
avd.seqno);
|
||||
avd.seqno, avd.flags);
|
||||
out2:
|
||||
kfree(tcon);
|
||||
out:
|
||||
@ -803,10 +803,6 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (count > PAGE_SIZE) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
page = (char *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!page) {
|
||||
ret = -ENOMEM;
|
||||
|
@ -410,6 +410,7 @@ static int context_struct_compute_av(struct context *scontext,
|
||||
avd->auditallow = 0;
|
||||
avd->auditdeny = 0xffffffff;
|
||||
avd->seqno = latest_granting;
|
||||
avd->flags = 0;
|
||||
|
||||
/*
|
||||
* Check for all the invalid cases.
|
||||
@ -528,31 +529,6 @@ inval_class:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a sid find if the type has the permissive flag set
|
||||
*/
|
||||
int security_permissive_sid(u32 sid)
|
||||
{
|
||||
struct context *context;
|
||||
u32 type;
|
||||
int rc;
|
||||
|
||||
read_lock(&policy_rwlock);
|
||||
|
||||
context = sidtab_search(&sidtab, sid);
|
||||
BUG_ON(!context);
|
||||
|
||||
type = context->type;
|
||||
/*
|
||||
* we are intentionally using type here, not type-1, the 0th bit may
|
||||
* someday indicate that we are globally setting permissive in policy.
|
||||
*/
|
||||
rc = ebitmap_get_bit(&policydb.permissive_map, type);
|
||||
|
||||
read_unlock(&policy_rwlock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int security_validtrans_handle_fail(struct context *ocontext,
|
||||
struct context *ncontext,
|
||||
struct context *tcontext,
|
||||
@ -767,6 +743,10 @@ int security_compute_av(u32 ssid,
|
||||
|
||||
rc = context_struct_compute_av(scontext, tcontext, tclass,
|
||||
requested, avd);
|
||||
|
||||
/* permissive domain? */
|
||||
if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
|
||||
avd->flags |= AVD_FLAGS_PERMISSIVE;
|
||||
out:
|
||||
read_unlock(&policy_rwlock);
|
||||
return rc;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <net/netlabel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/lsm_audit.h>
|
||||
|
||||
/*
|
||||
* Why 23? CIPSO is constrained to 30, so a 32 byte buffer is
|
||||
@ -178,6 +179,20 @@ struct smack_known {
|
||||
#define MAY_READWRITE (MAY_READ | MAY_WRITE)
|
||||
#define MAY_NOT 0
|
||||
|
||||
/*
|
||||
* Number of access types used by Smack (rwxa)
|
||||
*/
|
||||
#define SMK_NUM_ACCESS_TYPE 4
|
||||
|
||||
/*
|
||||
* Smack audit data; is empty if CONFIG_AUDIT not set
|
||||
* to save some stack
|
||||
*/
|
||||
struct smk_audit_info {
|
||||
#ifdef CONFIG_AUDIT
|
||||
struct common_audit_data a;
|
||||
#endif
|
||||
};
|
||||
/*
|
||||
* These functions are in smack_lsm.c
|
||||
*/
|
||||
@ -186,8 +201,8 @@ struct inode_smack *new_inode_smack(char *);
|
||||
/*
|
||||
* These functions are in smack_access.c
|
||||
*/
|
||||
int smk_access(char *, char *, int);
|
||||
int smk_curacc(char *, u32);
|
||||
int smk_access(char *, char *, int, struct smk_audit_info *);
|
||||
int smk_curacc(char *, u32, struct smk_audit_info *);
|
||||
int smack_to_cipso(const char *, struct smack_cipso *);
|
||||
void smack_from_cipso(u32, char *, char *);
|
||||
char *smack_from_secid(const u32);
|
||||
@ -237,4 +252,93 @@ static inline char *smk_of_inode(const struct inode *isp)
|
||||
return sip->smk_inode;
|
||||
}
|
||||
|
||||
/*
|
||||
* logging functions
|
||||
*/
|
||||
#define SMACK_AUDIT_DENIED 0x1
|
||||
#define SMACK_AUDIT_ACCEPT 0x2
|
||||
extern int log_policy;
|
||||
|
||||
void smack_log(char *subject_label, char *object_label,
|
||||
int request,
|
||||
int result, struct smk_audit_info *auditdata);
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
|
||||
/*
|
||||
* some inline functions to set up audit data
|
||||
* they do nothing if CONFIG_AUDIT is not set
|
||||
*
|
||||
*/
|
||||
static inline void smk_ad_init(struct smk_audit_info *a, const char *func,
|
||||
char type)
|
||||
{
|
||||
memset(a, 0, sizeof(*a));
|
||||
a->a.type = type;
|
||||
a->a.function = func;
|
||||
}
|
||||
|
||||
static inline void smk_ad_setfield_u_tsk(struct smk_audit_info *a,
|
||||
struct task_struct *t)
|
||||
{
|
||||
a->a.u.tsk = t;
|
||||
}
|
||||
static inline void smk_ad_setfield_u_fs_path_dentry(struct smk_audit_info *a,
|
||||
struct dentry *d)
|
||||
{
|
||||
a->a.u.fs.path.dentry = d;
|
||||
}
|
||||
static inline void smk_ad_setfield_u_fs_path_mnt(struct smk_audit_info *a,
|
||||
struct vfsmount *m)
|
||||
{
|
||||
a->a.u.fs.path.mnt = m;
|
||||
}
|
||||
static inline void smk_ad_setfield_u_fs_inode(struct smk_audit_info *a,
|
||||
struct inode *i)
|
||||
{
|
||||
a->a.u.fs.inode = i;
|
||||
}
|
||||
static inline void smk_ad_setfield_u_fs_path(struct smk_audit_info *a,
|
||||
struct path p)
|
||||
{
|
||||
a->a.u.fs.path = p;
|
||||
}
|
||||
static inline void smk_ad_setfield_u_net_sk(struct smk_audit_info *a,
|
||||
struct sock *sk)
|
||||
{
|
||||
a->a.u.net.sk = sk;
|
||||
}
|
||||
|
||||
#else /* no AUDIT */
|
||||
|
||||
static inline void smk_ad_init(struct smk_audit_info *a, const char *func,
|
||||
char type)
|
||||
{
|
||||
}
|
||||
static inline void smk_ad_setfield_u_tsk(struct smk_audit_info *a,
|
||||
struct task_struct *t)
|
||||
{
|
||||
}
|
||||
static inline void smk_ad_setfield_u_fs_path_dentry(struct smk_audit_info *a,
|
||||
struct dentry *d)
|
||||
{
|
||||
}
|
||||
static inline void smk_ad_setfield_u_fs_path_mnt(struct smk_audit_info *a,
|
||||
struct vfsmount *m)
|
||||
{
|
||||
}
|
||||
static inline void smk_ad_setfield_u_fs_inode(struct smk_audit_info *a,
|
||||
struct inode *i)
|
||||
{
|
||||
}
|
||||
static inline void smk_ad_setfield_u_fs_path(struct smk_audit_info *a,
|
||||
struct path p)
|
||||
{
|
||||
}
|
||||
static inline void smk_ad_setfield_u_net_sk(struct smk_audit_info *a,
|
||||
struct sock *sk)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SECURITY_SMACK_H */
|
||||
|
@ -59,11 +59,18 @@ LIST_HEAD(smack_known_list);
|
||||
*/
|
||||
static u32 smack_next_secid = 10;
|
||||
|
||||
/*
|
||||
* what events do we log
|
||||
* can be overwritten at run-time by /smack/logging
|
||||
*/
|
||||
int log_policy = SMACK_AUDIT_DENIED;
|
||||
|
||||
/**
|
||||
* smk_access - determine if a subject has a specific access to an object
|
||||
* @subject_label: a pointer to the subject's Smack label
|
||||
* @object_label: a pointer to the object's Smack label
|
||||
* @request: the access requested, in "MAY" format
|
||||
* @a : a pointer to the audit data
|
||||
*
|
||||
* This function looks up the subject/object pair in the
|
||||
* access rule list and returns 0 if the access is permitted,
|
||||
@ -78,10 +85,12 @@ static u32 smack_next_secid = 10;
|
||||
* will be on the list, so checking the pointers may be a worthwhile
|
||||
* optimization.
|
||||
*/
|
||||
int smk_access(char *subject_label, char *object_label, int request)
|
||||
int smk_access(char *subject_label, char *object_label, int request,
|
||||
struct smk_audit_info *a)
|
||||
{
|
||||
u32 may = MAY_NOT;
|
||||
struct smack_rule *srp;
|
||||
int rc = 0;
|
||||
|
||||
/*
|
||||
* Hardcoded comparisons.
|
||||
@ -89,8 +98,10 @@ int smk_access(char *subject_label, char *object_label, int request)
|
||||
* A star subject can't access any object.
|
||||
*/
|
||||
if (subject_label == smack_known_star.smk_known ||
|
||||
strcmp(subject_label, smack_known_star.smk_known) == 0)
|
||||
return -EACCES;
|
||||
strcmp(subject_label, smack_known_star.smk_known) == 0) {
|
||||
rc = -EACCES;
|
||||
goto out_audit;
|
||||
}
|
||||
/*
|
||||
* An internet object can be accessed by any subject.
|
||||
* Tasks cannot be assigned the internet label.
|
||||
@ -100,20 +111,20 @@ int smk_access(char *subject_label, char *object_label, int request)
|
||||
subject_label == smack_known_web.smk_known ||
|
||||
strcmp(object_label, smack_known_web.smk_known) == 0 ||
|
||||
strcmp(subject_label, smack_known_web.smk_known) == 0)
|
||||
return 0;
|
||||
goto out_audit;
|
||||
/*
|
||||
* A star object can be accessed by any subject.
|
||||
*/
|
||||
if (object_label == smack_known_star.smk_known ||
|
||||
strcmp(object_label, smack_known_star.smk_known) == 0)
|
||||
return 0;
|
||||
goto out_audit;
|
||||
/*
|
||||
* An object can be accessed in any way by a subject
|
||||
* with the same label.
|
||||
*/
|
||||
if (subject_label == object_label ||
|
||||
strcmp(subject_label, object_label) == 0)
|
||||
return 0;
|
||||
goto out_audit;
|
||||
/*
|
||||
* A hat subject can read any object.
|
||||
* A floor object can be read by any subject.
|
||||
@ -121,10 +132,10 @@ int smk_access(char *subject_label, char *object_label, int request)
|
||||
if ((request & MAY_ANYREAD) == request) {
|
||||
if (object_label == smack_known_floor.smk_known ||
|
||||
strcmp(object_label, smack_known_floor.smk_known) == 0)
|
||||
return 0;
|
||||
goto out_audit;
|
||||
if (subject_label == smack_known_hat.smk_known ||
|
||||
strcmp(subject_label, smack_known_hat.smk_known) == 0)
|
||||
return 0;
|
||||
goto out_audit;
|
||||
}
|
||||
/*
|
||||
* Beyond here an explicit relationship is required.
|
||||
@ -148,28 +159,36 @@ int smk_access(char *subject_label, char *object_label, int request)
|
||||
* This is a bit map operation.
|
||||
*/
|
||||
if ((request & may) == request)
|
||||
return 0;
|
||||
goto out_audit;
|
||||
|
||||
return -EACCES;
|
||||
rc = -EACCES;
|
||||
out_audit:
|
||||
#ifdef CONFIG_AUDIT
|
||||
if (a)
|
||||
smack_log(subject_label, object_label, request, rc, a);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_curacc - determine if current has a specific access to an object
|
||||
* @obj_label: a pointer to the object's Smack label
|
||||
* @mode: the access requested, in "MAY" format
|
||||
* @a : common audit data
|
||||
*
|
||||
* This function checks the current subject label/object label pair
|
||||
* in the access rule list and returns 0 if the access is permitted,
|
||||
* non zero otherwise. It allows that current may have the capability
|
||||
* to override the rules.
|
||||
*/
|
||||
int smk_curacc(char *obj_label, u32 mode)
|
||||
int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a)
|
||||
{
|
||||
int rc;
|
||||
char *sp = current_security();
|
||||
|
||||
rc = smk_access(current_security(), obj_label, mode);
|
||||
rc = smk_access(sp, obj_label, mode, NULL);
|
||||
if (rc == 0)
|
||||
return 0;
|
||||
goto out_audit;
|
||||
|
||||
/*
|
||||
* Return if a specific label has been designated as the
|
||||
@ -177,14 +196,105 @@ int smk_curacc(char *obj_label, u32 mode)
|
||||
* have that label.
|
||||
*/
|
||||
if (smack_onlycap != NULL && smack_onlycap != current->cred->security)
|
||||
return rc;
|
||||
goto out_audit;
|
||||
|
||||
if (capable(CAP_MAC_OVERRIDE))
|
||||
return 0;
|
||||
|
||||
out_audit:
|
||||
#ifdef CONFIG_AUDIT
|
||||
if (a)
|
||||
smack_log(sp, obj_label, mode, rc, a);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
/**
|
||||
* smack_str_from_perm : helper to transalate an int to a
|
||||
* readable string
|
||||
* @string : the string to fill
|
||||
* @access : the int
|
||||
*
|
||||
*/
|
||||
static inline void smack_str_from_perm(char *string, int access)
|
||||
{
|
||||
int i = 0;
|
||||
if (access & MAY_READ)
|
||||
string[i++] = 'r';
|
||||
if (access & MAY_WRITE)
|
||||
string[i++] = 'w';
|
||||
if (access & MAY_EXEC)
|
||||
string[i++] = 'x';
|
||||
if (access & MAY_APPEND)
|
||||
string[i++] = 'a';
|
||||
string[i] = '\0';
|
||||
}
|
||||
/**
|
||||
* smack_log_callback - SMACK specific information
|
||||
* will be called by generic audit code
|
||||
* @ab : the audit_buffer
|
||||
* @a : audit_data
|
||||
*
|
||||
*/
|
||||
static void smack_log_callback(struct audit_buffer *ab, void *a)
|
||||
{
|
||||
struct common_audit_data *ad = a;
|
||||
struct smack_audit_data *sad = &ad->lsm_priv.smack_audit_data;
|
||||
audit_log_format(ab, "lsm=SMACK fn=%s action=%s", ad->function,
|
||||
sad->result ? "denied" : "granted");
|
||||
audit_log_format(ab, " subject=");
|
||||
audit_log_untrustedstring(ab, sad->subject);
|
||||
audit_log_format(ab, " object=");
|
||||
audit_log_untrustedstring(ab, sad->object);
|
||||
audit_log_format(ab, " requested=%s", sad->request);
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_log - Audit the granting or denial of permissions.
|
||||
* @subject_label : smack label of the requester
|
||||
* @object_label : smack label of the object being accessed
|
||||
* @request: requested permissions
|
||||
* @result: result from smk_access
|
||||
* @a: auxiliary audit data
|
||||
*
|
||||
* Audit the granting or denial of permissions in accordance
|
||||
* with the policy.
|
||||
*/
|
||||
void smack_log(char *subject_label, char *object_label, int request,
|
||||
int result, struct smk_audit_info *ad)
|
||||
{
|
||||
char request_buffer[SMK_NUM_ACCESS_TYPE + 1];
|
||||
struct smack_audit_data *sad;
|
||||
struct common_audit_data *a = &ad->a;
|
||||
|
||||
/* check if we have to log the current event */
|
||||
if (result != 0 && (log_policy & SMACK_AUDIT_DENIED) == 0)
|
||||
return;
|
||||
if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0)
|
||||
return;
|
||||
|
||||
if (a->function == NULL)
|
||||
a->function = "unknown";
|
||||
|
||||
/* end preparing the audit data */
|
||||
sad = &a->lsm_priv.smack_audit_data;
|
||||
smack_str_from_perm(request_buffer, request);
|
||||
sad->subject = subject_label;
|
||||
sad->object = object_label;
|
||||
sad->request = request_buffer;
|
||||
sad->result = result;
|
||||
a->lsm_pre_audit = smack_log_callback;
|
||||
|
||||
common_lsm_audit(a);
|
||||
}
|
||||
#else /* #ifdef CONFIG_AUDIT */
|
||||
void smack_log(char *subject_label, char *object_label, int request,
|
||||
int result, struct smk_audit_info *ad)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static DEFINE_MUTEX(smack_known_lock);
|
||||
|
||||
/**
|
||||
@ -209,7 +319,8 @@ struct smack_known *smk_import_entry(const char *string, int len)
|
||||
if (found)
|
||||
smack[i] = '\0';
|
||||
else if (i >= len || string[i] > '~' || string[i] <= ' ' ||
|
||||
string[i] == '/') {
|
||||
string[i] == '/' || string[i] == '"' ||
|
||||
string[i] == '\\' || string[i] == '\'') {
|
||||
smack[i] = '\0';
|
||||
found = 1;
|
||||
} else
|
||||
|
@ -30,7 +30,6 @@
|
||||
#include <net/netlabel.h>
|
||||
#include <net/cipso_ipv4.h>
|
||||
#include <linux/audit.h>
|
||||
|
||||
#include "smack.h"
|
||||
|
||||
#define task_security(task) (task_cred_xxx((task), security))
|
||||
@ -103,14 +102,24 @@ struct inode_smack *new_inode_smack(char *smack)
|
||||
static int smack_ptrace_may_access(struct task_struct *ctp, unsigned int mode)
|
||||
{
|
||||
int rc;
|
||||
struct smk_audit_info ad;
|
||||
char *sp, *tsp;
|
||||
|
||||
rc = cap_ptrace_may_access(ctp, mode);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = smk_access(current_security(), task_security(ctp), MAY_READWRITE);
|
||||
sp = current_security();
|
||||
tsp = task_security(ctp);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
|
||||
smk_ad_setfield_u_tsk(&ad, ctp);
|
||||
|
||||
/* we won't log here, because rc can be overriden */
|
||||
rc = smk_access(sp, tsp, MAY_READWRITE, NULL);
|
||||
if (rc != 0 && capable(CAP_MAC_OVERRIDE))
|
||||
return 0;
|
||||
rc = 0;
|
||||
|
||||
smack_log(sp, tsp, MAY_READWRITE, rc, &ad);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -125,14 +134,24 @@ static int smack_ptrace_may_access(struct task_struct *ctp, unsigned int mode)
|
||||
static int smack_ptrace_traceme(struct task_struct *ptp)
|
||||
{
|
||||
int rc;
|
||||
struct smk_audit_info ad;
|
||||
char *sp, *tsp;
|
||||
|
||||
rc = cap_ptrace_traceme(ptp);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
rc = smk_access(task_security(ptp), current_security(), MAY_READWRITE);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
|
||||
smk_ad_setfield_u_tsk(&ad, ptp);
|
||||
|
||||
sp = current_security();
|
||||
tsp = task_security(ptp);
|
||||
/* we won't log here, because rc can be overriden */
|
||||
rc = smk_access(tsp, sp, MAY_READWRITE, NULL);
|
||||
if (rc != 0 && has_capability(ptp, CAP_MAC_OVERRIDE))
|
||||
return 0;
|
||||
rc = 0;
|
||||
|
||||
smack_log(tsp, sp, MAY_READWRITE, rc, &ad);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -327,8 +346,14 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
static int smack_sb_statfs(struct dentry *dentry)
|
||||
{
|
||||
struct superblock_smack *sbp = dentry->d_sb->s_security;
|
||||
int rc;
|
||||
struct smk_audit_info ad;
|
||||
|
||||
return smk_curacc(sbp->smk_floor, MAY_READ);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
|
||||
rc = smk_curacc(sbp->smk_floor, MAY_READ, &ad);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -346,8 +371,12 @@ static int smack_sb_mount(char *dev_name, struct path *path,
|
||||
char *type, unsigned long flags, void *data)
|
||||
{
|
||||
struct superblock_smack *sbp = path->mnt->mnt_sb->s_security;
|
||||
struct smk_audit_info ad;
|
||||
|
||||
return smk_curacc(sbp->smk_floor, MAY_WRITE);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path(&ad, *path);
|
||||
|
||||
return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -361,10 +390,14 @@ static int smack_sb_mount(char *dev_name, struct path *path,
|
||||
static int smack_sb_umount(struct vfsmount *mnt, int flags)
|
||||
{
|
||||
struct superblock_smack *sbp;
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, mnt->mnt_mountpoint);
|
||||
smk_ad_setfield_u_fs_path_mnt(&ad, mnt);
|
||||
|
||||
sbp = mnt->mnt_sb->s_security;
|
||||
|
||||
return smk_curacc(sbp->smk_floor, MAY_WRITE);
|
||||
return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -441,15 +474,20 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
|
||||
static int smack_inode_link(struct dentry *old_dentry, struct inode *dir,
|
||||
struct dentry *new_dentry)
|
||||
{
|
||||
int rc;
|
||||
char *isp;
|
||||
struct smk_audit_info ad;
|
||||
int rc;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry);
|
||||
|
||||
isp = smk_of_inode(old_dentry->d_inode);
|
||||
rc = smk_curacc(isp, MAY_WRITE);
|
||||
rc = smk_curacc(isp, MAY_WRITE, &ad);
|
||||
|
||||
if (rc == 0 && new_dentry->d_inode != NULL) {
|
||||
isp = smk_of_inode(new_dentry->d_inode);
|
||||
rc = smk_curacc(isp, MAY_WRITE);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, new_dentry);
|
||||
rc = smk_curacc(isp, MAY_WRITE, &ad);
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -466,18 +504,24 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir,
|
||||
static int smack_inode_unlink(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct inode *ip = dentry->d_inode;
|
||||
struct smk_audit_info ad;
|
||||
int rc;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
|
||||
/*
|
||||
* You need write access to the thing you're unlinking
|
||||
*/
|
||||
rc = smk_curacc(smk_of_inode(ip), MAY_WRITE);
|
||||
if (rc == 0)
|
||||
rc = smk_curacc(smk_of_inode(ip), MAY_WRITE, &ad);
|
||||
if (rc == 0) {
|
||||
/*
|
||||
* You also need write access to the containing directory
|
||||
*/
|
||||
rc = smk_curacc(smk_of_inode(dir), MAY_WRITE);
|
||||
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, NULL);
|
||||
smk_ad_setfield_u_fs_inode(&ad, dir);
|
||||
rc = smk_curacc(smk_of_inode(dir), MAY_WRITE, &ad);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -491,17 +535,24 @@ static int smack_inode_unlink(struct inode *dir, struct dentry *dentry)
|
||||
*/
|
||||
static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct smk_audit_info ad;
|
||||
int rc;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
|
||||
/*
|
||||
* You need write access to the thing you're removing
|
||||
*/
|
||||
rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);
|
||||
if (rc == 0)
|
||||
rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad);
|
||||
if (rc == 0) {
|
||||
/*
|
||||
* You also need write access to the containing directory
|
||||
*/
|
||||
rc = smk_curacc(smk_of_inode(dir), MAY_WRITE);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, NULL);
|
||||
smk_ad_setfield_u_fs_inode(&ad, dir);
|
||||
rc = smk_curacc(smk_of_inode(dir), MAY_WRITE, &ad);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -525,15 +576,19 @@ static int smack_inode_rename(struct inode *old_inode,
|
||||
{
|
||||
int rc;
|
||||
char *isp;
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry);
|
||||
|
||||
isp = smk_of_inode(old_dentry->d_inode);
|
||||
rc = smk_curacc(isp, MAY_READWRITE);
|
||||
rc = smk_curacc(isp, MAY_READWRITE, &ad);
|
||||
|
||||
if (rc == 0 && new_dentry->d_inode != NULL) {
|
||||
isp = smk_of_inode(new_dentry->d_inode);
|
||||
rc = smk_curacc(isp, MAY_READWRITE);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, new_dentry);
|
||||
rc = smk_curacc(isp, MAY_READWRITE, &ad);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -548,13 +603,15 @@ static int smack_inode_rename(struct inode *old_inode,
|
||||
*/
|
||||
static int smack_inode_permission(struct inode *inode, int mask)
|
||||
{
|
||||
struct smk_audit_info ad;
|
||||
/*
|
||||
* No permission to check. Existence test. Yup, it's there.
|
||||
*/
|
||||
if (mask == 0)
|
||||
return 0;
|
||||
|
||||
return smk_curacc(smk_of_inode(inode), mask);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_inode(&ad, inode);
|
||||
return smk_curacc(smk_of_inode(inode), mask, &ad);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -566,13 +623,16 @@ static int smack_inode_permission(struct inode *inode, int mask)
|
||||
*/
|
||||
static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||
{
|
||||
struct smk_audit_info ad;
|
||||
/*
|
||||
* Need to allow for clearing the setuid bit.
|
||||
*/
|
||||
if (iattr->ia_valid & ATTR_FORCE)
|
||||
return 0;
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
|
||||
return smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);
|
||||
return smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -584,7 +644,12 @@ static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||
*/
|
||||
static int smack_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
|
||||
{
|
||||
return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ);
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
smk_ad_setfield_u_fs_path_mnt(&ad, mnt);
|
||||
return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -602,6 +667,7 @@ static int smack_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
|
||||
static int smack_inode_setxattr(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags)
|
||||
{
|
||||
struct smk_audit_info ad;
|
||||
int rc = 0;
|
||||
|
||||
if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
|
||||
@ -619,8 +685,11 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,
|
||||
} else
|
||||
rc = cap_inode_setxattr(dentry, name, value, size, flags);
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
|
||||
if (rc == 0)
|
||||
rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);
|
||||
rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -672,7 +741,12 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
|
||||
*/
|
||||
static int smack_inode_getxattr(struct dentry *dentry, const char *name)
|
||||
{
|
||||
return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ);
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
|
||||
return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -686,6 +760,7 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name)
|
||||
*/
|
||||
static int smack_inode_removexattr(struct dentry *dentry, const char *name)
|
||||
{
|
||||
struct smk_audit_info ad;
|
||||
int rc = 0;
|
||||
|
||||
if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
|
||||
@ -696,8 +771,10 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name)
|
||||
} else
|
||||
rc = cap_inode_removexattr(dentry, name);
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
|
||||
if (rc == 0)
|
||||
rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);
|
||||
rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -856,12 +933,16 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path(&ad, file->f_path);
|
||||
|
||||
if (_IOC_DIR(cmd) & _IOC_WRITE)
|
||||
rc = smk_curacc(file->f_security, MAY_WRITE);
|
||||
rc = smk_curacc(file->f_security, MAY_WRITE, &ad);
|
||||
|
||||
if (rc == 0 && (_IOC_DIR(cmd) & _IOC_READ))
|
||||
rc = smk_curacc(file->f_security, MAY_READ);
|
||||
rc = smk_curacc(file->f_security, MAY_READ, &ad);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -875,7 +956,11 @@ static int smack_file_ioctl(struct file *file, unsigned int cmd,
|
||||
*/
|
||||
static int smack_file_lock(struct file *file, unsigned int cmd)
|
||||
{
|
||||
return smk_curacc(file->f_security, MAY_WRITE);
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path_dentry(&ad, file->f_path.dentry);
|
||||
return smk_curacc(file->f_security, MAY_WRITE, &ad);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -889,8 +974,12 @@ static int smack_file_lock(struct file *file, unsigned int cmd)
|
||||
static int smack_file_fcntl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct smk_audit_info ad;
|
||||
int rc;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
|
||||
smk_ad_setfield_u_fs_path(&ad, file->f_path);
|
||||
|
||||
switch (cmd) {
|
||||
case F_DUPFD:
|
||||
case F_GETFD:
|
||||
@ -898,7 +987,7 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd,
|
||||
case F_GETLK:
|
||||
case F_GETOWN:
|
||||
case F_GETSIG:
|
||||
rc = smk_curacc(file->f_security, MAY_READ);
|
||||
rc = smk_curacc(file->f_security, MAY_READ, &ad);
|
||||
break;
|
||||
case F_SETFD:
|
||||
case F_SETFL:
|
||||
@ -906,10 +995,10 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd,
|
||||
case F_SETLKW:
|
||||
case F_SETOWN:
|
||||
case F_SETSIG:
|
||||
rc = smk_curacc(file->f_security, MAY_WRITE);
|
||||
rc = smk_curacc(file->f_security, MAY_WRITE, &ad);
|
||||
break;
|
||||
default:
|
||||
rc = smk_curacc(file->f_security, MAY_READWRITE);
|
||||
rc = smk_curacc(file->f_security, MAY_READWRITE, &ad);
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -944,14 +1033,21 @@ static int smack_file_send_sigiotask(struct task_struct *tsk,
|
||||
{
|
||||
struct file *file;
|
||||
int rc;
|
||||
char *tsp = tsk->cred->security;
|
||||
struct smk_audit_info ad;
|
||||
|
||||
/*
|
||||
* struct fown_struct is never outside the context of a struct file
|
||||
*/
|
||||
file = container_of(fown, struct file, f_owner);
|
||||
rc = smk_access(file->f_security, tsk->cred->security, MAY_WRITE);
|
||||
/* we don't log here as rc can be overriden */
|
||||
rc = smk_access(file->f_security, tsp, MAY_WRITE, NULL);
|
||||
if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE))
|
||||
return 0;
|
||||
rc = 0;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
|
||||
smk_ad_setfield_u_tsk(&ad, tsk);
|
||||
smack_log(file->f_security, tsp, MAY_WRITE, rc, &ad);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -964,7 +1060,10 @@ static int smack_file_send_sigiotask(struct task_struct *tsk,
|
||||
static int smack_file_receive(struct file *file)
|
||||
{
|
||||
int may = 0;
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
|
||||
smk_ad_setfield_u_fs_path(&ad, file->f_path);
|
||||
/*
|
||||
* This code relies on bitmasks.
|
||||
*/
|
||||
@ -973,7 +1072,7 @@ static int smack_file_receive(struct file *file)
|
||||
if (file->f_mode & FMODE_WRITE)
|
||||
may |= MAY_WRITE;
|
||||
|
||||
return smk_curacc(file->f_security, may);
|
||||
return smk_curacc(file->f_security, may, &ad);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1052,6 +1151,22 @@ static int smack_kernel_create_files_as(struct cred *new,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_curacc_on_task - helper to log task related access
|
||||
* @p: the task object
|
||||
* @access : the access requested
|
||||
*
|
||||
* Return 0 if access is permitted
|
||||
*/
|
||||
static int smk_curacc_on_task(struct task_struct *p, int access)
|
||||
{
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
|
||||
smk_ad_setfield_u_tsk(&ad, p);
|
||||
return smk_curacc(task_security(p), access, &ad);
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_task_setpgid - Smack check on setting pgid
|
||||
* @p: the task object
|
||||
@ -1061,7 +1176,7 @@ static int smack_kernel_create_files_as(struct cred *new,
|
||||
*/
|
||||
static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
|
||||
{
|
||||
return smk_curacc(task_security(p), MAY_WRITE);
|
||||
return smk_curacc_on_task(p, MAY_WRITE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1072,7 +1187,7 @@ static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
|
||||
*/
|
||||
static int smack_task_getpgid(struct task_struct *p)
|
||||
{
|
||||
return smk_curacc(task_security(p), MAY_READ);
|
||||
return smk_curacc_on_task(p, MAY_READ);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1083,7 +1198,7 @@ static int smack_task_getpgid(struct task_struct *p)
|
||||
*/
|
||||
static int smack_task_getsid(struct task_struct *p)
|
||||
{
|
||||
return smk_curacc(task_security(p), MAY_READ);
|
||||
return smk_curacc_on_task(p, MAY_READ);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1111,7 +1226,7 @@ static int smack_task_setnice(struct task_struct *p, int nice)
|
||||
|
||||
rc = cap_task_setnice(p, nice);
|
||||
if (rc == 0)
|
||||
rc = smk_curacc(task_security(p), MAY_WRITE);
|
||||
rc = smk_curacc_on_task(p, MAY_WRITE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -1128,7 +1243,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio)
|
||||
|
||||
rc = cap_task_setioprio(p, ioprio);
|
||||
if (rc == 0)
|
||||
rc = smk_curacc(task_security(p), MAY_WRITE);
|
||||
rc = smk_curacc_on_task(p, MAY_WRITE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -1140,7 +1255,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio)
|
||||
*/
|
||||
static int smack_task_getioprio(struct task_struct *p)
|
||||
{
|
||||
return smk_curacc(task_security(p), MAY_READ);
|
||||
return smk_curacc_on_task(p, MAY_READ);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1158,7 +1273,7 @@ static int smack_task_setscheduler(struct task_struct *p, int policy,
|
||||
|
||||
rc = cap_task_setscheduler(p, policy, lp);
|
||||
if (rc == 0)
|
||||
rc = smk_curacc(task_security(p), MAY_WRITE);
|
||||
rc = smk_curacc_on_task(p, MAY_WRITE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -1170,7 +1285,7 @@ static int smack_task_setscheduler(struct task_struct *p, int policy,
|
||||
*/
|
||||
static int smack_task_getscheduler(struct task_struct *p)
|
||||
{
|
||||
return smk_curacc(task_security(p), MAY_READ);
|
||||
return smk_curacc_on_task(p, MAY_READ);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1181,7 +1296,7 @@ static int smack_task_getscheduler(struct task_struct *p)
|
||||
*/
|
||||
static int smack_task_movememory(struct task_struct *p)
|
||||
{
|
||||
return smk_curacc(task_security(p), MAY_WRITE);
|
||||
return smk_curacc_on_task(p, MAY_WRITE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1199,18 +1314,23 @@ static int smack_task_movememory(struct task_struct *p)
|
||||
static int smack_task_kill(struct task_struct *p, struct siginfo *info,
|
||||
int sig, u32 secid)
|
||||
{
|
||||
struct smk_audit_info ad;
|
||||
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
|
||||
smk_ad_setfield_u_tsk(&ad, p);
|
||||
/*
|
||||
* Sending a signal requires that the sender
|
||||
* can write the receiver.
|
||||
*/
|
||||
if (secid == 0)
|
||||
return smk_curacc(task_security(p), MAY_WRITE);
|
||||
return smk_curacc(task_security(p), MAY_WRITE, &ad);
|
||||
/*
|
||||
* If the secid isn't 0 we're dealing with some USB IO
|
||||
* specific behavior. This is not clean. For one thing
|
||||
* we can't take privilege into account.
|
||||
*/
|
||||
return smk_access(smack_from_secid(secid), task_security(p), MAY_WRITE);
|
||||
return smk_access(smack_from_secid(secid), task_security(p),
|
||||
MAY_WRITE, &ad);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1221,11 +1341,15 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
|
||||
*/
|
||||
static int smack_task_wait(struct task_struct *p)
|
||||
{
|
||||
struct smk_audit_info ad;
|
||||
char *sp = current_security();
|
||||
char *tsp = task_security(p);
|
||||
int rc;
|
||||
|
||||
rc = smk_access(current_security(), task_security(p), MAY_WRITE);
|
||||
/* we don't log here, we can be overriden */
|
||||
rc = smk_access(sp, tsp, MAY_WRITE, NULL);
|
||||
if (rc == 0)
|
||||
return 0;
|
||||
goto out_log;
|
||||
|
||||
/*
|
||||
* Allow the operation to succeed if either task
|
||||
@ -1239,8 +1363,12 @@ static int smack_task_wait(struct task_struct *p)
|
||||
* the smack value.
|
||||
*/
|
||||
if (capable(CAP_MAC_OVERRIDE) || has_capability(p, CAP_MAC_OVERRIDE))
|
||||
return 0;
|
||||
|
||||
rc = 0;
|
||||
/* we log only if we didn't get overriden */
|
||||
out_log:
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
|
||||
smk_ad_setfield_u_tsk(&ad, p);
|
||||
smack_log(sp, tsp, MAY_WRITE, rc, &ad);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -1456,12 +1584,19 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
|
||||
int sk_lbl;
|
||||
char *hostsp;
|
||||
struct socket_smack *ssp = sk->sk_security;
|
||||
struct smk_audit_info ad;
|
||||
|
||||
rcu_read_lock();
|
||||
hostsp = smack_host_label(sap);
|
||||
if (hostsp != NULL) {
|
||||
sk_lbl = SMACK_UNLABELED_SOCKET;
|
||||
rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE);
|
||||
#ifdef CONFIG_AUDIT
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET);
|
||||
ad.a.u.net.family = sap->sin_family;
|
||||
ad.a.u.net.dport = sap->sin_port;
|
||||
ad.a.u.net.v4info.daddr = sap->sin_addr.s_addr;
|
||||
#endif
|
||||
rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE, &ad);
|
||||
} else {
|
||||
sk_lbl = SMACK_CIPSO_SOCKET;
|
||||
rc = 0;
|
||||
@ -1656,6 +1791,25 @@ static void smack_shm_free_security(struct shmid_kernel *shp)
|
||||
isp->security = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_curacc_shm : check if current has access on shm
|
||||
* @shp : the object
|
||||
* @access : access requested
|
||||
*
|
||||
* Returns 0 if current has the requested access, error code otherwise
|
||||
*/
|
||||
static int smk_curacc_shm(struct shmid_kernel *shp, int access)
|
||||
{
|
||||
char *ssp = smack_of_shm(shp);
|
||||
struct smk_audit_info ad;
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC);
|
||||
ad.a.u.ipc_id = shp->shm_perm.id;
|
||||
#endif
|
||||
return smk_curacc(ssp, access, &ad);
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_shm_associate - Smack access check for shm
|
||||
* @shp: the object
|
||||
@ -1665,11 +1819,10 @@ static void smack_shm_free_security(struct shmid_kernel *shp)
|
||||
*/
|
||||
static int smack_shm_associate(struct shmid_kernel *shp, int shmflg)
|
||||
{
|
||||
char *ssp = smack_of_shm(shp);
|
||||
int may;
|
||||
|
||||
may = smack_flags_to_may(shmflg);
|
||||
return smk_curacc(ssp, may);
|
||||
return smk_curacc_shm(shp, may);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1681,7 +1834,6 @@ static int smack_shm_associate(struct shmid_kernel *shp, int shmflg)
|
||||
*/
|
||||
static int smack_shm_shmctl(struct shmid_kernel *shp, int cmd)
|
||||
{
|
||||
char *ssp;
|
||||
int may;
|
||||
|
||||
switch (cmd) {
|
||||
@ -1704,9 +1856,7 @@ static int smack_shm_shmctl(struct shmid_kernel *shp, int cmd)
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ssp = smack_of_shm(shp);
|
||||
return smk_curacc(ssp, may);
|
||||
return smk_curacc_shm(shp, may);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1720,11 +1870,10 @@ static int smack_shm_shmctl(struct shmid_kernel *shp, int cmd)
|
||||
static int smack_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr,
|
||||
int shmflg)
|
||||
{
|
||||
char *ssp = smack_of_shm(shp);
|
||||
int may;
|
||||
|
||||
may = smack_flags_to_may(shmflg);
|
||||
return smk_curacc(ssp, may);
|
||||
return smk_curacc_shm(shp, may);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1765,6 +1914,25 @@ static void smack_sem_free_security(struct sem_array *sma)
|
||||
isp->security = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_curacc_sem : check if current has access on sem
|
||||
* @sma : the object
|
||||
* @access : access requested
|
||||
*
|
||||
* Returns 0 if current has the requested access, error code otherwise
|
||||
*/
|
||||
static int smk_curacc_sem(struct sem_array *sma, int access)
|
||||
{
|
||||
char *ssp = smack_of_sem(sma);
|
||||
struct smk_audit_info ad;
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC);
|
||||
ad.a.u.ipc_id = sma->sem_perm.id;
|
||||
#endif
|
||||
return smk_curacc(ssp, access, &ad);
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_sem_associate - Smack access check for sem
|
||||
* @sma: the object
|
||||
@ -1774,11 +1942,10 @@ static void smack_sem_free_security(struct sem_array *sma)
|
||||
*/
|
||||
static int smack_sem_associate(struct sem_array *sma, int semflg)
|
||||
{
|
||||
char *ssp = smack_of_sem(sma);
|
||||
int may;
|
||||
|
||||
may = smack_flags_to_may(semflg);
|
||||
return smk_curacc(ssp, may);
|
||||
return smk_curacc_sem(sma, may);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1790,7 +1957,6 @@ static int smack_sem_associate(struct sem_array *sma, int semflg)
|
||||
*/
|
||||
static int smack_sem_semctl(struct sem_array *sma, int cmd)
|
||||
{
|
||||
char *ssp;
|
||||
int may;
|
||||
|
||||
switch (cmd) {
|
||||
@ -1819,8 +1985,7 @@ static int smack_sem_semctl(struct sem_array *sma, int cmd)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ssp = smack_of_sem(sma);
|
||||
return smk_curacc(ssp, may);
|
||||
return smk_curacc_sem(sma, may);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1837,9 +2002,7 @@ static int smack_sem_semctl(struct sem_array *sma, int cmd)
|
||||
static int smack_sem_semop(struct sem_array *sma, struct sembuf *sops,
|
||||
unsigned nsops, int alter)
|
||||
{
|
||||
char *ssp = smack_of_sem(sma);
|
||||
|
||||
return smk_curacc(ssp, MAY_READWRITE);
|
||||
return smk_curacc_sem(sma, MAY_READWRITE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1880,6 +2043,25 @@ static char *smack_of_msq(struct msg_queue *msq)
|
||||
return (char *)msq->q_perm.security;
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_curacc_msq : helper to check if current has access on msq
|
||||
* @msq : the msq
|
||||
* @access : access requested
|
||||
*
|
||||
* return 0 if current has access, error otherwise
|
||||
*/
|
||||
static int smk_curacc_msq(struct msg_queue *msq, int access)
|
||||
{
|
||||
char *msp = smack_of_msq(msq);
|
||||
struct smk_audit_info ad;
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC);
|
||||
ad.a.u.ipc_id = msq->q_perm.id;
|
||||
#endif
|
||||
return smk_curacc(msp, access, &ad);
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_msg_queue_associate - Smack access check for msg_queue
|
||||
* @msq: the object
|
||||
@ -1889,11 +2071,10 @@ static char *smack_of_msq(struct msg_queue *msq)
|
||||
*/
|
||||
static int smack_msg_queue_associate(struct msg_queue *msq, int msqflg)
|
||||
{
|
||||
char *msp = smack_of_msq(msq);
|
||||
int may;
|
||||
|
||||
may = smack_flags_to_may(msqflg);
|
||||
return smk_curacc(msp, may);
|
||||
return smk_curacc_msq(msq, may);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1905,7 +2086,6 @@ static int smack_msg_queue_associate(struct msg_queue *msq, int msqflg)
|
||||
*/
|
||||
static int smack_msg_queue_msgctl(struct msg_queue *msq, int cmd)
|
||||
{
|
||||
char *msp;
|
||||
int may;
|
||||
|
||||
switch (cmd) {
|
||||
@ -1927,8 +2107,7 @@ static int smack_msg_queue_msgctl(struct msg_queue *msq, int cmd)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
msp = smack_of_msq(msq);
|
||||
return smk_curacc(msp, may);
|
||||
return smk_curacc_msq(msq, may);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1942,11 +2121,10 @@ static int smack_msg_queue_msgctl(struct msg_queue *msq, int cmd)
|
||||
static int smack_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
|
||||
int msqflg)
|
||||
{
|
||||
char *msp = smack_of_msq(msq);
|
||||
int rc;
|
||||
int may;
|
||||
|
||||
rc = smack_flags_to_may(msqflg);
|
||||
return smk_curacc(msp, rc);
|
||||
may = smack_flags_to_may(msqflg);
|
||||
return smk_curacc_msq(msq, may);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1962,9 +2140,7 @@ static int smack_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
|
||||
static int smack_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
|
||||
struct task_struct *target, long type, int mode)
|
||||
{
|
||||
char *msp = smack_of_msq(msq);
|
||||
|
||||
return smk_curacc(msp, MAY_READWRITE);
|
||||
return smk_curacc_msq(msq, MAY_READWRITE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1977,10 +2153,14 @@ static int smack_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
|
||||
static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag)
|
||||
{
|
||||
char *isp = ipp->security;
|
||||
int may;
|
||||
int may = smack_flags_to_may(flag);
|
||||
struct smk_audit_info ad;
|
||||
|
||||
may = smack_flags_to_may(flag);
|
||||
return smk_curacc(isp, may);
|
||||
#ifdef CONFIG_AUDIT
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC);
|
||||
ad.a.u.ipc_id = ipp->id;
|
||||
#endif
|
||||
return smk_curacc(isp, may, &ad);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2239,8 +2419,12 @@ static int smack_unix_stream_connect(struct socket *sock,
|
||||
{
|
||||
struct inode *sp = SOCK_INODE(sock);
|
||||
struct inode *op = SOCK_INODE(other);
|
||||
struct smk_audit_info ad;
|
||||
|
||||
return smk_access(smk_of_inode(sp), smk_of_inode(op), MAY_READWRITE);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET);
|
||||
smk_ad_setfield_u_net_sk(&ad, other->sk);
|
||||
return smk_access(smk_of_inode(sp), smk_of_inode(op),
|
||||
MAY_READWRITE, &ad);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2255,8 +2439,11 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)
|
||||
{
|
||||
struct inode *sp = SOCK_INODE(sock);
|
||||
struct inode *op = SOCK_INODE(other);
|
||||
struct smk_audit_info ad;
|
||||
|
||||
return smk_access(smk_of_inode(sp), smk_of_inode(op), MAY_WRITE);
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET);
|
||||
smk_ad_setfield_u_net_sk(&ad, other->sk);
|
||||
return smk_access(smk_of_inode(sp), smk_of_inode(op), MAY_WRITE, &ad);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2371,7 +2558,7 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
||||
char smack[SMK_LABELLEN];
|
||||
char *csp;
|
||||
int rc;
|
||||
|
||||
struct smk_audit_info ad;
|
||||
if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
|
||||
return 0;
|
||||
|
||||
@ -2389,13 +2576,19 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
||||
|
||||
netlbl_secattr_destroy(&secattr);
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET);
|
||||
ad.a.u.net.family = sk->sk_family;
|
||||
ad.a.u.net.netif = skb->iif;
|
||||
ipv4_skb_to_auditdata(skb, &ad.a, NULL);
|
||||
#endif
|
||||
/*
|
||||
* Receiving a packet requires that the other end
|
||||
* be able to write here. Read access is not required.
|
||||
* This is the simplist possible security model
|
||||
* for networking.
|
||||
*/
|
||||
rc = smk_access(csp, ssp->smk_in, MAY_WRITE);
|
||||
rc = smk_access(csp, ssp->smk_in, MAY_WRITE, &ad);
|
||||
if (rc != 0)
|
||||
netlbl_skbuff_err(skb, rc, 0);
|
||||
return rc;
|
||||
@ -2524,6 +2717,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
|
||||
struct iphdr *hdr;
|
||||
char smack[SMK_LABELLEN];
|
||||
int rc;
|
||||
struct smk_audit_info ad;
|
||||
|
||||
/* handle mapped IPv4 packets arriving via IPv6 sockets */
|
||||
if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
|
||||
@ -2537,11 +2731,17 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
|
||||
strncpy(smack, smack_known_huh.smk_known, SMK_MAXLEN);
|
||||
netlbl_secattr_destroy(&secattr);
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET);
|
||||
ad.a.u.net.family = family;
|
||||
ad.a.u.net.netif = skb->iif;
|
||||
ipv4_skb_to_auditdata(skb, &ad.a, NULL);
|
||||
#endif
|
||||
/*
|
||||
* Receiving a packet requires that the other end be able to write
|
||||
* here. Read access is not required.
|
||||
*/
|
||||
rc = smk_access(smack, ssp->smk_in, MAY_WRITE);
|
||||
rc = smk_access(smack, ssp->smk_in, MAY_WRITE, &ad);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
@ -2643,6 +2843,7 @@ static int smack_key_permission(key_ref_t key_ref,
|
||||
const struct cred *cred, key_perm_t perm)
|
||||
{
|
||||
struct key *keyp;
|
||||
struct smk_audit_info ad;
|
||||
|
||||
keyp = key_ref_to_ptr(key_ref);
|
||||
if (keyp == NULL)
|
||||
@ -2658,8 +2859,13 @@ static int smack_key_permission(key_ref_t key_ref,
|
||||
*/
|
||||
if (cred->security == NULL)
|
||||
return -EACCES;
|
||||
|
||||
return smk_access(cred->security, keyp->security, MAY_READWRITE);
|
||||
#ifdef CONFIG_AUDIT
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_KEY);
|
||||
ad.a.u.key_struct.key = keyp->serial;
|
||||
ad.a.u.key_struct.key_desc = keyp->description;
|
||||
#endif
|
||||
return smk_access(cred->security, keyp->security,
|
||||
MAY_READWRITE, &ad);
|
||||
}
|
||||
#endif /* CONFIG_KEYS */
|
||||
|
||||
@ -2828,15 +3034,7 @@ struct security_operations smack_ops = {
|
||||
|
||||
.ptrace_may_access = smack_ptrace_may_access,
|
||||
.ptrace_traceme = smack_ptrace_traceme,
|
||||
.capget = cap_capget,
|
||||
.capset = cap_capset,
|
||||
.capable = cap_capable,
|
||||
.syslog = smack_syslog,
|
||||
.settime = cap_settime,
|
||||
.vm_enough_memory = cap_vm_enough_memory,
|
||||
|
||||
.bprm_set_creds = cap_bprm_set_creds,
|
||||
.bprm_secureexec = cap_bprm_secureexec,
|
||||
|
||||
.sb_alloc_security = smack_sb_alloc_security,
|
||||
.sb_free_security = smack_sb_free_security,
|
||||
@ -2860,8 +3058,6 @@ struct security_operations smack_ops = {
|
||||
.inode_post_setxattr = smack_inode_post_setxattr,
|
||||
.inode_getxattr = smack_inode_getxattr,
|
||||
.inode_removexattr = smack_inode_removexattr,
|
||||
.inode_need_killpriv = cap_inode_need_killpriv,
|
||||
.inode_killpriv = cap_inode_killpriv,
|
||||
.inode_getsecurity = smack_inode_getsecurity,
|
||||
.inode_setsecurity = smack_inode_setsecurity,
|
||||
.inode_listsecurity = smack_inode_listsecurity,
|
||||
@ -2882,7 +3078,6 @@ struct security_operations smack_ops = {
|
||||
.cred_commit = smack_cred_commit,
|
||||
.kernel_act_as = smack_kernel_act_as,
|
||||
.kernel_create_files_as = smack_kernel_create_files_as,
|
||||
.task_fix_setuid = cap_task_fix_setuid,
|
||||
.task_setpgid = smack_task_setpgid,
|
||||
.task_getpgid = smack_task_getpgid,
|
||||
.task_getsid = smack_task_getsid,
|
||||
@ -2896,7 +3091,6 @@ struct security_operations smack_ops = {
|
||||
.task_kill = smack_task_kill,
|
||||
.task_wait = smack_task_wait,
|
||||
.task_to_inode = smack_task_to_inode,
|
||||
.task_prctl = cap_task_prctl,
|
||||
|
||||
.ipc_permission = smack_ipc_permission,
|
||||
.ipc_getsecid = smack_ipc_getsecid,
|
||||
@ -2923,9 +3117,6 @@ struct security_operations smack_ops = {
|
||||
.sem_semctl = smack_sem_semctl,
|
||||
.sem_semop = smack_sem_semop,
|
||||
|
||||
.netlink_send = cap_netlink_send,
|
||||
.netlink_recv = cap_netlink_recv,
|
||||
|
||||
.d_instantiate = smack_d_instantiate,
|
||||
|
||||
.getprocattr = smack_getprocattr,
|
||||
|
@ -41,6 +41,7 @@ enum smk_inos {
|
||||
SMK_AMBIENT = 7, /* internet ambient label */
|
||||
SMK_NETLBLADDR = 8, /* single label hosts */
|
||||
SMK_ONLYCAP = 9, /* the only "capable" label */
|
||||
SMK_LOGGING = 10, /* logging */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -775,7 +776,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
|
||||
struct sockaddr_in newname;
|
||||
char smack[SMK_LABELLEN];
|
||||
char *sp;
|
||||
char data[SMK_NETLBLADDRMAX];
|
||||
char data[SMK_NETLBLADDRMAX + 1];
|
||||
char *host = (char *)&newname.sin_addr.s_addr;
|
||||
int rc;
|
||||
struct netlbl_audit audit_info;
|
||||
@ -1191,6 +1192,69 @@ static const struct file_operations smk_onlycap_ops = {
|
||||
.write = smk_write_onlycap,
|
||||
};
|
||||
|
||||
/**
|
||||
* smk_read_logging - read() for /smack/logging
|
||||
* @filp: file pointer, not actually used
|
||||
* @buf: where to put the result
|
||||
* @cn: maximum to send along
|
||||
* @ppos: where to start
|
||||
*
|
||||
* Returns number of bytes read or error code, as appropriate
|
||||
*/
|
||||
static ssize_t smk_read_logging(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char temp[32];
|
||||
ssize_t rc;
|
||||
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
|
||||
sprintf(temp, "%d\n", log_policy);
|
||||
rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* smk_write_logging - write() for /smack/logging
|
||||
* @file: file pointer, not actually used
|
||||
* @buf: where to get the data from
|
||||
* @count: bytes sent
|
||||
* @ppos: where to start
|
||||
*
|
||||
* Returns number of bytes written or error code, as appropriate
|
||||
*/
|
||||
static ssize_t smk_write_logging(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char temp[32];
|
||||
int i;
|
||||
|
||||
if (!capable(CAP_MAC_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (count >= sizeof(temp) || count == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(temp, buf, count) != 0)
|
||||
return -EFAULT;
|
||||
|
||||
temp[count] = '\0';
|
||||
|
||||
if (sscanf(temp, "%d", &i) != 1)
|
||||
return -EINVAL;
|
||||
if (i < 0 || i > 3)
|
||||
return -EINVAL;
|
||||
log_policy = i;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const struct file_operations smk_logging_ops = {
|
||||
.read = smk_read_logging,
|
||||
.write = smk_write_logging,
|
||||
};
|
||||
/**
|
||||
* smk_fill_super - fill the /smackfs superblock
|
||||
* @sb: the empty superblock
|
||||
@ -1221,6 +1285,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{"netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR},
|
||||
[SMK_ONLYCAP] =
|
||||
{"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
|
||||
[SMK_LOGGING] =
|
||||
{"logging", &smk_logging_ops, S_IRUGO|S_IWUSR},
|
||||
/* last one */ {""}
|
||||
};
|
||||
|
||||
|
@ -28,7 +28,13 @@ static const char *tomoyo_mode_2[4] = {
|
||||
"disabled", "enabled", "enabled", "enabled"
|
||||
};
|
||||
|
||||
/* Table for profile. */
|
||||
/*
|
||||
* tomoyo_control_array is a static data which contains
|
||||
*
|
||||
* (1) functionality name used by /sys/kernel/security/tomoyo/profile .
|
||||
* (2) initial values for "struct tomoyo_profile".
|
||||
* (3) max values for "struct tomoyo_profile".
|
||||
*/
|
||||
static struct {
|
||||
const char *keyword;
|
||||
unsigned int current_value;
|
||||
@ -39,7 +45,13 @@ static struct {
|
||||
[TOMOYO_VERBOSE] = { "TOMOYO_VERBOSE", 1, 1 },
|
||||
};
|
||||
|
||||
/* Profile table. Memory is allocated as needed. */
|
||||
/*
|
||||
* tomoyo_profile is a structure which is used for holding the mode of access
|
||||
* controls. TOMOYO has 4 modes: disabled, learning, permissive, enforcing.
|
||||
* An administrator can define up to 256 profiles.
|
||||
* The ->profile of "struct tomoyo_domain_info" is used for remembering
|
||||
* the profile's number (0 - 255) assigned to that domain.
|
||||
*/
|
||||
static struct tomoyo_profile {
|
||||
unsigned int value[TOMOYO_MAX_CONTROL_INDEX];
|
||||
const struct tomoyo_path_info *comment;
|
||||
@ -428,7 +440,6 @@ void tomoyo_fill_path_info(struct tomoyo_path_info *ptr)
|
||||
const char *name = ptr->name;
|
||||
const int len = strlen(name);
|
||||
|
||||
ptr->total_len = len;
|
||||
ptr->const_len = tomoyo_const_part_length(name);
|
||||
ptr->is_dir = len && (name[len - 1] == '/');
|
||||
ptr->is_patterned = (ptr->const_len < len);
|
||||
@ -866,7 +877,6 @@ static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned
|
||||
|
||||
if (profile >= TOMOYO_MAX_PROFILES)
|
||||
return NULL;
|
||||
/***** EXCLUSIVE SECTION START *****/
|
||||
mutex_lock(&lock);
|
||||
ptr = tomoyo_profile_ptr[profile];
|
||||
if (ptr)
|
||||
@ -880,7 +890,6 @@ static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned
|
||||
tomoyo_profile_ptr[profile] = ptr;
|
||||
ok:
|
||||
mutex_unlock(&lock);
|
||||
/***** EXCLUSIVE SECTION END *****/
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@ -1009,7 +1018,19 @@ static int tomoyo_read_profile(struct tomoyo_io_buffer *head)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Structure for policy manager. */
|
||||
/*
|
||||
* tomoyo_policy_manager_entry is a structure which is used for holding list of
|
||||
* domainnames or programs which are permitted to modify configuration via
|
||||
* /sys/kernel/security/tomoyo/ interface.
|
||||
* It has following fields.
|
||||
*
|
||||
* (1) "list" which is linked to tomoyo_policy_manager_list .
|
||||
* (2) "manager" is a domainname or a program's pathname.
|
||||
* (3) "is_domain" is a bool which is true if "manager" is a domainname, false
|
||||
* otherwise.
|
||||
* (4) "is_deleted" is a bool which is true if marked as deleted, false
|
||||
* otherwise.
|
||||
*/
|
||||
struct tomoyo_policy_manager_entry {
|
||||
struct list_head list;
|
||||
/* A path to program or a domainname. */
|
||||
@ -1018,7 +1039,36 @@ struct tomoyo_policy_manager_entry {
|
||||
bool is_deleted; /* True if this entry is deleted. */
|
||||
};
|
||||
|
||||
/* The list for "struct tomoyo_policy_manager_entry". */
|
||||
/*
|
||||
* tomoyo_policy_manager_list is used for holding list of domainnames or
|
||||
* programs which are permitted to modify configuration via
|
||||
* /sys/kernel/security/tomoyo/ interface.
|
||||
*
|
||||
* An entry is added by
|
||||
*
|
||||
* # echo '<kernel> /sbin/mingetty /bin/login /bin/bash' > \
|
||||
* /sys/kernel/security/tomoyo/manager
|
||||
* (if you want to specify by a domainname)
|
||||
*
|
||||
* or
|
||||
*
|
||||
* # echo '/usr/lib/ccs/editpolicy' > /sys/kernel/security/tomoyo/manager
|
||||
* (if you want to specify by a program's location)
|
||||
*
|
||||
* and is deleted by
|
||||
*
|
||||
* # echo 'delete <kernel> /sbin/mingetty /bin/login /bin/bash' > \
|
||||
* /sys/kernel/security/tomoyo/manager
|
||||
*
|
||||
* or
|
||||
*
|
||||
* # echo 'delete /usr/lib/ccs/editpolicy' > \
|
||||
* /sys/kernel/security/tomoyo/manager
|
||||
*
|
||||
* and all entries are retrieved by
|
||||
*
|
||||
* # cat /sys/kernel/security/tomoyo/manager
|
||||
*/
|
||||
static LIST_HEAD(tomoyo_policy_manager_list);
|
||||
static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
|
||||
|
||||
@ -1050,7 +1100,6 @@ static int tomoyo_update_manager_entry(const char *manager,
|
||||
saved_manager = tomoyo_save_name(manager);
|
||||
if (!saved_manager)
|
||||
return -ENOMEM;
|
||||
/***** EXCLUSIVE SECTION START *****/
|
||||
down_write(&tomoyo_policy_manager_list_lock);
|
||||
list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
|
||||
if (ptr->manager != saved_manager)
|
||||
@ -1072,7 +1121,6 @@ static int tomoyo_update_manager_entry(const char *manager,
|
||||
error = 0;
|
||||
out:
|
||||
up_write(&tomoyo_policy_manager_list_lock);
|
||||
/***** EXCLUSIVE SECTION END *****/
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1117,10 +1165,9 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
|
||||
list);
|
||||
if (ptr->is_deleted)
|
||||
continue;
|
||||
if (!tomoyo_io_printf(head, "%s\n", ptr->manager->name)) {
|
||||
done = false;
|
||||
done = tomoyo_io_printf(head, "%s\n", ptr->manager->name);
|
||||
if (!done)
|
||||
break;
|
||||
}
|
||||
}
|
||||
up_read(&tomoyo_policy_manager_list_lock);
|
||||
head->read_eof = done;
|
||||
@ -1197,13 +1244,11 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
|
||||
|
||||
if (sscanf(data, "pid=%u", &pid) == 1) {
|
||||
struct task_struct *p;
|
||||
/***** CRITICAL SECTION START *****/
|
||||
read_lock(&tasklist_lock);
|
||||
p = find_task_by_vpid(pid);
|
||||
if (p)
|
||||
domain = tomoyo_real_domain(p);
|
||||
read_unlock(&tasklist_lock);
|
||||
/***** CRITICAL SECTION END *****/
|
||||
} else if (!strncmp(data, "domain=", 7)) {
|
||||
if (tomoyo_is_domain_def(data + 7)) {
|
||||
down_read(&tomoyo_domain_list_lock);
|
||||
@ -1447,15 +1492,14 @@ static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
|
||||
TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ)
|
||||
ignore_global_allow_read
|
||||
= TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n";
|
||||
if (!tomoyo_io_printf(head,
|
||||
"%s\n" TOMOYO_KEYWORD_USE_PROFILE "%u\n"
|
||||
"%s%s%s\n", domain->domainname->name,
|
||||
domain->profile, quota_exceeded,
|
||||
transition_failed,
|
||||
ignore_global_allow_read)) {
|
||||
done = false;
|
||||
done = tomoyo_io_printf(head, "%s\n" TOMOYO_KEYWORD_USE_PROFILE
|
||||
"%u\n%s%s%s\n",
|
||||
domain->domainname->name,
|
||||
domain->profile, quota_exceeded,
|
||||
transition_failed,
|
||||
ignore_global_allow_read);
|
||||
if (!done)
|
||||
break;
|
||||
}
|
||||
head->read_step = 2;
|
||||
acl_loop:
|
||||
if (head->read_step == 3)
|
||||
@ -1463,24 +1507,22 @@ acl_loop:
|
||||
/* Print ACL entries in the domain. */
|
||||
down_read(&tomoyo_domain_acl_info_list_lock);
|
||||
list_for_each_cookie(apos, head->read_var2,
|
||||
&domain->acl_info_list) {
|
||||
&domain->acl_info_list) {
|
||||
struct tomoyo_acl_info *ptr
|
||||
= list_entry(apos, struct tomoyo_acl_info,
|
||||
list);
|
||||
if (!tomoyo_print_entry(head, ptr)) {
|
||||
done = false;
|
||||
list);
|
||||
done = tomoyo_print_entry(head, ptr);
|
||||
if (!done)
|
||||
break;
|
||||
}
|
||||
}
|
||||
up_read(&tomoyo_domain_acl_info_list_lock);
|
||||
if (!done)
|
||||
break;
|
||||
head->read_step = 3;
|
||||
tail_mark:
|
||||
if (!tomoyo_io_printf(head, "\n")) {
|
||||
done = false;
|
||||
done = tomoyo_io_printf(head, "\n");
|
||||
if (!done)
|
||||
break;
|
||||
}
|
||||
head->read_step = 1;
|
||||
if (head->read_single_domain)
|
||||
break;
|
||||
@ -1550,11 +1592,10 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
|
||||
domain = list_entry(pos, struct tomoyo_domain_info, list);
|
||||
if (domain->is_deleted)
|
||||
continue;
|
||||
if (!tomoyo_io_printf(head, "%u %s\n", domain->profile,
|
||||
domain->domainname->name)) {
|
||||
done = false;
|
||||
done = tomoyo_io_printf(head, "%u %s\n", domain->profile,
|
||||
domain->domainname->name);
|
||||
if (!done)
|
||||
break;
|
||||
}
|
||||
}
|
||||
up_read(&tomoyo_domain_list_lock);
|
||||
head->read_eof = done;
|
||||
@ -1594,13 +1635,11 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head)
|
||||
const int pid = head->read_step;
|
||||
struct task_struct *p;
|
||||
struct tomoyo_domain_info *domain = NULL;
|
||||
/***** CRITICAL SECTION START *****/
|
||||
read_lock(&tasklist_lock);
|
||||
p = find_task_by_vpid(pid);
|
||||
if (p)
|
||||
domain = tomoyo_real_domain(p);
|
||||
read_unlock(&tasklist_lock);
|
||||
/***** CRITICAL SECTION END *****/
|
||||
if (domain)
|
||||
tomoyo_io_printf(head, "%d %u %s", pid, domain->profile,
|
||||
domain->domainname->name);
|
||||
@ -2138,7 +2177,13 @@ static ssize_t tomoyo_write(struct file *file, const char __user *buf,
|
||||
return tomoyo_write_control(file, buf, count);
|
||||
}
|
||||
|
||||
/* Operations for /sys/kernel/security/tomoyo/ interface. */
|
||||
/*
|
||||
* tomoyo_operations is a "struct file_operations" which is used for handling
|
||||
* /sys/kernel/security/tomoyo/ interface.
|
||||
*
|
||||
* Some files under /sys/kernel/security/tomoyo/ directory accept open(O_RDWR).
|
||||
* See tomoyo_io_buffer for internals.
|
||||
*/
|
||||
static const struct file_operations tomoyo_operations = {
|
||||
.open = tomoyo_open,
|
||||
.release = tomoyo_release,
|
||||
|
@ -26,16 +26,43 @@
|
||||
struct dentry;
|
||||
struct vfsmount;
|
||||
|
||||
/* Temporary buffer for holding pathnames. */
|
||||
/*
|
||||
* tomoyo_page_buffer is a structure which is used for holding a pathname
|
||||
* obtained from "struct dentry" and "struct vfsmount" pair.
|
||||
* As of now, it is 4096 bytes. If users complain that 4096 bytes is too small
|
||||
* (because TOMOYO escapes non ASCII printable characters using \ooo format),
|
||||
* we will make the buffer larger.
|
||||
*/
|
||||
struct tomoyo_page_buffer {
|
||||
char buffer[4096];
|
||||
};
|
||||
|
||||
/* Structure for holding a token. */
|
||||
/*
|
||||
* tomoyo_path_info is a structure which is used for holding a string data
|
||||
* used by TOMOYO.
|
||||
* This structure has several fields for supporting pattern matching.
|
||||
*
|
||||
* (1) "name" is the '\0' terminated string data.
|
||||
* (2) "hash" is full_name_hash(name, strlen(name)).
|
||||
* This allows tomoyo_pathcmp() to compare by hash before actually compare
|
||||
* using strcmp().
|
||||
* (3) "const_len" is the length of the initial segment of "name" which
|
||||
* consists entirely of non wildcard characters. In other words, the length
|
||||
* which we can compare two strings using strncmp().
|
||||
* (4) "is_dir" is a bool which is true if "name" ends with "/",
|
||||
* false otherwise.
|
||||
* TOMOYO distinguishes directory and non-directory. A directory ends with
|
||||
* "/" and non-directory does not end with "/".
|
||||
* (5) "is_patterned" is a bool which is true if "name" contains wildcard
|
||||
* characters, false otherwise. This allows TOMOYO to use "hash" and
|
||||
* strcmp() for string comparison if "is_patterned" is false.
|
||||
* (6) "depth" is calculated using the number of "/" characters in "name".
|
||||
* This allows TOMOYO to avoid comparing two pathnames which never match
|
||||
* (e.g. whether "/var/www/html/index.html" matches "/tmp/sh-thd-\$").
|
||||
*/
|
||||
struct tomoyo_path_info {
|
||||
const char *name;
|
||||
u32 hash; /* = full_name_hash(name, strlen(name)) */
|
||||
u16 total_len; /* = strlen(name) */
|
||||
u16 const_len; /* = tomoyo_const_part_length(name) */
|
||||
bool is_dir; /* = tomoyo_strendswith(name, "/") */
|
||||
bool is_patterned; /* = tomoyo_path_contains_pattern(name) */
|
||||
@ -51,7 +78,20 @@ struct tomoyo_path_info {
|
||||
*/
|
||||
#define TOMOYO_MAX_PATHNAME_LEN 4000
|
||||
|
||||
/* Structure for holding requested pathname. */
|
||||
/*
|
||||
* tomoyo_path_info_with_data is a structure which is used for holding a
|
||||
* pathname obtained from "struct dentry" and "struct vfsmount" pair.
|
||||
*
|
||||
* "struct tomoyo_path_info_with_data" consists of "struct tomoyo_path_info"
|
||||
* and buffer for the pathname, while "struct tomoyo_page_buffer" consists of
|
||||
* buffer for the pathname only.
|
||||
*
|
||||
* "struct tomoyo_path_info_with_data" is intended to allow TOMOYO to release
|
||||
* both "struct tomoyo_path_info" and buffer for the pathname by single kfree()
|
||||
* so that we don't need to return two pointers to the caller. If the caller
|
||||
* puts "struct tomoyo_path_info" on stack memory, we will be able to remove
|
||||
* "struct tomoyo_path_info_with_data".
|
||||
*/
|
||||
struct tomoyo_path_info_with_data {
|
||||
/* Keep "head" first, for this pointer is passed to tomoyo_free(). */
|
||||
struct tomoyo_path_info head;
|
||||
@ -61,7 +101,15 @@ struct tomoyo_path_info_with_data {
|
||||
};
|
||||
|
||||
/*
|
||||
* Common header for holding ACL entries.
|
||||
* tomoyo_acl_info is a structure which is used for holding
|
||||
*
|
||||
* (1) "list" which is linked to the ->acl_info_list of
|
||||
* "struct tomoyo_domain_info"
|
||||
* (2) "type" which tells
|
||||
* (a) type & 0x7F : type of the entry (either
|
||||
* "struct tomoyo_single_path_acl_record" or
|
||||
* "struct tomoyo_double_path_acl_record")
|
||||
* (b) type & 0x80 : whether the entry is marked as "deleted".
|
||||
*
|
||||
* Packing "struct tomoyo_acl_info" allows
|
||||
* "struct tomoyo_single_path_acl_record" to embed "u16" and
|
||||
@ -81,7 +129,28 @@ struct tomoyo_acl_info {
|
||||
/* This ACL entry is deleted. */
|
||||
#define TOMOYO_ACL_DELETED 0x80
|
||||
|
||||
/* Structure for domain information. */
|
||||
/*
|
||||
* tomoyo_domain_info is a structure which is used for holding permissions
|
||||
* (e.g. "allow_read /lib/libc-2.5.so") given to each domain.
|
||||
* It has following fields.
|
||||
*
|
||||
* (1) "list" which is linked to tomoyo_domain_list .
|
||||
* (2) "acl_info_list" which is linked to "struct tomoyo_acl_info".
|
||||
* (3) "domainname" which holds the name of the domain.
|
||||
* (4) "profile" which remembers profile number assigned to this domain.
|
||||
* (5) "is_deleted" is a bool which is true if this domain is marked as
|
||||
* "deleted", false otherwise.
|
||||
* (6) "quota_warned" is a bool which is used for suppressing warning message
|
||||
* when learning mode learned too much entries.
|
||||
* (7) "flags" which remembers this domain's attributes.
|
||||
*
|
||||
* A domain's lifecycle is an analogy of files on / directory.
|
||||
* Multiple domains with the same domainname cannot be created (as with
|
||||
* creating files with the same filename fails with -EEXIST).
|
||||
* If a process reached a domain, that process can reside in that domain after
|
||||
* that domain is marked as "deleted" (as with a process can access an already
|
||||
* open()ed file after that file was unlink()ed).
|
||||
*/
|
||||
struct tomoyo_domain_info {
|
||||
struct list_head list;
|
||||
struct list_head acl_info_list;
|
||||
@ -108,10 +177,18 @@ struct tomoyo_domain_info {
|
||||
#define TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED 2
|
||||
|
||||
/*
|
||||
* Structure for "allow_read/write", "allow_execute", "allow_read",
|
||||
* "allow_write", "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
|
||||
* "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar",
|
||||
* "allow_truncate", "allow_symlink" and "allow_rewrite" directive.
|
||||
* tomoyo_single_path_acl_record is a structure which is used for holding an
|
||||
* entry with one pathname operation (e.g. open(), mkdir()).
|
||||
* It has following fields.
|
||||
*
|
||||
* (1) "head" which is a "struct tomoyo_acl_info".
|
||||
* (2) "perm" which is a bitmask of permitted operations.
|
||||
* (3) "filename" is the pathname.
|
||||
*
|
||||
* Directives held by this structure are "allow_read/write", "allow_execute",
|
||||
* "allow_read", "allow_write", "allow_create", "allow_unlink", "allow_mkdir",
|
||||
* "allow_rmdir", "allow_mkfifo", "allow_mksock", "allow_mkblock",
|
||||
* "allow_mkchar", "allow_truncate", "allow_symlink" and "allow_rewrite".
|
||||
*/
|
||||
struct tomoyo_single_path_acl_record {
|
||||
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_SINGLE_PATH_ACL */
|
||||
@ -120,7 +197,18 @@ struct tomoyo_single_path_acl_record {
|
||||
const struct tomoyo_path_info *filename;
|
||||
};
|
||||
|
||||
/* Structure for "allow_rename" and "allow_link" directive. */
|
||||
/*
|
||||
* tomoyo_double_path_acl_record is a structure which is used for holding an
|
||||
* entry with two pathnames operation (i.e. link() and rename()).
|
||||
* It has following fields.
|
||||
*
|
||||
* (1) "head" which is a "struct tomoyo_acl_info".
|
||||
* (2) "perm" which is a bitmask of permitted operations.
|
||||
* (3) "filename1" is the source/old pathname.
|
||||
* (4) "filename2" is the destination/new pathname.
|
||||
*
|
||||
* Directives held by this structure are "allow_rename" and "allow_link".
|
||||
*/
|
||||
struct tomoyo_double_path_acl_record {
|
||||
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_DOUBLE_PATH_ACL */
|
||||
u8 perm;
|
||||
@ -153,7 +241,29 @@ struct tomoyo_double_path_acl_record {
|
||||
#define TOMOYO_VERBOSE 2
|
||||
#define TOMOYO_MAX_CONTROL_INDEX 3
|
||||
|
||||
/* Structure for reading/writing policy via securityfs interfaces. */
|
||||
/*
|
||||
* tomoyo_io_buffer is a structure which is used for reading and modifying
|
||||
* configuration via /sys/kernel/security/tomoyo/ interface.
|
||||
* It has many fields. ->read_var1 , ->read_var2 , ->write_var1 are used as
|
||||
* cursors.
|
||||
*
|
||||
* Since the content of /sys/kernel/security/tomoyo/domain_policy is a list of
|
||||
* "struct tomoyo_domain_info" entries and each "struct tomoyo_domain_info"
|
||||
* entry has a list of "struct tomoyo_acl_info", we need two cursors when
|
||||
* reading (one is for traversing tomoyo_domain_list and the other is for
|
||||
* traversing "struct tomoyo_acl_info"->acl_info_list ).
|
||||
*
|
||||
* If a line written to /sys/kernel/security/tomoyo/domain_policy starts with
|
||||
* "select ", TOMOYO seeks the cursor ->read_var1 and ->write_var1 to the
|
||||
* domain with the domainname specified by the rest of that line (NULL is set
|
||||
* if seek failed).
|
||||
* If a line written to /sys/kernel/security/tomoyo/domain_policy starts with
|
||||
* "delete ", TOMOYO deletes an entry or a domain specified by the rest of that
|
||||
* line (->write_var1 is set to NULL if a domain was deleted).
|
||||
* If a line written to /sys/kernel/security/tomoyo/domain_policy starts with
|
||||
* neither "select " nor "delete ", an entry or a domain specified by that line
|
||||
* is appended.
|
||||
*/
|
||||
struct tomoyo_io_buffer {
|
||||
int (*read) (struct tomoyo_io_buffer *);
|
||||
int (*write) (struct tomoyo_io_buffer *);
|
||||
|
@ -19,11 +19,63 @@
|
||||
/* The initial domain. */
|
||||
struct tomoyo_domain_info tomoyo_kernel_domain;
|
||||
|
||||
/* The list for "struct tomoyo_domain_info". */
|
||||
/*
|
||||
* tomoyo_domain_list is used for holding list of domains.
|
||||
* The ->acl_info_list of "struct tomoyo_domain_info" is used for holding
|
||||
* permissions (e.g. "allow_read /lib/libc-2.5.so") given to each domain.
|
||||
*
|
||||
* An entry is added by
|
||||
*
|
||||
* # ( echo "<kernel>"; echo "allow_execute /sbin/init" ) > \
|
||||
* /sys/kernel/security/tomoyo/domain_policy
|
||||
*
|
||||
* and is deleted by
|
||||
*
|
||||
* # ( echo "<kernel>"; echo "delete allow_execute /sbin/init" ) > \
|
||||
* /sys/kernel/security/tomoyo/domain_policy
|
||||
*
|
||||
* and all entries are retrieved by
|
||||
*
|
||||
* # cat /sys/kernel/security/tomoyo/domain_policy
|
||||
*
|
||||
* A domain is added by
|
||||
*
|
||||
* # echo "<kernel>" > /sys/kernel/security/tomoyo/domain_policy
|
||||
*
|
||||
* and is deleted by
|
||||
*
|
||||
* # echo "delete <kernel>" > /sys/kernel/security/tomoyo/domain_policy
|
||||
*
|
||||
* and all domains are retrieved by
|
||||
*
|
||||
* # grep '^<kernel>' /sys/kernel/security/tomoyo/domain_policy
|
||||
*
|
||||
* Normally, a domainname is monotonically getting longer because a domainname
|
||||
* which the process will belong to if an execve() operation succeeds is
|
||||
* defined as a concatenation of "current domainname" + "pathname passed to
|
||||
* execve()".
|
||||
* See tomoyo_domain_initializer_list and tomoyo_domain_keeper_list for
|
||||
* exceptions.
|
||||
*/
|
||||
LIST_HEAD(tomoyo_domain_list);
|
||||
DECLARE_RWSEM(tomoyo_domain_list_lock);
|
||||
|
||||
/* Structure for "initialize_domain" and "no_initialize_domain" keyword. */
|
||||
/*
|
||||
* tomoyo_domain_initializer_entry is a structure which is used for holding
|
||||
* "initialize_domain" and "no_initialize_domain" entries.
|
||||
* It has following fields.
|
||||
*
|
||||
* (1) "list" which is linked to tomoyo_domain_initializer_list .
|
||||
* (2) "domainname" which is "a domainname" or "the last component of a
|
||||
* domainname". This field is NULL if "from" clause is not specified.
|
||||
* (3) "program" which is a program's pathname.
|
||||
* (4) "is_deleted" is a bool which is true if marked as deleted, false
|
||||
* otherwise.
|
||||
* (5) "is_not" is a bool which is true if "no_initialize_domain", false
|
||||
* otherwise.
|
||||
* (6) "is_last_name" is a bool which is true if "domainname" is "the last
|
||||
* component of a domainname", false otherwise.
|
||||
*/
|
||||
struct tomoyo_domain_initializer_entry {
|
||||
struct list_head list;
|
||||
const struct tomoyo_path_info *domainname; /* This may be NULL */
|
||||
@ -34,7 +86,23 @@ struct tomoyo_domain_initializer_entry {
|
||||
bool is_last_name;
|
||||
};
|
||||
|
||||
/* Structure for "keep_domain" and "no_keep_domain" keyword. */
|
||||
/*
|
||||
* tomoyo_domain_keeper_entry is a structure which is used for holding
|
||||
* "keep_domain" and "no_keep_domain" entries.
|
||||
* It has following fields.
|
||||
*
|
||||
* (1) "list" which is linked to tomoyo_domain_keeper_list .
|
||||
* (2) "domainname" which is "a domainname" or "the last component of a
|
||||
* domainname".
|
||||
* (3) "program" which is a program's pathname.
|
||||
* This field is NULL if "from" clause is not specified.
|
||||
* (4) "is_deleted" is a bool which is true if marked as deleted, false
|
||||
* otherwise.
|
||||
* (5) "is_not" is a bool which is true if "no_initialize_domain", false
|
||||
* otherwise.
|
||||
* (6) "is_last_name" is a bool which is true if "domainname" is "the last
|
||||
* component of a domainname", false otherwise.
|
||||
*/
|
||||
struct tomoyo_domain_keeper_entry {
|
||||
struct list_head list;
|
||||
const struct tomoyo_path_info *domainname;
|
||||
@ -45,7 +113,16 @@ struct tomoyo_domain_keeper_entry {
|
||||
bool is_last_name;
|
||||
};
|
||||
|
||||
/* Structure for "alias" keyword. */
|
||||
/*
|
||||
* tomoyo_alias_entry is a structure which is used for holding "alias" entries.
|
||||
* It has following fields.
|
||||
*
|
||||
* (1) "list" which is linked to tomoyo_alias_list .
|
||||
* (2) "original_name" which is a dereferenced pathname.
|
||||
* (3) "aliased_name" which is a symlink's pathname.
|
||||
* (4) "is_deleted" is a bool which is true if marked as deleted, false
|
||||
* otherwise.
|
||||
*/
|
||||
struct tomoyo_alias_entry {
|
||||
struct list_head list;
|
||||
const struct tomoyo_path_info *original_name;
|
||||
@ -67,14 +144,12 @@ void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
|
||||
{
|
||||
/* We need to serialize because this is bitfield operation. */
|
||||
static DEFINE_SPINLOCK(lock);
|
||||
/***** CRITICAL SECTION START *****/
|
||||
spin_lock(&lock);
|
||||
if (!is_delete)
|
||||
domain->flags |= flags;
|
||||
else
|
||||
domain->flags &= ~flags;
|
||||
spin_unlock(&lock);
|
||||
/***** CRITICAL SECTION END *****/
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,7 +169,42 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain)
|
||||
return cp0;
|
||||
}
|
||||
|
||||
/* The list for "struct tomoyo_domain_initializer_entry". */
|
||||
/*
|
||||
* tomoyo_domain_initializer_list is used for holding list of programs which
|
||||
* triggers reinitialization of domainname. Normally, a domainname is
|
||||
* monotonically getting longer. But sometimes, we restart daemon programs.
|
||||
* It would be convenient for us that "a daemon started upon system boot" and
|
||||
* "the daemon restarted from console" belong to the same domain. Thus, TOMOYO
|
||||
* provides a way to shorten domainnames.
|
||||
*
|
||||
* An entry is added by
|
||||
*
|
||||
* # echo 'initialize_domain /usr/sbin/httpd' > \
|
||||
* /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* and is deleted by
|
||||
*
|
||||
* # echo 'delete initialize_domain /usr/sbin/httpd' > \
|
||||
* /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* and all entries are retrieved by
|
||||
*
|
||||
* # grep ^initialize_domain /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* In the example above, /usr/sbin/httpd will belong to
|
||||
* "<kernel> /usr/sbin/httpd" domain.
|
||||
*
|
||||
* You may specify a domainname using "from" keyword.
|
||||
* "initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd"
|
||||
* will cause "/usr/sbin/httpd" executed from "<kernel> /etc/rc.d/init.d/httpd"
|
||||
* domain to belong to "<kernel> /usr/sbin/httpd" domain.
|
||||
*
|
||||
* You may add "no_" prefix to "initialize_domain".
|
||||
* "initialize_domain /usr/sbin/httpd" and
|
||||
* "no_initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd"
|
||||
* will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain
|
||||
* unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain.
|
||||
*/
|
||||
static LIST_HEAD(tomoyo_domain_initializer_list);
|
||||
static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
|
||||
|
||||
@ -135,7 +245,6 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
|
||||
saved_program = tomoyo_save_name(program);
|
||||
if (!saved_program)
|
||||
return -ENOMEM;
|
||||
/***** EXCLUSIVE SECTION START *****/
|
||||
down_write(&tomoyo_domain_initializer_list_lock);
|
||||
list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
|
||||
if (ptr->is_not != is_not ||
|
||||
@ -161,7 +270,6 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
|
||||
error = 0;
|
||||
out:
|
||||
up_write(&tomoyo_domain_initializer_list_lock);
|
||||
/***** EXCLUSIVE SECTION END *****/
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -193,13 +301,12 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
|
||||
from = " from ";
|
||||
domain = ptr->domainname->name;
|
||||
}
|
||||
if (!tomoyo_io_printf(head,
|
||||
"%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN
|
||||
"%s%s%s\n", no, ptr->program->name, from,
|
||||
domain)) {
|
||||
done = false;
|
||||
done = tomoyo_io_printf(head,
|
||||
"%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN
|
||||
"%s%s%s\n", no, ptr->program->name,
|
||||
from, domain);
|
||||
if (!done)
|
||||
break;
|
||||
}
|
||||
}
|
||||
up_read(&tomoyo_domain_initializer_list_lock);
|
||||
return done;
|
||||
@ -273,7 +380,44 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
|
||||
return flag;
|
||||
}
|
||||
|
||||
/* The list for "struct tomoyo_domain_keeper_entry". */
|
||||
/*
|
||||
* tomoyo_domain_keeper_list is used for holding list of domainnames which
|
||||
* suppresses domain transition. Normally, a domainname is monotonically
|
||||
* getting longer. But sometimes, we want to suppress domain transition.
|
||||
* It would be convenient for us that programs executed from a login session
|
||||
* belong to the same domain. Thus, TOMOYO provides a way to suppress domain
|
||||
* transition.
|
||||
*
|
||||
* An entry is added by
|
||||
*
|
||||
* # echo 'keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \
|
||||
* /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* and is deleted by
|
||||
*
|
||||
* # echo 'delete keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \
|
||||
* /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* and all entries are retrieved by
|
||||
*
|
||||
* # grep ^keep_domain /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* In the example above, any process which belongs to
|
||||
* "<kernel> /usr/sbin/sshd /bin/bash" domain will remain in that domain,
|
||||
* unless explicitly specified by "initialize_domain" or "no_keep_domain".
|
||||
*
|
||||
* You may specify a program using "from" keyword.
|
||||
* "keep_domain /bin/pwd from <kernel> /usr/sbin/sshd /bin/bash"
|
||||
* will cause "/bin/pwd" executed from "<kernel> /usr/sbin/sshd /bin/bash"
|
||||
* domain to remain in "<kernel> /usr/sbin/sshd /bin/bash" domain.
|
||||
*
|
||||
* You may add "no_" prefix to "keep_domain".
|
||||
* "keep_domain <kernel> /usr/sbin/sshd /bin/bash" and
|
||||
* "no_keep_domain /usr/bin/passwd from <kernel> /usr/sbin/sshd /bin/bash" will
|
||||
* cause "/usr/bin/passwd" to belong to
|
||||
* "<kernel> /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless
|
||||
* explicitly specified by "initialize_domain".
|
||||
*/
|
||||
static LIST_HEAD(tomoyo_domain_keeper_list);
|
||||
static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
|
||||
|
||||
@ -296,7 +440,6 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
|
||||
struct tomoyo_domain_keeper_entry *ptr;
|
||||
const struct tomoyo_path_info *saved_domainname;
|
||||
const struct tomoyo_path_info *saved_program = NULL;
|
||||
static DEFINE_MUTEX(lock);
|
||||
int error = -ENOMEM;
|
||||
bool is_last_name = false;
|
||||
|
||||
@ -315,7 +458,6 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
|
||||
saved_domainname = tomoyo_save_name(domainname);
|
||||
if (!saved_domainname)
|
||||
return -ENOMEM;
|
||||
/***** EXCLUSIVE SECTION START *****/
|
||||
down_write(&tomoyo_domain_keeper_list_lock);
|
||||
list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
|
||||
if (ptr->is_not != is_not ||
|
||||
@ -341,7 +483,6 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
|
||||
error = 0;
|
||||
out:
|
||||
up_write(&tomoyo_domain_keeper_list_lock);
|
||||
/***** EXCLUSIVE SECTION END *****/
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -394,13 +535,12 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
|
||||
from = " from ";
|
||||
program = ptr->program->name;
|
||||
}
|
||||
if (!tomoyo_io_printf(head,
|
||||
"%s" TOMOYO_KEYWORD_KEEP_DOMAIN
|
||||
"%s%s%s\n", no, program, from,
|
||||
ptr->domainname->name)) {
|
||||
done = false;
|
||||
done = tomoyo_io_printf(head,
|
||||
"%s" TOMOYO_KEYWORD_KEEP_DOMAIN
|
||||
"%s%s%s\n", no, program, from,
|
||||
ptr->domainname->name);
|
||||
if (!done)
|
||||
break;
|
||||
}
|
||||
}
|
||||
up_read(&tomoyo_domain_keeper_list_lock);
|
||||
return done;
|
||||
@ -446,7 +586,36 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
|
||||
return flag;
|
||||
}
|
||||
|
||||
/* The list for "struct tomoyo_alias_entry". */
|
||||
/*
|
||||
* tomoyo_alias_list is used for holding list of symlink's pathnames which are
|
||||
* allowed to be passed to an execve() request. Normally, the domainname which
|
||||
* the current process will belong to after execve() succeeds is calculated
|
||||
* using dereferenced pathnames. But some programs behave differently depending
|
||||
* on the name passed to argv[0]. For busybox, calculating domainname using
|
||||
* dereferenced pathnames will cause all programs in the busybox to belong to
|
||||
* the same domain. Thus, TOMOYO provides a way to allow use of symlink's
|
||||
* pathname for checking execve()'s permission and calculating domainname which
|
||||
* the current process will belong to after execve() succeeds.
|
||||
*
|
||||
* An entry is added by
|
||||
*
|
||||
* # echo 'alias /bin/busybox /bin/cat' > \
|
||||
* /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* and is deleted by
|
||||
*
|
||||
* # echo 'delete alias /bin/busybox /bin/cat' > \
|
||||
* /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* and all entries are retrieved by
|
||||
*
|
||||
* # grep ^alias /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* In the example above, if /bin/cat is a symlink to /bin/busybox and execution
|
||||
* of /bin/cat is requested, permission is checked for /bin/cat rather than
|
||||
* /bin/busybox and domainname which the current process will belong to after
|
||||
* execve() succeeds is calculated using /bin/cat rather than /bin/busybox .
|
||||
*/
|
||||
static LIST_HEAD(tomoyo_alias_list);
|
||||
static DECLARE_RWSEM(tomoyo_alias_list_lock);
|
||||
|
||||
@ -476,7 +645,6 @@ static int tomoyo_update_alias_entry(const char *original_name,
|
||||
saved_aliased_name = tomoyo_save_name(aliased_name);
|
||||
if (!saved_original_name || !saved_aliased_name)
|
||||
return -ENOMEM;
|
||||
/***** EXCLUSIVE SECTION START *****/
|
||||
down_write(&tomoyo_alias_list_lock);
|
||||
list_for_each_entry(ptr, &tomoyo_alias_list, list) {
|
||||
if (ptr->original_name != saved_original_name ||
|
||||
@ -499,7 +667,6 @@ static int tomoyo_update_alias_entry(const char *original_name,
|
||||
error = 0;
|
||||
out:
|
||||
up_write(&tomoyo_alias_list_lock);
|
||||
/***** EXCLUSIVE SECTION END *****/
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -522,12 +689,11 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
|
||||
ptr = list_entry(pos, struct tomoyo_alias_entry, list);
|
||||
if (ptr->is_deleted)
|
||||
continue;
|
||||
if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n",
|
||||
ptr->original_name->name,
|
||||
ptr->aliased_name->name)) {
|
||||
done = false;
|
||||
done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n",
|
||||
ptr->original_name->name,
|
||||
ptr->aliased_name->name);
|
||||
if (!done)
|
||||
break;
|
||||
}
|
||||
}
|
||||
up_read(&tomoyo_alias_list_lock);
|
||||
return done;
|
||||
@ -567,7 +733,6 @@ int tomoyo_delete_domain(char *domainname)
|
||||
|
||||
name.name = domainname;
|
||||
tomoyo_fill_path_info(&name);
|
||||
/***** EXCLUSIVE SECTION START *****/
|
||||
down_write(&tomoyo_domain_list_lock);
|
||||
/* Is there an active domain? */
|
||||
list_for_each_entry(domain, &tomoyo_domain_list, list) {
|
||||
@ -581,7 +746,6 @@ int tomoyo_delete_domain(char *domainname)
|
||||
break;
|
||||
}
|
||||
up_write(&tomoyo_domain_list_lock);
|
||||
/***** EXCLUSIVE SECTION END *****/
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -600,7 +764,6 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
|
||||
struct tomoyo_domain_info *domain = NULL;
|
||||
const struct tomoyo_path_info *saved_domainname;
|
||||
|
||||
/***** EXCLUSIVE SECTION START *****/
|
||||
down_write(&tomoyo_domain_list_lock);
|
||||
domain = tomoyo_find_domain(domainname);
|
||||
if (domain)
|
||||
@ -619,7 +782,6 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
|
||||
domain->domainname != saved_domainname)
|
||||
continue;
|
||||
flag = false;
|
||||
/***** CRITICAL SECTION START *****/
|
||||
read_lock(&tasklist_lock);
|
||||
for_each_process(p) {
|
||||
if (tomoyo_real_domain(p) != domain)
|
||||
@ -628,7 +790,6 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
|
||||
break;
|
||||
}
|
||||
read_unlock(&tasklist_lock);
|
||||
/***** CRITICAL SECTION END *****/
|
||||
if (flag)
|
||||
continue;
|
||||
list_for_each_entry(ptr, &domain->acl_info_list, list) {
|
||||
@ -651,7 +812,6 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
|
||||
}
|
||||
out:
|
||||
up_write(&tomoyo_domain_list_lock);
|
||||
/***** EXCLUSIVE SECTION END *****/
|
||||
return domain;
|
||||
}
|
||||
|
||||
@ -739,7 +899,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm,
|
||||
}
|
||||
|
||||
/* Check execute permission. */
|
||||
retval = tomoyo_check_exec_perm(old_domain, &r, tmp);
|
||||
retval = tomoyo_check_exec_perm(old_domain, &r);
|
||||
if (retval < 0)
|
||||
goto out;
|
||||
|
||||
|
@ -14,21 +14,50 @@
|
||||
#include "realpath.h"
|
||||
#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
|
||||
|
||||
/* Structure for "allow_read" keyword. */
|
||||
/*
|
||||
* tomoyo_globally_readable_file_entry is a structure which is used for holding
|
||||
* "allow_read" entries.
|
||||
* It has following fields.
|
||||
*
|
||||
* (1) "list" which is linked to tomoyo_globally_readable_list .
|
||||
* (2) "filename" is a pathname which is allowed to open(O_RDONLY).
|
||||
* (3) "is_deleted" is a bool which is true if marked as deleted, false
|
||||
* otherwise.
|
||||
*/
|
||||
struct tomoyo_globally_readable_file_entry {
|
||||
struct list_head list;
|
||||
const struct tomoyo_path_info *filename;
|
||||
bool is_deleted;
|
||||
};
|
||||
|
||||
/* Structure for "file_pattern" keyword. */
|
||||
/*
|
||||
* tomoyo_pattern_entry is a structure which is used for holding
|
||||
* "tomoyo_pattern_list" entries.
|
||||
* It has following fields.
|
||||
*
|
||||
* (1) "list" which is linked to tomoyo_pattern_list .
|
||||
* (2) "pattern" is a pathname pattern which is used for converting pathnames
|
||||
* to pathname patterns during learning mode.
|
||||
* (3) "is_deleted" is a bool which is true if marked as deleted, false
|
||||
* otherwise.
|
||||
*/
|
||||
struct tomoyo_pattern_entry {
|
||||
struct list_head list;
|
||||
const struct tomoyo_path_info *pattern;
|
||||
bool is_deleted;
|
||||
};
|
||||
|
||||
/* Structure for "deny_rewrite" keyword. */
|
||||
/*
|
||||
* tomoyo_no_rewrite_entry is a structure which is used for holding
|
||||
* "deny_rewrite" entries.
|
||||
* It has following fields.
|
||||
*
|
||||
* (1) "list" which is linked to tomoyo_no_rewrite_list .
|
||||
* (2) "pattern" is a pathname which is by default not permitted to modify
|
||||
* already existing content.
|
||||
* (3) "is_deleted" is a bool which is true if marked as deleted, false
|
||||
* otherwise.
|
||||
*/
|
||||
struct tomoyo_no_rewrite_entry {
|
||||
struct list_head list;
|
||||
const struct tomoyo_path_info *pattern;
|
||||
@ -141,7 +170,31 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
|
||||
struct tomoyo_domain_info *
|
||||
const domain, const bool is_delete);
|
||||
|
||||
/* The list for "struct tomoyo_globally_readable_file_entry". */
|
||||
/*
|
||||
* tomoyo_globally_readable_list is used for holding list of pathnames which
|
||||
* are by default allowed to be open()ed for reading by any process.
|
||||
*
|
||||
* An entry is added by
|
||||
*
|
||||
* # echo 'allow_read /lib/libc-2.5.so' > \
|
||||
* /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* and is deleted by
|
||||
*
|
||||
* # echo 'delete allow_read /lib/libc-2.5.so' > \
|
||||
* /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* and all entries are retrieved by
|
||||
*
|
||||
* # grep ^allow_read /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* In the example above, any process is allowed to
|
||||
* open("/lib/libc-2.5.so", O_RDONLY).
|
||||
* One exception is, if the domain which current process belongs to is marked
|
||||
* as "ignore_global_allow_read", current process can't do so unless explicitly
|
||||
* given "allow_read /lib/libc-2.5.so" to the domain which current process
|
||||
* belongs to.
|
||||
*/
|
||||
static LIST_HEAD(tomoyo_globally_readable_list);
|
||||
static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
|
||||
|
||||
@ -166,7 +219,6 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
|
||||
saved_filename = tomoyo_save_name(filename);
|
||||
if (!saved_filename)
|
||||
return -ENOMEM;
|
||||
/***** EXCLUSIVE SECTION START *****/
|
||||
down_write(&tomoyo_globally_readable_list_lock);
|
||||
list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
|
||||
if (ptr->filename != saved_filename)
|
||||
@ -187,7 +239,6 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
|
||||
error = 0;
|
||||
out:
|
||||
up_write(&tomoyo_globally_readable_list_lock);
|
||||
/***** EXCLUSIVE SECTION END *****/
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -249,17 +300,44 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
|
||||
list);
|
||||
if (ptr->is_deleted)
|
||||
continue;
|
||||
if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n",
|
||||
ptr->filename->name)) {
|
||||
done = false;
|
||||
done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n",
|
||||
ptr->filename->name);
|
||||
if (!done)
|
||||
break;
|
||||
}
|
||||
}
|
||||
up_read(&tomoyo_globally_readable_list_lock);
|
||||
return done;
|
||||
}
|
||||
|
||||
/* The list for "struct tomoyo_pattern_entry". */
|
||||
/* tomoyo_pattern_list is used for holding list of pathnames which are used for
|
||||
* converting pathnames to pathname patterns during learning mode.
|
||||
*
|
||||
* An entry is added by
|
||||
*
|
||||
* # echo 'file_pattern /proc/\$/mounts' > \
|
||||
* /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* and is deleted by
|
||||
*
|
||||
* # echo 'delete file_pattern /proc/\$/mounts' > \
|
||||
* /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* and all entries are retrieved by
|
||||
*
|
||||
* # grep ^file_pattern /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* In the example above, if a process which belongs to a domain which is in
|
||||
* learning mode requested open("/proc/1/mounts", O_RDONLY),
|
||||
* "allow_read /proc/\$/mounts" is automatically added to the domain which that
|
||||
* process belongs to.
|
||||
*
|
||||
* It is not a desirable behavior that we have to use /proc/\$/ instead of
|
||||
* /proc/self/ when current process needs to access only current process's
|
||||
* information. As of now, LSM version of TOMOYO is using __d_path() for
|
||||
* calculating pathname. Non LSM version of TOMOYO is using its own function
|
||||
* which pretends as if /proc/self/ is not a symlink; so that we can forbid
|
||||
* current process from accessing other process's information.
|
||||
*/
|
||||
static LIST_HEAD(tomoyo_pattern_list);
|
||||
static DECLARE_RWSEM(tomoyo_pattern_list_lock);
|
||||
|
||||
@ -284,7 +362,6 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
|
||||
saved_pattern = tomoyo_save_name(pattern);
|
||||
if (!saved_pattern)
|
||||
return -ENOMEM;
|
||||
/***** EXCLUSIVE SECTION START *****/
|
||||
down_write(&tomoyo_pattern_list_lock);
|
||||
list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
|
||||
if (saved_pattern != ptr->pattern)
|
||||
@ -305,7 +382,6 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
|
||||
error = 0;
|
||||
out:
|
||||
up_write(&tomoyo_pattern_list_lock);
|
||||
/***** EXCLUSIVE SECTION END *****/
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -373,17 +449,44 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
|
||||
ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
|
||||
if (ptr->is_deleted)
|
||||
continue;
|
||||
if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN "%s\n",
|
||||
ptr->pattern->name)) {
|
||||
done = false;
|
||||
done = tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN
|
||||
"%s\n", ptr->pattern->name);
|
||||
if (!done)
|
||||
break;
|
||||
}
|
||||
}
|
||||
up_read(&tomoyo_pattern_list_lock);
|
||||
return done;
|
||||
}
|
||||
|
||||
/* The list for "struct tomoyo_no_rewrite_entry". */
|
||||
/*
|
||||
* tomoyo_no_rewrite_list is used for holding list of pathnames which are by
|
||||
* default forbidden to modify already written content of a file.
|
||||
*
|
||||
* An entry is added by
|
||||
*
|
||||
* # echo 'deny_rewrite /var/log/messages' > \
|
||||
* /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* and is deleted by
|
||||
*
|
||||
* # echo 'delete deny_rewrite /var/log/messages' > \
|
||||
* /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* and all entries are retrieved by
|
||||
*
|
||||
* # grep ^deny_rewrite /sys/kernel/security/tomoyo/exception_policy
|
||||
*
|
||||
* In the example above, if a process requested to rewrite /var/log/messages ,
|
||||
* the process can't rewrite unless the domain which that process belongs to
|
||||
* has "allow_rewrite /var/log/messages" entry.
|
||||
*
|
||||
* It is not a desirable behavior that we have to add "\040(deleted)" suffix
|
||||
* when we want to allow rewriting already unlink()ed file. As of now,
|
||||
* LSM version of TOMOYO is using __d_path() for calculating pathname.
|
||||
* Non LSM version of TOMOYO is using its own function which doesn't append
|
||||
* " (deleted)" suffix if the file is already unlink()ed; so that we don't
|
||||
* need to worry whether the file is already unlink()ed or not.
|
||||
*/
|
||||
static LIST_HEAD(tomoyo_no_rewrite_list);
|
||||
static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
|
||||
|
||||
@ -407,7 +510,6 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
|
||||
saved_pattern = tomoyo_save_name(pattern);
|
||||
if (!saved_pattern)
|
||||
return -ENOMEM;
|
||||
/***** EXCLUSIVE SECTION START *****/
|
||||
down_write(&tomoyo_no_rewrite_list_lock);
|
||||
list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
|
||||
if (ptr->pattern != saved_pattern)
|
||||
@ -428,7 +530,6 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
|
||||
error = 0;
|
||||
out:
|
||||
up_write(&tomoyo_no_rewrite_list_lock);
|
||||
/***** EXCLUSIVE SECTION END *****/
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -489,11 +590,10 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
|
||||
ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
|
||||
if (ptr->is_deleted)
|
||||
continue;
|
||||
if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE "%s\n",
|
||||
ptr->pattern->name)) {
|
||||
done = false;
|
||||
done = tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE
|
||||
"%s\n", ptr->pattern->name);
|
||||
if (!done)
|
||||
break;
|
||||
}
|
||||
}
|
||||
up_read(&tomoyo_no_rewrite_list_lock);
|
||||
return done;
|
||||
@ -745,7 +845,6 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
|
||||
saved_filename = tomoyo_save_name(filename);
|
||||
if (!saved_filename)
|
||||
return -ENOMEM;
|
||||
/***** EXCLUSIVE SECTION START *****/
|
||||
down_write(&tomoyo_domain_acl_info_list_lock);
|
||||
if (is_delete)
|
||||
goto delete;
|
||||
@ -800,7 +899,6 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
|
||||
}
|
||||
out:
|
||||
up_write(&tomoyo_domain_acl_info_list_lock);
|
||||
/***** EXCLUSIVE SECTION END *****/
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -836,7 +934,6 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
|
||||
saved_filename2 = tomoyo_save_name(filename2);
|
||||
if (!saved_filename1 || !saved_filename2)
|
||||
return -ENOMEM;
|
||||
/***** EXCLUSIVE SECTION START *****/
|
||||
down_write(&tomoyo_domain_acl_info_list_lock);
|
||||
if (is_delete)
|
||||
goto delete;
|
||||
@ -884,7 +981,6 @@ static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
|
||||
}
|
||||
out:
|
||||
up_write(&tomoyo_domain_acl_info_list_lock);
|
||||
/***** EXCLUSIVE SECTION END *****/
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1025,13 +1121,11 @@ int tomoyo_check_file_perm(struct tomoyo_domain_info *domain,
|
||||
*
|
||||
* @domain: Pointer to "struct tomoyo_domain_info".
|
||||
* @filename: Check permission for "execute".
|
||||
* @tmp: Buffer for temporary use.
|
||||
*
|
||||
* Returns 0 on success, negativevalue otherwise.
|
||||
*/
|
||||
int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
|
||||
const struct tomoyo_path_info *filename,
|
||||
struct tomoyo_page_buffer *tmp)
|
||||
const struct tomoyo_path_info *filename)
|
||||
{
|
||||
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
|
||||
|
||||
|
@ -220,7 +220,6 @@ void *tomoyo_alloc_element(const unsigned int size)
|
||||
= roundup(size, max(sizeof(void *), sizeof(long)));
|
||||
if (word_aligned_size > PATH_MAX)
|
||||
return NULL;
|
||||
/***** EXCLUSIVE SECTION START *****/
|
||||
mutex_lock(&lock);
|
||||
if (buf_used_len + word_aligned_size > PATH_MAX) {
|
||||
if (!tomoyo_quota_for_elements ||
|
||||
@ -251,7 +250,6 @@ void *tomoyo_alloc_element(const unsigned int size)
|
||||
}
|
||||
}
|
||||
mutex_unlock(&lock);
|
||||
/***** EXCLUSIVE SECTION END *****/
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@ -267,7 +265,16 @@ static unsigned int tomoyo_quota_for_savename;
|
||||
*/
|
||||
#define TOMOYO_MAX_HASH 256
|
||||
|
||||
/* Structure for string data. */
|
||||
/*
|
||||
* tomoyo_name_entry is a structure which is used for linking
|
||||
* "struct tomoyo_path_info" into tomoyo_name_list .
|
||||
*
|
||||
* Since tomoyo_name_list manages a list of strings which are shared by
|
||||
* multiple processes (whereas "struct tomoyo_path_info" inside
|
||||
* "struct tomoyo_path_info_with_data" is not shared), a reference counter will
|
||||
* be added to "struct tomoyo_name_entry" rather than "struct tomoyo_path_info"
|
||||
* when TOMOYO starts supporting garbage collector.
|
||||
*/
|
||||
struct tomoyo_name_entry {
|
||||
struct list_head list;
|
||||
struct tomoyo_path_info entry;
|
||||
@ -281,10 +288,10 @@ struct tomoyo_free_memory_block_list {
|
||||
};
|
||||
|
||||
/*
|
||||
* The list for "struct tomoyo_name_entry".
|
||||
*
|
||||
* This list is updated only inside tomoyo_save_name(), thus
|
||||
* no global mutex exists.
|
||||
* tomoyo_name_list is used for holding string data used by TOMOYO.
|
||||
* Since same string data is likely used for multiple times (e.g.
|
||||
* "/lib/libc-2.5.so"), TOMOYO shares string data in the form of
|
||||
* "const struct tomoyo_path_info *".
|
||||
*/
|
||||
static struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
|
||||
|
||||
@ -318,7 +325,6 @@ const struct tomoyo_path_info *tomoyo_save_name(const char *name)
|
||||
return NULL;
|
||||
}
|
||||
hash = full_name_hash((const unsigned char *) name, len - 1);
|
||||
/***** EXCLUSIVE SECTION START *****/
|
||||
mutex_lock(&lock);
|
||||
list_for_each_entry(ptr, &tomoyo_name_list[hash % TOMOYO_MAX_HASH],
|
||||
list) {
|
||||
@ -366,7 +372,6 @@ const struct tomoyo_path_info *tomoyo_save_name(const char *name)
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&lock);
|
||||
/***** EXCLUSIVE SECTION END *****/
|
||||
return ptr ? &ptr->entry : NULL;
|
||||
}
|
||||
|
||||
|
@ -262,6 +262,10 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
|
||||
return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* tomoyo_security_ops is a "struct security_operations" which is used for
|
||||
* registering TOMOYO.
|
||||
*/
|
||||
static struct security_operations tomoyo_security_ops = {
|
||||
.name = "tomoyo",
|
||||
.cred_prepare = tomoyo_cred_prepare,
|
||||
|
@ -17,13 +17,11 @@ struct path;
|
||||
struct inode;
|
||||
struct linux_binprm;
|
||||
struct pt_regs;
|
||||
struct tomoyo_page_buffer;
|
||||
|
||||
int tomoyo_check_file_perm(struct tomoyo_domain_info *domain,
|
||||
const char *filename, const u8 perm);
|
||||
int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
|
||||
const struct tomoyo_path_info *filename,
|
||||
struct tomoyo_page_buffer *buf);
|
||||
const struct tomoyo_path_info *filename);
|
||||
int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
|
||||
struct path *path, const int flag);
|
||||
int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
|
||||
@ -90,17 +88,10 @@ static inline struct tomoyo_domain_info *tomoyo_domain(void)
|
||||
return current_cred()->security;
|
||||
}
|
||||
|
||||
/* Caller holds tasklist_lock spinlock. */
|
||||
static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
|
||||
*task)
|
||||
{
|
||||
/***** CRITICAL SECTION START *****/
|
||||
const struct cred *cred = get_task_cred(task);
|
||||
struct tomoyo_domain_info *domain = cred->security;
|
||||
|
||||
put_cred(cred);
|
||||
return domain;
|
||||
/***** CRITICAL SECTION END *****/
|
||||
return task_cred_xxx(task, security);
|
||||
}
|
||||
|
||||
#endif /* !defined(_SECURITY_TOMOYO_TOMOYO_H) */
|
||||
|
Loading…
Reference in New Issue
Block a user