Skip to content

Commit

Permalink
CRED: Add some configurable debugging [try microsoft#6]
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
dhowells authored and James Morris committed Sep 2, 2009
1 parent ed6d76e commit e0e8173
Show file tree
Hide file tree
Showing 11 changed files with 346 additions and 12 deletions.
4 changes: 4 additions & 0 deletions fs/nfsd/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions fs/nfsd/nfssvc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions fs/nfsd/vfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}

Expand Down
2 changes: 2 additions & 0 deletions fs/open.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
65 changes: 64 additions & 1 deletion include/linux/cred.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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);
}

Expand All @@ -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);
}
Expand Down
Loading

0 comments on commit e0e8173

Please sign in to comment.