diff options
Diffstat (limited to 'security/keys/process_keys.c')
| -rw-r--r-- | security/keys/process_keys.c | 333 | 
1 files changed, 164 insertions, 169 deletions
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); - -	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; -	} - -	task_lock(tsk); -	old = tsk->cred->thread_keyring; -	tsk->cred->thread_keyring = keyring; -	task_unlock(tsk); +	new = prepare_creds(); +	if (!new) +		return -ENOMEM; -	ret = 0; +	BUG_ON(new->thread_keyring); -	key_put(old); -error: -	return ret; +	ret = install_thread_keyring_to_cred(new); +	if (ret < 0) { +		abort_creds(new); +		return ret; +	} -} /* end install_thread_keyring() */ +	return commit_creds(new); +} -/*****************************************************************************/  /* - * make sure a process keyring is installed + * 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(void) +int install_process_keyring_to_cred(struct cred *new)  { -	struct task_struct *tsk = current;  	struct key *keyring; -	char buf[20];  	int ret; -	might_sleep(); +	if (new->tgcred->process_keyring) +		return -EEXIST; -	if (!tsk->cred->tgcred->process_keyring) { -		sprintf(buf, "_pid.%u", tsk->tgid); +	keyring = keyring_alloc("_pid", new->uid, new->gid, +				new, KEY_ALLOC_QUOTA_OVERRUN, NULL); +	if (IS_ERR(keyring)) +		return PTR_ERR(keyring); -		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; -		} +	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; +} -		/* 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); +/* + * make sure a process keyring is installed + * - we + */ +static int install_process_keyring(void) +{ +	struct cred *new; +	int ret; -		key_put(keyring); -	} +	new = prepare_creds(); +	if (!new) +		return -ENOMEM; -	ret = 0; -error: -	return ret; +	ret = install_process_keyring_to_cred(new); +	if (ret < 0) { +		abort_creds(new); +		return ret != -EEXIST ?: 0; +	} -} /* 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() */ +}  |