CRED: Add some configurable debugging [try #6]
Add a config option (CONFIG_DEBUG_CREDENTIALS) to turn on some debug checking for credential management. The additional code keeps track of the number of pointers from task_structs to any given cred struct, and checks to see that this number never exceeds the usage count of the cred struct (which includes all references, not just those from task_structs). Furthermore, if SELinux is enabled, the code also checks that the security pointer in the cred struct is never seen to be invalid. This attempts to catch the bug whereby inode_has_perm() faults in an nfsd kernel thread on seeing cred->security be a NULL pointer (it appears that the credential struct has been previously released): http://www.kerneloops.org/oops.php?number=252883 Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: James Morris <jmorris@namei.org>
This commit is contained in:
parent
ed6d76e4c3
commit
e0e817392b
@ -34,6 +34,8 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
|
||||
int flags = nfsexp_flags(rqstp, exp);
|
||||
int ret;
|
||||
|
||||
validate_process_creds();
|
||||
|
||||
/* discard any old override before preparing the new set */
|
||||
revert_creds(get_cred(current->real_cred));
|
||||
new = prepare_creds();
|
||||
@ -86,8 +88,10 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
|
||||
else
|
||||
new->cap_effective = cap_raise_nfsd_set(new->cap_effective,
|
||||
new->cap_permitted);
|
||||
validate_process_creds();
|
||||
put_cred(override_creds(new));
|
||||
put_cred(new);
|
||||
validate_process_creds();
|
||||
return 0;
|
||||
|
||||
oom:
|
||||
|
@ -496,7 +496,9 @@ nfsd(void *vrqstp)
|
||||
/* Lock the export hash tables for reading. */
|
||||
exp_readlock();
|
||||
|
||||
validate_process_creds();
|
||||
svc_process(rqstp);
|
||||
validate_process_creds();
|
||||
|
||||
/* Unlock export hash tables */
|
||||
exp_readunlock();
|
||||
|
@ -684,6 +684,8 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
|
||||
__be32 err;
|
||||
int host_err;
|
||||
|
||||
validate_process_creds();
|
||||
|
||||
/*
|
||||
* If we get here, then the client has already done an "open",
|
||||
* and (hopefully) checked permission - so allow OWNER_OVERRIDE
|
||||
@ -740,6 +742,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
|
||||
out_nfserr:
|
||||
err = nfserrno(host_err);
|
||||
out:
|
||||
validate_process_creds();
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -959,6 +959,8 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags,
|
||||
int error;
|
||||
struct file *f;
|
||||
|
||||
validate_creds(cred);
|
||||
|
||||
/*
|
||||
* We must always pass in a valid mount pointer. Historically
|
||||
* callers got away with not passing it, but we must enforce this at
|
||||
|
@ -114,6 +114,13 @@ struct thread_group_cred {
|
||||
*/
|
||||
struct cred {
|
||||
atomic_t usage;
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
atomic_t subscribers; /* number of processes subscribed */
|
||||
void *put_addr;
|
||||
unsigned magic;
|
||||
#define CRED_MAGIC 0x43736564
|
||||
#define CRED_MAGIC_DEAD 0x44656144
|
||||
#endif
|
||||
uid_t uid; /* real UID of the task */
|
||||
gid_t gid; /* real GID of the task */
|
||||
uid_t suid; /* saved UID of the task */
|
||||
@ -143,6 +150,7 @@ struct cred {
|
||||
};
|
||||
|
||||
extern void __put_cred(struct cred *);
|
||||
extern void exit_creds(struct task_struct *);
|
||||
extern int copy_creds(struct task_struct *, unsigned long);
|
||||
extern struct cred *prepare_creds(void);
|
||||
extern struct cred *prepare_exec_creds(void);
|
||||
@ -158,6 +166,60 @@ extern int set_security_override_from_ctx(struct cred *, const char *);
|
||||
extern int set_create_files_as(struct cred *, struct inode *);
|
||||
extern void __init cred_init(void);
|
||||
|
||||
/*
|
||||
* check for validity of credentials
|
||||
*/
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
extern void __invalid_creds(const struct cred *, const char *, unsigned);
|
||||
extern void __validate_process_creds(struct task_struct *,
|
||||
const char *, unsigned);
|
||||
|
||||
static inline bool creds_are_invalid(const struct cred *cred)
|
||||
{
|
||||
if (cred->magic != CRED_MAGIC)
|
||||
return true;
|
||||
if (atomic_read(&cred->usage) < atomic_read(&cred->subscribers))
|
||||
return true;
|
||||
#ifdef CONFIG_SECURITY_SELINUX
|
||||
if ((unsigned long) cred->security < PAGE_SIZE)
|
||||
return true;
|
||||
if ((*(u32*)cred->security & 0xffffff00) ==
|
||||
(POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8))
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void __validate_creds(const struct cred *cred,
|
||||
const char *file, unsigned line)
|
||||
{
|
||||
if (unlikely(creds_are_invalid(cred)))
|
||||
__invalid_creds(cred, file, line);
|
||||
}
|
||||
|
||||
#define validate_creds(cred) \
|
||||
do { \
|
||||
__validate_creds((cred), __FILE__, __LINE__); \
|
||||
} while(0)
|
||||
|
||||
#define validate_process_creds() \
|
||||
do { \
|
||||
__validate_process_creds(current, __FILE__, __LINE__); \
|
||||
} while(0)
|
||||
|
||||
extern void validate_creds_for_do_exit(struct task_struct *);
|
||||
#else
|
||||
static inline void validate_creds(const struct cred *cred)
|
||||
{
|
||||
}
|
||||
static inline void validate_creds_for_do_exit(struct task_struct *tsk)
|
||||
{
|
||||
}
|
||||
static inline void validate_process_creds(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* get_new_cred - Get a reference on a new set of credentials
|
||||
* @cred: The new credentials to reference
|
||||
@ -187,6 +249,7 @@ static inline struct cred *get_new_cred(struct cred *cred)
|
||||
static inline const struct cred *get_cred(const struct cred *cred)
|
||||
{
|
||||
struct cred *nonconst_cred = (struct cred *) cred;
|
||||
validate_creds(cred);
|
||||
return get_new_cred(nonconst_cred);
|
||||
}
|
||||
|
||||
@ -205,7 +268,7 @@ static inline void put_cred(const struct cred *_cred)
|
||||
{
|
||||
struct cred *cred = (struct cred *) _cred;
|
||||
|
||||
BUG_ON(atomic_read(&(cred)->usage) <= 0);
|
||||
validate_creds(cred);
|
||||
if (atomic_dec_and_test(&(cred)->usage))
|
||||
__put_cred(cred);
|
||||
}
|
||||
|
250
kernel/cred.c
250
kernel/cred.c
@ -18,6 +18,18 @@
|
||||
#include <linux/cn_proc.h>
|
||||
#include "cred-internals.h"
|
||||
|
||||
#if 0
|
||||
#define kdebug(FMT, ...) \
|
||||
printk("[%-5.5s%5u] "FMT"\n", current->comm, current->pid ,##__VA_ARGS__)
|
||||
#else
|
||||
static inline __attribute__((format(printf, 1, 2)))
|
||||
void no_printk(const char *fmt, ...)
|
||||
{
|
||||
}
|
||||
#define kdebug(FMT, ...) \
|
||||
no_printk("[%-5.5s%5u] "FMT"\n", current->comm, current->pid ,##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
static struct kmem_cache *cred_jar;
|
||||
|
||||
/*
|
||||
@ -36,6 +48,10 @@ static struct thread_group_cred init_tgcred = {
|
||||
*/
|
||||
struct cred init_cred = {
|
||||
.usage = ATOMIC_INIT(4),
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
.subscribers = ATOMIC_INIT(2),
|
||||
.magic = CRED_MAGIC,
|
||||
#endif
|
||||
.securebits = SECUREBITS_DEFAULT,
|
||||
.cap_inheritable = CAP_INIT_INH_SET,
|
||||
.cap_permitted = CAP_FULL_SET,
|
||||
@ -48,6 +64,31 @@ struct cred init_cred = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline void set_cred_subscribers(struct cred *cred, int n)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
atomic_set(&cred->subscribers, n);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int read_cred_subscribers(const struct cred *cred)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
return atomic_read(&cred->subscribers);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void alter_cred_subscribers(const struct cred *_cred, int n)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
struct cred *cred = (struct cred *) _cred;
|
||||
|
||||
atomic_add(n, &cred->subscribers);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Dispose of the shared task group credentials
|
||||
*/
|
||||
@ -85,9 +126,22 @@ static void put_cred_rcu(struct rcu_head *rcu)
|
||||
{
|
||||
struct cred *cred = container_of(rcu, struct cred, rcu);
|
||||
|
||||
kdebug("put_cred_rcu(%p)", cred);
|
||||
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
if (cred->magic != CRED_MAGIC_DEAD ||
|
||||
atomic_read(&cred->usage) != 0 ||
|
||||
read_cred_subscribers(cred) != 0)
|
||||
panic("CRED: put_cred_rcu() sees %p with"
|
||||
" mag %x, put %p, usage %d, subscr %d\n",
|
||||
cred, cred->magic, cred->put_addr,
|
||||
atomic_read(&cred->usage),
|
||||
read_cred_subscribers(cred));
|
||||
#else
|
||||
if (atomic_read(&cred->usage) != 0)
|
||||
panic("CRED: put_cred_rcu() sees %p with usage %d\n",
|
||||
cred, atomic_read(&cred->usage));
|
||||
#endif
|
||||
|
||||
security_cred_free(cred);
|
||||
key_put(cred->thread_keyring);
|
||||
@ -106,12 +160,47 @@ static void put_cred_rcu(struct rcu_head *rcu)
|
||||
*/
|
||||
void __put_cred(struct cred *cred)
|
||||
{
|
||||
kdebug("__put_cred(%p{%d,%d})", cred,
|
||||
atomic_read(&cred->usage),
|
||||
read_cred_subscribers(cred));
|
||||
|
||||
BUG_ON(atomic_read(&cred->usage) != 0);
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
BUG_ON(read_cred_subscribers(cred) != 0);
|
||||
cred->magic = CRED_MAGIC_DEAD;
|
||||
cred->put_addr = __builtin_return_address(0);
|
||||
#endif
|
||||
BUG_ON(cred == current->cred);
|
||||
BUG_ON(cred == current->real_cred);
|
||||
|
||||
call_rcu(&cred->rcu, put_cred_rcu);
|
||||
}
|
||||
EXPORT_SYMBOL(__put_cred);
|
||||
|
||||
/*
|
||||
* Clean up a task's credentials when it exits
|
||||
*/
|
||||
void exit_creds(struct task_struct *tsk)
|
||||
{
|
||||
struct cred *cred;
|
||||
|
||||
kdebug("exit_creds(%u,%p,%p,{%d,%d})", tsk->pid, tsk->real_cred, tsk->cred,
|
||||
atomic_read(&tsk->cred->usage),
|
||||
read_cred_subscribers(tsk->cred));
|
||||
|
||||
cred = (struct cred *) tsk->real_cred;
|
||||
tsk->real_cred = NULL;
|
||||
validate_creds(cred);
|
||||
alter_cred_subscribers(cred, -1);
|
||||
put_cred(cred);
|
||||
|
||||
cred = (struct cred *) tsk->cred;
|
||||
tsk->cred = NULL;
|
||||
validate_creds(cred);
|
||||
alter_cred_subscribers(cred, -1);
|
||||
put_cred(cred);
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare_creds - Prepare a new set of credentials for modification
|
||||
*
|
||||
@ -132,16 +221,19 @@ struct cred *prepare_creds(void)
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
|
||||
BUG_ON(atomic_read(&task->real_cred->usage) < 1);
|
||||
validate_process_creds();
|
||||
|
||||
new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
kdebug("prepare_creds() alloc %p", new);
|
||||
|
||||
old = task->cred;
|
||||
memcpy(new, old, sizeof(struct cred));
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
set_cred_subscribers(new, 0);
|
||||
get_group_info(new->group_info);
|
||||
get_uid(new->user);
|
||||
|
||||
@ -157,6 +249,7 @@ struct cred *prepare_creds(void)
|
||||
|
||||
if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
|
||||
goto error;
|
||||
validate_creds(new);
|
||||
return new;
|
||||
|
||||
error:
|
||||
@ -229,9 +322,12 @@ struct cred *prepare_usermodehelper_creds(void)
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
kdebug("prepare_usermodehelper_creds() alloc %p", new);
|
||||
|
||||
memcpy(new, &init_cred, sizeof(struct cred));
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
set_cred_subscribers(new, 0);
|
||||
get_group_info(new->group_info);
|
||||
get_uid(new->user);
|
||||
|
||||
@ -250,6 +346,7 @@ struct cred *prepare_usermodehelper_creds(void)
|
||||
#endif
|
||||
if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0)
|
||||
goto error;
|
||||
validate_creds(new);
|
||||
|
||||
BUG_ON(atomic_read(&new->usage) != 1);
|
||||
return new;
|
||||
@ -286,6 +383,10 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
|
||||
) {
|
||||
p->real_cred = get_cred(p->cred);
|
||||
get_cred(p->cred);
|
||||
alter_cred_subscribers(p->cred, 2);
|
||||
kdebug("share_creds(%p{%d,%d})",
|
||||
p->cred, atomic_read(&p->cred->usage),
|
||||
read_cred_subscribers(p->cred));
|
||||
atomic_inc(&p->cred->user->processes);
|
||||
return 0;
|
||||
}
|
||||
@ -331,6 +432,8 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
|
||||
|
||||
atomic_inc(&new->user->processes);
|
||||
p->cred = p->real_cred = get_cred(new);
|
||||
alter_cred_subscribers(new, 2);
|
||||
validate_creds(new);
|
||||
return 0;
|
||||
|
||||
error_put:
|
||||
@ -355,13 +458,20 @@ error_put:
|
||||
int commit_creds(struct cred *new)
|
||||
{
|
||||
struct task_struct *task = current;
|
||||
const struct cred *old;
|
||||
const struct cred *old = task->real_cred;
|
||||
|
||||
BUG_ON(task->cred != task->real_cred);
|
||||
BUG_ON(atomic_read(&task->real_cred->usage) < 2);
|
||||
kdebug("commit_creds(%p{%d,%d})", new,
|
||||
atomic_read(&new->usage),
|
||||
read_cred_subscribers(new));
|
||||
|
||||
BUG_ON(task->cred != old);
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
BUG_ON(read_cred_subscribers(old) < 2);
|
||||
validate_creds(old);
|
||||
validate_creds(new);
|
||||
#endif
|
||||
BUG_ON(atomic_read(&new->usage) < 1);
|
||||
|
||||
old = task->real_cred;
|
||||
security_commit_creds(new, old);
|
||||
|
||||
get_cred(new); /* we will require a ref for the subj creds too */
|
||||
@ -390,12 +500,14 @@ int commit_creds(struct cred *new)
|
||||
* cheaply with the new uid cache, so if it matters
|
||||
* we should be checking for it. -DaveM
|
||||
*/
|
||||
alter_cred_subscribers(new, 2);
|
||||
if (new->user != old->user)
|
||||
atomic_inc(&new->user->processes);
|
||||
rcu_assign_pointer(task->real_cred, new);
|
||||
rcu_assign_pointer(task->cred, new);
|
||||
if (new->user != old->user)
|
||||
atomic_dec(&old->user->processes);
|
||||
alter_cred_subscribers(old, -2);
|
||||
|
||||
sched_switch_user(task);
|
||||
|
||||
@ -428,6 +540,13 @@ EXPORT_SYMBOL(commit_creds);
|
||||
*/
|
||||
void abort_creds(struct cred *new)
|
||||
{
|
||||
kdebug("abort_creds(%p{%d,%d})", new,
|
||||
atomic_read(&new->usage),
|
||||
read_cred_subscribers(new));
|
||||
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
BUG_ON(read_cred_subscribers(new) != 0);
|
||||
#endif
|
||||
BUG_ON(atomic_read(&new->usage) < 1);
|
||||
put_cred(new);
|
||||
}
|
||||
@ -444,7 +563,20 @@ const struct cred *override_creds(const struct cred *new)
|
||||
{
|
||||
const struct cred *old = current->cred;
|
||||
|
||||
rcu_assign_pointer(current->cred, get_cred(new));
|
||||
kdebug("override_creds(%p{%d,%d})", new,
|
||||
atomic_read(&new->usage),
|
||||
read_cred_subscribers(new));
|
||||
|
||||
validate_creds(old);
|
||||
validate_creds(new);
|
||||
get_cred(new);
|
||||
alter_cred_subscribers(new, 1);
|
||||
rcu_assign_pointer(current->cred, new);
|
||||
alter_cred_subscribers(old, -1);
|
||||
|
||||
kdebug("override_creds() = %p{%d,%d}", old,
|
||||
atomic_read(&old->usage),
|
||||
read_cred_subscribers(old));
|
||||
return old;
|
||||
}
|
||||
EXPORT_SYMBOL(override_creds);
|
||||
@ -460,7 +592,15 @@ void revert_creds(const struct cred *old)
|
||||
{
|
||||
const struct cred *override = current->cred;
|
||||
|
||||
kdebug("revert_creds(%p{%d,%d})", old,
|
||||
atomic_read(&old->usage),
|
||||
read_cred_subscribers(old));
|
||||
|
||||
validate_creds(old);
|
||||
validate_creds(override);
|
||||
alter_cred_subscribers(old, 1);
|
||||
rcu_assign_pointer(current->cred, old);
|
||||
alter_cred_subscribers(override, -1);
|
||||
put_cred(override);
|
||||
}
|
||||
EXPORT_SYMBOL(revert_creds);
|
||||
@ -502,11 +642,15 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
kdebug("prepare_kernel_cred() alloc %p", new);
|
||||
|
||||
if (daemon)
|
||||
old = get_task_cred(daemon);
|
||||
else
|
||||
old = get_cred(&init_cred);
|
||||
|
||||
validate_creds(old);
|
||||
|
||||
*new = *old;
|
||||
get_uid(new->user);
|
||||
get_group_info(new->group_info);
|
||||
@ -526,7 +670,9 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
|
||||
goto error;
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
set_cred_subscribers(new, 0);
|
||||
put_cred(old);
|
||||
validate_creds(new);
|
||||
return new;
|
||||
|
||||
error:
|
||||
@ -589,3 +735,95 @@ int set_create_files_as(struct cred *new, struct inode *inode)
|
||||
return security_kernel_create_files_as(new, inode);
|
||||
}
|
||||
EXPORT_SYMBOL(set_create_files_as);
|
||||
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
|
||||
/*
|
||||
* dump invalid credentials
|
||||
*/
|
||||
static void dump_invalid_creds(const struct cred *cred, const char *label,
|
||||
const struct task_struct *tsk)
|
||||
{
|
||||
printk(KERN_ERR "CRED: %s credentials: %p %s%s%s\n",
|
||||
label, cred,
|
||||
cred == &init_cred ? "[init]" : "",
|
||||
cred == tsk->real_cred ? "[real]" : "",
|
||||
cred == tsk->cred ? "[eff]" : "");
|
||||
printk(KERN_ERR "CRED: ->magic=%x, put_addr=%p\n",
|
||||
cred->magic, cred->put_addr);
|
||||
printk(KERN_ERR "CRED: ->usage=%d, subscr=%d\n",
|
||||
atomic_read(&cred->usage),
|
||||
read_cred_subscribers(cred));
|
||||
printk(KERN_ERR "CRED: ->*uid = { %d,%d,%d,%d }\n",
|
||||
cred->uid, cred->euid, cred->suid, cred->fsuid);
|
||||
printk(KERN_ERR "CRED: ->*gid = { %d,%d,%d,%d }\n",
|
||||
cred->gid, cred->egid, cred->sgid, cred->fsgid);
|
||||
#ifdef CONFIG_SECURITY
|
||||
printk(KERN_ERR "CRED: ->security is %p\n", cred->security);
|
||||
if ((unsigned long) cred->security >= PAGE_SIZE &&
|
||||
(((unsigned long) cred->security & 0xffffff00) !=
|
||||
(POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8)))
|
||||
printk(KERN_ERR "CRED: ->security {%x, %x}\n",
|
||||
((u32*)cred->security)[0],
|
||||
((u32*)cred->security)[1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* report use of invalid credentials
|
||||
*/
|
||||
void __invalid_creds(const struct cred *cred, const char *file, unsigned line)
|
||||
{
|
||||
printk(KERN_ERR "CRED: Invalid credentials\n");
|
||||
printk(KERN_ERR "CRED: At %s:%u\n", file, line);
|
||||
dump_invalid_creds(cred, "Specified", current);
|
||||
BUG();
|
||||
}
|
||||
EXPORT_SYMBOL(__invalid_creds);
|
||||
|
||||
/*
|
||||
* check the credentials on a process
|
||||
*/
|
||||
void __validate_process_creds(struct task_struct *tsk,
|
||||
const char *file, unsigned line)
|
||||
{
|
||||
if (tsk->cred == tsk->real_cred) {
|
||||
if (unlikely(read_cred_subscribers(tsk->cred) < 2 ||
|
||||
creds_are_invalid(tsk->cred)))
|
||||
goto invalid_creds;
|
||||
} else {
|
||||
if (unlikely(read_cred_subscribers(tsk->real_cred) < 1 ||
|
||||
read_cred_subscribers(tsk->cred) < 1 ||
|
||||
creds_are_invalid(tsk->real_cred) ||
|
||||
creds_are_invalid(tsk->cred)))
|
||||
goto invalid_creds;
|
||||
}
|
||||
return;
|
||||
|
||||
invalid_creds:
|
||||
printk(KERN_ERR "CRED: Invalid process credentials\n");
|
||||
printk(KERN_ERR "CRED: At %s:%u\n", file, line);
|
||||
|
||||
dump_invalid_creds(tsk->real_cred, "Real", tsk);
|
||||
if (tsk->cred != tsk->real_cred)
|
||||
dump_invalid_creds(tsk->cred, "Effective", tsk);
|
||||
else
|
||||
printk(KERN_ERR "CRED: Effective creds == Real creds\n");
|
||||
BUG();
|
||||
}
|
||||
EXPORT_SYMBOL(__validate_process_creds);
|
||||
|
||||
/*
|
||||
* check creds for do_exit()
|
||||
*/
|
||||
void validate_creds_for_do_exit(struct task_struct *tsk)
|
||||
{
|
||||
kdebug("validate_creds_for_do_exit(%p,%p{%d,%d})",
|
||||
tsk->real_cred, tsk->cred,
|
||||
atomic_read(&tsk->cred->usage),
|
||||
read_cred_subscribers(tsk->cred));
|
||||
|
||||
__validate_process_creds(tsk, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DEBUG_CREDENTIALS */
|
||||
|
@ -901,6 +901,8 @@ NORET_TYPE void do_exit(long code)
|
||||
|
||||
tracehook_report_exit(&code);
|
||||
|
||||
validate_creds_for_do_exit(tsk);
|
||||
|
||||
/*
|
||||
* We're taking recursive faults here in do_exit. Safest is to just
|
||||
* leave this task alone and wait for reboot.
|
||||
@ -1009,6 +1011,8 @@ NORET_TYPE void do_exit(long code)
|
||||
if (tsk->splice_pipe)
|
||||
__free_pipe_info(tsk->splice_pipe);
|
||||
|
||||
validate_creds_for_do_exit(tsk);
|
||||
|
||||
preempt_disable();
|
||||
/* causes final put_task_struct in finish_task_switch(). */
|
||||
tsk->state = TASK_DEAD;
|
||||
|
@ -152,8 +152,7 @@ void __put_task_struct(struct task_struct *tsk)
|
||||
WARN_ON(atomic_read(&tsk->usage));
|
||||
WARN_ON(tsk == current);
|
||||
|
||||
put_cred(tsk->real_cred);
|
||||
put_cred(tsk->cred);
|
||||
exit_creds(tsk);
|
||||
delayacct_tsk_free(tsk);
|
||||
|
||||
if (!profile_handoff_task(tsk))
|
||||
@ -1307,8 +1306,7 @@ bad_fork_cleanup_put_domain:
|
||||
module_put(task_thread_info(p)->exec_domain->module);
|
||||
bad_fork_cleanup_count:
|
||||
atomic_dec(&p->cred->user->processes);
|
||||
put_cred(p->real_cred);
|
||||
put_cred(p->cred);
|
||||
exit_creds(p);
|
||||
bad_fork_free:
|
||||
free_task(p);
|
||||
fork_out:
|
||||
|
@ -466,6 +466,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
|
||||
int retval = 0;
|
||||
|
||||
BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
|
||||
validate_creds(sub_info->cred);
|
||||
|
||||
helper_lock();
|
||||
if (sub_info->path[0] == '\0')
|
||||
|
@ -653,6 +653,21 @@ config DEBUG_NOTIFIERS
|
||||
This is a relatively cheap check but if you care about maximum
|
||||
performance, say N.
|
||||
|
||||
config DEBUG_CREDENTIALS
|
||||
bool "Debug credential management"
|
||||
depends on DEBUG_KERNEL
|
||||
help
|
||||
Enable this to turn on some debug checking for credential
|
||||
management. The additional code keeps track of the number of
|
||||
pointers from task_structs to any given cred struct, and checks to
|
||||
see that this number never exceeds the usage count of the cred
|
||||
struct.
|
||||
|
||||
Furthermore, if SELinux is enabled, this also checks that the
|
||||
security pointer in the cred struct is never seen to be invalid.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
#
|
||||
# Select this config option from the architecture Kconfig, if it
|
||||
# it is preferred to always offer frame pointers as a config
|
||||
|
@ -1531,6 +1531,8 @@ static int inode_has_perm(const struct cred *cred,
|
||||
struct common_audit_data ad;
|
||||
u32 sid;
|
||||
|
||||
validate_creds(cred);
|
||||
|
||||
if (unlikely(IS_PRIVATE(inode)))
|
||||
return 0;
|
||||
|
||||
@ -3236,7 +3238,9 @@ static int selinux_task_create(unsigned long clone_flags)
|
||||
static void selinux_cred_free(struct cred *cred)
|
||||
{
|
||||
struct task_security_struct *tsec = cred->security;
|
||||
cred->security = NULL;
|
||||
|
||||
BUG_ON((unsigned long) cred->security < PAGE_SIZE);
|
||||
cred->security = (void *) 0x7UL;
|
||||
kfree(tsec);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user