diff options
Diffstat (limited to 'security/keys/request_key.c')
| -rw-r--r-- | security/keys/request_key.c | 271 | 
1 files changed, 181 insertions, 90 deletions
diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 0088dd8bf68..381411941cc 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -8,7 +8,7 @@   * as published by the Free Software Foundation; either version   * 2 of the License, or (at your option) any later version.   * - * See Documentation/keys-request-key.txt + * See Documentation/security/keys-request-key.txt   */  #include <linux/module.h> @@ -39,8 +39,14 @@ static int key_wait_bit_intr(void *flags)  	return signal_pending(current) ? -ERESTARTSYS : 0;  } -/* - * call to complete the construction of a key +/** + * complete_request_key - Complete the construction of a key. + * @cons: The key construction record. + * @error: The success or failute of the construction. + * + * Complete the attempt to construct a key.  The key will be negated + * if an error is indicated.  The authorisation key will be revoked + * unconditionally.   */  void complete_request_key(struct key_construction *cons, int error)  { @@ -58,40 +64,49 @@ void complete_request_key(struct key_construction *cons, int error)  }  EXPORT_SYMBOL(complete_request_key); -static int umh_keys_init(struct subprocess_info *info) +/* + * Initialise a usermode helper that is going to have a specific session + * keyring. + * + * This is called in context of freshly forked kthread before kernel_execve(), + * so we can simply install the desired session_keyring at this point. + */ +static int umh_keys_init(struct subprocess_info *info, struct cred *cred)  { -	struct cred *cred = (struct cred*)current_cred();  	struct key *keyring = info->data; -	/* -	 * This is called in context of freshly forked kthread before -	 * kernel_execve(), we can just change our ->session_keyring. -	 */ +  	return install_session_keyring_to_cred(cred, keyring);  } +/* + * Clean up a usermode helper with session keyring. + */  static void umh_keys_cleanup(struct subprocess_info *info)  {  	struct key *keyring = info->data;  	key_put(keyring);  } +/* + * Call a usermode helper with a specific session keyring. + */  static int call_usermodehelper_keys(char *path, char **argv, char **envp, -			 struct key *session_keyring, enum umh_wait wait) +					struct key *session_keyring, int wait)  { -	gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; -	struct subprocess_info *info = -		call_usermodehelper_setup(path, argv, envp, gfp_mask); +	struct subprocess_info *info; +	info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL, +					  umh_keys_init, umh_keys_cleanup, +					  session_keyring);  	if (!info)  		return -ENOMEM; -	call_usermodehelper_setfns(info, umh_keys_init, umh_keys_cleanup, -					key_get(session_keyring)); +	key_get(session_keyring);  	return call_usermodehelper_exec(info, wait);  }  /* - * request userspace finish the construction of a key + * Request userspace finish the construction of a key   * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>"   */  static int call_sbin_request_key(struct key_construction *cons, @@ -118,6 +133,7 @@ static int call_sbin_request_key(struct key_construction *cons,  	cred = get_current_cred();  	keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred, +				KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,  				KEY_ALLOC_QUOTA_OVERRUN, NULL);  	put_cred(cred);  	if (IS_ERR(keyring)) { @@ -131,8 +147,8 @@ static int call_sbin_request_key(struct key_construction *cons,  		goto error_link;  	/* record the UID and GID */ -	sprintf(uid_str, "%d", cred->fsuid); -	sprintf(gid_str, "%d", cred->fsgid); +	sprintf(uid_str, "%d", from_kuid(&init_user_ns, cred->fsuid)); +	sprintf(gid_str, "%d", from_kgid(&init_user_ns, cred->fsgid));  	/* we say which key is under construction */  	sprintf(key_str, "%d", key->serial); @@ -142,12 +158,12 @@ static int call_sbin_request_key(struct key_construction *cons,  		cred->thread_keyring ? cred->thread_keyring->serial : 0);  	prkey = 0; -	if (cred->tgcred->process_keyring) -		prkey = cred->tgcred->process_keyring->serial; +	if (cred->process_keyring) +		prkey = cred->process_keyring->serial;  	sprintf(keyring_str[1], "%d", prkey);  	rcu_read_lock(); -	session = rcu_dereference(cred->tgcred->session_keyring); +	session = rcu_dereference(cred->session_keyring);  	if (!session)  		session = cred->user->session_keyring;  	sskey = session->serial; @@ -198,8 +214,9 @@ error_alloc:  }  /* - * call out to userspace for key construction - * - we ignore program failure and go on key status instead + * Call out to userspace for key construction. + * + * Program failure is ignored in favour of key status.   */  static int construct_key(struct key *key, const void *callout_info,  			 size_t callout_len, void *aux, @@ -246,9 +263,10 @@ static int construct_key(struct key *key, const void *callout_info,  }  /* - * get the appropriate destination keyring for the request - * - we return whatever keyring we select with an extra reference upon it which - *   the caller must release + * Get the appropriate destination keyring for the request. + * + * The keyring selected is returned with an extra reference upon it which the + * caller must release.   */  static void construct_get_dest_keyring(struct key **_dest_keyring)  { @@ -287,14 +305,14 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)  				break;  		case KEY_REQKEY_DEFL_PROCESS_KEYRING: -			dest_keyring = key_get(cred->tgcred->process_keyring); +			dest_keyring = key_get(cred->process_keyring);  			if (dest_keyring)  				break;  		case KEY_REQKEY_DEFL_SESSION_KEYRING:  			rcu_read_lock();  			dest_keyring = key_get( -				rcu_dereference(cred->tgcred->session_keyring)); +				rcu_dereference(cred->session_keyring));  			rcu_read_unlock();  			if (dest_keyring) @@ -321,38 +339,48 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)  }  /* - * allocate a new key in under-construction state and attempt to link it in to - * the requested place - * - may return a key that's already under construction instead + * Allocate a new key in under-construction state and attempt to link it in to + * the requested keyring. + * + * May return a key that's already under construction instead if there was a + * race between two thread calling request_key().   */ -static int construct_alloc_key(struct key_type *type, -			       const char *description, +static int construct_alloc_key(struct keyring_search_context *ctx,  			       struct key *dest_keyring,  			       unsigned long flags,  			       struct key_user *user,  			       struct key **_key)  { -	struct keyring_list *prealloc; -	const struct cred *cred = current_cred(); +	struct assoc_array_edit *edit;  	struct key *key; +	key_perm_t perm;  	key_ref_t key_ref;  	int ret; -	kenter("%s,%s,,,", type->name, description); +	kenter("%s,%s,,,", +	       ctx->index_key.type->name, ctx->index_key.description);  	*_key = NULL;  	mutex_lock(&user->cons_lock); -	key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred, -			KEY_POS_ALL, flags); +	perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR; +	perm |= KEY_USR_VIEW; +	if (ctx->index_key.type->read) +		perm |= KEY_POS_READ; +	if (ctx->index_key.type == &key_type_keyring || +	    ctx->index_key.type->update) +		perm |= KEY_POS_WRITE; + +	key = key_alloc(ctx->index_key.type, ctx->index_key.description, +			ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred, +			perm, flags);  	if (IS_ERR(key))  		goto alloc_failed;  	set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);  	if (dest_keyring) { -		ret = __key_link_begin(dest_keyring, type, description, -				       &prealloc); +		ret = __key_link_begin(dest_keyring, &ctx->index_key, &edit);  		if (ret < 0)  			goto link_prealloc_failed;  	} @@ -362,16 +390,16 @@ static int construct_alloc_key(struct key_type *type,  	 * waited for locks */  	mutex_lock(&key_construction_mutex); -	key_ref = search_process_keyrings(type, description, type->match, cred); +	key_ref = search_process_keyrings(ctx);  	if (!IS_ERR(key_ref))  		goto key_already_present;  	if (dest_keyring) -		__key_link(dest_keyring, key, &prealloc); +		__key_link(key, &edit);  	mutex_unlock(&key_construction_mutex);  	if (dest_keyring) -		__key_link_end(dest_keyring, type, prealloc); +		__key_link_end(dest_keyring, &ctx->index_key, edit);  	mutex_unlock(&user->cons_lock);  	*_key = key;  	kleave(" = 0 [%d]", key_serial(key)); @@ -386,8 +414,8 @@ key_already_present:  	if (dest_keyring) {  		ret = __key_link_check_live_key(dest_keyring, key);  		if (ret == 0) -			__key_link(dest_keyring, key, &prealloc); -		__key_link_end(dest_keyring, type, prealloc); +			__key_link(key, &edit); +		__key_link_end(dest_keyring, &ctx->index_key, edit);  		if (ret < 0)  			goto link_check_failed;  	} @@ -403,7 +431,6 @@ link_check_failed:  	return ret;  link_prealloc_failed: -	up_write(&dest_keyring->sem);  	mutex_unlock(&user->cons_lock);  	kleave(" = %d [prelink]", ret);  	return ret; @@ -415,10 +442,9 @@ alloc_failed:  }  /* - * commence key construction + * Commence key construction.   */ -static struct key *construct_key_and_link(struct key_type *type, -					  const char *description, +static struct key *construct_key_and_link(struct keyring_search_context *ctx,  					  const char *callout_info,  					  size_t callout_len,  					  void *aux, @@ -431,14 +457,13 @@ static struct key *construct_key_and_link(struct key_type *type,  	kenter(""); -	user = key_user_lookup(current_fsuid(), current_user_ns()); +	user = key_user_lookup(current_fsuid());  	if (!user)  		return ERR_PTR(-ENOMEM);  	construct_get_dest_keyring(&dest_keyring); -	ret = construct_alloc_key(type, description, dest_keyring, flags, user, -				  &key); +	ret = construct_alloc_key(ctx, dest_keyring, flags, user, &key);  	key_user_put(user);  	if (ret == 0) { @@ -451,7 +476,7 @@ static struct key *construct_key_and_link(struct key_type *type,  	} else if (ret == -EINPROGRESS) {  		ret = 0;  	} else { -		key = ERR_PTR(ret); +		goto couldnt_alloc_key;  	}  	key_put(dest_keyring); @@ -461,17 +486,38 @@ static struct key *construct_key_and_link(struct key_type *type,  construction_failed:  	key_negate_and_link(key, key_negative_timeout, NULL, NULL);  	key_put(key); +couldnt_alloc_key:  	key_put(dest_keyring);  	kleave(" = %d", ret);  	return ERR_PTR(ret);  } -/* - * request a key - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided - * - cache the key in an appropriate keyring +/** + * request_key_and_link - Request a key and cache it in a keyring. + * @type: The type of key we want. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * @callout_len: The length of callout_info. + * @aux: Auxiliary data for the upcall. + * @dest_keyring: Where to cache the key. + * @flags: Flags to key_alloc(). + * + * A key matching the specified criteria is searched for in the process's + * keyrings and returned with its usage count incremented if found.  Otherwise, + * if callout_info is not NULL, a key will be allocated and some service + * (probably in userspace) will be asked to instantiate it. + * + * If successfully found or created, the key will be linked to the destination + * keyring if one is provided. + * + * Returns a pointer to the key if successful; -EACCES, -ENOKEY, -EKEYREVOKED + * or -EKEYEXPIRED if an inaccessible, negative, revoked or expired key was + * found; -ENOKEY if no key was found and no @callout_info was given; -EDQUOT + * if insufficient key quota was available to create a new key; or -ENOMEM if + * insufficient memory was available. + * + * If the returned key was created, then it may still be under construction, + * and wait_for_key_construction() should be used to wait for that to complete.   */  struct key *request_key_and_link(struct key_type *type,  				 const char *description, @@ -481,18 +527,24 @@ struct key *request_key_and_link(struct key_type *type,  				 struct key *dest_keyring,  				 unsigned long flags)  { -	const struct cred *cred = current_cred(); +	struct keyring_search_context ctx = { +		.index_key.type		= type, +		.index_key.description	= description, +		.cred			= current_cred(), +		.match			= type->match, +		.match_data		= description, +		.flags			= KEYRING_SEARCH_LOOKUP_DIRECT, +	};  	struct key *key;  	key_ref_t key_ref;  	int ret;  	kenter("%s,%s,%p,%zu,%p,%p,%lx", -	       type->name, description, callout_info, callout_len, aux, -	       dest_keyring, flags); +	       ctx.index_key.type->name, ctx.index_key.description, +	       callout_info, callout_len, aux, dest_keyring, flags);  	/* search all the process keyrings for a key */ -	key_ref = search_process_keyrings(type, description, type->match, -					  cred); +	key_ref = search_process_keyrings(&ctx);  	if (!IS_ERR(key_ref)) {  		key = key_ref_to_ptr(key_ref); @@ -515,9 +567,8 @@ struct key *request_key_and_link(struct key_type *type,  		if (!callout_info)  			goto error; -		key = construct_key_and_link(type, description, callout_info, -					     callout_len, aux, dest_keyring, -					     flags); +		key = construct_key_and_link(&ctx, callout_info, callout_len, +					     aux, dest_keyring, flags);  	}  error: @@ -525,8 +576,16 @@ error:  	return key;  } -/* - * wait for construction of a key to complete +/** + * wait_for_key_construction - Wait for construction of a key to complete + * @key: The key being waited for. + * @intr: Whether to wait interruptibly. + * + * Wait for a key to finish being constructed. + * + * Returns 0 if successful; -ERESTARTSYS if the wait was interrupted; -ENOKEY + * if the key was negated; or -EKEYREVOKED or -EKEYEXPIRED if the key was + * revoked or expired.   */  int wait_for_key_construction(struct key *key, bool intr)  { @@ -537,18 +596,27 @@ int wait_for_key_construction(struct key *key, bool intr)  			  intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);  	if (ret < 0)  		return ret; -	if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) -		return -ENOKEY; +	if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { +		smp_rmb(); +		return key->type_data.reject_error; +	}  	return key_validate(key);  }  EXPORT_SYMBOL(wait_for_key_construction); -/* - * request a key - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided - * - waits uninterruptible for creation to complete +/** + * request_key - Request a key and wait for construction + * @type: Type of key. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * + * As for request_key_and_link() except that it does not add the returned key + * to a keyring if found, new keys are always allocated in the user's quota, + * the callout_info must be a NUL-terminated string and no auxiliary data can + * be passed. + * + * Furthermore, it then works as wait_for_key_construction() to wait for the + * completion of keys undergoing construction with a non-interruptible wait.   */  struct key *request_key(struct key_type *type,  			const char *description, @@ -573,12 +641,19 @@ struct key *request_key(struct key_type *type,  }  EXPORT_SYMBOL(request_key); -/* - * request a key with auxiliary data for the upcaller - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided - * - waits uninterruptible for creation to complete +/** + * request_key_with_auxdata - Request a key with auxiliary data for the upcaller + * @type: The type of key we want. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * @callout_len: The length of callout_info. + * @aux: Auxiliary data for the upcall. + * + * As for request_key_and_link() except that it does not add the returned key + * to a keyring if found and new keys are always allocated in the user's quota. + * + * Furthermore, it then works as wait_for_key_construction() to wait for the + * completion of keys undergoing construction with a non-interruptible wait.   */  struct key *request_key_with_auxdata(struct key_type *type,  				     const char *description, @@ -603,10 +678,18 @@ struct key *request_key_with_auxdata(struct key_type *type,  EXPORT_SYMBOL(request_key_with_auxdata);  /* - * request a key (allow async construction) - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided + * request_key_async - Request a key (allow async construction) + * @type: Type of key. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * @callout_len: The length of callout_info. + * + * As for request_key_and_link() except that it does not add the returned key + * to a keyring if found, new keys are always allocated in the user's quota and + * no auxiliary data can be passed. + * + * The caller should call wait_for_key_construction() to wait for the + * completion of the returned key if it is still undergoing construction.   */  struct key *request_key_async(struct key_type *type,  			      const char *description, @@ -621,9 +704,17 @@ EXPORT_SYMBOL(request_key_async);  /*   * request a key with auxiliary data for the upcaller (allow async construction) - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided + * @type: Type of key. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * @callout_len: The length of callout_info. + * @aux: Auxiliary data for the upcall. + * + * As for request_key_and_link() except that it does not add the returned key + * to a keyring if found and new keys are always allocated in the user's quota. + * + * The caller should call wait_for_key_construction() to wait for the + * completion of the returned key if it is still undergoing construction.   */  struct key *request_key_async_with_auxdata(struct key_type *type,  					   const char *description,  | 
