Commit 6d9c939d authored by Casey Schaufler's avatar Casey Schaufler Committed by Kees Cook

procfs: add smack subdir to attrs

Back in 2007 I made what turned out to be a rather serious
mistake in the implementation of the Smack security module.
The SELinux module used an interface in /proc to manipulate
the security context on processes. Rather than use a similar
interface, I used the same interface. The AppArmor team did
likewise. Now /proc/.../attr/current will tell you the
security "context" of the process, but it will be different
depending on the security module you're using.

This patch provides a subdirectory in /proc/.../attr for
Smack. Smack user space can use the "current" file in
this subdirectory and never have to worry about getting
SELinux attributes by mistake. Programs that use the
old interface will continue to work (or fail, as the case
may be) as before.

The proposed S.A.R.A security module is dependent on
the mechanism to create its own attr subdirectory.

The original implementation is by Kees Cook.
Signed-off-by: default avatarCasey Schaufler <casey@schaufler-ca.com>
Reviewed-by: default avatarKees Cook <keescook@chromium.org>
Signed-off-by: default avatarKees Cook <keescook@chromium.org>
parent d117a154
...@@ -17,9 +17,8 @@ MAC extensions, other extensions can be built using the LSM to provide ...@@ -17,9 +17,8 @@ MAC extensions, other extensions can be built using the LSM to provide
specific changes to system operation when these tweaks are not available specific changes to system operation when these tweaks are not available
in the core functionality of Linux itself. in the core functionality of Linux itself.
Without a specific LSM built into the kernel, the default LSM will be the The Linux capabilities modules will always be included. This may be
Linux capabilities system. Most LSMs choose to extend the capabilities followed by any number of "minor" modules and at most one "major" module.
system, building their checks on top of the defined capability hooks.
For more details on capabilities, see ``capabilities(7)`` in the Linux For more details on capabilities, see ``capabilities(7)`` in the Linux
man-pages project. man-pages project.
...@@ -30,6 +29,14 @@ order in which checks are made. The capability module will always ...@@ -30,6 +29,14 @@ order in which checks are made. The capability module will always
be first, followed by any "minor" modules (e.g. Yama) and then be first, followed by any "minor" modules (e.g. Yama) and then
the one "major" module (e.g. SELinux) if there is one configured. the one "major" module (e.g. SELinux) if there is one configured.
Process attributes associated with "major" security modules should
be accessed and maintained using the special files in ``/proc/.../attr``.
A security module may maintain a module specific subdirectory there,
named after the module. ``/proc/.../attr/smack`` is provided by the Smack
security module and contains all its special files. The files directly
in ``/proc/.../attr`` remain as legacy interfaces for modules that provide
subdirectories.
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
......
...@@ -140,9 +140,13 @@ struct pid_entry { ...@@ -140,9 +140,13 @@ struct pid_entry {
#define REG(NAME, MODE, fops) \ #define REG(NAME, MODE, fops) \
NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {}) NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {})
#define ONE(NAME, MODE, show) \ #define ONE(NAME, MODE, show) \
NOD(NAME, (S_IFREG|(MODE)), \ NOD(NAME, (S_IFREG|(MODE)), \
NULL, &proc_single_file_operations, \ NULL, &proc_single_file_operations, \
{ .proc_show = show } ) { .proc_show = show } )
#define ATTR(LSM, NAME, MODE) \
NOD(NAME, (S_IFREG|(MODE)), \
NULL, &proc_pid_attr_operations, \
{ .lsm = LSM })
/* /*
* Count the number of hardlinks for the pid_entry table, excluding the . * Count the number of hardlinks for the pid_entry table, excluding the .
...@@ -2525,7 +2529,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf, ...@@ -2525,7 +2529,7 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf,
if (!task) if (!task)
return -ESRCH; return -ESRCH;
length = security_getprocattr(task, length = security_getprocattr(task, PROC_I(inode)->op.lsm,
(char*)file->f_path.dentry->d_name.name, (char*)file->f_path.dentry->d_name.name,
&p); &p);
put_task_struct(task); put_task_struct(task);
...@@ -2574,7 +2578,9 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf, ...@@ -2574,7 +2578,9 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
if (rv < 0) if (rv < 0)
goto out_free; goto out_free;
rv = security_setprocattr(file->f_path.dentry->d_name.name, page, count); rv = security_setprocattr(PROC_I(inode)->op.lsm,
file->f_path.dentry->d_name.name, page,
count);
mutex_unlock(&current->signal->cred_guard_mutex); mutex_unlock(&current->signal->cred_guard_mutex);
out_free: out_free:
kfree(page); kfree(page);
...@@ -2588,13 +2594,53 @@ static const struct file_operations proc_pid_attr_operations = { ...@@ -2588,13 +2594,53 @@ static const struct file_operations proc_pid_attr_operations = {
.llseek = generic_file_llseek, .llseek = generic_file_llseek,
}; };
#define LSM_DIR_OPS(LSM) \
static int proc_##LSM##_attr_dir_iterate(struct file *filp, \
struct dir_context *ctx) \
{ \
return proc_pident_readdir(filp, ctx, \
LSM##_attr_dir_stuff, \
ARRAY_SIZE(LSM##_attr_dir_stuff)); \
} \
\
static const struct file_operations proc_##LSM##_attr_dir_ops = { \
.read = generic_read_dir, \
.iterate = proc_##LSM##_attr_dir_iterate, \
.llseek = default_llseek, \
}; \
\
static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \
struct dentry *dentry, unsigned int flags) \
{ \
return proc_pident_lookup(dir, dentry, \
LSM##_attr_dir_stuff, \
ARRAY_SIZE(LSM##_attr_dir_stuff)); \
} \
\
static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \
.lookup = proc_##LSM##_attr_dir_lookup, \
.getattr = pid_getattr, \
.setattr = proc_setattr, \
}
#ifdef CONFIG_SECURITY_SMACK
static const struct pid_entry smack_attr_dir_stuff[] = {
ATTR("smack", "current", 0666),
};
LSM_DIR_OPS(smack);
#endif
static const struct pid_entry attr_dir_stuff[] = { static const struct pid_entry attr_dir_stuff[] = {
REG("current", S_IRUGO|S_IWUGO, proc_pid_attr_operations), ATTR(NULL, "current", 0666),
REG("prev", S_IRUGO, proc_pid_attr_operations), ATTR(NULL, "prev", 0444),
REG("exec", S_IRUGO|S_IWUGO, proc_pid_attr_operations), ATTR(NULL, "exec", 0666),
REG("fscreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), ATTR(NULL, "fscreate", 0666),
REG("keycreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), ATTR(NULL, "keycreate", 0666),
REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations), ATTR(NULL, "sockcreate", 0666),
#ifdef CONFIG_SECURITY_SMACK
DIR("smack", 0555,
proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops),
#endif
}; };
static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx) static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx)
......
...@@ -81,6 +81,7 @@ union proc_op { ...@@ -81,6 +81,7 @@ union proc_op {
int (*proc_show)(struct seq_file *m, int (*proc_show)(struct seq_file *m,
struct pid_namespace *ns, struct pid *pid, struct pid_namespace *ns, struct pid *pid,
struct task_struct *task); struct task_struct *task);
const char *lsm;
}; };
struct proc_inode { struct proc_inode {
......
...@@ -366,8 +366,10 @@ int security_sem_semctl(struct kern_ipc_perm *sma, int cmd); ...@@ -366,8 +366,10 @@ int security_sem_semctl(struct kern_ipc_perm *sma, int cmd);
int security_sem_semop(struct kern_ipc_perm *sma, struct sembuf *sops, int security_sem_semop(struct kern_ipc_perm *sma, struct sembuf *sops,
unsigned nsops, int alter); unsigned nsops, int alter);
void security_d_instantiate(struct dentry *dentry, struct inode *inode); void security_d_instantiate(struct dentry *dentry, struct inode *inode);
int security_getprocattr(struct task_struct *p, char *name, char **value); int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
int security_setprocattr(const char *name, void *value, size_t size); char **value);
int security_setprocattr(const char *lsm, const char *name, void *value,
size_t size);
int security_netlink_send(struct sock *sk, struct sk_buff *skb); int security_netlink_send(struct sock *sk, struct sk_buff *skb);
int security_ismaclabel(const char *name); int security_ismaclabel(const char *name);
int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
...@@ -1112,15 +1114,18 @@ static inline int security_sem_semop(struct kern_ipc_perm *sma, ...@@ -1112,15 +1114,18 @@ static inline int security_sem_semop(struct kern_ipc_perm *sma,
return 0; return 0;
} }
static inline void security_d_instantiate(struct dentry *dentry, struct inode *inode) static inline void security_d_instantiate(struct dentry *dentry,
struct inode *inode)
{ } { }
static inline int security_getprocattr(struct task_struct *p, char *name, char **value) static inline int security_getprocattr(struct task_struct *p, const char *lsm,
char *name, char **value)
{ {
return -EINVAL; return -EINVAL;
} }
static inline int security_setprocattr(char *name, void *value, size_t size) static inline int security_setprocattr(const char *lsm, char *name,
void *value, size_t size)
{ {
return -EINVAL; return -EINVAL;
} }
......
...@@ -1485,14 +1485,30 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode) ...@@ -1485,14 +1485,30 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode)
} }
EXPORT_SYMBOL(security_d_instantiate); EXPORT_SYMBOL(security_d_instantiate);
int security_getprocattr(struct task_struct *p, char *name, char **value) int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
char **value)
{ {
return call_int_hook(getprocattr, -EINVAL, p, name, value); struct security_hook_list *hp;
hlist_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
if (lsm != NULL && strcmp(lsm, hp->lsm))
continue;
return hp->hook.getprocattr(p, name, value);
}
return -EINVAL;
} }
int security_setprocattr(const char *name, void *value, size_t size) int security_setprocattr(const char *lsm, const char *name, void *value,
size_t size)
{ {
return call_int_hook(setprocattr, -EINVAL, name, value, size); struct security_hook_list *hp;
hlist_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
if (lsm != NULL && strcmp(lsm, hp->lsm))
continue;
return hp->hook.setprocattr(name, value, size);
}
return -EINVAL;
} }
int security_netlink_send(struct sock *sk, struct sk_buff *skb) int security_netlink_send(struct sock *sk, struct sk_buff *skb)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment