| David Howells | 98870ab | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 1 | /* Task credentials management - see Documentation/credentials.txt | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 2 |  * | 
 | 3 |  * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. | 
 | 4 |  * Written by David Howells (dhowells@redhat.com) | 
 | 5 |  * | 
 | 6 |  * This program is free software; you can redistribute it and/or | 
 | 7 |  * modify it under the terms of the GNU General Public Licence | 
 | 8 |  * as published by the Free Software Foundation; either version | 
 | 9 |  * 2 of the Licence, or (at your option) any later version. | 
 | 10 |  */ | 
 | 11 | #include <linux/module.h> | 
 | 12 | #include <linux/cred.h> | 
 | 13 | #include <linux/sched.h> | 
 | 14 | #include <linux/key.h> | 
 | 15 | #include <linux/keyctl.h> | 
 | 16 | #include <linux/init_task.h> | 
 | 17 | #include <linux/security.h> | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 18 | #include <linux/cn_proc.h> | 
 | 19 | #include "cred-internals.h" | 
 | 20 |  | 
 | 21 | static struct kmem_cache *cred_jar; | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 22 |  | 
 | 23 | /* | 
| David Howells | bb952bb | 2008-11-14 10:39:20 +1100 | [diff] [blame] | 24 |  * The common credentials for the initial task's thread group | 
 | 25 |  */ | 
 | 26 | #ifdef CONFIG_KEYS | 
 | 27 | static struct thread_group_cred init_tgcred = { | 
 | 28 | 	.usage	= ATOMIC_INIT(2), | 
 | 29 | 	.tgid	= 0, | 
 | 30 | 	.lock	= SPIN_LOCK_UNLOCKED, | 
 | 31 | }; | 
 | 32 | #endif | 
 | 33 |  | 
 | 34 | /* | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 35 |  * The initial credentials for the initial task | 
 | 36 |  */ | 
 | 37 | struct cred init_cred = { | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 38 | 	.usage			= ATOMIC_INIT(4), | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 39 | 	.securebits		= SECUREBITS_DEFAULT, | 
 | 40 | 	.cap_inheritable	= CAP_INIT_INH_SET, | 
 | 41 | 	.cap_permitted		= CAP_FULL_SET, | 
 | 42 | 	.cap_effective		= CAP_INIT_EFF_SET, | 
 | 43 | 	.cap_bset		= CAP_INIT_BSET, | 
 | 44 | 	.user			= INIT_USER, | 
 | 45 | 	.group_info		= &init_groups, | 
| David Howells | bb952bb | 2008-11-14 10:39:20 +1100 | [diff] [blame] | 46 | #ifdef CONFIG_KEYS | 
 | 47 | 	.tgcred			= &init_tgcred, | 
 | 48 | #endif | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 49 | }; | 
 | 50 |  | 
 | 51 | /* | 
| David Howells | bb952bb | 2008-11-14 10:39:20 +1100 | [diff] [blame] | 52 |  * Dispose of the shared task group credentials | 
 | 53 |  */ | 
 | 54 | #ifdef CONFIG_KEYS | 
 | 55 | static void release_tgcred_rcu(struct rcu_head *rcu) | 
 | 56 | { | 
 | 57 | 	struct thread_group_cred *tgcred = | 
 | 58 | 		container_of(rcu, struct thread_group_cred, rcu); | 
 | 59 |  | 
 | 60 | 	BUG_ON(atomic_read(&tgcred->usage) != 0); | 
 | 61 |  | 
 | 62 | 	key_put(tgcred->session_keyring); | 
 | 63 | 	key_put(tgcred->process_keyring); | 
 | 64 | 	kfree(tgcred); | 
 | 65 | } | 
 | 66 | #endif | 
 | 67 |  | 
 | 68 | /* | 
 | 69 |  * Release a set of thread group credentials. | 
 | 70 |  */ | 
| David Howells | a6f76f2 | 2008-11-14 10:39:24 +1100 | [diff] [blame] | 71 | static void release_tgcred(struct cred *cred) | 
| David Howells | bb952bb | 2008-11-14 10:39:20 +1100 | [diff] [blame] | 72 | { | 
 | 73 | #ifdef CONFIG_KEYS | 
 | 74 | 	struct thread_group_cred *tgcred = cred->tgcred; | 
 | 75 |  | 
 | 76 | 	if (atomic_dec_and_test(&tgcred->usage)) | 
 | 77 | 		call_rcu(&tgcred->rcu, release_tgcred_rcu); | 
 | 78 | #endif | 
 | 79 | } | 
 | 80 |  | 
 | 81 | /* | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 82 |  * The RCU callback to actually dispose of a set of credentials | 
 | 83 |  */ | 
 | 84 | static void put_cred_rcu(struct rcu_head *rcu) | 
 | 85 | { | 
 | 86 | 	struct cred *cred = container_of(rcu, struct cred, rcu); | 
 | 87 |  | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 88 | 	if (atomic_read(&cred->usage) != 0) | 
 | 89 | 		panic("CRED: put_cred_rcu() sees %p with usage %d\n", | 
 | 90 | 		      cred, atomic_read(&cred->usage)); | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 91 |  | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 92 | 	security_cred_free(cred); | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 93 | 	key_put(cred->thread_keyring); | 
 | 94 | 	key_put(cred->request_key_auth); | 
| David Howells | bb952bb | 2008-11-14 10:39:20 +1100 | [diff] [blame] | 95 | 	release_tgcred(cred); | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 96 | 	put_group_info(cred->group_info); | 
 | 97 | 	free_uid(cred->user); | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 98 | 	kmem_cache_free(cred_jar, cred); | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 99 | } | 
 | 100 |  | 
 | 101 | /** | 
 | 102 |  * __put_cred - Destroy a set of credentials | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 103 |  * @cred: The record to release | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 104 |  * | 
 | 105 |  * Destroy a set of credentials on which no references remain. | 
 | 106 |  */ | 
 | 107 | void __put_cred(struct cred *cred) | 
 | 108 | { | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 109 | 	BUG_ON(atomic_read(&cred->usage) != 0); | 
 | 110 |  | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 111 | 	call_rcu(&cred->rcu, put_cred_rcu); | 
 | 112 | } | 
 | 113 | EXPORT_SYMBOL(__put_cred); | 
 | 114 |  | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 115 | /** | 
 | 116 |  * prepare_creds - Prepare a new set of credentials for modification | 
 | 117 |  * | 
 | 118 |  * Prepare a new set of task credentials for modification.  A task's creds | 
 | 119 |  * shouldn't generally be modified directly, therefore this function is used to | 
 | 120 |  * prepare a new copy, which the caller then modifies and then commits by | 
 | 121 |  * calling commit_creds(). | 
 | 122 |  * | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 123 |  * Preparation involves making a copy of the objective creds for modification. | 
 | 124 |  * | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 125 |  * Returns a pointer to the new creds-to-be if successful, NULL otherwise. | 
 | 126 |  * | 
 | 127 |  * Call commit_creds() or abort_creds() to clean up. | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 128 |  */ | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 129 | struct cred *prepare_creds(void) | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 130 | { | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 131 | 	struct task_struct *task = current; | 
 | 132 | 	const struct cred *old; | 
 | 133 | 	struct cred *new; | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 134 |  | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 135 | 	BUG_ON(atomic_read(&task->real_cred->usage) < 1); | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 136 |  | 
 | 137 | 	new = kmem_cache_alloc(cred_jar, GFP_KERNEL); | 
 | 138 | 	if (!new) | 
 | 139 | 		return NULL; | 
 | 140 |  | 
 | 141 | 	old = task->cred; | 
 | 142 | 	memcpy(new, old, sizeof(struct cred)); | 
 | 143 |  | 
 | 144 | 	atomic_set(&new->usage, 1); | 
 | 145 | 	get_group_info(new->group_info); | 
 | 146 | 	get_uid(new->user); | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 147 |  | 
| David Howells | bb952bb | 2008-11-14 10:39:20 +1100 | [diff] [blame] | 148 | #ifdef CONFIG_KEYS | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 149 | 	key_get(new->thread_keyring); | 
 | 150 | 	key_get(new->request_key_auth); | 
 | 151 | 	atomic_inc(&new->tgcred->usage); | 
| David Howells | bb952bb | 2008-11-14 10:39:20 +1100 | [diff] [blame] | 152 | #endif | 
 | 153 |  | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 154 | #ifdef CONFIG_SECURITY | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 155 | 	new->security = NULL; | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 156 | #endif | 
 | 157 |  | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 158 | 	if (security_prepare_creds(new, old, GFP_KERNEL) < 0) | 
 | 159 | 		goto error; | 
 | 160 | 	return new; | 
 | 161 |  | 
 | 162 | error: | 
 | 163 | 	abort_creds(new); | 
 | 164 | 	return NULL; | 
 | 165 | } | 
 | 166 | EXPORT_SYMBOL(prepare_creds); | 
 | 167 |  | 
 | 168 | /* | 
| David Howells | a6f76f2 | 2008-11-14 10:39:24 +1100 | [diff] [blame] | 169 |  * Prepare credentials for current to perform an execve() | 
 | 170 |  * - The caller must hold current->cred_exec_mutex | 
 | 171 |  */ | 
 | 172 | struct cred *prepare_exec_creds(void) | 
 | 173 | { | 
 | 174 | 	struct thread_group_cred *tgcred = NULL; | 
 | 175 | 	struct cred *new; | 
 | 176 |  | 
 | 177 | #ifdef CONFIG_KEYS | 
 | 178 | 	tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL); | 
 | 179 | 	if (!tgcred) | 
 | 180 | 		return NULL; | 
 | 181 | #endif | 
 | 182 |  | 
 | 183 | 	new = prepare_creds(); | 
 | 184 | 	if (!new) { | 
 | 185 | 		kfree(tgcred); | 
 | 186 | 		return new; | 
 | 187 | 	} | 
 | 188 |  | 
 | 189 | #ifdef CONFIG_KEYS | 
 | 190 | 	/* newly exec'd tasks don't get a thread keyring */ | 
 | 191 | 	key_put(new->thread_keyring); | 
 | 192 | 	new->thread_keyring = NULL; | 
 | 193 |  | 
 | 194 | 	/* create a new per-thread-group creds for all this set of threads to | 
 | 195 | 	 * share */ | 
 | 196 | 	memcpy(tgcred, new->tgcred, sizeof(struct thread_group_cred)); | 
 | 197 |  | 
 | 198 | 	atomic_set(&tgcred->usage, 1); | 
 | 199 | 	spin_lock_init(&tgcred->lock); | 
 | 200 |  | 
 | 201 | 	/* inherit the session keyring; new process keyring */ | 
 | 202 | 	key_get(tgcred->session_keyring); | 
 | 203 | 	tgcred->process_keyring = NULL; | 
 | 204 |  | 
 | 205 | 	release_tgcred(new); | 
 | 206 | 	new->tgcred = tgcred; | 
 | 207 | #endif | 
 | 208 |  | 
 | 209 | 	return new; | 
 | 210 | } | 
 | 211 |  | 
 | 212 | /* | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 213 |  * prepare new credentials for the usermode helper dispatcher | 
 | 214 |  */ | 
 | 215 | struct cred *prepare_usermodehelper_creds(void) | 
 | 216 | { | 
 | 217 | #ifdef CONFIG_KEYS | 
 | 218 | 	struct thread_group_cred *tgcred = NULL; | 
 | 219 | #endif | 
 | 220 | 	struct cred *new; | 
 | 221 |  | 
 | 222 | #ifdef CONFIG_KEYS | 
 | 223 | 	tgcred = kzalloc(sizeof(*new->tgcred), GFP_ATOMIC); | 
 | 224 | 	if (!tgcred) | 
 | 225 | 		return NULL; | 
 | 226 | #endif | 
 | 227 |  | 
 | 228 | 	new = kmem_cache_alloc(cred_jar, GFP_ATOMIC); | 
 | 229 | 	if (!new) | 
 | 230 | 		return NULL; | 
 | 231 |  | 
 | 232 | 	memcpy(new, &init_cred, sizeof(struct cred)); | 
 | 233 |  | 
 | 234 | 	atomic_set(&new->usage, 1); | 
 | 235 | 	get_group_info(new->group_info); | 
 | 236 | 	get_uid(new->user); | 
 | 237 |  | 
 | 238 | #ifdef CONFIG_KEYS | 
 | 239 | 	new->thread_keyring = NULL; | 
 | 240 | 	new->request_key_auth = NULL; | 
 | 241 | 	new->jit_keyring = KEY_REQKEY_DEFL_DEFAULT; | 
 | 242 |  | 
 | 243 | 	atomic_set(&tgcred->usage, 1); | 
 | 244 | 	spin_lock_init(&tgcred->lock); | 
 | 245 | 	new->tgcred = tgcred; | 
 | 246 | #endif | 
 | 247 |  | 
 | 248 | #ifdef CONFIG_SECURITY | 
 | 249 | 	new->security = NULL; | 
 | 250 | #endif | 
 | 251 | 	if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0) | 
 | 252 | 		goto error; | 
 | 253 |  | 
 | 254 | 	BUG_ON(atomic_read(&new->usage) != 1); | 
 | 255 | 	return new; | 
 | 256 |  | 
 | 257 | error: | 
 | 258 | 	put_cred(new); | 
 | 259 | 	return NULL; | 
 | 260 | } | 
 | 261 |  | 
 | 262 | /* | 
 | 263 |  * Copy credentials for the new process created by fork() | 
 | 264 |  * | 
 | 265 |  * We share if we can, but under some circumstances we have to generate a new | 
 | 266 |  * set. | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 267 |  * | 
 | 268 |  * The new process gets the current process's subjective credentials as its | 
 | 269 |  * objective and subjective credentials | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 270 |  */ | 
 | 271 | int copy_creds(struct task_struct *p, unsigned long clone_flags) | 
 | 272 | { | 
 | 273 | #ifdef CONFIG_KEYS | 
 | 274 | 	struct thread_group_cred *tgcred; | 
 | 275 | #endif | 
 | 276 | 	struct cred *new; | 
| Serge Hallyn | 18b6e04 | 2008-10-15 16:38:45 -0500 | [diff] [blame] | 277 | 	int ret; | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 278 |  | 
 | 279 | 	mutex_init(&p->cred_exec_mutex); | 
 | 280 |  | 
 | 281 | 	if ( | 
 | 282 | #ifdef CONFIG_KEYS | 
 | 283 | 		!p->cred->thread_keyring && | 
 | 284 | #endif | 
 | 285 | 		clone_flags & CLONE_THREAD | 
 | 286 | 	    ) { | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 287 | 		p->real_cred = get_cred(p->cred); | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 288 | 		get_cred(p->cred); | 
 | 289 | 		atomic_inc(&p->cred->user->processes); | 
 | 290 | 		return 0; | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 291 | 	} | 
 | 292 |  | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 293 | 	new = prepare_creds(); | 
 | 294 | 	if (!new) | 
 | 295 | 		return -ENOMEM; | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 296 |  | 
| Serge Hallyn | 18b6e04 | 2008-10-15 16:38:45 -0500 | [diff] [blame] | 297 | 	if (clone_flags & CLONE_NEWUSER) { | 
 | 298 | 		ret = create_user_ns(new); | 
 | 299 | 		if (ret < 0) | 
 | 300 | 			goto error_put; | 
 | 301 | 	} | 
 | 302 |  | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 303 | #ifdef CONFIG_KEYS | 
 | 304 | 	/* new threads get their own thread keyrings if their parent already | 
 | 305 | 	 * had one */ | 
 | 306 | 	if (new->thread_keyring) { | 
 | 307 | 		key_put(new->thread_keyring); | 
 | 308 | 		new->thread_keyring = NULL; | 
 | 309 | 		if (clone_flags & CLONE_THREAD) | 
 | 310 | 			install_thread_keyring_to_cred(new); | 
 | 311 | 	} | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 312 |  | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 313 | 	/* we share the process and session keyrings between all the threads in | 
 | 314 | 	 * a process - this is slightly icky as we violate COW credentials a | 
 | 315 | 	 * bit */ | 
 | 316 | 	if (!(clone_flags & CLONE_THREAD)) { | 
 | 317 | 		tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL); | 
 | 318 | 		if (!tgcred) { | 
| Serge Hallyn | 18b6e04 | 2008-10-15 16:38:45 -0500 | [diff] [blame] | 319 | 			ret = -ENOMEM; | 
 | 320 | 			goto error_put; | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 321 | 		} | 
 | 322 | 		atomic_set(&tgcred->usage, 1); | 
 | 323 | 		spin_lock_init(&tgcred->lock); | 
 | 324 | 		tgcred->process_keyring = NULL; | 
 | 325 | 		tgcred->session_keyring = key_get(new->tgcred->session_keyring); | 
 | 326 |  | 
 | 327 | 		release_tgcred(new); | 
 | 328 | 		new->tgcred = tgcred; | 
 | 329 | 	} | 
 | 330 | #endif | 
 | 331 |  | 
 | 332 | 	atomic_inc(&new->user->processes); | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 333 | 	p->cred = p->real_cred = get_cred(new); | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 334 | 	return 0; | 
| Serge Hallyn | 18b6e04 | 2008-10-15 16:38:45 -0500 | [diff] [blame] | 335 |  | 
 | 336 | error_put: | 
 | 337 | 	put_cred(new); | 
 | 338 | 	return ret; | 
| David Howells | f1752ee | 2008-11-14 10:39:17 +1100 | [diff] [blame] | 339 | } | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 340 |  | 
 | 341 | /** | 
 | 342 |  * commit_creds - Install new credentials upon the current task | 
 | 343 |  * @new: The credentials to be assigned | 
 | 344 |  * | 
 | 345 |  * Install a new set of credentials to the current task, using RCU to replace | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 346 |  * the old set.  Both the objective and the subjective credentials pointers are | 
 | 347 |  * updated.  This function may not be called if the subjective credentials are | 
 | 348 |  * in an overridden state. | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 349 |  * | 
 | 350 |  * This function eats the caller's reference to the new credentials. | 
 | 351 |  * | 
 | 352 |  * Always returns 0 thus allowing this function to be tail-called at the end | 
 | 353 |  * of, say, sys_setgid(). | 
 | 354 |  */ | 
 | 355 | int commit_creds(struct cred *new) | 
 | 356 | { | 
 | 357 | 	struct task_struct *task = current; | 
 | 358 | 	const struct cred *old; | 
 | 359 |  | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 360 | 	BUG_ON(task->cred != task->real_cred); | 
 | 361 | 	BUG_ON(atomic_read(&task->real_cred->usage) < 2); | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 362 | 	BUG_ON(atomic_read(&new->usage) < 1); | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 363 |  | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 364 | 	old = task->real_cred; | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 365 | 	security_commit_creds(new, old); | 
 | 366 |  | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 367 | 	get_cred(new); /* we will require a ref for the subj creds too */ | 
 | 368 |  | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 369 | 	/* dumpability changes */ | 
 | 370 | 	if (old->euid != new->euid || | 
 | 371 | 	    old->egid != new->egid || | 
 | 372 | 	    old->fsuid != new->fsuid || | 
 | 373 | 	    old->fsgid != new->fsgid || | 
 | 374 | 	    !cap_issubset(new->cap_permitted, old->cap_permitted)) { | 
| David Howells | b945637 | 2009-01-08 11:18:31 +0000 | [diff] [blame] | 375 | 		if (task->mm) | 
 | 376 | 			set_dumpable(task->mm, suid_dumpable); | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 377 | 		task->pdeath_signal = 0; | 
 | 378 | 		smp_wmb(); | 
 | 379 | 	} | 
 | 380 |  | 
 | 381 | 	/* alter the thread keyring */ | 
 | 382 | 	if (new->fsuid != old->fsuid) | 
 | 383 | 		key_fsuid_changed(task); | 
 | 384 | 	if (new->fsgid != old->fsgid) | 
 | 385 | 		key_fsgid_changed(task); | 
 | 386 |  | 
 | 387 | 	/* do it | 
 | 388 | 	 * - What if a process setreuid()'s and this brings the | 
 | 389 | 	 *   new uid over his NPROC rlimit?  We can check this now | 
 | 390 | 	 *   cheaply with the new uid cache, so if it matters | 
 | 391 | 	 *   we should be checking for it.  -DaveM | 
 | 392 | 	 */ | 
 | 393 | 	if (new->user != old->user) | 
 | 394 | 		atomic_inc(&new->user->processes); | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 395 | 	rcu_assign_pointer(task->real_cred, new); | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 396 | 	rcu_assign_pointer(task->cred, new); | 
 | 397 | 	if (new->user != old->user) | 
 | 398 | 		atomic_dec(&old->user->processes); | 
 | 399 |  | 
 | 400 | 	sched_switch_user(task); | 
 | 401 |  | 
 | 402 | 	/* send notifications */ | 
 | 403 | 	if (new->uid   != old->uid  || | 
 | 404 | 	    new->euid  != old->euid || | 
 | 405 | 	    new->suid  != old->suid || | 
 | 406 | 	    new->fsuid != old->fsuid) | 
 | 407 | 		proc_id_connector(task, PROC_EVENT_UID); | 
 | 408 |  | 
 | 409 | 	if (new->gid   != old->gid  || | 
 | 410 | 	    new->egid  != old->egid || | 
 | 411 | 	    new->sgid  != old->sgid || | 
 | 412 | 	    new->fsgid != old->fsgid) | 
 | 413 | 		proc_id_connector(task, PROC_EVENT_GID); | 
 | 414 |  | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 415 | 	/* release the old obj and subj refs both */ | 
 | 416 | 	put_cred(old); | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 417 | 	put_cred(old); | 
 | 418 | 	return 0; | 
 | 419 | } | 
 | 420 | EXPORT_SYMBOL(commit_creds); | 
 | 421 |  | 
 | 422 | /** | 
 | 423 |  * abort_creds - Discard a set of credentials and unlock the current task | 
 | 424 |  * @new: The credentials that were going to be applied | 
 | 425 |  * | 
 | 426 |  * Discard a set of credentials that were under construction and unlock the | 
 | 427 |  * current task. | 
 | 428 |  */ | 
 | 429 | void abort_creds(struct cred *new) | 
 | 430 | { | 
 | 431 | 	BUG_ON(atomic_read(&new->usage) < 1); | 
 | 432 | 	put_cred(new); | 
 | 433 | } | 
 | 434 | EXPORT_SYMBOL(abort_creds); | 
 | 435 |  | 
 | 436 | /** | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 437 |  * override_creds - Override the current process's subjective credentials | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 438 |  * @new: The credentials to be assigned | 
 | 439 |  * | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 440 |  * Install a set of temporary override subjective credentials on the current | 
 | 441 |  * process, returning the old set for later reversion. | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 442 |  */ | 
 | 443 | const struct cred *override_creds(const struct cred *new) | 
 | 444 | { | 
 | 445 | 	const struct cred *old = current->cred; | 
 | 446 |  | 
 | 447 | 	rcu_assign_pointer(current->cred, get_cred(new)); | 
 | 448 | 	return old; | 
 | 449 | } | 
 | 450 | EXPORT_SYMBOL(override_creds); | 
 | 451 |  | 
 | 452 | /** | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 453 |  * revert_creds - Revert a temporary subjective credentials override | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 454 |  * @old: The credentials to be restored | 
 | 455 |  * | 
| David Howells | 3b11a1d | 2008-11-14 10:39:26 +1100 | [diff] [blame] | 456 |  * Revert a temporary set of override subjective credentials to an old set, | 
 | 457 |  * discarding the override set. | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 458 |  */ | 
 | 459 | void revert_creds(const struct cred *old) | 
 | 460 | { | 
 | 461 | 	const struct cred *override = current->cred; | 
 | 462 |  | 
 | 463 | 	rcu_assign_pointer(current->cred, old); | 
 | 464 | 	put_cred(override); | 
 | 465 | } | 
 | 466 | EXPORT_SYMBOL(revert_creds); | 
 | 467 |  | 
 | 468 | /* | 
 | 469 |  * initialise the credentials stuff | 
 | 470 |  */ | 
 | 471 | void __init cred_init(void) | 
 | 472 | { | 
 | 473 | 	/* allocate a slab in which we can store credentials */ | 
 | 474 | 	cred_jar = kmem_cache_create("cred_jar", sizeof(struct cred), | 
 | 475 | 				     0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); | 
 | 476 | } | 
| David Howells | 3a3b7ce | 2008-11-14 10:39:28 +1100 | [diff] [blame] | 477 |  | 
 | 478 | /** | 
 | 479 |  * prepare_kernel_cred - Prepare a set of credentials for a kernel service | 
 | 480 |  * @daemon: A userspace daemon to be used as a reference | 
 | 481 |  * | 
 | 482 |  * Prepare a set of credentials for a kernel service.  This can then be used to | 
 | 483 |  * override a task's own credentials so that work can be done on behalf of that | 
 | 484 |  * task that requires a different subjective context. | 
 | 485 |  * | 
 | 486 |  * @daemon is used to provide a base for the security record, but can be NULL. | 
 | 487 |  * If @daemon is supplied, then the security data will be derived from that; | 
 | 488 |  * otherwise they'll be set to 0 and no groups, full capabilities and no keys. | 
 | 489 |  * | 
 | 490 |  * The caller may change these controls afterwards if desired. | 
 | 491 |  * | 
 | 492 |  * Returns the new credentials or NULL if out of memory. | 
 | 493 |  * | 
 | 494 |  * Does not take, and does not return holding current->cred_replace_mutex. | 
 | 495 |  */ | 
 | 496 | struct cred *prepare_kernel_cred(struct task_struct *daemon) | 
 | 497 | { | 
 | 498 | 	const struct cred *old; | 
 | 499 | 	struct cred *new; | 
 | 500 |  | 
 | 501 | 	new = kmem_cache_alloc(cred_jar, GFP_KERNEL); | 
 | 502 | 	if (!new) | 
 | 503 | 		return NULL; | 
 | 504 |  | 
 | 505 | 	if (daemon) | 
 | 506 | 		old = get_task_cred(daemon); | 
 | 507 | 	else | 
 | 508 | 		old = get_cred(&init_cred); | 
 | 509 |  | 
| David Howells | 43529c9 | 2009-01-09 16:13:46 +0000 | [diff] [blame] | 510 | 	*new = *old; | 
| David Howells | 3a3b7ce | 2008-11-14 10:39:28 +1100 | [diff] [blame] | 511 | 	get_uid(new->user); | 
 | 512 | 	get_group_info(new->group_info); | 
 | 513 |  | 
 | 514 | #ifdef CONFIG_KEYS | 
 | 515 | 	atomic_inc(&init_tgcred.usage); | 
 | 516 | 	new->tgcred = &init_tgcred; | 
 | 517 | 	new->request_key_auth = NULL; | 
 | 518 | 	new->thread_keyring = NULL; | 
 | 519 | 	new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; | 
 | 520 | #endif | 
 | 521 |  | 
 | 522 | #ifdef CONFIG_SECURITY | 
 | 523 | 	new->security = NULL; | 
 | 524 | #endif | 
 | 525 | 	if (security_prepare_creds(new, old, GFP_KERNEL) < 0) | 
 | 526 | 		goto error; | 
 | 527 |  | 
 | 528 | 	atomic_set(&new->usage, 1); | 
 | 529 | 	put_cred(old); | 
 | 530 | 	return new; | 
 | 531 |  | 
 | 532 | error: | 
 | 533 | 	put_cred(new); | 
| David Howells | 0de3368 | 2009-01-09 16:13:41 +0000 | [diff] [blame] | 534 | 	put_cred(old); | 
| David Howells | 3a3b7ce | 2008-11-14 10:39:28 +1100 | [diff] [blame] | 535 | 	return NULL; | 
 | 536 | } | 
 | 537 | EXPORT_SYMBOL(prepare_kernel_cred); | 
 | 538 |  | 
 | 539 | /** | 
 | 540 |  * set_security_override - Set the security ID in a set of credentials | 
 | 541 |  * @new: The credentials to alter | 
 | 542 |  * @secid: The LSM security ID to set | 
 | 543 |  * | 
 | 544 |  * Set the LSM security ID in a set of credentials so that the subjective | 
 | 545 |  * security is overridden when an alternative set of credentials is used. | 
 | 546 |  */ | 
 | 547 | int set_security_override(struct cred *new, u32 secid) | 
 | 548 | { | 
 | 549 | 	return security_kernel_act_as(new, secid); | 
 | 550 | } | 
 | 551 | EXPORT_SYMBOL(set_security_override); | 
 | 552 |  | 
 | 553 | /** | 
 | 554 |  * set_security_override_from_ctx - Set the security ID in a set of credentials | 
 | 555 |  * @new: The credentials to alter | 
 | 556 |  * @secctx: The LSM security context to generate the security ID from. | 
 | 557 |  * | 
 | 558 |  * Set the LSM security ID in a set of credentials so that the subjective | 
 | 559 |  * security is overridden when an alternative set of credentials is used.  The | 
 | 560 |  * security ID is specified in string form as a security context to be | 
 | 561 |  * interpreted by the LSM. | 
 | 562 |  */ | 
 | 563 | int set_security_override_from_ctx(struct cred *new, const char *secctx) | 
 | 564 | { | 
 | 565 | 	u32 secid; | 
 | 566 | 	int ret; | 
 | 567 |  | 
 | 568 | 	ret = security_secctx_to_secid(secctx, strlen(secctx), &secid); | 
 | 569 | 	if (ret < 0) | 
 | 570 | 		return ret; | 
 | 571 |  | 
 | 572 | 	return set_security_override(new, secid); | 
 | 573 | } | 
 | 574 | EXPORT_SYMBOL(set_security_override_from_ctx); | 
 | 575 |  | 
 | 576 | /** | 
 | 577 |  * set_create_files_as - Set the LSM file create context in a set of credentials | 
 | 578 |  * @new: The credentials to alter | 
 | 579 |  * @inode: The inode to take the context from | 
 | 580 |  * | 
 | 581 |  * Change the LSM file creation context in a set of credentials to be the same | 
 | 582 |  * as the object context of the specified inode, so that the new inodes have | 
 | 583 |  * the same MAC context as that inode. | 
 | 584 |  */ | 
 | 585 | int set_create_files_as(struct cred *new, struct inode *inode) | 
 | 586 | { | 
 | 587 | 	new->fsuid = inode->i_uid; | 
 | 588 | 	new->fsgid = inode->i_gid; | 
 | 589 | 	return security_kernel_create_files_as(new, inode); | 
 | 590 | } | 
 | 591 | EXPORT_SYMBOL(set_create_files_as); |