diff options
Diffstat (limited to 'security/keys')
| -rw-r--r-- | security/keys/encrypted-keys/encrypted.c | 16 | ||||
| -rw-r--r-- | security/keys/key.c | 114 | ||||
| -rw-r--r-- | security/keys/keyctl.c | 18 | ||||
| -rw-r--r-- | security/keys/keyring.c | 6 | ||||
| -rw-r--r-- | security/keys/request_key_auth.c | 8 | ||||
| -rw-r--r-- | security/keys/trusted.c | 16 | ||||
| -rw-r--r-- | security/keys/user_defined.c | 14 | 
7 files changed, 129 insertions, 63 deletions
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 2d1bb8af769..9e1e005c759 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -773,8 +773,8 @@ static int encrypted_init(struct encrypted_key_payload *epayload,   *   * On success, return 0. Otherwise return errno.   */ -static int encrypted_instantiate(struct key *key, const void *data, -				 size_t datalen) +static int encrypted_instantiate(struct key *key, +				 struct key_preparsed_payload *prep)  {  	struct encrypted_key_payload *epayload = NULL;  	char *datablob = NULL; @@ -782,16 +782,17 @@ static int encrypted_instantiate(struct key *key, const void *data,  	char *master_desc = NULL;  	char *decrypted_datalen = NULL;  	char *hex_encoded_iv = NULL; +	size_t datalen = prep->datalen;  	int ret; -	if (datalen <= 0 || datalen > 32767 || !data) +	if (datalen <= 0 || datalen > 32767 || !prep->data)  		return -EINVAL;  	datablob = kmalloc(datalen + 1, GFP_KERNEL);  	if (!datablob)  		return -ENOMEM;  	datablob[datalen] = 0; -	memcpy(datablob, data, datalen); +	memcpy(datablob, prep->data, datalen);  	ret = datablob_parse(datablob, &format, &master_desc,  			     &decrypted_datalen, &hex_encoded_iv);  	if (ret < 0) @@ -834,16 +835,17 @@ static void encrypted_rcu_free(struct rcu_head *rcu)   *   * On success, return 0. Otherwise return errno.   */ -static int encrypted_update(struct key *key, const void *data, size_t datalen) +static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)  {  	struct encrypted_key_payload *epayload = key->payload.data;  	struct encrypted_key_payload *new_epayload;  	char *buf;  	char *new_master_desc = NULL;  	const char *format = NULL; +	size_t datalen = prep->datalen;  	int ret = 0; -	if (datalen <= 0 || datalen > 32767 || !data) +	if (datalen <= 0 || datalen > 32767 || !prep->data)  		return -EINVAL;  	buf = kmalloc(datalen + 1, GFP_KERNEL); @@ -851,7 +853,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen)  		return -ENOMEM;  	buf[datalen] = 0; -	memcpy(buf, data, datalen); +	memcpy(buf, prep->data, datalen);  	ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL);  	if (ret < 0)  		goto out; diff --git a/security/keys/key.c b/security/keys/key.c index a30e9273490..a15c9da8f97 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -405,8 +405,7 @@ EXPORT_SYMBOL(key_payload_reserve);   * key_construction_mutex.   */  static int __key_instantiate_and_link(struct key *key, -				      const void *data, -				      size_t datalen, +				      struct key_preparsed_payload *prep,  				      struct key *keyring,  				      struct key *authkey,  				      unsigned long *_prealloc) @@ -424,7 +423,7 @@ static int __key_instantiate_and_link(struct key *key,  	/* can't instantiate twice */  	if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {  		/* instantiate the key */ -		ret = key->type->instantiate(key, data, datalen); +		ret = key->type->instantiate(key, prep);  		if (ret == 0) {  			/* mark the key as being instantiated */ @@ -475,22 +474,37 @@ int key_instantiate_and_link(struct key *key,  			     struct key *keyring,  			     struct key *authkey)  { +	struct key_preparsed_payload prep;  	unsigned long prealloc;  	int ret; +	memset(&prep, 0, sizeof(prep)); +	prep.data = data; +	prep.datalen = datalen; +	prep.quotalen = key->type->def_datalen; +	if (key->type->preparse) { +		ret = key->type->preparse(&prep); +		if (ret < 0) +			goto error; +	} +  	if (keyring) {  		ret = __key_link_begin(keyring, key->type, key->description,  				       &prealloc);  		if (ret < 0) -			return ret; +			goto error_free_preparse;  	} -	ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey, +	ret = __key_instantiate_and_link(key, &prep, keyring, authkey,  					 &prealloc);  	if (keyring)  		__key_link_end(keyring, key->type, prealloc); +error_free_preparse: +	if (key->type->preparse) +		key->type->free_preparse(&prep); +error:  	return ret;  } @@ -699,7 +713,7 @@ void key_type_put(struct key_type *ktype)   * if we get an error.   */  static inline key_ref_t __key_update(key_ref_t key_ref, -				     const void *payload, size_t plen) +				     struct key_preparsed_payload *prep)  {  	struct key *key = key_ref_to_ptr(key_ref);  	int ret; @@ -715,7 +729,7 @@ static inline key_ref_t __key_update(key_ref_t key_ref,  	down_write(&key->sem); -	ret = key->type->update(key, payload, plen); +	ret = key->type->update(key, prep);  	if (ret == 0)  		/* updating a negative key instantiates it */  		clear_bit(KEY_FLAG_NEGATIVE, &key->flags); @@ -767,6 +781,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,  			       unsigned long flags)  {  	unsigned long prealloc; +	struct key_preparsed_payload prep;  	const struct cred *cred = current_cred();  	struct key_type *ktype;  	struct key *keyring, *key = NULL; @@ -782,8 +797,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,  	}  	key_ref = ERR_PTR(-EINVAL); -	if (!ktype->match || !ktype->instantiate) -		goto error_2; +	if (!ktype->match || !ktype->instantiate || +	    (!description && !ktype->preparse)) +		goto error_put_type;  	keyring = key_ref_to_ptr(keyring_ref); @@ -791,18 +807,37 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,  	key_ref = ERR_PTR(-ENOTDIR);  	if (keyring->type != &key_type_keyring) -		goto error_2; +		goto error_put_type; + +	memset(&prep, 0, sizeof(prep)); +	prep.data = payload; +	prep.datalen = plen; +	prep.quotalen = ktype->def_datalen; +	if (ktype->preparse) { +		ret = ktype->preparse(&prep); +		if (ret < 0) { +			key_ref = ERR_PTR(ret); +			goto error_put_type; +		} +		if (!description) +			description = prep.description; +		key_ref = ERR_PTR(-EINVAL); +		if (!description) +			goto error_free_prep; +	}  	ret = __key_link_begin(keyring, ktype, description, &prealloc); -	if (ret < 0) -		goto error_2; +	if (ret < 0) { +		key_ref = ERR_PTR(ret); +		goto error_free_prep; +	}  	/* if we're going to allocate a new key, we're going to have  	 * to modify the keyring */  	ret = key_permission(keyring_ref, KEY_WRITE);  	if (ret < 0) {  		key_ref = ERR_PTR(ret); -		goto error_3; +		goto error_link_end;  	}  	/* if it's possible to update this type of key, search for an existing @@ -833,25 +868,27 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,  			perm, flags);  	if (IS_ERR(key)) {  		key_ref = ERR_CAST(key); -		goto error_3; +		goto error_link_end;  	}  	/* instantiate it and link it into the target keyring */ -	ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL, -					 &prealloc); +	ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &prealloc);  	if (ret < 0) {  		key_put(key);  		key_ref = ERR_PTR(ret); -		goto error_3; +		goto error_link_end;  	}  	key_ref = make_key_ref(key, is_key_possessed(keyring_ref)); - error_3: +error_link_end:  	__key_link_end(keyring, ktype, prealloc); - error_2: +error_free_prep: +	if (ktype->preparse) +		ktype->free_preparse(&prep); +error_put_type:  	key_type_put(ktype); - error: +error:  	return key_ref;   found_matching_key: @@ -859,10 +896,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,  	 * - we can drop the locks first as we have the key pinned  	 */  	__key_link_end(keyring, ktype, prealloc); -	key_type_put(ktype); -	key_ref = __key_update(key_ref, payload, plen); -	goto error; +	key_ref = __key_update(key_ref, &prep); +	goto error_free_prep;  }  EXPORT_SYMBOL(key_create_or_update); @@ -881,6 +917,7 @@ EXPORT_SYMBOL(key_create_or_update);   */  int key_update(key_ref_t key_ref, const void *payload, size_t plen)  { +	struct key_preparsed_payload prep;  	struct key *key = key_ref_to_ptr(key_ref);  	int ret; @@ -893,18 +930,31 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)  	/* attempt to update it if supported */  	ret = -EOPNOTSUPP; -	if (key->type->update) { -		down_write(&key->sem); - -		ret = key->type->update(key, payload, plen); -		if (ret == 0) -			/* updating a negative key instantiates it */ -			clear_bit(KEY_FLAG_NEGATIVE, &key->flags); +	if (!key->type->update) +		goto error; -		up_write(&key->sem); +	memset(&prep, 0, sizeof(prep)); +	prep.data = payload; +	prep.datalen = plen; +	prep.quotalen = key->type->def_datalen; +	if (key->type->preparse) { +		ret = key->type->preparse(&prep); +		if (ret < 0) +			goto error;  	} - error: +	down_write(&key->sem); + +	ret = key->type->update(key, &prep); +	if (ret == 0) +		/* updating a negative key instantiates it */ +		clear_bit(KEY_FLAG_NEGATIVE, &key->flags); + +	up_write(&key->sem); + +	if (key->type->preparse) +		key->type->free_preparse(&prep); +error:  	return ret;  }  EXPORT_SYMBOL(key_update); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 305ecb76519..5d34b4e827d 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -46,6 +46,9 @@ static int key_get_type_from_user(char *type,   * Extract the description of a new key from userspace and either add it as a   * new key to the specified keyring or update a matching key in that keyring.   * + * If the description is NULL or an empty string, the key type is asked to + * generate one from the payload. + *   * The keyring must be writable so that we can attach the key to it.   *   * If successful, the new key's serial number is returned, otherwise an error @@ -72,10 +75,17 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,  	if (ret < 0)  		goto error; -	description = strndup_user(_description, PAGE_SIZE); -	if (IS_ERR(description)) { -		ret = PTR_ERR(description); -		goto error; +	description = NULL; +	if (_description) { +		description = strndup_user(_description, PAGE_SIZE); +		if (IS_ERR(description)) { +			ret = PTR_ERR(description); +			goto error; +		} +		if (!*description) { +			kfree(description); +			description = NULL; +		}  	}  	/* pull the payload in if one was supplied */ diff --git a/security/keys/keyring.c b/security/keys/keyring.c index a5f5c4b6edc..6e42df15a24 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -66,7 +66,7 @@ static inline unsigned keyring_hash(const char *desc)   * operations.   */  static int keyring_instantiate(struct key *keyring, -			       const void *data, size_t datalen); +			       struct key_preparsed_payload *prep);  static int keyring_match(const struct key *keyring, const void *criterion);  static void keyring_revoke(struct key *keyring);  static void keyring_destroy(struct key *keyring); @@ -121,12 +121,12 @@ static void keyring_publish_name(struct key *keyring)   * Returns 0 on success, -EINVAL if given any data.   */  static int keyring_instantiate(struct key *keyring, -			       const void *data, size_t datalen) +			       struct key_preparsed_payload *prep)  {  	int ret;  	ret = -EINVAL; -	if (datalen == 0) { +	if (prep->datalen == 0) {  		/* make the keyring available by name if it has one */  		keyring_publish_name(keyring);  		ret = 0; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 60d4e3f5e4b..85730d5a5a5 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -19,7 +19,8 @@  #include <asm/uaccess.h>  #include "internal.h" -static int request_key_auth_instantiate(struct key *, const void *, size_t); +static int request_key_auth_instantiate(struct key *, +					struct key_preparsed_payload *);  static void request_key_auth_describe(const struct key *, struct seq_file *);  static void request_key_auth_revoke(struct key *);  static void request_key_auth_destroy(struct key *); @@ -42,10 +43,9 @@ struct key_type key_type_request_key_auth = {   * Instantiate a request-key authorisation key.   */  static int request_key_auth_instantiate(struct key *key, -					const void *data, -					size_t datalen) +					struct key_preparsed_payload *prep)  { -	key->payload.data = (struct request_key_auth *) data; +	key->payload.data = (struct request_key_auth *)prep->data;  	return 0;  } diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 3f163d0489a..e13fcf7636f 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -895,23 +895,24 @@ static struct trusted_key_payload *trusted_payload_alloc(struct key *key)   *   * On success, return 0. Otherwise return errno.   */ -static int trusted_instantiate(struct key *key, const void *data, -			       size_t datalen) +static int trusted_instantiate(struct key *key, +			       struct key_preparsed_payload *prep)  {  	struct trusted_key_payload *payload = NULL;  	struct trusted_key_options *options = NULL; +	size_t datalen = prep->datalen;  	char *datablob;  	int ret = 0;  	int key_cmd;  	size_t key_len; -	if (datalen <= 0 || datalen > 32767 || !data) +	if (datalen <= 0 || datalen > 32767 || !prep->data)  		return -EINVAL;  	datablob = kmalloc(datalen + 1, GFP_KERNEL);  	if (!datablob)  		return -ENOMEM; -	memcpy(datablob, data, datalen); +	memcpy(datablob, prep->data, datalen);  	datablob[datalen] = '\0';  	options = trusted_options_alloc(); @@ -981,17 +982,18 @@ static void trusted_rcu_free(struct rcu_head *rcu)  /*   * trusted_update - reseal an existing key with new PCR values   */ -static int trusted_update(struct key *key, const void *data, size_t datalen) +static int trusted_update(struct key *key, struct key_preparsed_payload *prep)  {  	struct trusted_key_payload *p = key->payload.data;  	struct trusted_key_payload *new_p;  	struct trusted_key_options *new_o; +	size_t datalen = prep->datalen;  	char *datablob;  	int ret = 0;  	if (!p->migratable)  		return -EPERM; -	if (datalen <= 0 || datalen > 32767 || !data) +	if (datalen <= 0 || datalen > 32767 || !prep->data)  		return -EINVAL;  	datablob = kmalloc(datalen + 1, GFP_KERNEL); @@ -1008,7 +1010,7 @@ static int trusted_update(struct key *key, const void *data, size_t datalen)  		goto out;  	} -	memcpy(datablob, data, datalen); +	memcpy(datablob, prep->data, datalen);  	datablob[datalen] = '\0';  	ret = datablob_parse(datablob, new_p, new_o);  	if (ret != Opt_update) { diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index c7660a25a3e..55dc8893918 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -58,13 +58,14 @@ EXPORT_SYMBOL_GPL(key_type_logon);  /*   * instantiate a user defined key   */ -int user_instantiate(struct key *key, const void *data, size_t datalen) +int user_instantiate(struct key *key, struct key_preparsed_payload *prep)  {  	struct user_key_payload *upayload; +	size_t datalen = prep->datalen;  	int ret;  	ret = -EINVAL; -	if (datalen <= 0 || datalen > 32767 || !data) +	if (datalen <= 0 || datalen > 32767 || !prep->data)  		goto error;  	ret = key_payload_reserve(key, datalen); @@ -78,7 +79,7 @@ int user_instantiate(struct key *key, const void *data, size_t datalen)  	/* attach the data */  	upayload->datalen = datalen; -	memcpy(upayload->data, data, datalen); +	memcpy(upayload->data, prep->data, datalen);  	rcu_assign_keypointer(key, upayload);  	ret = 0; @@ -92,13 +93,14 @@ EXPORT_SYMBOL_GPL(user_instantiate);   * update a user defined key   * - the key's semaphore is write-locked   */ -int user_update(struct key *key, const void *data, size_t datalen) +int user_update(struct key *key, struct key_preparsed_payload *prep)  {  	struct user_key_payload *upayload, *zap; +	size_t datalen = prep->datalen;  	int ret;  	ret = -EINVAL; -	if (datalen <= 0 || datalen > 32767 || !data) +	if (datalen <= 0 || datalen > 32767 || !prep->data)  		goto error;  	/* construct a replacement payload */ @@ -108,7 +110,7 @@ int user_update(struct key *key, const void *data, size_t datalen)  		goto error;  	upayload->datalen = datalen; -	memcpy(upayload->data, data, datalen); +	memcpy(upayload->data, prep->data, datalen);  	/* check the quota and attach the new data */  	zap = upayload;  |