aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2009-09-02 04:13:40 -0400
committerJames Morris <jmorris@namei.org>2009-09-02 07:29:01 -0400
commite0e817392b9acf2c98d3be80c233dddb1b52003d (patch)
treeee680c020039313c9f9c40ab3542bb30a7363381
parented6d76e4c32de0c2ad5f1d572b948ef49e465176 (diff)
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>
-rw-r--r--fs/nfsd/auth.c4
-rw-r--r--fs/nfsd/nfssvc.c2
-rw-r--r--fs/nfsd/vfs.c3
-rw-r--r--fs/open.c2
-rw-r--r--include/linux/cred.h65
-rw-r--r--kernel/cred.c250
-rw-r--r--kernel/exit.c4
-rw-r--r--kernel/fork.c6
-rw-r--r--kernel/kmod.c1
-rw-r--r--lib/Kconfig.debug15
-rw-r--r--security/selinux/hooks.c6
11 files changed, 346 insertions, 12 deletions
diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c
index 5573508f707f..36fcabbf5186 100644
--- a/fs/nfsd/auth.c
+++ b/fs/nfsd/auth.c
@@ -34,6 +34,8 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
34 int flags = nfsexp_flags(rqstp, exp); 34 int flags = nfsexp_flags(rqstp, exp);
35 int ret; 35 int ret;
36 36
37 validate_process_creds();
38
37 /* discard any old override before preparing the new set */ 39 /* discard any old override before preparing the new set */
38 revert_creds(get_cred(current->real_cred)); 40 revert_creds(get_cred(current->real_cred));
39 new = prepare_creds(); 41 new = prepare_creds();
@@ -86,8 +88,10 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
86 else 88 else
87 new->cap_effective = cap_raise_nfsd_set(new->cap_effective, 89 new->cap_effective = cap_raise_nfsd_set(new->cap_effective,
88 new->cap_permitted); 90 new->cap_permitted);
91 validate_process_creds();
89 put_cred(override_creds(new)); 92 put_cred(override_creds(new));
90 put_cred(new); 93 put_cred(new);
94 validate_process_creds();
91 return 0; 95 return 0;
92 96
93oom: 97oom:
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 492c79b7800b..24d58adfe5fd 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -496,7 +496,9 @@ nfsd(void *vrqstp)
496 /* Lock the export hash tables for reading. */ 496 /* Lock the export hash tables for reading. */
497 exp_readlock(); 497 exp_readlock();
498 498
499 validate_process_creds();
499 svc_process(rqstp); 500 svc_process(rqstp);
501 validate_process_creds();
500 502
501 /* Unlock export hash tables */ 503 /* Unlock export hash tables */
502 exp_readunlock(); 504 exp_readunlock();
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 23341c1063bc..8fa09bfbcba7 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -684,6 +684,8 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
684 __be32 err; 684 __be32 err;
685 int host_err; 685 int host_err;
686 686
687 validate_process_creds();
688
687 /* 689 /*
688 * If we get here, then the client has already done an "open", 690 * If we get here, then the client has already done an "open",
689 * and (hopefully) checked permission - so allow OWNER_OVERRIDE 691 * and (hopefully) checked permission - so allow OWNER_OVERRIDE
@@ -740,6 +742,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
740out_nfserr: 742out_nfserr:
741 err = nfserrno(host_err); 743 err = nfserrno(host_err);
742out: 744out:
745 validate_process_creds();
743 return err; 746 return err;
744} 747}
745 748
diff --git a/fs/open.c b/fs/open.c
index 40d1fa25f5aa..31191bf513e4 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -959,6 +959,8 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags,
959 int error; 959 int error;
960 struct file *f; 960 struct file *f;
961 961
962 validate_creds(cred);
963
962 /* 964 /*
963 * We must always pass in a valid mount pointer. Historically 965 * We must always pass in a valid mount pointer. Historically
964 * callers got away with not passing it, but we must enforce this at 966 * callers got away with not passing it, but we must enforce this at
diff --git a/include/linux/cred.h b/include/linux/cred.h
index b3c76e815d66..85439abdbc80 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -114,6 +114,13 @@ struct thread_group_cred {
114 */ 114 */
115struct cred { 115struct cred {
116 atomic_t usage; 116 atomic_t usage;
117#ifdef CONFIG_DEBUG_CREDENTIALS
118 atomic_t subscribers; /* number of processes subscribed */
119 void *put_addr;
120 unsigned magic;
121#define CRED_MAGIC 0x43736564
122#define CRED_MAGIC_DEAD 0x44656144
123#endif
117 uid_t uid; /* real UID of the task */ 124 uid_t uid; /* real UID of the task */
118 gid_t gid; /* real GID of the task */ 125 gid_t gid; /* real GID of the task */
119 uid_t suid; /* saved UID of the task */ 126 uid_t suid; /* saved UID of the task */
@@ -143,6 +150,7 @@ struct cred {
143}; 150};
144 151
145extern void __put_cred(struct cred *); 152extern void __put_cred(struct cred *);
153extern void exit_creds(struct task_struct *);
146extern int copy_creds(struct task_struct *, unsigned long); 154extern int copy_creds(struct task_struct *, unsigned long);
147extern struct cred *prepare_creds(void); 155extern struct cred *prepare_creds(void);
148extern struct cred *prepare_exec_creds(void); 156extern struct cred *prepare_exec_creds(void);
@@ -158,6 +166,60 @@ extern int set_security_override_from_ctx(struct cred *, const char *);
158extern int set_create_files_as(struct cred *, struct inode *); 166extern int set_create_files_as(struct cred *, struct inode *);
159extern void __init cred_init(void); 167extern void __init cred_init(void);
160 168
169/*
170 * check for validity of credentials
171 */
172#ifdef CONFIG_DEBUG_CREDENTIALS
173extern void __invalid_creds(const struct cred *, const char *, unsigned);
174extern void __validate_process_creds(struct task_struct *,
175 const char *, unsigned);
176
177static inline bool creds_are_invalid(const struct cred *cred)
178{
179 if (cred->magic != CRED_MAGIC)
180 return true;
181 if (atomic_read(&cred->usage) < atomic_read(&cred->subscribers))
182 return true;
183#ifdef CONFIG_SECURITY_SELINUX
184 if ((unsigned long) cred->security < PAGE_SIZE)
185 return true;
186 if ((*(u32*)cred->security & 0xffffff00) ==
187 (POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8))
188 return true;
189#endif
190 return false;
191}
192
193static inline void __validate_creds(const struct cred *cred,
194 const char *file, unsigned line)
195{
196 if (unlikely(creds_are_invalid(cred)))
197 __invalid_creds(cred, file, line);
198}
199
200#define validate_creds(cred) \
201do { \
202 __validate_creds((cred), __FILE__, __LINE__); \
203} while(0)
204
205#define validate_process_creds() \
206do { \
207 __validate_process_creds(current, __FILE__, __LINE__); \
208} while(0)
209
210extern void validate_creds_for_do_exit(struct task_struct *);
211#else
212static inline void validate_creds(const struct cred *cred)
213{
214}
215static inline void validate_creds_for_do_exit(struct task_struct *tsk)
216{
217}
218static inline void validate_process_creds(void)
219{
220}
221#endif
222
161/** 223/**
162 * get_new_cred - Get a reference on a new set of credentials 224 * get_new_cred - Get a reference on a new set of credentials
163 * @cred: The new credentials to reference 225 * @cred: The new credentials to reference
@@ -187,6 +249,7 @@ static inline struct cred *get_new_cred(struct cred *cred)
187static inline const struct cred *get_cred(const struct cred *cred) 249static inline const struct cred *get_cred(const struct cred *cred)
188{ 250{
189 struct cred *nonconst_cred = (struct cred *) cred; 251 struct cred *nonconst_cred = (struct cred *) cred;
252 validate_creds(cred);
190 return get_new_cred(nonconst_cred); 253 return get_new_cred(nonconst_cred);
191} 254}
192 255
@@ -205,7 +268,7 @@ static inline void put_cred(const struct cred *_cred)
205{ 268{
206 struct cred *cred = (struct cred *) _cred; 269 struct cred *cred = (struct cred *) _cred;
207 270
208 BUG_ON(atomic_read(&(cred)->usage) <= 0); 271 validate_creds(cred);
209 if (atomic_dec_and_test(&(cred)->usage)) 272 if (atomic_dec_and_test(&(cred)->usage))
210 __put_cred(cred); 273 __put_cred(cred);
211} 274}
diff --git a/kernel/cred.c b/kernel/cred.c
index 1bb4d7e5d616..24dd2f5104b1 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -18,6 +18,18 @@
18#include <linux/cn_proc.h> 18#include <linux/cn_proc.h>
19#include "cred-internals.h" 19#include "cred-internals.h"
20 20
21#if 0
22#define kdebug(FMT, ...) \
23 printk("[%-5.5s%5u] "FMT"\n", current->comm, current->pid ,##__VA_ARGS__)
24#else
25static inline __attribute__((format(printf, 1, 2)))
26void no_printk(const char *fmt, ...)
27{
28}
29#define kdebug(FMT, ...) \
30 no_printk("[%-5.5s%5u] "FMT"\n", current->comm, current->pid ,##__VA_ARGS__)
31#endif
32
21static struct kmem_cache *cred_jar; 33static struct kmem_cache *cred_jar;
22 34
23/* 35/*
@@ -36,6 +48,10 @@ static struct thread_group_cred init_tgcred = {
36 */ 48 */
37struct cred init_cred = { 49struct cred init_cred = {
38 .usage = ATOMIC_INIT(4), 50 .usage = ATOMIC_INIT(4),
51#ifdef CONFIG_DEBUG_CREDENTIALS
52 .subscribers = ATOMIC_INIT(2),
53 .magic = CRED_MAGIC,
54#endif
39 .securebits = SECUREBITS_DEFAULT, 55 .securebits = SECUREBITS_DEFAULT,
40 .cap_inheritable = CAP_INIT_INH_SET, 56 .cap_inheritable = CAP_INIT_INH_SET,
41 .cap_permitted = CAP_FULL_SET, 57 .cap_permitted = CAP_FULL_SET,
@@ -48,6 +64,31 @@ struct cred init_cred = {
48#endif 64#endif
49}; 65};
50 66
67static inline void set_cred_subscribers(struct cred *cred, int n)
68{
69#ifdef CONFIG_DEBUG_CREDENTIALS
70 atomic_set(&cred->subscribers, n);
71#endif
72}
73
74static inline int read_cred_subscribers(const struct cred *cred)
75{
76#ifdef CONFIG_DEBUG_CREDENTIALS
77 return atomic_read(&cred->subscribers);
78#else
79 return 0;
80#endif
81}
82
83static inline void alter_cred_subscribers(const struct cred *_cred, int n)
84{
85#ifdef CONFIG_DEBUG_CREDENTIALS
86 struct cred *cred = (struct cred *) _cred;
87
88 atomic_add(n, &cred->subscribers);
89#endif
90}
91
51/* 92/*
52 * Dispose of the shared task group credentials 93 * Dispose of the shared task group credentials
53 */ 94 */
@@ -85,9 +126,22 @@ static void put_cred_rcu(struct rcu_head *rcu)
85{ 126{
86 struct cred *cred = container_of(rcu, struct cred, rcu); 127 struct cred *cred = container_of(rcu, struct cred, rcu);
87 128
129 kdebug("put_cred_rcu(%p)", cred);
130
131#ifdef CONFIG_DEBUG_CREDENTIALS
132 if (cred->magic != CRED_MAGIC_DEAD ||
133 atomic_read(&cred->usage) != 0 ||
134 read_cred_subscribers(cred) != 0)
135 panic("CRED: put_cred_rcu() sees %p with"
136 " mag %x, put %p, usage %d, subscr %d\n",
137 cred, cred->magic, cred->put_addr,
138 atomic_read(&cred->usage),
139 read_cred_subscribers(cred));
140#else
88 if (atomic_read(&cred->usage) != 0) 141 if (atomic_read(&cred->usage) != 0)
89 panic("CRED: put_cred_rcu() sees %p with usage %d\n", 142 panic("CRED: put_cred_rcu() sees %p with usage %d\n",
90 cred, atomic_read(&cred->usage)); 143 cred, atomic_read(&cred->usage));
144#endif
91 145
92 security_cred_free(cred); 146 security_cred_free(cred);
93 key_put(cred->thread_keyring); 147 key_put(cred->thread_keyring);
@@ -106,12 +160,47 @@ static void put_cred_rcu(struct rcu_head *rcu)
106 */ 160 */
107void __put_cred(struct cred *cred) 161void __put_cred(struct cred *cred)
108{ 162{
163 kdebug("__put_cred(%p{%d,%d})", cred,
164 atomic_read(&cred->usage),
165 read_cred_subscribers(cred));
166
109 BUG_ON(atomic_read(&cred->usage) != 0); 167 BUG_ON(atomic_read(&cred->usage) != 0);
168#ifdef CONFIG_DEBUG_CREDENTIALS
169 BUG_ON(read_cred_subscribers(cred) != 0);
170 cred->magic = CRED_MAGIC_DEAD;
171 cred->put_addr = __builtin_return_address(0);
172#endif
173 BUG_ON(cred == current->cred);
174 BUG_ON(cred == current->real_cred);
110 175
111 call_rcu(&cred->rcu, put_cred_rcu); 176 call_rcu(&cred->rcu, put_cred_rcu);
112} 177}
113EXPORT_SYMBOL(__put_cred); 178EXPORT_SYMBOL(__put_cred);
114 179
180/*
181 * Clean up a task's credentials when it exits
182 */
183void exit_creds(struct task_struct *tsk)
184{
185 struct cred *cred;
186
187 kdebug("exit_creds(%u,%p,%p,{%d,%d})", tsk->pid, tsk->real_cred, tsk->cred,
188 atomic_read(&tsk->cred->usage),
189 read_cred_subscribers(tsk->cred));
190
191 cred = (struct cred *) tsk->real_cred;
192 tsk->real_cred = NULL;
193 validate_creds(cred);
194 alter_cred_subscribers(cred, -1);
195 put_cred(cred);
196
197 cred = (struct cred *) tsk->cred;
198 tsk->cred = NULL;
199 validate_creds(cred);
200 alter_cred_subscribers(cred, -1);
201 put_cred(cred);
202}
203
115/** 204/**
116 * prepare_creds - Prepare a new set of credentials for modification 205 * prepare_creds - Prepare a new set of credentials for modification
117 * 206 *
@@ -132,16 +221,19 @@ struct cred *prepare_creds(void)
132 const struct cred *old; 221 const struct cred *old;
133 struct cred *new; 222 struct cred *new;
134 223
135 BUG_ON(atomic_read(&task->real_cred->usage) < 1); 224 validate_process_creds();
136 225
137 new = kmem_cache_alloc(cred_jar, GFP_KERNEL); 226 new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
138 if (!new) 227 if (!new)
139 return NULL; 228 return NULL;
140 229
230 kdebug("prepare_creds() alloc %p", new);
231
141 old = task->cred; 232 old = task->cred;
142 memcpy(new, old, sizeof(struct cred)); 233 memcpy(new, old, sizeof(struct cred));
143 234
144 atomic_set(&new->usage, 1); 235 atomic_set(&new->usage, 1);
236 set_cred_subscribers(new, 0);
145 get_group_info(new->group_info); 237 get_group_info(new->group_info);
146 get_uid(new->user); 238 get_uid(new->user);
147 239
@@ -157,6 +249,7 @@ struct cred *prepare_creds(void)
157 249
158 if (security_prepare_creds(new, old, GFP_KERNEL) < 0) 250 if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
159 goto error; 251 goto error;
252 validate_creds(new);
160 return new; 253 return new;
161 254
162error: 255error:
@@ -229,9 +322,12 @@ struct cred *prepare_usermodehelper_creds(void)
229 if (!new) 322 if (!new)
230 return NULL; 323 return NULL;
231 324
325 kdebug("prepare_usermodehelper_creds() alloc %p", new);
326
232 memcpy(new, &init_cred, sizeof(struct cred)); 327 memcpy(new, &init_cred, sizeof(struct cred));
233 328
234 atomic_set(&new->usage, 1); 329 atomic_set(&new->usage, 1);
330 set_cred_subscribers(new, 0);
235 get_group_info(new->group_info); 331 get_group_info(new->group_info);
236 get_uid(new->user); 332 get_uid(new->user);
237 333
@@ -250,6 +346,7 @@ struct cred *prepare_usermodehelper_creds(void)
250#endif 346#endif
251 if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0) 347 if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0)
252 goto error; 348 goto error;
349 validate_creds(new);
253 350
254 BUG_ON(atomic_read(&new->usage) != 1); 351 BUG_ON(atomic_read(&new->usage) != 1);
255 return new; 352 return new;
@@ -286,6 +383,10 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
286 ) { 383 ) {
287 p->real_cred = get_cred(p->cred); 384 p->real_cred = get_cred(p->cred);
288 get_cred(p->cred); 385 get_cred(p->cred);
386 alter_cred_subscribers(p->cred, 2);
387 kdebug("share_creds(%p{%d,%d})",
388 p->cred, atomic_read(&p->cred->usage),
389 read_cred_subscribers(p->cred));
289 atomic_inc(&p->cred->user->processes); 390 atomic_inc(&p->cred->user->processes);
290 return 0; 391 return 0;
291 } 392 }
@@ -331,6 +432,8 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
331 432
332 atomic_inc(&new->user->processes); 433 atomic_inc(&new->user->processes);
333 p->cred = p->real_cred = get_cred(new); 434 p->cred = p->real_cred = get_cred(new);
435 alter_cred_subscribers(new, 2);
436 validate_creds(new);
334 return 0; 437 return 0;
335 438
336error_put: 439error_put:
@@ -355,13 +458,20 @@ error_put:
355int commit_creds(struct cred *new) 458int commit_creds(struct cred *new)
356{ 459{
357 struct task_struct *task = current; 460 struct task_struct *task = current;
358 const struct cred *old; 461 const struct cred *old = task->real_cred;
359 462
360 BUG_ON(task->cred != task->real_cred); 463 kdebug("commit_creds(%p{%d,%d})", new,
361 BUG_ON(atomic_read(&task->real_cred->usage) < 2); 464 atomic_read(&new->usage),
465 read_cred_subscribers(new));
466
467 BUG_ON(task->cred != old);
468#ifdef CONFIG_DEBUG_CREDENTIALS
469 BUG_ON(read_cred_subscribers(old) < 2);
470 validate_creds(old);
471 validate_creds(new);
472#endif
362 BUG_ON(atomic_read(&new->usage) < 1); 473 BUG_ON(atomic_read(&new->usage) < 1);
363 474
364 old = task->real_cred;
365 security_commit_creds(new, old); 475 security_commit_creds(new, old);
366 476
367 get_cred(new); /* we will require a ref for the subj creds too */ 477 get_cred(new); /* we will require a ref for the subj creds too */
@@ -390,12 +500,14 @@ int commit_creds(struct cred *new)
390 * cheaply with the new uid cache, so if it matters 500 * cheaply with the new uid cache, so if it matters
391 * we should be checking for it. -DaveM 501 * we should be checking for it. -DaveM
392 */ 502 */
503 alter_cred_subscribers(new, 2);
393 if (new->user != old->user) 504 if (new->user != old->user)
394 atomic_inc(&new->user->processes); 505 atomic_inc(&new->user->processes);
395 rcu_assign_pointer(task->real_cred, new); 506 rcu_assign_pointer(task->real_cred, new);
396 rcu_assign_pointer(task->cred, new); 507 rcu_assign_pointer(task->cred, new);
397 if (new->user != old->user) 508 if (new->user != old->user)
398 atomic_dec(&old->user->processes); 509 atomic_dec(&old->user->processes);
510 alter_cred_subscribers(old, -2);
399 511
400 sched_switch_user(task); 512 sched_switch_user(task);
401 513
@@ -428,6 +540,13 @@ EXPORT_SYMBOL(commit_creds);
428 */ 540 */
429void abort_creds(struct cred *new) 541void abort_creds(struct cred *new)
430{ 542{
543 kdebug("abort_creds(%p{%d,%d})", new,
544 atomic_read(&new->usage),
545 read_cred_subscribers(new));
546
547#ifdef CONFIG_DEBUG_CREDENTIALS
548 BUG_ON(read_cred_subscribers(new) != 0);
549#endif
431 BUG_ON(atomic_read(&new->usage) < 1); 550 BUG_ON(atomic_read(&new->usage) < 1);
432 put_cred(new); 551 put_cred(new);
433} 552}
@@ -444,7 +563,20 @@ const struct cred *override_creds(const struct cred *new)
444{ 563{
445 const struct cred *old = current->cred; 564 const struct cred *old = current->cred;
446 565
447 rcu_assign_pointer(current->cred, get_cred(new)); 566 kdebug("override_creds(%p{%d,%d})", new,
567 atomic_read(&new->usage),
568 read_cred_subscribers(new));
569
570 validate_creds(old);
571 validate_creds(new);
572 get_cred(new);
573 alter_cred_subscribers(new, 1);
574 rcu_assign_pointer(current->cred, new);
575 alter_cred_subscribers(old, -1);
576
577 kdebug("override_creds() = %p{%d,%d}", old,
578 atomic_read(&old->usage),
579 read_cred_subscribers(old));
448 return old; 580 return old;
449} 581}
450EXPORT_SYMBOL(override_creds); 582EXPORT_SYMBOL(override_creds);
@@ -460,7 +592,15 @@ void revert_creds(const struct cred *old)
460{ 592{
461 const struct cred *override = current->cred; 593 const struct cred *override = current->cred;
462 594
595 kdebug("revert_creds(%p{%d,%d})", old,
596 atomic_read(&old->usage),
597 read_cred_subscribers(old));
598
599 validate_creds(old);
600 validate_creds(override);
601 alter_cred_subscribers(old, 1);
463 rcu_assign_pointer(current->cred, old); 602 rcu_assign_pointer(current->cred, old);
603 alter_cred_subscribers(override, -1);
464 put_cred(override); 604 put_cred(override);
465} 605}
466EXPORT_SYMBOL(revert_creds); 606EXPORT_SYMBOL(revert_creds);
@@ -502,11 +642,15 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
502 if (!new) 642 if (!new)
503 return NULL; 643 return NULL;
504 644
645 kdebug("prepare_kernel_cred() alloc %p", new);
646
505 if (daemon) 647 if (daemon)
506 old = get_task_cred(daemon); 648 old = get_task_cred(daemon);
507 else 649 else
508 old = get_cred(&init_cred); 650 old = get_cred(&init_cred);
509 651
652 validate_creds(old);
653
510 *new = *old; 654 *new = *old;
511 get_uid(new->user); 655 get_uid(new->user);
512 get_group_info(new->group_info); 656 get_group_info(new->group_info);
@@ -526,7 +670,9 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
526 goto error; 670 goto error;
527 671
528 atomic_set(&new->usage, 1); 672 atomic_set(&new->usage, 1);
673 set_cred_subscribers(new, 0);
529 put_cred(old); 674 put_cred(old);
675 validate_creds(new);
530 return new; 676 return new;
531 677
532error: 678error:
@@ -589,3 +735,95 @@ int set_create_files_as(struct cred *new, struct inode *inode)
589 return security_kernel_create_files_as(new, inode); 735 return security_kernel_create_files_as(new, inode);
590} 736}
591EXPORT_SYMBOL(set_create_files_as); 737EXPORT_SYMBOL(set_create_files_as);
738
739#ifdef CONFIG_DEBUG_CREDENTIALS
740
741/*
742 * dump invalid credentials
743 */
744static void dump_invalid_creds(const struct cred *cred, const char *label,
745 const struct task_struct *tsk)
746{
747 printk(KERN_ERR "CRED: %s credentials: %p %s%s%s\n",
748 label, cred,
749 cred == &init_cred ? "[init]" : "",
750 cred == tsk->real_cred ? "[real]" : "",
751 cred == tsk->cred ? "[eff]" : "");
752 printk(KERN_ERR "CRED: ->magic=%x, put_addr=%p\n",
753 cred->magic, cred->put_addr);
754 printk(KERN_ERR "CRED: ->usage=%d, subscr=%d\n",
755 atomic_read(&cred->usage),
756 read_cred_subscribers(cred));
757 printk(KERN_ERR "CRED: ->*uid = { %d,%d,%d,%d }\n",
758 cred->uid, cred->euid, cred->suid, cred->fsuid);
759 printk(KERN_ERR "CRED: ->*gid = { %d,%d,%d,%d }\n",
760 cred->gid, cred->egid, cred->sgid, cred->fsgid);
761#ifdef CONFIG_SECURITY
762 printk(KERN_ERR "CRED: ->security is %p\n", cred->security);
763 if ((unsigned long) cred->security >= PAGE_SIZE &&
764 (((unsigned long) cred->security & 0xffffff00) !=
765 (POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8)))
766 printk(KERN_ERR "CRED: ->security {%x, %x}\n",
767 ((u32*)cred->security)[0],
768 ((u32*)cred->security)[1]);
769#endif
770}
771
772/*
773 * report use of invalid credentials
774 */
775void __invalid_creds(const struct cred *cred, const char *file, unsigned line)
776{
777 printk(KERN_ERR "CRED: Invalid credentials\n");
778 printk(KERN_ERR "CRED: At %s:%u\n", file, line);
779 dump_invalid_creds(cred, "Specified", current);
780 BUG();
781}
782EXPORT_SYMBOL(__invalid_creds);
783
784/*
785 * check the credentials on a process
786 */
787void __validate_process_creds(struct task_struct *tsk,
788 const char *file, unsigned line)
789{
790 if (tsk->cred == tsk->real_cred) {
791 if (unlikely(read_cred_subscribers(tsk->cred) < 2 ||
792 creds_are_invalid(tsk->cred)))
793 goto invalid_creds;
794 } else {
795 if (unlikely(read_cred_subscribers(tsk->real_cred) < 1 ||
796 read_cred_subscribers(tsk->cred) < 1 ||
797 creds_are_invalid(tsk->real_cred) ||
798 creds_are_invalid(tsk->cred)))
799 goto invalid_creds;
800 }
801 return;
802
803invalid_creds:
804 printk(KERN_ERR "CRED: Invalid process credentials\n");
805 printk(KERN_ERR "CRED: At %s:%u\n", file, line);
806
807 dump_invalid_creds(tsk->real_cred, "Real", tsk);
808 if (tsk->cred != tsk->real_cred)
809 dump_invalid_creds(tsk->cred, "Effective", tsk);
810 else
811 printk(KERN_ERR "CRED: Effective creds == Real creds\n");
812 BUG();
813}
814EXPORT_SYMBOL(__validate_process_creds);
815
816/*
817 * check creds for do_exit()
818 */
819void validate_creds_for_do_exit(struct task_struct *tsk)
820{
821 kdebug("validate_creds_for_do_exit(%p,%p{%d,%d})",
822 tsk->real_cred, tsk->cred,
823 atomic_read(&tsk->cred->usage),
824 read_cred_subscribers(tsk->cred));
825
826 __validate_process_creds(tsk, __FILE__, __LINE__);
827}
828
829#endif /* CONFIG_DEBUG_CREDENTIALS */
diff --git a/kernel/exit.c b/kernel/exit.c
index 869dc221733e..c98ff7a8025f 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -901,6 +901,8 @@ NORET_TYPE void do_exit(long code)
901 901
902 tracehook_report_exit(&code); 902 tracehook_report_exit(&code);
903 903
904 validate_creds_for_do_exit(tsk);
905
904 /* 906 /*
905 * We're taking recursive faults here in do_exit. Safest is to just 907 * We're taking recursive faults here in do_exit. Safest is to just
906 * leave this task alone and wait for reboot. 908 * leave this task alone and wait for reboot.
@@ -1009,6 +1011,8 @@ NORET_TYPE void do_exit(long code)
1009 if (tsk->splice_pipe) 1011 if (tsk->splice_pipe)
1010 __free_pipe_info(tsk->splice_pipe); 1012 __free_pipe_info(tsk->splice_pipe);
1011 1013
1014 validate_creds_for_do_exit(tsk);
1015
1012 preempt_disable(); 1016 preempt_disable();
1013 /* causes final put_task_struct in finish_task_switch(). */ 1017 /* causes final put_task_struct in finish_task_switch(). */
1014 tsk->state = TASK_DEAD; 1018 tsk->state = TASK_DEAD;
diff --git a/kernel/fork.c b/kernel/fork.c
index 144326b7af50..043b5d88049b 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -152,8 +152,7 @@ void __put_task_struct(struct task_struct *tsk)
152 WARN_ON(atomic_read(&tsk->usage)); 152 WARN_ON(atomic_read(&tsk->usage));
153 WARN_ON(tsk == current); 153 WARN_ON(tsk == current);
154 154
155 put_cred(tsk->real_cred); 155 exit_creds(tsk);
156 put_cred(tsk->cred);
157 delayacct_tsk_free(tsk); 156 delayacct_tsk_free(tsk);
158 157
159 if (!profile_handoff_task(tsk)) 158 if (!profile_handoff_task(tsk))
@@ -1307,8 +1306,7 @@ bad_fork_cleanup_put_domain:
1307 module_put(task_thread_info(p)->exec_domain->module); 1306 module_put(task_thread_info(p)->exec_domain->module);
1308bad_fork_cleanup_count: 1307bad_fork_cleanup_count:
1309 atomic_dec(&p->cred->user->processes); 1308 atomic_dec(&p->cred->user->processes);
1310 put_cred(p->real_cred); 1309 exit_creds(p);
1311 put_cred(p->cred);
1312bad_fork_free: 1310bad_fork_free:
1313 free_task(p); 1311 free_task(p);
1314fork_out: 1312fork_out:
diff --git a/kernel/kmod.c b/kernel/kmod.c
index 5a7ae57f983f..4e8cae2e9148 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -466,6 +466,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
466 int retval = 0; 466 int retval = 0;
467 467
468 BUG_ON(atomic_read(&sub_info->cred->usage) != 1); 468 BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
469 validate_creds(sub_info->cred);
469 470
470 helper_lock(); 471 helper_lock();
471 if (sub_info->path[0] == '\0') 472 if (sub_info->path[0] == '\0')
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 12327b2bb785..fbb87cf138c5 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -653,6 +653,21 @@ config DEBUG_NOTIFIERS
653 This is a relatively cheap check but if you care about maximum 653 This is a relatively cheap check but if you care about maximum
654 performance, say N. 654 performance, say N.
655 655
656config DEBUG_CREDENTIALS
657 bool "Debug credential management"
658 depends on DEBUG_KERNEL
659 help
660 Enable this to turn on some debug checking for credential
661 management. The additional code keeps track of the number of
662 pointers from task_structs to any given cred struct, and checks to
663 see that this number never exceeds the usage count of the cred
664 struct.
665
666 Furthermore, if SELinux is enabled, this also checks that the
667 security pointer in the cred struct is never seen to be invalid.
668
669 If unsure, say N.
670
656# 671#
657# Select this config option from the architecture Kconfig, if it 672# Select this config option from the architecture Kconfig, if it
658# it is preferred to always offer frame pointers as a config 673# it is preferred to always offer frame pointers as a config
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 27b4c5527358..c3bb31ecc5aa 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1531,6 +1531,8 @@ static int inode_has_perm(const struct cred *cred,
1531 struct common_audit_data ad; 1531 struct common_audit_data ad;
1532 u32 sid; 1532 u32 sid;
1533 1533
1534 validate_creds(cred);
1535
1534 if (unlikely(IS_PRIVATE(inode))) 1536 if (unlikely(IS_PRIVATE(inode)))
1535 return 0; 1537 return 0;
1536 1538
@@ -3236,7 +3238,9 @@ static int selinux_task_create(unsigned long clone_flags)
3236static void selinux_cred_free(struct cred *cred) 3238static void selinux_cred_free(struct cred *cred)
3237{ 3239{
3238 struct task_security_struct *tsec = cred->security; 3240 struct task_security_struct *tsec = cred->security;
3239 cred->security = NULL; 3241
3242 BUG_ON((unsigned long) cred->security < PAGE_SIZE);
3243 cred->security = (void *) 0x7UL;
3240 kfree(tsec); 3244 kfree(tsec);
3241} 3245}
3242 3246