aboutsummaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/capability.c21
-rw-r--r--security/commoncap.c265
-rw-r--r--security/keys/internal.h17
-rw-r--r--security/keys/key.c25
-rw-r--r--security/keys/keyctl.c95
-rw-r--r--security/keys/keyring.c14
-rw-r--r--security/keys/permission.c24
-rw-r--r--security/keys/proc.c8
-rw-r--r--security/keys/process_keys.c333
-rw-r--r--security/keys/request_key.c29
-rw-r--r--security/keys/request_key_auth.c41
-rw-r--r--security/security.c58
-rw-r--r--security/selinux/hooks.c286
-rw-r--r--security/smack/smack_lsm.c82
14 files changed, 668 insertions, 630 deletions
diff --git a/security/capability.c b/security/capability.c
index fac2f61b69a..efeb6d9e0e6 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -340,12 +340,16 @@ static int cap_task_create(unsigned long clone_flags)
return 0;
}
-static int cap_cred_alloc_security(struct cred *cred)
+static void cap_cred_free(struct cred *cred)
+{
+}
+
+static int cap_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp)
{
return 0;
}
-static void cap_cred_free(struct cred *cred)
+static void cap_cred_commit(struct cred *new, const struct cred *old)
{
}
@@ -750,7 +754,7 @@ static void cap_release_secctx(char *secdata, u32 seclen)
}
#ifdef CONFIG_KEYS
-static int cap_key_alloc(struct key *key, struct task_struct *ctx,
+static int cap_key_alloc(struct key *key, const struct cred *cred,
unsigned long flags)
{
return 0;
@@ -760,7 +764,7 @@ static void cap_key_free(struct key *key)
{
}
-static int cap_key_permission(key_ref_t key_ref, struct task_struct *context,
+static int cap_key_permission(key_ref_t key_ref, const struct cred *cred,
key_perm_t perm)
{
return 0;
@@ -814,8 +818,7 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, ptrace_may_access);
set_to_cap_if_null(ops, ptrace_traceme);
set_to_cap_if_null(ops, capget);
- set_to_cap_if_null(ops, capset_check);
- set_to_cap_if_null(ops, capset_set);
+ set_to_cap_if_null(ops, capset);
set_to_cap_if_null(ops, acct);
set_to_cap_if_null(ops, capable);
set_to_cap_if_null(ops, quotactl);
@@ -890,10 +893,11 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, file_receive);
set_to_cap_if_null(ops, dentry_open);
set_to_cap_if_null(ops, task_create);
- set_to_cap_if_null(ops, cred_alloc_security);
set_to_cap_if_null(ops, cred_free);
+ set_to_cap_if_null(ops, cred_prepare);
+ set_to_cap_if_null(ops, cred_commit);
set_to_cap_if_null(ops, task_setuid);
- set_to_cap_if_null(ops, task_post_setuid);
+ set_to_cap_if_null(ops, task_fix_setuid);
set_to_cap_if_null(ops, task_setgid);
set_to_cap_if_null(ops, task_setpgid);
set_to_cap_if_null(ops, task_getpgid);
@@ -910,7 +914,6 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, task_wait);
set_to_cap_if_null(ops, task_kill);
set_to_cap_if_null(ops, task_prctl);
- set_to_cap_if_null(ops, task_reparent_to_init);
set_to_cap_if_null(ops, task_to_inode);
set_to_cap_if_null(ops, ipc_permission);
set_to_cap_if_null(ops, ipc_getsecid);
diff --git a/security/commoncap.c b/security/commoncap.c
index 0384bf95db6..b5419273f92 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -72,8 +72,8 @@ int cap_ptrace_may_access(struct task_struct *child, unsigned int mode)
int ret = 0;
rcu_read_lock();
- if (!cap_issubset(child->cred->cap_permitted,
- current->cred->cap_permitted) &&
+ if (!cap_issubset(__task_cred(child)->cap_permitted,
+ current_cred()->cap_permitted) &&
!capable(CAP_SYS_PTRACE))
ret = -EPERM;
rcu_read_unlock();
@@ -85,8 +85,8 @@ int cap_ptrace_traceme(struct task_struct *parent)
int ret = 0;
rcu_read_lock();
- if (!cap_issubset(current->cred->cap_permitted,
- parent->cred->cap_permitted) &&
+ if (!cap_issubset(current_cred()->cap_permitted,
+ __task_cred(parent)->cap_permitted) &&
!has_capability(parent, CAP_SYS_PTRACE))
ret = -EPERM;
rcu_read_unlock();
@@ -117,7 +117,7 @@ static inline int cap_inh_is_capped(void)
* to the old permitted set. That is, if the current task
* does *not* possess the CAP_SETPCAP capability.
*/
- return (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0);
+ return cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0;
}
static inline int cap_limit_ptraced_target(void) { return 1; }
@@ -132,52 +132,39 @@ static inline int cap_limit_ptraced_target(void)
#endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */
-int cap_capset_check(const kernel_cap_t *effective,
- const kernel_cap_t *inheritable,
- const kernel_cap_t *permitted)
+int cap_capset(struct cred *new,
+ const struct cred *old,
+ const kernel_cap_t *effective,
+ const kernel_cap_t *inheritable,
+ const kernel_cap_t *permitted)
{
- const struct cred *cred = current->cred;
-
- if (cap_inh_is_capped()
- && !cap_issubset(*inheritable,
- cap_combine(cred->cap_inheritable,
- cred->cap_permitted))) {
+ if (cap_inh_is_capped() &&
+ !cap_issubset(*inheritable,
+ cap_combine(old->cap_inheritable,
+ old->cap_permitted)))
/* incapable of using this inheritable set */
return -EPERM;
- }
+
if (!cap_issubset(*inheritable,
- cap_combine(cred->cap_inheritable,
- cred->cap_bset))) {
+ cap_combine(old->cap_inheritable,
+ old->cap_bset)))
/* no new pI capabilities outside bounding set */
return -EPERM;
- }
/* verify restrictions on target's new Permitted set */
- if (!cap_issubset (*permitted,
- cap_combine (cred->cap_permitted,
- cred->cap_permitted))) {
+ if (!cap_issubset(*permitted, old->cap_permitted))
return -EPERM;
- }
/* verify the _new_Effective_ is a subset of the _new_Permitted_ */
- if (!cap_issubset (*effective, *permitted)) {
+ if (!cap_issubset(*effective, *permitted))
return -EPERM;
- }
+ new->cap_effective = *effective;
+ new->cap_inheritable = *inheritable;
+ new->cap_permitted = *permitted;
return 0;
}
-void cap_capset_set(const kernel_cap_t *effective,
- const kernel_cap_t *inheritable,
- const kernel_cap_t *permitted)
-{
- struct cred *cred = current->cred;
-
- cred->cap_effective = *effective;
- cred->cap_inheritable = *inheritable;
- cred->cap_permitted = *permitted;
-}
-
static inline void bprm_clear_caps(struct linux_binprm *bprm)
{
cap_clear(bprm->cap_post_exec_permitted);
@@ -382,41 +369,46 @@ int cap_bprm_set_security (struct linux_binprm *bprm)
return ret;
}
-void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
+int cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
{
- struct cred *cred = current->cred;
+ const struct cred *old = current_cred();
+ struct cred *new;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
- if (bprm->e_uid != cred->uid || bprm->e_gid != cred->gid ||
+ if (bprm->e_uid != old->uid || bprm->e_gid != old->gid ||
!cap_issubset(bprm->cap_post_exec_permitted,
- cred->cap_permitted)) {
+ old->cap_permitted)) {
set_dumpable(current->mm, suid_dumpable);
current->pdeath_signal = 0;
if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
if (!capable(CAP_SETUID)) {
- bprm->e_uid = cred->uid;
- bprm->e_gid = cred->gid;
+ bprm->e_uid = old->uid;
+ bprm->e_gid = old->gid;
}
if (cap_limit_ptraced_target()) {
bprm->cap_post_exec_permitted = cap_intersect(
bprm->cap_post_exec_permitted,
- cred->cap_permitted);
+ new->cap_permitted);
}
}
}
- cred->suid = cred->euid = cred->fsuid = bprm->e_uid;
- cred->sgid = cred->egid = cred->fsgid = bprm->e_gid;
+ new->suid = new->euid = new->fsuid = bprm->e_uid;
+ new->sgid = new->egid = new->fsgid = bprm->e_gid;
/* For init, we want to retain the capabilities set
* in the init_task struct. Thus we skip the usual
* capability rules */
if (!is_global_init(current)) {
- cred->cap_permitted = bprm->cap_post_exec_permitted;
+ new->cap_permitted = bprm->cap_post_exec_permitted;
if (bprm->cap_effective)
- cred->cap_effective = bprm->cap_post_exec_permitted;
+ new->cap_effective = bprm->cap_post_exec_permitted;
else
- cap_clear(cred->cap_effective);
+ cap_clear(new->cap_effective);
}
/*
@@ -431,15 +423,15 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
* Number 1 above might fail if you don't have a full bset, but I think
* that is interesting information to audit.
*/
- if (!cap_isclear(cred->cap_effective)) {
- if (!cap_issubset(CAP_FULL_SET, cred->cap_effective) ||
- (bprm->e_uid != 0) || (cred->uid != 0) ||
+ if (!cap_isclear(new->cap_effective)) {
+ if (!cap_issubset(CAP_FULL_SET, new->cap_effective) ||
+ bprm->e_uid != 0 || new->uid != 0 ||
issecure(SECURE_NOROOT))
- audit_log_bprm_fcaps(bprm, &cred->cap_permitted,
- &cred->cap_effective);
+ audit_log_bprm_fcaps(bprm, new, old);
}
- cred->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
+ new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
+ return commit_creds(new);
}
int cap_bprm_secureexec (struct linux_binprm *bprm)
@@ -514,65 +506,49 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name)
* files..
* Thanks to Olaf Kirch and Peter Benie for spotting this.
*/
-static inline void cap_emulate_setxuid (int old_ruid, int old_euid,
- int old_suid)
+static inline void cap_emulate_setxuid(struct cred *new, const struct cred *old)
{
- struct cred *cred = current->cred;
-
- if ((old_ruid == 0 || old_euid == 0 || old_suid == 0) &&
- (cred->uid != 0 && cred->euid != 0 && cred->suid != 0) &&
+ if ((old->uid == 0 || old->euid == 0 || old->suid == 0) &&
+ (new->uid != 0 && new->euid != 0 && new->suid != 0) &&
!issecure(SECURE_KEEP_CAPS)) {
- cap_clear(cred->cap_permitted);
- cap_clear(cred->cap_effective);
- }
- if (old_euid == 0 && cred->euid != 0) {
- cap_clear(cred->cap_effective);
- }
- if (old_euid != 0 && cred->euid == 0) {
- cred->cap_effective = cred->cap_permitted;
+ cap_clear(new->cap_permitted);
+ cap_clear(new->cap_effective);
}
+ if (old->euid == 0 && new->euid != 0)
+ cap_clear(new->cap_effective);
+ if (old->euid != 0 && new->euid == 0)
+ new->cap_effective = new->cap_permitted;
}
-int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid,
- int flags)
+int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags)
{
- struct cred *cred = current->cred;
-
switch (flags) {
case LSM_SETID_RE:
case LSM_SETID_ID:
case LSM_SETID_RES:
/* Copied from kernel/sys.c:setreuid/setuid/setresuid. */
- if (!issecure (SECURE_NO_SETUID_FIXUP)) {
- cap_emulate_setxuid (old_ruid, old_euid, old_suid);
- }
+ if (!issecure(SECURE_NO_SETUID_FIXUP))
+ cap_emulate_setxuid(new, old);
break;
case LSM_SETID_FS:
- {
- uid_t old_fsuid = old_ruid;
-
- /* Copied from kernel/sys.c:setfsuid. */
+ /* Copied from kernel/sys.c:setfsuid. */
- /*
- * FIXME - is fsuser used for all CAP_FS_MASK capabilities?
- * if not, we might be a bit too harsh here.
- */
-
- if (!issecure (SECURE_NO_SETUID_FIXUP)) {
- if (old_fsuid == 0 && cred->fsuid != 0) {
- cred->cap_effective =
- cap_drop_fs_set(
- cred->cap_effective);
- }
- if (old_fsuid != 0 && cred->fsuid == 0) {
- cred->cap_effective =
- cap_raise_fs_set(
- cred->cap_effective,
- cred->cap_permitted);
- }
+ /*
+ * FIXME - is fsuser used for all CAP_FS_MASK capabilities?
+ * if not, we might be a bit too harsh here.
+ */
+ if (!issecure(SECURE_NO_SETUID_FIXUP)) {
+ if (old->fsuid == 0 && new->fsuid != 0) {
+ new->cap_effective =
+ cap_drop_fs_set(new->cap_effective);
+ }
+ if (old->fsuid != 0 && new->fsuid == 0) {
+ new->cap_effective =
+ cap_raise_fs_set(new->cap_effective,
+ new->cap_permitted);
}
- break;
}
+ break;
default:
return -EINVAL;
}
@@ -628,13 +604,14 @@ int cap_task_setnice (struct task_struct *p, int nice)
* this task could get inconsistent info. There can be no
* racing writer bc a task can only change its own caps.
*/
-static long cap_prctl_drop(unsigned long cap)
+static long cap_prctl_drop(struct cred *new, unsigned long cap)
{
if (!capable(CAP_SETPCAP))
return -EPERM;
if (!cap_valid(cap))
return -EINVAL;
- cap_lower(current->cred->cap_bset, cap);
+
+ cap_lower(new->cap_bset, cap);
return 0;
}
@@ -655,22 +632,29 @@ int cap_task_setnice (struct task_struct *p, int nice)
#endif
int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
- unsigned long arg4, unsigned long arg5, long *rc_p)
+ unsigned long arg4, unsigned long arg5)
{
- struct cred *cred = current_cred();
+ struct cred *new;
long error = 0;
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
switch (option) {
case PR_CAPBSET_READ:
+ error = -EINVAL;
if (!cap_valid(arg2))
- error = -EINVAL;
- else
- error = !!cap_raised(cred->cap_bset, arg2);
- break;
+ goto error;
+ error = !!cap_raised(new->cap_bset, arg2);
+ goto no_change;
+
#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
case PR_CAPBSET_DROP:
- error = cap_prctl_drop(arg2);
- break;
+ error = cap_prctl_drop(new, arg2);
+ if (error < 0)
+ goto error;
+ goto changed;
/*
* The next four prctl's remain to assist with transitioning a
@@ -692,12 +676,12 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
* capability-based-privilege environment.
*/
case PR_SET_SECUREBITS:
- if ((((cred->securebits & SECURE_ALL_LOCKS) >> 1)
- & (cred->securebits ^ arg2)) /*[1]*/
- || ((cred->securebits & SECURE_ALL_LOCKS
- & ~arg2)) /*[2]*/
- || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
- || (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0)) { /*[4]*/
+ error = -EPERM;
+ if ((((new->securebits & SECURE_ALL_LOCKS) >> 1)
+ & (new->securebits ^ arg2)) /*[1]*/
+ || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/
+ || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
+ || (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/
/*
* [1] no changing of bits that are locked
* [2] no unlocking of locks
@@ -705,50 +689,51 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
* [4] doing anything requires privilege (go read about
* the "sendmail capabilities bug")
*/
- error = -EPERM; /* cannot change a locked bit */
- } else {
- cred->securebits = arg2;
- }
- break;
+ )
+ /* cannot change a locked bit */
+ goto error;
+ new->securebits = arg2;
+ goto changed;
+
case PR_GET_SECUREBITS:
- error = cred->securebits;
- break;
+ error = new->securebits;
+ goto no_change;
#endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */
case PR_GET_KEEPCAPS:
if (issecure(SECURE_KEEP_CAPS))
error = 1;
- break;
+ goto no_change;
+
case PR_SET_KEEPCAPS:
+ error = -EINVAL;
if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */
- error = -EINVAL;
- else if (issecure(SECURE_KEEP_CAPS_LOCKED))
- error = -EPERM;
- else if (arg2)
- cred->securebits |= issecure_mask(SECURE_KEEP_CAPS);
+ goto error;
+ error = -EPERM;
+ if (issecure(SECURE_KEEP_CAPS_LOCKED))
+ goto error;
+ if (arg2)
+ new->securebits |= issecure_mask(SECURE_KEEP_CAPS);
else
- cred->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
- break;
+ new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
+ goto changed;
default:
/* No functionality available - continue with default */
- return 0;
+ error = -ENOSYS;
+ goto error;
}
/* Functionality provided */
- *rc_p = error;
- return 1;
-}
-
-void cap_task_reparent_to_init (struct task_struct *p)
-{
- struct cred *cred = p->cred;
-
- cap_set_init_eff(cred->cap_effective);
- cap_clear(cred->cap_inheritable);
- cap_set_full(cred->cap_permitted);
- p->cred->securebits = SECUREBITS_DEFAULT;
+changed:
+ return commit_creds(new);
+
+no_change:
+ error = 0;
+error:
+ abort_creds(new);
+ return error;
}
int cap_syslog (int type)
diff --git a/security/keys/internal.h b/security/keys/internal.h
index d1586c62978..81932abefe7 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -12,6 +12,7 @@
#ifndef _INTERNAL_H
#define _INTERNAL_H
+#include <linux/sched.h>
#include <linux/key-type.h>
static inline __attribute__((format(printf, 1, 2)))
@@ -25,7 +26,7 @@ void no_printk(const char *fmt, ...)
#define kleave(FMT, ...) \
printk(KERN_DEBUG "<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
#define kdebug(FMT, ...) \
- printk(KERN_DEBUG "xxx" FMT"yyy\n", ##__VA_ARGS__)
+ printk(KERN_DEBUG " "FMT"\n", ##__VA_ARGS__)
#else
#define kenter(FMT, ...) \
no_printk(KERN_DEBUG "==> %s("FMT")\n", __func__, ##__VA_ARGS__)
@@ -97,7 +98,7 @@ extern struct key *keyring_search_instkey(struct key *keyring,
typedef int (*key_match_func_t)(const struct key *, const void *);
extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
- struct task_struct *tsk,
+ const struct cred *cred,
struct key_type *type,
const void *description,
key_match_func_t match);
@@ -105,13 +106,13 @@ extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
extern key_ref_t search_process_keyrings(struct key_type *type,
const void *description,
key_match_func_t match,
- struct task_struct *tsk);
+ const struct cred *cred);
extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
extern int install_user_keyrings(void);
-extern int install_thread_keyring(void);
-extern int install_process_keyring(void);
+extern int install_thread_keyring_to_cred(struct cred *);
+extern int install_process_keyring_to_cred(struct cred *);
extern struct key *request_key_and_link(struct key_type *type,
const char *description,
@@ -130,12 +131,12 @@ extern long join_session_keyring(const char *name);
* check to see whether permission is granted to use a key in the desired way
*/
extern int key_task_permission(const key_ref_t key_ref,
- struct task_struct *context,
+ const struct cred *cred,
key_perm_t perm);
static inline int key_permission(const key_ref_t key_ref, key_perm_t perm)
{
- return key_task_permission(key_ref, current, perm);
+ return key_task_permission(key_ref, current_cred(), perm);
}
/* required permissions */
@@ -153,7 +154,7 @@ static inline int key_permission(const key_ref_t key_ref, key_perm_t perm)
struct request_key_auth {
struct key *target_key;
struct key *dest_keyring;
- struct task_struct *context;
+ const struct cred *cred;
void *callout_info;
size_t callout_len;
pid_t pid;
diff --git a/security/keys/key.c b/security/keys/key.c
index a6ca39ed3b0..f76c8a546fd 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -218,7 +218,7 @@ serial_exists:
* instantiate the key or discard it before returning
*/
struct key *key_alloc(struct key_type *type, const char *desc,
- uid_t uid, gid_t gid, struct task_struct *ctx,
+ uid_t uid, gid_t gid, const struct cred *cred,
key_perm_t perm, unsigned long flags)
{
struct key_user *user = NULL;
@@ -294,7 +294,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
#endif
/* let the security module know about the key */
- ret = security_key_alloc(key, ctx, flags);
+ ret = security_key_alloc(key, cred, flags);
if (ret < 0)
goto security_error;
@@ -391,7 +391,7 @@ static int __key_instantiate_and_link(struct key *key,
const void *data,
size_t datalen,
struct key *keyring,
- struct key *instkey)
+ struct key *authkey)
{
int ret, awaken;
@@ -421,8 +421,8 @@ static int __key_instantiate_and_link(struct key *key,
ret = __key_link(keyring, key);
/* disable the authorisation key */
- if (instkey)
- key_revoke(instkey);
+ if (authkey)
+ key_revoke(authkey);
}
}
@@ -444,14 +444,14 @@ int key_instantiate_and_link(struct key *key,
const void *data,
size_t datalen,
struct key *keyring,
- struct key *instkey)
+ struct key *authkey)
{
int ret;
if (keyring)
down_write(&keyring->sem);
- ret = __key_instantiate_and_link(key, data, datalen, keyring, instkey);
+ ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey);
if (keyring)
up_write(&keyring->sem);
@@ -469,7 +469,7 @@ EXPORT_SYMBOL(key_instantiate_and_link);
int key_negate_and_link(struct key *key,
unsigned timeout,
struct key *keyring,
- struct key *instkey)
+ struct key *authkey)
{
struct timespec now;
int ret, awaken;
@@ -504,8 +504,8 @@ int key_negate_and_link(struct key *key,
ret = __key_link(keyring, key);
/* disable the authorisation key */
- if (instkey)
- key_revoke(instkey);
+ if (authkey)
+ key_revoke(authkey);
}
mutex_unlock(&key_construction_mutex);
@@ -743,6 +743,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_perm_t perm,
unsigned long flags)
{
+ const struct cred *cred = current_cred();
struct key_type *ktype;
struct key *keyring, *key = NULL;
key_ref_t key_ref;
@@ -802,8 +803,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
}
/* allocate a new key */
- key = key_alloc(ktype, description, current_fsuid(), current_fsgid(),
- current, perm, flags);
+ key = key_alloc(ktype, description, cred->fsuid, cred->fsgid, cred,
+ perm, flags);
if (IS_ERR(key)) {
key_ref = ERR_CAST(key);
goto error_3;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 8833b447ade..7c72baa02f2 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -866,6 +866,23 @@ static long get_instantiation_keyring(key_serial_t ringid,
return -ENOKEY;
}
+/*
+ * change the request_key authorisation key on the current process
+ */
+static int keyctl_change_reqkey_auth(struct key *key)
+{
+ struct cred *new;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
+ key_put(new->request_key_auth);
+ new->request_key_auth = key_get(key);
+
+ return commit_creds(new);
+}
+
/*****************************************************************************/
/*
* instantiate the key with the specified payload, and, if one is given, link
@@ -876,12 +893,15 @@ long keyctl_instantiate_key(key_serial_t id,
size_t plen,
key_serial_t ringid)
{
+ const struct cred *cred = current_cred();
struct request_key_auth *rka;
struct key *instkey, *dest_keyring;
void *payload;
long ret;
bool vm = false;
+ kenter("%d,,%zu,%d", id, plen, ringid);
+
ret = -EINVAL;
if (plen > 1024 * 1024 - 1)
goto error;
@@ -889,7 +909,7 @@ long keyctl_instantiate_key(key_serial_t id,
/* the appropriate instantiation authorisation key must have been
* assumed before calling this */
ret = -EPERM;
- instkey = current->cred->request_key_auth;
+ instkey = cred->request_key_auth;
if (!instkey)
goto error;
@@ -931,10 +951,8 @@ long keyctl_instantiate_key(key_serial_t id,
/* discard the assumed authority if it's just been disabled by
* instantiation of the key */
- if (ret == 0) {
- key_put(current->cred->request_key_auth);
- current->cred->request_key_auth = NULL;
- }
+ if (ret == 0)
+ keyctl_change_reqkey_auth(NULL);
error2:
if (!vm)
@@ -953,14 +971,17 @@ error:
*/
long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
{
+ const struct cred *cred = current_cred();
struct request_key_auth *rka;
struct key *instkey, *dest_keyring;
long ret;
+ kenter("%d,%u,%d", id, timeout, ringid);
+
/* the appropriate instantiation authorisation key must have been
* assumed before calling this */
ret = -EPERM;
- instkey = current->cred->request_key_auth;
+ instkey = cred->request_key_auth;
if (!instkey)
goto error;
@@ -982,10 +1003,8 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
/* discard the assumed authority if it's just been disabled by
* instantiation of the key */
- if (ret == 0) {
- key_put(current->cred->request_key_auth);
- current->cred->request_key_auth = NULL;
- }
+ if (ret == 0)
+ keyctl_change_reqkey_auth(NULL);
error:
return ret;
@@ -999,36 +1018,56 @@ error:
*/
long keyctl_set_reqkey_keyring(int reqkey_defl)
{
- struct cred *cred = current->cred;
- int ret;
+ struct cred *new;
+ int ret, old_setting;
+
+ old_setting = current_cred_xxx(jit_keyring);
+
+ if (reqkey_defl == KEY_REQKEY_DEFL_NO_CHANGE)
+ return old_setting;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
switch (reqkey_defl) {
case KEY_REQKEY_DEFL_THREAD_KEYRING:
- ret = install_thread_keyring();
+ ret = install_thread_keyring_to_cred(new);
if (ret < 0)
- return ret;
+ goto error;
goto set;
case KEY_REQKEY_DEFL_PROCESS_KEYRING:
- ret = install_process_keyring();
- if (ret < 0)
- return ret;
+ ret = install_process_keyring_to_cred(new);
+ if (ret < 0) {
+ if (ret != -EEXIST)
+ goto error;
+ ret = 0;
+ }
+ goto set;
case KEY_REQKEY_DEFL_DEFAULT:
case KEY_REQKEY_DEFL_SESSION_KEYRING:
case KEY_REQKEY_DEFL_USER_KEYRING:
case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
- set:
- cred->jit_keyring = reqkey_defl;
+ case KEY_REQKEY_DEFL_REQUESTOR_KEYRING:
+ goto set;
case KEY_REQKEY_DEFL_NO_CHANGE:
- return cred->jit_keyring;
-
case KEY_REQKEY_DEFL_GROUP_KEYRING:
default:
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
+set:
+ new->jit_keyring = reqkey_defl;
+ commit_creds(new);
+ return old_setting;
+error:
+ abort_creds(new);
+ return -EINVAL;
+
} /* end keyctl_set_reqkey_keyring() */
/*****************************************************************************/
@@ -1087,9 +1126,7 @@ long keyctl_assume_authority(key_serial_t id)
/* we divest ourselves of authority if given an ID of 0 */
if (id == 0) {
- key_put(current->cred->request_key_auth);
- current->cred->request_key_auth = NULL;
- ret = 0;
+ ret = keyctl_change_reqkey_auth(NULL);
goto error;
}
@@ -1104,10 +1141,12 @@ long keyctl_assume_authority(key_serial_t id)
goto error;
}
- key_put(current->cred->request_key_auth);
- current->cred->request_key_auth = authkey;
- ret = authkey->serial;
+ ret = keyctl_change_reqkey_auth(authkey);
+ if (ret < 0)
+ goto error;
+ key_put(authkey);
+ ret = authkey->serial;
error:
return ret;
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index fdf75f90199..ed851574d07 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -245,14 +245,14 @@ static long keyring_read(const struct key *keyring,
* allocate a keyring and link into the destination keyring
*/
struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
- struct task_struct *ctx, unsigned long flags,
+ const struct cred *cred, unsigned long flags,
struct key *dest)
{
struct key *keyring;
int ret;
keyring = key_alloc(&key_type_keyring, description,
- uid, gid, ctx,
+ uid, gid, cred,
(KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
flags);
@@ -281,7 +281,7 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
* - we propagate the possession attribute from the keyring ref to the key ref
*/
key_ref_t keyring_search_aux(key_ref_t keyring_ref,
- struct task_struct *context,
+ const struct cred *cred,
struct key_type *type,
const void *description,
key_match_func_t match)
@@ -304,7 +304,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
key_check(keyring);
/* top keyring must have search permission to begin the search */
- err = key_task_permission(keyring_ref, context, KEY_SEARCH);
+ err = key_task_permission(keyring_ref, cred, KEY_SEARCH);
if (err < 0) {
key_ref = ERR_PTR(err);
goto error;
@@ -377,7 +377,7 @@ descend:
/* key must have search permissions */
if (key_task_permission(make_key_ref(key, possessed),
- context, KEY_SEARCH) < 0)
+ cred, KEY_SEARCH) < 0)
continue;
/* we set a different error code if we pass a negative key */
@@ -404,7 +404,7 @@ ascend:
continue;
if (key_task_permission(make_key_ref(key, possessed),
- context, KEY_SEARCH) < 0)
+ cred, KEY_SEARCH) < 0)
continue;
/* stack the current position */
@@ -459,7 +459,7 @@ key_ref_t keyring_search(key_ref_t keyring,
if (!type->match)
return ERR_PTR(-ENOKEY);
- return keyring_search_aux(keyring, current,
+ return keyring_search_aux(keyring, current->cred,
type, description, type->match);
} /* end keyring_search() */
diff --git a/security/keys/permission.c b/security/keys/permission.c
index 13c36164f28..5d9fc7b93f2 100644
--- a/security/keys/permission.c
+++ b/security/keys/permission.c
@@ -14,24 +14,27 @@
#include "internal.h"
/*****************************************************************************/
-/*
- * check to see whether permission is granted to use a key in the desired way,
- * but permit the security modules to override
+/**
+ * key_task_permission - Check a key can be used
+ * @key_ref: The key to check
+ * @cred: The credentials to use
+ * @perm: The permissions to check for
+ *
+ * Check to see whether permission is granted to use a key in the desired way,
+ * but permit the security modules to override.
+ *
+ * The caller must hold either a ref on cred or must hold the RCU readlock or a
+ * spinlock.
*/
-int key_task_permission(const key_ref_t key_ref,
- struct task_struct *context,
+int key_task_permission(const key_ref_t key_ref, const struct cred *cred,
key_perm_t perm)
{
- const struct cred *cred;
struct key *key;
key_perm_t kperm;
int ret;
key = key_ref_to_ptr(key_ref);
- rcu_read_lock();
- cred = __task_cred(context);
-
/* use the second 8-bits of permissions for keys the caller owns */
if (key->uid == cred->fsuid) {
kperm = key->perm >> 16;
@@ -57,7 +60,6 @@ int key_task_permission(const key_ref_t key_ref,
kperm = key->perm;
use_these_perms:
- rcu_read_lock();
/* use the top 8-bits of permissions for keys the caller possesses
* - possessor permissions are additive with other permissions
@@ -71,7 +73,7 @@ use_these_perms:
return -EACCES;
/* let LSM be the final arbiter */
- return security_key_permission(key_ref, context, perm);
+ return security_key_permission(key_ref, cred, perm);
} /* end key_task_permission() */
diff --git a/security/keys/proc.c b/security/keys/proc.c
index f619170da76..7f508def50e 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -136,8 +136,12 @@ static int proc_keys_show(struct seq_file *m, void *v)
int rc;
/* check whether the current task is allowed to view the key (assuming
- * non-possession) */
- rc = key_task_permission(make_key_ref(key, 0), current, KEY_VIEW);
+ * non-possession)
+ * - the caller holds a spinlock, and thus the RCU read lock, making our
+ * access to __current_cred() safe
+ */
+ rc = key_task_permission(make_key_ref(key, 0), current_cred(),
+ KEY_VIEW);
if (rc < 0)
return 0;
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 70ee93406f3..df329f684a6 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -42,11 +42,15 @@ struct key_user root_key_user = {
*/
int install_user_keyrings(void)
{
- struct user_struct *user = current->cred->user;
+ struct user_struct *user;
+ const struct cred *cred;
struct key *uid_keyring, *session_keyring;
char buf[20];
int ret;
+ cred = current_cred();
+ user = cred->user;
+
kenter("%p{%u}", user, user->uid);
if (user->uid_keyring) {
@@ -67,7 +71,7 @@ int install_user_keyrings(void)
uid_keyring = find_keyring_by_name(buf, true);
if (IS_ERR(uid_keyring)) {
uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1,
- current, KEY_ALLOC_IN_QUOTA,
+ cred, KEY_ALLOC_IN_QUOTA,
NULL);
if (IS_ERR(uid_keyring)) {
ret = PTR_ERR(uid_keyring);
@@ -83,8 +87,7 @@ int install_user_keyrings(void)
if (IS_ERR(session_keyring)) {
session_keyring =
keyring_alloc(buf, user->uid, (gid_t) -1,
- current, KEY_ALLOC_IN_QUOTA,
- NULL);
+ cred, KEY_ALLOC_IN_QUOTA, NULL);
if (IS_ERR(session_keyring)) {
ret = PTR_ERR(session_keyring);
goto error_release;
@@ -116,142 +119,128 @@ error:
return ret;
}
-/*****************************************************************************/
/*
- * deal with the UID changing
+ * install a fresh thread keyring directly to new credentials
*/
-void switch_uid_keyring(struct user_struct *new_user)
+int install_thread_keyring_to_cred(struct cred *new)
{
-#if 0 /* do nothing for now */
- struct key *old;
-
- /* switch to the new user's session keyring if we were running under
- * root's default session keyring */
- if (new_user->uid != 0 &&
- current->session_keyring == &root_session_keyring
- ) {
- atomic_inc(&new_user->session_keyring->usage);
-
- task_lock(current);
- old = current->session_keyring;
- current->session_keyring = new_user->session_keyring;
- task_unlock(current);
+ struct key *keyring;
- key_put(old);
- }
-#endif
+ keyring = keyring_alloc("_tid", new->uid, new->gid, new,
+ KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ if (IS_ERR(keyring))
+ return PTR_ERR(keyring);
-} /* end switch_uid_keyring() */
+ new->thread_keyring = keyring;
+ return 0;
+}
-/*****************************************************************************/
/*
* install a fresh thread keyring, discarding the old one
*/
-int install_thread_keyring(void)
+static int install_thread_keyring(void)
{
- struct task_struct *tsk = current;
- struct key *keyring, *old;
- char buf[20];
+ struct cred *new;
int ret;
- sprintf(buf, "_tid.%u", tsk->pid);
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
- keyring = keyring_alloc(buf, tsk->cred->uid, tsk->cred->gid, tsk,
- KEY_ALLOC_QUOTA_OVERRUN, NULL);
- if (IS_ERR(keyring)) {
- ret = PTR_ERR(keyring);
- goto error;
+ BUG_ON(new->thread_keyring);
+
+ ret = install_thread_keyring_to_cred(new);
+ if (ret < 0) {
+ abort_creds(new);
+ return ret;
}
- task_lock(tsk);
- old = tsk->cred->thread_keyring;
- tsk->cred->thread_keyring = keyring;
- task_unlock(tsk);
+ return commit_creds(new);
+}
- ret = 0;
+/*
+ * install a process keyring directly to a credentials struct
+ * - returns -EEXIST if there was already a process keyring, 0 if one installed,
+ * and other -ve on any other error
+ */
+int install_process_keyring_to_cred(struct cred *new)
+{
+ struct key *keyring;
+ int ret;
- key_put(old);
-error:
+ if (new->tgcred->process_keyring)
+ return -EEXIST;
+
+ keyring = keyring_alloc("_pid", new->uid, new->gid,
+ new, KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ if (IS_ERR(keyring))
+ return PTR_ERR(keyring);
+
+ spin_lock_irq(&new->tgcred->lock);
+ if (!new->tgcred->process_keyring) {
+ new->tgcred->process_keyring = keyring;
+ keyring = NULL;
+ ret = 0;
+ } else {
+ ret = -EEXIST;
+ }
+ spin_unlock_irq(&new->tgcred->lock);
+ key_put(keyring);
return ret;
+}
-} /* end install_thread_keyring() */
-
-/*****************************************************************************/
/*
* make sure a process keyring is installed
+ * - we
*/
-int install_process_keyring(void)
+static int install_process_keyring(void)
{
- struct task_struct *tsk = current;
- struct key *keyring;
- char buf[20];
+ struct cred *new;
int ret;
- might_sleep();
-
- if (!tsk->cred->tgcred->process_keyring) {
- sprintf(buf, "_pid.%u", tsk->tgid);
-
- keyring = keyring_alloc(buf, tsk->cred->uid, tsk->cred->gid, tsk,
- KEY_ALLOC_QUOTA_OVERRUN, NULL);
- if (IS_ERR(keyring)) {
- ret = PTR_ERR(keyring);
- goto error;
- }
-
- /* attach keyring */
- spin_lock_irq(&tsk->cred->tgcred->lock);
- if (!tsk->cred->tgcred->process_keyring) {
- tsk->cred->tgcred->process_keyring = keyring;
- keyring = NULL;
- }
- spin_unlock_irq(&tsk->cred->tgcred->lock);
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
- key_put(keyring);
+ ret = install_process_keyring_to_cred(new);
+ if (ret < 0) {
+ abort_creds(new);
+ return ret != -EEXIST ?: 0;
}
- ret = 0;
-error:
- return ret;
-
-} /* end install_process_keyring() */
+ return commit_creds(new);
+}
-/*****************************************************************************/
/*
- * install a session keyring, discarding the old one
- * - if a keyring is not supplied, an empty one is invented
+ * install a session keyring directly to a credentials struct
*/
-static int install_session_keyring(struct key *keyring)
+static int install_session_keyring_to_cred(struct cred *cred,
+ struct key *keyring)
{
- struct task_struct *tsk = current;
unsigned long flags;
struct key *old;
- char buf[20];
might_sleep();
/* create an empty session keyring */
if (!keyring) {
- sprintf(buf, "_ses.%u", tsk->tgid);
-
flags = KEY_ALLOC_QUOTA_OVERRUN;
- if (tsk->cred->tgcred->session_keyring)
+ if (cred->tgcred->session_keyring)
flags = KEY_ALLOC_IN_QUOTA;
- keyring = keyring_alloc(buf, tsk->cred->uid, tsk->cred->gid,
- tsk, flags, NULL);
+ keyring = keyring_alloc("_ses", cred->uid, cred->gid,
+ cred, flags, NULL);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
- }
- else {
+ } else {
atomic_inc(&keyring->usage);
}
/* install the keyring */
- spin_lock_irq(&tsk->cred->tgcred->lock);
- old = tsk->cred->tgcred->session_keyring;
- rcu_assign_pointer(tsk->cred->tgcred->session_keyring, keyring);
- spin_unlock_irq(&tsk->cred->tgcred->lock);
+ spin_lock_irq(&cred->tgcred->lock);
+ old = cred->tgcred->session_keyring;
+ rcu_assign_pointer(cred->tgcred->session_keyring, keyring);
+ spin_unlock_irq(&cred->tgcred->lock);
/* we're using RCU on the pointer, but there's no point synchronising
* on it if it didn't previously point to anything */
@@ -261,38 +250,29 @@ static int install_session_keyring(struct key *keyring)
}
return 0;
+}
-} /* end install_session_keyring() */
-
-/*****************************************************************************/
/*
- * copy the keys for fork
+ * install a session keyring, discarding the old one
+ * - if a keyring is not supplied, an empty one is invented
*/
-int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
+static int install_session_keyring(struct key *keyring)
{
- key_check(tsk->cred->thread_keyring);
- key_check(tsk->cred->request_key_auth);
-
- /* no thread keyring yet */
- tsk->cred->thread_keyring = NULL;
-
- /* copy the request_key() authorisation for this thread */
- key_get(tsk->cred->request_key_auth);
-
- return 0;
+ struct cred *new;
+ int ret;
-} /* end copy_keys() */
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
-/*****************************************************************************/
-/*
- * dispose of per-thread keys upon thread exit
- */
-void exit_keys(struct task_struct *tsk)
-{
- key_put(tsk->cred->thread_keyring);
- key_put(tsk->cred->request_key_auth);
+ ret = install_session_keyring_to_cred(new, NULL);
+ if (ret < 0) {
+ abort_creds(new);
+ return ret;
+ }
-} /* end exit_keys() */
+ return commit_creds(new);
+}
/*****************************************************************************/
/*
@@ -300,38 +280,41 @@ void exit_keys(struct task_struct *tsk)
*/
int exec_keys(struct task_struct *tsk)
{
- struct key *old;
+ struct thread_group_cred *tgcred = NULL;
+ struct cred *new;
- /* newly exec'd tasks don't get a thread keyring */
- task_lock(tsk);
- old = tsk->cred->thread_keyring;
- tsk->cred->thread_keyring = NULL;
- task_unlock(tsk);
+#ifdef CONFIG_KEYS
+ tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
+ if (!tgcred)
+ return -ENOMEM;
+#endif
- key_put(old);
+ new = prepare_creds();
+ if (new < 0)
+ return -ENOMEM;
- /* discard the process keyring from a newly exec'd task */
- spin_lock_irq(&tsk->cred->tgcred->lock);
- old = tsk->cred->tgcred->process_keyring;
- tsk->cred->tgcred->process_keyring = NULL;
- spin_unlock_irq(&tsk->cred->tgcred->lock);
+ /* newly exec'd tasks don't get a thread keyring */
+ key_put(new->thread_keyring);
+ new->thread_keyring = NULL;
- key_put(old);
+ /* create a new per-thread-group creds for all this set of threads to
+ * share */
+ memcpy(tgcred, new->tgcred, sizeof(struct thread_group_cred));
- return 0;
+ atomic_set(&tgcred->usage, 1);
+ spin_lock_init(&tgcred->lock);
-} /* end exec_keys() */
+ /* inherit the session keyring; new process keyring */
+ key_get(tgcred->session_keyring);
+ tgcred->process_keyring = NULL;
-/*****************************************************************************/
-/*
- * deal with SUID programs
- * - we might want to make this invent a new session keyring
- */
-int suid_keys(struct task_struct *tsk)
-{
+ release_tgcred(new);
+ new->tgcred = tgcred;
+
+ commit_creds(new);
return 0;
-} /* end suid_keys() */
+} /* end exec_keys() */
/*****************************************************************************/
/*
@@ -376,16 +359,13 @@ void key_fsgid_changed(struct task_struct *tsk)
key_ref_t search_process_keyrings(struct key_type *type,
const void *description,
key_match_func_t match,
- struct task_struct *context)
+ const struct cred *cred)
{
struct request_key_auth *rka;
- struct cred *cred;
key_ref_t key_ref, ret, err;
might_sleep();
- cred = get_task_cred(context);
-
/* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
* searchable, but we failed to find a key or we found a negative key;
* otherwise we want to return a sample error (probably -EACCES) if
@@ -401,7 +381,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
if (cred->thread_keyring) {
key_ref = keyring_search_aux(
make_key_ref(cred->thread_keyring, 1),
- context, type, description, match);
+ cred, type, description, match);
if (!IS_ERR(key_ref))
goto found;
@@ -422,7 +402,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
if (cred->tgcred->process_keyring) {
key_ref = keyring_search_aux(
make_key_ref(cred->tgcred->process_keyring, 1),
- context, type, description, match);
+ cred, type, description, match);
if (!IS_ERR(key_ref))
goto found;
@@ -446,7 +426,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
make_key_ref(rcu_dereference(
cred->tgcred->session_keyring),
1),
- context, type, description, match);
+ cred, type, description, match);
rcu_read_unlock();
if (!IS_ERR(key_ref))
@@ -468,7 +448,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
else if (cred->user->session_keyring) {
key_ref = keyring_search_aux(
make_key_ref(cred->user->session_keyring, 1),
- context, type, description, match);
+ cred, type, description, match);
if (!IS_ERR(key_ref))
goto found;
@@ -490,7 +470,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
* - we don't permit access to request_key auth keys via this method
*/
if (cred->request_key_auth &&
- context == current &&
+ cred == current_cred() &&
type != &key_type_request_key_auth
) {
/* defend against the auth key being revoked */
@@ -500,7 +480,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
rka = cred->request_key_auth->payload.data;
key_ref = search_process_keyrings(type, description,
- match, rka->context);
+ match, rka->cred);
up_read(&cred->request_key_auth->sem);
@@ -527,7 +507,6 @@ key_ref_t search_process_keyrings(struct key_type *type,
key_ref = ret ? ret : err;
found:
- put_cred(cred);
return key_ref;
} /* end search_process_keyrings() */
@@ -552,8 +531,7 @@ key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
key_perm_t perm)
{
struct request_key_auth *rka;
- struct task_struct *t = current;
- struct cred *cred;
+ const struct cred *cred;
struct key *key;
key_ref_t key_ref, skey_ref;
int ret;
@@ -608,6 +586,7 @@ try_again:
goto error;
ret = install_session_keyring(
cred->user->session_keyring);
+
if (ret < 0)
goto error;
goto reget_creds;
@@ -693,7 +672,7 @@ try_again:
/* check to see if we possess the key */
skey_ref = search_process_keyrings(key->type, key,
lookup_user_key_possessed,
- current);
+ cred);
if (!IS_ERR(skey_ref)) {
key_put(key);
@@ -725,7 +704,7 @@ try_again:
goto invalid_key;
/* check the permissions */
- ret = key_task_permission(key_ref, t, perm);
+ ret = key_task_permission(key_ref, cred, perm);
if (ret < 0)
goto invalid_key;
@@ -755,21 +734,33 @@ reget_creds:
*/
long join_session_keyring(const char *name)
{
- struct task_struct *tsk = current;
- struct cred *cred = current->cred;
+ const struct cred *old;
+ struct cred *new;
struct key *keyring;
- long ret;
+ long ret, serial;
+
+ /* only permit this if there's a single thread in the thread group -
+ * this avoids us having to adjust the creds on all threads and risking
+ * ENOMEM */
+ if (!is_single_threaded(current))
+ return -EMLINK;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ old = current_cred();
/* if no name is provided, install an anonymous keyring */
if (!name) {
- ret = install_session_keyring(NULL);
+ ret = install_session_keyring_to_cred(new, NULL);
if (ret < 0)
goto error;
- rcu_read_lock();
- ret = rcu_dereference(cred->tgcred->session_keyring)->serial;
- rcu_read_unlock();
- goto error;
+ serial = new->tgcred->session_keyring->serial;
+ ret = commit_creds(new);
+ if (ret == 0)
+ ret = serial;
+ goto okay;
}
/* allow the user to join or create a named keyring */
@@ -779,29 +770,33 @@ long join_session_keyring(const char *name)
keyring = find_keyring_by_name(name, false);
if (PTR_ERR(keyring) == -ENOKEY) {
/* not found - try and create a new one */
- keyring = keyring_alloc(name, cred->uid, cred->gid, tsk,
+ keyring = keyring_alloc(name, old->uid, old->gid, old,
KEY_ALLOC_IN_QUOTA, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error2;
}
- }
- else if (IS_ERR(keyring)) {
+ } else if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error2;
}
/* we've got a keyring - now to install it */
- ret = install_session_keyring(keyring);
+ ret = install_session_keyring_to_cred(new, keyring);
if (ret < 0)
goto error2;
+ commit_creds(new);
+ mutex_unlock(&key_session_mutex);
+
ret = keyring->serial;
key_put(keyring);
+okay:
+ return ret;
error2:
mutex_unlock(&key_session_mutex);
error:
+ abort_creds(new);
return ret;
-
-} /* end join_session_keyring() */
+}
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 3d12558362d..0e04f72ef2d 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -83,8 +83,10 @@ static int call_sbin_request_key(struct key_construction *cons,
/* allocate a new session keyring */
sprintf(desc, "_req.%u", key->serial);
- keyring = keyring_alloc(desc, current_fsuid(), current_fsgid(), current,
+ cred = get_current_cred();
+ keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred,
KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ put_cred(cred);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error_alloc;
@@ -104,8 +106,7 @@ static int call_sbin_request_key(struct key_construction *cons,
/* we specify the process's default keyrings */
sprintf(keyring_str[0], "%d",
- cred->thread_keyring ?
- cred->thread_keyring->serial : 0);
+ cred->thread_keyring ? cred->thread_keyring->serial : 0);
prkey = 0;
if (cred->tgcred->process_keyring)
@@ -155,8 +156,8 @@ error_link:
key_put(keyring);
error_alloc:
- kleave(" = %d", ret);
complete_request_key(cons, ret);
+ kleave(" = %d", ret);
return ret;
}
@@ -295,6 +296,7 @@ static int construct_alloc_key(struct key_type *type,
struct key_user *user,
struct key **_key)
{
+ const struct cred *cred = current_cred();
struct key *key;
key_ref_t key_ref;
@@ -302,9 +304,8 @@ static int construct_alloc_key(struct key_type *type,
mutex_lock(&user->cons_lock);
- key = key_alloc(type, description,
- current_fsuid(), current_fsgid(), current, KEY_POS_ALL,
- flags);
+ key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred,
+ KEY_POS_ALL, flags);
if (IS_ERR(key))
goto alloc_failed;
@@ -317,8 +318,7 @@ static int construct_alloc_key(struct key_type *type,
* waited for locks */
mutex_lock(&key_construction_mutex);
- key_ref = search_process_keyrings(type, description, type->match,
- current);
+ key_ref = search_process_keyrings(type, description, type->match, cred);
if (!IS_ERR(key_ref))
goto key_already_present;
@@ -363,6 +363,8 @@ static struct key *construct_key_and_link(struct key_type *type,
struct key *key;
int ret;
+ kenter("");
+
user = key_user_lookup(current_fsuid());
if (!user)
return ERR_PTR(-ENOMEM);
@@ -376,17 +378,21 @@ static struct key *construct_key_and_link(struct key_type *type,
if (ret == 0) {
ret = construct_key(key, callout_info, callout_len, aux,
dest_keyring);
- if (ret < 0)
+ if (ret < 0) {
+ kdebug("cons failed");
goto construction_failed;
+ }
}
key_put(dest_keyring);
+ kleave(" = key %d", key_serial(key));
return key;
construction_failed:
key_negate_and_link(key, key_negative_timeout, NULL, NULL);
key_put(key);
key_put(dest_keyring);
+ kleave(" = %d", ret);
return ERR_PTR(ret);
}
@@ -405,6 +411,7 @@ struct key *request_key_and_link(struct key_type *type,
struct key *dest_keyring,
unsigned long flags)
{
+ const struct cred *cred = current_cred();
struct key *key;
key_ref_t key_ref;
@@ -414,7 +421,7 @@ struct key *request_key_and_link(struct key_type *type,
/* search all the process keyrings for a key */
key_ref = search_process_keyrings(type, description, type->match,
- current);
+ cred);
if (!IS_ERR(key_ref)) {
key = key_ref_to_ptr(key_ref);
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 2125579d5d7..86747151ee5 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -105,9 +105,9 @@ static void request_key_auth_revoke(struct key *key)
kenter("{%d}", key->serial);
- if (rka->context) {
- put_task_struct(rka->context);
- rka->context = NULL;
+ if (rka->cred) {
+ put_cred(rka->cred);
+ rka->cred = NULL;
}
} /* end request_key_auth_revoke() */
@@ -122,9 +122,9 @@ static void request_key_auth_destroy(struct key *key)
kenter("{%d}", key->serial);
- if (rka->context) {
- put_task_struct(rka->context);
- rka->context = NULL;
+ if (rka->cred) {
+ put_cred(rka->cred);
+ rka->cred = NULL;
}
key_put(rka->target_key);
@@ -143,6 +143,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
size_t callout_len, struct key *dest_keyring)
{
struct request_key_auth *rka, *irka;
+ const struct cred *cred = current->cred;
struct key *authkey = NULL;
char desc[20];
int ret;
@@ -164,28 +165,25 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
/* see if the calling process is already servicing the key request of
* another process */
- if (current->cred->request_key_auth) {
+ if (cred->request_key_auth) {
/* it is - use that instantiation context here too */
- down_read(&current->cred->request_key_auth->sem);
+ down_read(&cred->request_key_auth->sem);
/* if the auth key has been revoked, then the key we're
* servicing is already instantiated */
- if (test_bit(KEY_FLAG_REVOKED,
- &current->cred->request_key_auth->flags))
+ if (test_bit(KEY_FLAG_REVOKED, &cred->request_key_auth->flags))
goto auth_key_revoked;
- irka = current->cred->request_key_auth->payload.data;
- rka->context = irka->context;
+ irka = cred->request_key_auth->payload.data;
+ rka->cred = get_cred(irka->cred);
rka->pid = irka->pid;
- get_task_struct(rka->context);
- up_read(&current->cred->request_key_auth->sem);
+ up_read(&cred->request_key_auth->sem);
}
else {
/* it isn't - use this process as the context */
- rka->context = current;
+ rka->cred = get_cred(cred);
rka->pid = current->pid;
- get_task_struct(rka->context);
}
rka->target_key = key_get(target);
@@ -197,7 +195,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
sprintf(desc, "%x", target->serial);
authkey = key_alloc(&key_type_request_key_auth, desc,
- current_fsuid(), current_fsgid(), current,
+ cred->fsuid, cred->fsgid, cred,
KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(authkey)) {
@@ -205,16 +203,16 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
goto error_alloc;
}
- /* construct and attach to the keyring */
+ /* construct the auth key */
ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL);
if (ret < 0)
goto error_inst;
- kleave(" = {%d}", authkey->serial);
+ kleave(" = {%d,%d}", authkey->serial, atomic_read(&authkey->usage));
return authkey;
auth_key_revoked:
- up_read(&current->cred->request_key_auth->sem);
+ up_read(&cred->request_key_auth->sem);
kfree(rka->callout_info);
kfree(rka);
kleave("= -EKEYREVOKED");
@@ -257,6 +255,7 @@ static int key_get_instantiation_authkey_match(const struct key *key,
*/
struct key *key_get_instantiation_authkey(key_serial_t target_id)
{
+ const struct cred *cred = current_cred();
struct key *authkey;
key_ref_t authkey_ref;
@@ -264,7 +263,7 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id)
&key_type_request_key_auth,
(void *) (unsigned long) target_id,
key_get_instantiation_authkey_match,
- current);
+ cred);
if (IS_ERR(authkey_ref)) {
authkey = ERR_CAST(authkey_ref);
diff --git a/security/security.c b/security/security.c
index f40a0a04c3c..a55d739c686 100644
--- a/security/security.c
+++ b/security/security.c
@@ -145,18 +145,13 @@ int security_capget(struct task_struct *target,
return security_ops->capget(target, effective, inheritable, permitted);
}
-int security_capset_check(const kernel_cap_t *effective,
- const kernel_cap_t *inheritable,
- const kernel_cap_t *permitted)
+int security_capset(struct cred *new, const struct cred *old,
+ const kernel_cap_t *effective,
+ const kernel_cap_t *inheritable,
+ const kernel_cap_t *permitted)
{
- return security_ops->capset_check(effective, inheritable, permitted);
-}
-
-void security_capset_set(const kernel_cap_t *effective,
- const kernel_cap_t *inheritable,
- const kernel_cap_t *permitted)
-{
- security_ops->capset_set(effective, inheritable, permitted);
+ return security_ops->capset(new, old,
+ effective, inheritable, permitted);
}
int security_capable(struct task_struct *tsk, int cap)
@@ -228,9 +223,9 @@ void security_bprm_free(struct linux_binprm *bprm)
security_ops->bprm_free_security(bprm);
}
-void security_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
+int security_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
{
- security_ops->bprm_apply_creds(bprm, unsafe);
+ return security_ops->bprm_apply_creds(bprm, unsafe);
}
void security_bprm_post_apply_creds(struct linux_binprm *bprm)
@@ -616,14 +611,19 @@ int security_task_create(unsigned long clone_flags)
return security_ops->task_create(clone_flags);
}
-int security_cred_alloc(struct cred *cred)
+void security_cred_free(struct cred *cred)
{
- return security_ops->cred_alloc_security(cred);
+ security_ops->cred_free(cred);
}
-void security_cred_free(struct cred *cred)
+int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
{
- security_ops->cred_free(cred);
+ return security_ops->cred_prepare(new, old, gfp);
+}
+
+void security_commit_creds(struct cred *new, const struct cred *old)
+{
+ return security_ops->cred_commit(new, old);
}
int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
@@ -631,10 +631,10 @@ int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
return security_ops->task_setuid(id0, id1, id2, flags);
}
-int security_task_post_setuid(uid_t old_ruid, uid_t old_euid,
- uid_t old_suid, int flags)
+int security_task_fix_setuid(struct cred *new, const struct cred *old,
+ int flags)
{
- return security_ops->task_post_setuid(old_ruid, old_euid, old_suid, flags);
+ return security_ops->task_fix_setuid(new, old, flags);
}
int security_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
@@ -716,14 +716,9 @@ int security_task_wait(struct task_struct *p)
}
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
- unsigned long arg4, unsigned long arg5, long *rc_p)
-{
- return security_ops->task_prctl(option, arg2, arg3, arg4, arg5, rc_p);
-}
-
-void security_task_reparent_to_init(struct task_struct *p)
+ unsigned long arg4, unsigned long arg5)
{
- security_ops->task_reparent_to_init(p);
+ return security_ops->task_prctl(option, arg2, arg3, arg4, arg5);
}
void security_task_to_inode(struct task_struct *p, struct inode *inode)
@@ -1123,9 +1118,10 @@ EXPORT_SYMBOL(security_skb_classify_flow);
#ifdef CONFIG_KEYS
-int security_key_alloc(struct key *key, struct task_struct *tsk, unsigned long flags)
+int security_key_alloc(struct key *key, const struct cred *cred,
+ unsigned long flags)
{
- return security_ops->key_alloc(key, tsk, flags);
+ return security_ops->key_alloc(key, cred, flags);
}
void security_key_free(struct key *key)
@@ -1134,9 +1130,9 @@ void security_key_free(struct key *key)
}
int security_key_permission(key_ref_t key_ref,
- struct task_struct *context, key_perm_t perm)
+ const struct cred *cred, key_perm_t perm)
{
- return security_ops->key_permission(key_ref, context, perm);
+ return security_ops->key_permission(key_ref, cred, perm);
}
int security_key_getsecurity(struct key *key, char **_buffer)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index f20cbd681ba..c71bba78872 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -156,20 +156,20 @@ static int selinux_secmark_enabled(void)
return (atomic_read(&selinux_secmark_refcount) > 0);
}
-/* Allocate and free functions for each kind of security blob. */
-
-static int cred_alloc_security(struct cred *cred)
+/*
+ * initialise the security for the init task
+ */
+static void cred_init_security(void)
{
+ struct cred *cred = (struct cred *) current->cred;
struct task_security_struct *tsec;
tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
if (!tsec)
- return -ENOMEM;
+ panic("SELinux: Failed to initialize initial task.\n");
- tsec->osid = tsec->sid = SECINITSID_UNLABELED;
+ tsec->osid = tsec->sid = SECINITSID_KERNEL;
cred->security = tsec;
-
- return 0;
}
/*
@@ -1379,6 +1379,19 @@ static inline u32 signal_to_av(int sig)
}
/*
+ * Check permission between a pair of credentials
+ * fork check, ptrace check, etc.
+ */
+static int cred_has_perm(const struct cred *actor,
+ const struct cred *target,
+ u32 perms)
+{
+ u32 asid = cred_sid(actor), tsid = cred_sid(target);
+
+ return avc_has_perm(asid, tsid, SECCLASS_PROCESS, perms, NULL);
+}
+
+/*
* Check permission between a pair of tasks, e.g. signal checks,
* fork check, ptrace check, etc.
* tsk1 is the actor and tsk2 is the target
@@ -1820,24 +1833,19 @@ static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
return secondary_ops->capget(target, effective, inheritable, permitted);
}
-static int selinux_capset_check(const kernel_cap_t *effective,
- const kernel_cap_t *inheritable,
- const kernel_cap_t *permitted)
+static int selinux_capset(struct cred *new, const struct cred *old,
+ const kernel_cap_t *effective,
+ const kernel_cap_t *inheritable,
+ const kernel_cap_t *permitted)
{
int error;
- error = secondary_ops->capset_check(effective, inheritable, permitted);
+ error = secondary_ops->capset(new, old,
+ effective, inheritable, permitted);
if (error)
return error;
- return task_has_perm(current, current, PROCESS__SETCAP);
-}
-
-static void selinux_capset_set(const kernel_cap_t *effective,
- const kernel_cap_t *inheritable,
- const kernel_cap_t *permitted)
-{
- secondary_ops->capset_set(effective, inheritable, permitted);
+ return cred_has_perm(old, new, PROCESS__SETCAP);
}
static int selinux_capable(struct task_struct *tsk, int cap, int audit)
@@ -2244,16 +2252,23 @@ static inline void flush_unauthorized_files(const struct cred *cred,
spin_unlock(&files->file_lock);
}
-static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
+static int selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
{
struct task_security_struct *tsec;
struct bprm_security_struct *bsec;
+ struct cred *new;
u32 sid;
int rc;
- secondary_ops->bprm_apply_creds(bprm, unsafe);
+ rc = secondary_ops->bprm_apply_creds(bprm, unsafe);
+ if (rc < 0)
+ return rc;
- tsec = current_security();
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
+ tsec = new->security;
bsec = bprm->security;
sid = bsec->sid;
@@ -2268,7 +2283,7 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
PROCESS__SHARE, NULL);
if (rc) {
bsec->unsafe = 1;
- return;
+ goto out;
}
}
@@ -2292,12 +2307,16 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
PROCESS__PTRACE, NULL);
if (rc) {
bsec->unsafe = 1;
- return;
+ goto out;
}
}
}
tsec->sid = sid;
}
+
+out:
+ commit_creds(new);
+ return 0;
}
/*
@@ -3021,6 +3040,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
{
const struct cred *cred = current_cred();
+ int rc = 0;
#ifndef CONFIG_PPC32
if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
@@ -3029,9 +3049,9 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
* private file mapping that will also be writable.
* This has an additional check.
*/
- int rc = task_has_perm(current, current, PROCESS__EXECMEM);
+ rc = cred_has_perm(cred, cred, PROCESS__EXECMEM);
if (rc)
- return rc;
+ goto error;
}
#endif
@@ -3048,7 +3068,9 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
return file_has_perm(cred, file, av);
}
- return 0;
+
+error:
+ return rc;
}
static int selinux_file_mmap(struct file *file, unsigned long reqprot,
@@ -3090,8 +3112,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
rc = 0;
if (vma->vm_start >= vma->vm_mm->start_brk &&
vma->vm_end <= vma->vm_mm->brk) {
- rc = task_has_perm(current, current,
- PROCESS__EXECHEAP);
+ rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);
} else if (!vma->vm_file &&
vma->vm_start <= vma->vm_mm->start_stack &&
vma->vm_end >= vma->vm_mm->start_stack) {
@@ -3104,8 +3125,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
* modified content. This typically should only
* occur for text relocations.
*/
- rc = file_has_perm(cred, vma->vm_file,
- FILE__EXECMOD);
+ rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD);
}
if (rc)
return rc;
@@ -3211,6 +3231,7 @@ static int selinux_dentry_open(struct file *file, const struct cred *cred)
struct file_security_struct *fsec;
struct inode *inode;
struct inode_security_struct *isec;
+
inode = file->f_path.dentry->d_inode;
fsec = file->f_security;
isec = inode->i_security;
@@ -3247,38 +3268,41 @@ static int selinux_task_create(unsigned long clone_flags)
return task_has_perm(current, current, PROCESS__FORK);
}
-static int selinux_cred_alloc_security(struct cred *cred)
+/*
+ * detach and free the LSM part of a set of credentials
+ */
+static void selinux_cred_free(struct cred *cred)
{
- struct task_security_struct *tsec1, *tsec2;
- int rc;
-
- tsec1 = current_security();
+ struct task_security_struct *tsec = cred->security;
+ cred->security = NULL;
+ kfree(tsec);
+}
- rc = cred_alloc_security(cred);
- if (rc)
- return rc;
- tsec2 = cred->security;
+/*
+ * prepare a new set of credentials for modification
+ */
+static int selinux_cred_prepare(struct cred *new, const struct cred *old,
+ gfp_t gfp)
+{
+ const struct task_security_struct *old_tsec;
+ struct task_security_struct *tsec;
- tsec2->osid = tsec1->osid;
- tsec2->sid = tsec1->sid;
+ old_tsec = old->security;
- /* Retain the exec, fs, key, and sock SIDs across fork */
- tsec2->exec_sid = tsec1->exec_sid;
- tsec2->create_sid = tsec1->create_sid;
- tsec2->keycreate_sid = tsec1->keycreate_sid;
- tsec2->sockcreate_sid = tsec1->sockcreate_sid;
+ tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp);
+ if (!tsec)
+ return -ENOMEM;
+ new->security = tsec;
return 0;
}
/*
- * detach and free the LSM part of a set of credentials
+ * commit new credentials
*/
-static void selinux_cred_free(struct cred *cred)
+static void selinux_cred_commit(struct cred *new, const struct cred *old)
{
- struct task_security_struct *tsec = cred->security;
- cred->security = NULL;
- kfree(tsec);
+ secondary_ops->cred_commit(new, old);
}
static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
@@ -3292,9 +3316,10 @@ static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
return 0;
}
-static int selinux_task_post_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
+static int selinux_task_fix_setuid(struct cred *new, const struct cred *old,
+ int flags)
{
- return secondary_ops->task_post_setuid(id0, id1, id2, flags);
+ return secondary_ops->task_fix_setuid(new, old, flags);
}
static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
@@ -3368,7 +3393,7 @@ static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim
/* Control the ability to change the hard limit (whether
lowering or raising it), so that the hard limit can
later be used as a safe reset point for the soft limit
- upon context transitions. See selinux_bprm_apply_creds. */
+ upon context transitions. See selinux_bprm_committing_creds. */
if (old_rlim->rlim_max != new_rlim->rlim_max)
return task_has_perm(current, current, PROCESS__SETRLIMIT);
@@ -3422,13 +3447,12 @@ static int selinux_task_prctl(int option,
unsigned long arg2,
unsigned long arg3,
unsigned long arg4,
- unsigned long arg5,
- long *rc_p)
+ unsigned long arg5)
{
/* The current prctl operations do not appear to require
any SELinux controls since they merely observe or modify
the state of the current process. */
- return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5, rc_p);
+ return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5);
}
static int selinux_task_wait(struct task_struct *p)
@@ -3436,18 +3460,6 @@ static int selinux_task_wait(struct task_struct *p)
return task_has_perm(p, current, PROCESS__SIGCHLD);
}
-static void selinux_task_reparent_to_init(struct task_struct *p)
-{
- struct task_security_struct *tsec;
-
- secondary_ops->task_reparent_to_init(p);
-
- tsec = p->cred->security;
- tsec->osid = tsec->sid;
- tsec->sid = SECINITSID_KERNEL;
- return;
-}
-
static void selinux_task_to_inode(struct task_struct *p,
struct inode *inode)
{
@@ -5325,7 +5337,8 @@ static int selinux_setprocattr(struct task_struct *p,
{
struct task_security_struct *tsec;
struct task_struct *tracer;
- u32 sid = 0;
+ struct cred *new;
+ u32 sid = 0, ptsid;
int error;
char *str = value;
@@ -5372,86 +5385,75 @@ static int selinux_setprocattr(struct task_struct *p,
return error;
}
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
/* Permission checking based on the specified context is
performed during the actual operation (execve,
open/mkdir/...), when we know the full context of the
- operation. See selinux_bprm_set_security for the execve
+ operation. See selinux_bprm_set_creds for the execve
checks and may_create for the file creation checks. The
operation will then fail if the context is not permitted. */
- tsec = p->cred->security;
- if (!strcmp(name, "exec"))
+ tsec = new->security;
+ if (!strcmp(name, "exec")) {
tsec->exec_sid = sid;
- else if (!strcmp(name, "fscreate"))
+ } else if (!strcmp(name, "fscreate")) {
tsec->create_sid = sid;
- else if (!strcmp(name, "keycreate")) {
+ } else if (!strcmp(name, "keycreate")) {
error = may_create_key(sid, p);
if (error)
- return error;
+ goto abort_change;
tsec->keycreate_sid = sid;
- } else if (!strcmp(name, "sockcreate"))
+ } else if (!strcmp(name, "sockcreate")) {
tsec->sockcreate_sid = sid;
- else if (!strcmp(name, "current")) {
- struct av_decision avd;
-
+ } else if (!strcmp(name, "current")) {
+ error = -EINVAL;
if (sid == 0)
- return -EINVAL;
- /*
- * SELinux allows to change context in the following case only.
- * - Single threaded processes.
- * - Multi threaded processes intend to change its context into
- * more restricted domain (defined by TYPEBOUNDS statement).
- */
- if (atomic_read(&p->mm->mm_users) != 1) {
- struct task_struct *g, *t;
- struct mm_struct *mm = p->mm;
- read_lock(&tasklist_lock);
- do_each_thread(g, t) {
- if (t->mm == mm && t != p) {
- read_unlock(&tasklist_lock);
- error = security_bounded_transition(tsec->sid, sid);
- if (!error)
- goto boundary_ok;
-
- return error;
- }
- } while_each_thread(g, t);
- read_unlock(&tasklist_lock);
+ goto abort_change;
+
+ /* Only allow single threaded processes to change context */
+ error = -EPERM;
+ if (!is_single_threaded(p)) {
+ error = security_bounded_transition(tsec->sid, sid);
+ if (error)
+ goto abort_change;
}
-boundary_ok:
/* Check permissions for the transition. */
error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
PROCESS__DYNTRANSITION, NULL);
if (error)
- return error;
+ goto abort_change;
/* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and fail. */
+ ptsid = 0;
task_lock(p);
- rcu_read_lock();
tracer = tracehook_tracer_task(p);
- if (tracer != NULL) {
- u32 ptsid = task_sid(tracer);
- rcu_read_unlock();
- error = avc_has_perm_noaudit(ptsid, sid,
- SECCLASS_PROCESS,
- PROCESS__PTRACE, 0, &avd);
- if (!error)
- tsec->sid = sid;
- task_unlock(p);
- avc_audit(ptsid, sid, SECCLASS_PROCESS,
- PROCESS__PTRACE, &avd, error, NULL);
+ if (tracer)
+ ptsid = task_sid(tracer);
+ task_unlock(p);
+
+ if (tracer) {
+ error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
+ PROCESS__PTRACE, NULL);
if (error)
- return error;
- } else {
- rcu_read_unlock();
- tsec->sid = sid;
- task_unlock(p);
+ goto abort_change;
}
- } else
- return -EINVAL;
+ tsec->sid = sid;
+ } else {
+ error = -EINVAL;
+ goto abort_change;
+ }
+
+ commit_creds(new);
return size;
+
+abort_change:
+ abort_creds(new);
+ return error;
}
static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
@@ -5471,23 +5473,21 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
#ifdef CONFIG_KEYS
-static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
+static int selinux_key_alloc(struct key *k, const struct cred *cred,
unsigned long flags)
{
- const struct task_security_struct *__tsec;
+ const struct task_security_struct *tsec;
struct key_security_struct *ksec;
ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
if (!ksec)
return -ENOMEM;
- rcu_read_lock();
- __tsec = __task_cred(tsk)->security;
- if (__tsec->keycreate_sid)
- ksec->sid = __tsec->keycreate_sid;
+ tsec = cred->security;
+ if (tsec->keycreate_sid)
+ ksec->sid = tsec->keycreate_sid;
else
- ksec->sid = __tsec->sid;
- rcu_read_unlock();
+ ksec->sid = tsec->sid;
k->security = ksec;
return 0;
@@ -5502,8 +5502,8 @@ static void selinux_key_free(struct key *k)
}
static int selinux_key_permission(key_ref_t key_ref,
- struct task_struct *ctx,
- key_perm_t perm)
+ const struct cred *cred,
+ key_perm_t perm)
{
struct key *key;
struct key_security_struct *ksec;
@@ -5515,7 +5515,7 @@ static int selinux_key_permission(key_ref_t key_ref,
if (perm == 0)
return 0;
- sid = task_sid(ctx);
+ sid = cred_sid(cred);
key = key_ref_to_ptr(key_ref);
ksec = key->security;
@@ -5545,8 +5545,7 @@ static struct security_operations selinux_ops = {
.ptrace_may_access = selinux_ptrace_may_access,
.ptrace_traceme = selinux_ptrace_traceme,
.capget = selinux_capget,
- .capset_check = selinux_capset_check,
- .capset_set = selinux_capset_set,
+ .capset = selinux_capset,
.sysctl = selinux_sysctl,
.capable = selinux_capable,
.quotactl = selinux_quotactl,
@@ -5621,10 +5620,11 @@ static struct security_operations selinux_ops = {
.dentry_open = selinux_dentry_open,
.task_create = selinux_task_create,
- .cred_alloc_security = selinux_cred_alloc_security,
.cred_free = selinux_cred_free,
+ .cred_prepare = selinux_cred_prepare,
+ .cred_commit = selinux_cred_commit,
.task_setuid = selinux_task_setuid,
- .task_post_setuid = selinux_task_post_setuid,
+ .task_fix_setuid = selinux_task_fix_setuid,
.task_setgid = selinux_task_setgid,
.task_setpgid = selinux_task_setpgid,
.task_getpgid = selinux_task_getpgid,
@@ -5641,7 +5641,6 @@ static struct security_operations selinux_ops = {
.task_kill = selinux_task_kill,
.task_wait = selinux_task_wait,
.task_prctl = selinux_task_prctl,
- .task_reparent_to_init = selinux_task_reparent_to_init,
.task_to_inode = selinux_task_to_inode,
.ipc_permission = selinux_ipc_permission,
@@ -5737,8 +5736,6 @@ static struct security_operations selinux_ops = {
static __init int selinux_init(void)
{
- struct task_security_struct *tsec;
-
if (!security_module_enable(&selinux_ops)) {
selinux_enabled = 0;
return 0;
@@ -5752,10 +5749,7 @@ static __init int selinux_init(void)
printk(KERN_INFO "SELinux: Initializing.\n");
/* Set the security state for the initial task. */
- if (cred_alloc_security(current->cred))
- panic("SELinux: Failed to initialize initial task.\n");
- tsec = current->cred->security;
- tsec->osid = tsec->sid = SECINITSID_KERNEL;
+ cred_init_security();
sel_inode_cache = kmem_cache_create("selinux_inode_security",
sizeof(struct inode_security_struct),
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 11167fd567b..e952b397153 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -104,8 +104,7 @@ static int smack_ptrace_may_access(struct task_struct *ctp, unsigned int mode)
if (rc != 0)
return rc;
- rc = smk_access(current->cred->security, ctp->cred->security,
- MAY_READWRITE);
+ rc = smk_access(current_security(), task_security(ctp), MAY_READWRITE);
if (rc != 0 && capable(CAP_MAC_OVERRIDE))
return 0;
return rc;
@@ -127,8 +126,7 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
if (rc != 0)
return rc;
- rc = smk_access(ptp->cred->security, current->cred->security,
- MAY_READWRITE);
+ rc = smk_access(task_security(ptp), current_security(), MAY_READWRITE);
if (rc != 0 && has_capability(ptp, CAP_MAC_OVERRIDE))
return 0;
return rc;
@@ -977,22 +975,6 @@ static int smack_file_receive(struct file *file)
*/
/**
- * smack_cred_alloc_security - "allocate" a task cred blob
- * @cred: the task creds in need of a blob
- *
- * Smack isn't using copies of blobs. Everyone
- * points to an immutable list. No alloc required.
- * No data copy required.
- *
- * Always returns 0
- */
-static int smack_cred_alloc_security(struct cred *cred)
-{
- cred->security = current_security();
- return 0;
-}
-
-/**
* smack_cred_free - "free" task-level security credentials
* @cred: the credentials in question
*
@@ -1006,6 +988,30 @@ static void smack_cred_free(struct cred *cred)
}
/**
+ * smack_cred_prepare - prepare new set of credentials for modification
+ * @new: the new credentials
+ * @old: the original credentials
+ * @gfp: the atomicity of any memory allocations
+ *
+ * Prepare a new set of credentials for modification.
+ */
+static int smack_cred_prepare(struct cred *new, const struct cred *old,
+ gfp_t gfp)
+{
+ new->security = old->security;
+ return 0;
+}
+
+/*
+ * commit new credentials
+ * @new: the new credentials
+ * @old: the original credentials
+ */
+static void smack_cred_commit(struct cred *new, const struct cred *old)
+{
+}
+
+/**
* smack_task_setpgid - Smack check on setting pgid
* @p: the task object
* @pgid: unused
@@ -2036,6 +2042,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
static int smack_setprocattr(struct task_struct *p, char *name,
void *value, size_t size)
{
+ struct cred *new;
char *newsmack;
/*
@@ -2058,7 +2065,11 @@ static int smack_setprocattr(struct task_struct *p, char *name,
if (newsmack == NULL)
return -EINVAL;
- p->cred->security = newsmack;
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ new->security = newsmack;
+ commit_creds(new);
return size;
}
@@ -2354,17 +2365,17 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
/**
* smack_key_alloc - Set the key security blob
* @key: object
- * @tsk: the task associated with the key
+ * @cred: the credentials to use
* @flags: unused
*
* No allocation required
*
* Returns 0
*/
-static int smack_key_alloc(struct key *key, struct task_struct *tsk,
+static int smack_key_alloc(struct key *key, const struct cred *cred,
unsigned long flags)
{
- key->security = tsk->cred->security;
+ key->security = cred->security;
return 0;
}
@@ -2382,14 +2393,14 @@ static void smack_key_free(struct key *key)
/*
* smack_key_permission - Smack access on a key
* @key_ref: gets to the object
- * @context: task involved
+ * @cred: the credentials to use
* @perm: unused
*
* Return 0 if the task has read and write to the object,
* an error code otherwise
*/
static int smack_key_permission(key_ref_t key_ref,
- struct task_struct *context, key_perm_t perm)
+ const struct cred *cred, key_perm_t perm)
{
struct key *keyp;
@@ -2405,11 +2416,10 @@ static int smack_key_permission(key_ref_t key_ref,
/*
* This should not occur
*/
- if (context->cred->security == NULL)
+ if (cred->security == NULL)
return -EACCES;
- return smk_access(context->cred->security, keyp->security,
- MAY_READWRITE);
+ return smk_access(cred->security, keyp->security, MAY_READWRITE);
}
#endif /* CONFIG_KEYS */
@@ -2580,8 +2590,7 @@ struct security_operations smack_ops = {
.ptrace_may_access = smack_ptrace_may_access,
.ptrace_traceme = smack_ptrace_traceme,
.capget = cap_capget,
- .capset_check = cap_capset_check,
- .capset_set = cap_capset_set,
+ .capset = cap_capset,
.capable = cap_capable,
.syslog = smack_syslog,
.settime = cap_settime,
@@ -2630,9 +2639,10 @@ struct security_operations smack_ops = {
.file_send_sigiotask = smack_file_send_sigiotask,
.file_receive = smack_file_receive,
- .cred_alloc_security = smack_cred_alloc_security,
.cred_free = smack_cred_free,
- .task_post_setuid = cap_task_post_setuid,
+ .cred_prepare = smack_cred_prepare,
+ .cred_commit = smack_cred_commit,
+ .task_fix_setuid = cap_task_fix_setuid,
.task_setpgid = smack_task_setpgid,
.task_getpgid = smack_task_getpgid,
.task_getsid = smack_task_getsid,
@@ -2645,7 +2655,6 @@ struct security_operations smack_ops = {
.task_movememory = smack_task_movememory,
.task_kill = smack_task_kill,
.task_wait = smack_task_wait,
- .task_reparent_to_init = cap_task_reparent_to_init,
.task_to_inode = smack_task_to_inode,
.task_prctl = cap_task_prctl,
@@ -2721,6 +2730,8 @@ struct security_operations smack_ops = {
*/
static __init int smack_init(void)
{
+ struct cred *cred;
+
if (!security_module_enable(&smack_ops))
return 0;
@@ -2729,7 +2740,8 @@ static __init int smack_init(void)
/*
* Set the security state for the initial task.
*/
- current->cred->security = &smack_known_floor.smk_known;
+ cred = (struct cred *) current->cred;
+ cred->security = &smack_known_floor.smk_known;
/*
* Initialize locks