diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index a4c33f1a7c6de5dc2207a21bab00846266668f90..8727c194ca16a56f483b2f0cd2712654c0b948b4 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1150,20 +1150,24 @@ The structure has a number of fields, some of which are mandatory: const void *data; size_t datalen; size_t quotalen; + time_t expiry; }; Before calling the method, the caller will fill in data and datalen with the payload blob parameters; quotalen will be filled in with the default - quota size from the key type and the rest will be cleared. + quota size from the key type; expiry will be set to TIME_T_MAX and the + rest will be cleared. If a description can be proposed from the payload contents, that should be attached as a string to the description field. This will be used for the key description if the caller of add_key() passes NULL or "". The method can attach anything it likes to type_data[] and payload. These - are merely passed along to the instantiate() or update() operations. + are merely passed along to the instantiate() or update() operations. If + set, the expiry time will be applied to the key if it is instantiated from + this data. - The method should return 0 if success ful or a negative error code + The method should return 0 if successful or a negative error code otherwise. @@ -1172,7 +1176,9 @@ The structure has a number of fields, some of which are mandatory: This method is only required if the preparse() method is provided, otherwise it is unused. It cleans up anything attached to the description, type_data and payload fields of the key_preparsed_payload - struct as filled in by the preparse() method. + struct as filled in by the preparse() method. It will always be called + after preparse() returns successfully, even if instantiate() or update() + succeed. (*) int (*instantiate)(struct key *key, struct key_preparsed_payload *prep); diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index b77eb53047882ad26de8de4892e8568a4b792326..21960a4e74e8c193e8a69e07e0611c7139aaa82f 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -156,36 +156,13 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) pr_devel("==>%s()\n", __func__); if (subtype) { - subtype->destroy(prep->payload); + subtype->destroy(prep->payload[0]); module_put(subtype->owner); } kfree(prep->type_data[1]); kfree(prep->description); } -/* - * Instantiate a asymmetric_key defined key. The key was preparsed, so we just - * have to transfer the data here. - */ -static int asymmetric_key_instantiate(struct key *key, struct key_preparsed_payload *prep) -{ - int ret; - - pr_devel("==>%s()\n", __func__); - - ret = key_payload_reserve(key, prep->quotalen); - if (ret == 0) { - key->type_data.p[0] = prep->type_data[0]; - key->type_data.p[1] = prep->type_data[1]; - key->payload.data = prep->payload; - prep->type_data[0] = NULL; - prep->type_data[1] = NULL; - prep->payload = NULL; - } - pr_devel("<==%s() = %d\n", __func__, ret); - return ret; -} - /* * dispose of the data dangling from the corpse of a asymmetric key */ @@ -205,7 +182,7 @@ struct key_type key_type_asymmetric = { .name = "asymmetric", .preparse = asymmetric_key_preparse, .free_preparse = asymmetric_key_free_preparse, - .instantiate = asymmetric_key_instantiate, + .instantiate = generic_key_instantiate, .match = asymmetric_key_match, .destroy = asymmetric_key_destroy, .describe = asymmetric_key_describe, diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 382ef0d2ff2e5e030c0068d1076155364d48f43d..3fc8a0634ed7daa5eabd40a25f79e02374e67fbb 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -177,7 +177,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) __module_get(public_key_subtype.owner); prep->type_data[0] = &public_key_subtype; prep->type_data[1] = cert->fingerprint; - prep->payload = cert->pub; + prep->payload[0] = cert->pub; prep->description = desc; prep->quotalen = 100; diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 567983d2c0ebada3105f5c1ef42bc0c4595c2ca6..59b217a3266d17d170161cbe690cd8999735abaa 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -174,7 +174,9 @@ static int nfs_map_numeric_to_string(__u32 id, char *buf, size_t buflen) static struct key_type key_type_id_resolver = { .name = "id_resolver", - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .match = user_match, .revoke = user_revoke, .destroy = user_destroy, @@ -394,7 +396,9 @@ static const struct rpc_pipe_ops idmap_upcall_ops = { static struct key_type key_type_id_resolver_legacy = { .name = "id_legacy", - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .match = user_match, .revoke = user_revoke, .destroy = user_destroy, diff --git a/include/keys/big_key-type.h b/include/keys/big_key-type.h index d69bc8af32920eb87f66c30611d10684bb822f18..e0970a578188ccaf563340f115e2aaeadb65267e 100644 --- a/include/keys/big_key-type.h +++ b/include/keys/big_key-type.h @@ -16,7 +16,8 @@ extern struct key_type key_type_big_key; -extern int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep); +extern int big_key_preparse(struct key_preparsed_payload *prep); +extern void big_key_free_preparse(struct key_preparsed_payload *prep); extern void big_key_revoke(struct key *key); extern void big_key_destroy(struct key *key); extern void big_key_describe(const struct key *big_key, struct seq_file *m); diff --git a/include/keys/user-type.h b/include/keys/user-type.h index 5e452c84f1e6741bc1b85405b2fce971c12051f0..3ab1873a4bfab05571bdbc60a60c8a31df5dfde5 100644 --- a/include/keys/user-type.h +++ b/include/keys/user-type.h @@ -37,7 +37,8 @@ extern struct key_type key_type_logon; struct key_preparsed_payload; -extern int user_instantiate(struct key *key, struct key_preparsed_payload *prep); +extern int user_preparse(struct key_preparsed_payload *prep); +extern void user_free_preparse(struct key_preparsed_payload *prep); extern int user_update(struct key *key, struct key_preparsed_payload *prep); extern int user_match(const struct key *key, const void *criterion); extern void user_revoke(struct key *key); diff --git a/include/linux/key-type.h b/include/linux/key-type.h index a74c3a84dfdd05223cc0613c9445bdc396e1afe9..44792ee649de2513b8f5dba53d29758cacef2f24 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -41,10 +41,11 @@ struct key_construction { struct key_preparsed_payload { char *description; /* Proposed key description (or NULL) */ void *type_data[2]; /* Private key-type data */ - void *payload; /* Proposed payload */ + void *payload[2]; /* Proposed payload */ const void *data; /* Raw data */ size_t datalen; /* Raw datalen */ size_t quotalen; /* Quota length for proposed payload */ + time_t expiry; /* Expiry time of key */ bool trusted; /* True if key is trusted */ }; @@ -159,5 +160,7 @@ static inline int key_negate_and_link(struct key *key, return key_reject_and_link(key, timeout, ENOKEY, keyring, instkey); } +extern int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep); + #endif /* CONFIG_KEYS */ #endif /* _LINUX_KEY_TYPE_H */ diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c index 6e7a236525b6ff92d9cd2e44770cb6f7334b0b02..ffeba8f9dda929df4f111a9ef652cf1830f348a9 100644 --- a/net/ceph/crypto.c +++ b/net/ceph/crypto.c @@ -8,6 +8,7 @@ #include #include +#include #include #include "crypto.h" @@ -423,8 +424,7 @@ int ceph_encrypt2(struct ceph_crypto_key *secret, void *dst, size_t *dst_len, } } -static int ceph_key_instantiate(struct key *key, - struct key_preparsed_payload *prep) +static int ceph_key_preparse(struct key_preparsed_payload *prep) { struct ceph_crypto_key *ckey; size_t datalen = prep->datalen; @@ -435,10 +435,6 @@ static int ceph_key_instantiate(struct key *key, if (datalen <= 0 || datalen > 32767 || !prep->data) goto err; - ret = key_payload_reserve(key, datalen); - if (ret < 0) - goto err; - ret = -ENOMEM; ckey = kmalloc(sizeof(*ckey), GFP_KERNEL); if (!ckey) @@ -450,7 +446,8 @@ static int ceph_key_instantiate(struct key *key, if (ret < 0) goto err_ckey; - key->payload.data = ckey; + prep->payload[0] = ckey; + prep->quotalen = datalen; return 0; err_ckey: @@ -459,12 +456,15 @@ static int ceph_key_instantiate(struct key *key, return ret; } -static int ceph_key_match(const struct key *key, const void *description) +static void ceph_key_free_preparse(struct key_preparsed_payload *prep) { - return strcmp(key->description, description) == 0; + struct ceph_crypto_key *ckey = prep->payload[0]; + ceph_crypto_key_destroy(ckey); + kfree(ckey); } -static void ceph_key_destroy(struct key *key) { +static void ceph_key_destroy(struct key *key) +{ struct ceph_crypto_key *ckey = key->payload.data; ceph_crypto_key_destroy(ckey); @@ -473,8 +473,10 @@ static void ceph_key_destroy(struct key *key) { struct key_type key_type_ceph = { .name = "ceph", - .instantiate = ceph_key_instantiate, - .match = ceph_key_match, + .preparse = ceph_key_preparse, + .free_preparse = ceph_key_free_preparse, + .instantiate = generic_key_instantiate, + .match = user_match, .destroy = ceph_key_destroy, }; diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index bf85843390488d7768e3b3457c7e9e1139e50c19..f380b2c581781433f2091fec38c7ad77f46f1fae 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -46,7 +46,7 @@ const struct cred *dns_resolver_cache; #define DNS_ERRORNO_OPTION "dnserror" /* - * Instantiate a user defined key for dns_resolver. + * Preparse instantiation data for a dns_resolver key. * * The data must be a NUL-terminated string, with the NUL char accounted in * datalen. @@ -58,17 +58,15 @@ const struct cred *dns_resolver_cache; * "ip1,ip2,...#foo=bar" */ static int -dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep) +dns_resolver_preparse(struct key_preparsed_payload *prep) { struct user_key_payload *upayload; unsigned long derrno; int ret; - size_t datalen = prep->datalen, result_len = 0; + int datalen = prep->datalen, result_len = 0; const char *data = prep->data, *end, *opt; - kenter("%%%d,%s,'%*.*s',%zu", - key->serial, key->description, - (int)datalen, (int)datalen, data, datalen); + kenter("'%*.*s',%u", datalen, datalen, data, datalen); if (datalen <= 1 || !data || data[datalen - 1] != '\0') return -EINVAL; @@ -95,8 +93,7 @@ dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep) opt_len = next_opt - opt; if (!opt_len) { printk(KERN_WARNING - "Empty option to dns_resolver key %d\n", - key->serial); + "Empty option to dns_resolver key\n"); return -EINVAL; } @@ -125,30 +122,28 @@ dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep) goto bad_option_value; kdebug("dns error no. = %lu", derrno); - key->type_data.x[0] = -derrno; + prep->type_data[0] = ERR_PTR(-derrno); continue; } bad_option_value: printk(KERN_WARNING - "Option '%*.*s' to dns_resolver key %d:" + "Option '%*.*s' to dns_resolver key:" " bad/missing value\n", - opt_nlen, opt_nlen, opt, key->serial); + opt_nlen, opt_nlen, opt); return -EINVAL; } while (opt = next_opt + 1, opt < end); } /* don't cache the result if we're caching an error saying there's no * result */ - if (key->type_data.x[0]) { - kleave(" = 0 [h_error %ld]", key->type_data.x[0]); + if (prep->type_data[0]) { + kleave(" = 0 [h_error %ld]", PTR_ERR(prep->type_data[0])); return 0; } kdebug("store result"); - ret = key_payload_reserve(key, result_len); - if (ret < 0) - return -EINVAL; + prep->quotalen = result_len; upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL); if (!upayload) { @@ -159,12 +154,22 @@ dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep) upayload->datalen = result_len; memcpy(upayload->data, data, result_len); upayload->data[result_len] = '\0'; - rcu_assign_pointer(key->payload.data, upayload); + prep->payload[0] = upayload; kleave(" = 0"); return 0; } +/* + * Clean up the preparse data + */ +static void dns_resolver_free_preparse(struct key_preparsed_payload *prep) +{ + pr_devel("==>%s()\n", __func__); + + kfree(prep->payload[0]); +} + /* * The description is of the form "[:]" * @@ -234,7 +239,9 @@ static long dns_resolver_read(const struct key *key, struct key_type key_type_dns_resolver = { .name = "dns_resolver", - .instantiate = dns_resolver_instantiate, + .preparse = dns_resolver_preparse, + .free_preparse = dns_resolver_free_preparse, + .instantiate = generic_key_instantiate, .match = dns_resolver_match, .revoke = user_revoke, .destroy = user_destroy, diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c index 0ad080790a32a341a1ddc57d632302563964a247..3907add75932d9eafb6fd6de8da225171eb5dae5 100644 --- a/net/rxrpc/ar-key.c +++ b/net/rxrpc/ar-key.c @@ -26,8 +26,10 @@ #include "ar-internal.h" static int rxrpc_vet_description_s(const char *); -static int rxrpc_instantiate(struct key *, struct key_preparsed_payload *); -static int rxrpc_instantiate_s(struct key *, struct key_preparsed_payload *); +static int rxrpc_preparse(struct key_preparsed_payload *); +static int rxrpc_preparse_s(struct key_preparsed_payload *); +static void rxrpc_free_preparse(struct key_preparsed_payload *); +static void rxrpc_free_preparse_s(struct key_preparsed_payload *); static void rxrpc_destroy(struct key *); static void rxrpc_destroy_s(struct key *); static void rxrpc_describe(const struct key *, struct seq_file *); @@ -39,7 +41,9 @@ static long rxrpc_read(const struct key *, char __user *, size_t); */ struct key_type key_type_rxrpc = { .name = "rxrpc", - .instantiate = rxrpc_instantiate, + .preparse = rxrpc_preparse, + .free_preparse = rxrpc_free_preparse, + .instantiate = generic_key_instantiate, .match = user_match, .destroy = rxrpc_destroy, .describe = rxrpc_describe, @@ -54,7 +58,9 @@ EXPORT_SYMBOL(key_type_rxrpc); struct key_type key_type_rxrpc_s = { .name = "rxrpc_s", .vet_description = rxrpc_vet_description_s, - .instantiate = rxrpc_instantiate_s, + .preparse = rxrpc_preparse_s, + .free_preparse = rxrpc_free_preparse_s, + .instantiate = generic_key_instantiate, .match = user_match, .destroy = rxrpc_destroy_s, .describe = rxrpc_describe, @@ -81,13 +87,13 @@ static int rxrpc_vet_description_s(const char *desc) * parse an RxKAD type XDR format token * - the caller guarantees we have at least 4 words */ -static int rxrpc_instantiate_xdr_rxkad(struct key *key, const __be32 *xdr, - unsigned int toklen) +static int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep, + size_t datalen, + const __be32 *xdr, unsigned int toklen) { struct rxrpc_key_token *token, **pptoken; size_t plen; u32 tktlen; - int ret; _enter(",{%x,%x,%x,%x},%u", ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]), @@ -103,9 +109,7 @@ static int rxrpc_instantiate_xdr_rxkad(struct key *key, const __be32 *xdr, return -EKEYREJECTED; plen = sizeof(*token) + sizeof(*token->kad) + tktlen; - ret = key_payload_reserve(key, key->datalen + plen); - if (ret < 0) - return ret; + prep->quotalen = datalen + plen; plen -= sizeof(*token); token = kzalloc(sizeof(*token), GFP_KERNEL); @@ -146,16 +150,16 @@ static int rxrpc_instantiate_xdr_rxkad(struct key *key, const __be32 *xdr, token->kad->ticket[6], token->kad->ticket[7]); /* count the number of tokens attached */ - key->type_data.x[0]++; + prep->type_data[0] = (void *)((unsigned long)prep->type_data[0] + 1); /* attach the data */ - for (pptoken = (struct rxrpc_key_token **)&key->payload.data; + for (pptoken = (struct rxrpc_key_token **)&prep->payload[0]; *pptoken; pptoken = &(*pptoken)->next) continue; *pptoken = token; - if (token->kad->expiry < key->expiry) - key->expiry = token->kad->expiry; + if (token->kad->expiry < prep->expiry) + prep->expiry = token->kad->expiry; _leave(" = 0"); return 0; @@ -418,8 +422,9 @@ static int rxrpc_krb5_decode_ticket(u8 **_ticket, u16 *_tktlen, * parse an RxK5 type XDR format token * - the caller guarantees we have at least 4 words */ -static int rxrpc_instantiate_xdr_rxk5(struct key *key, const __be32 *xdr, - unsigned int toklen) +static int rxrpc_preparse_xdr_rxk5(struct key_preparsed_payload *prep, + size_t datalen, + const __be32 *xdr, unsigned int toklen) { struct rxrpc_key_token *token, **pptoken; struct rxk5_key *rxk5; @@ -432,9 +437,7 @@ static int rxrpc_instantiate_xdr_rxk5(struct key *key, const __be32 *xdr, /* reserve some payload space for this subkey - the length of the token * is a reasonable approximation */ - ret = key_payload_reserve(key, key->datalen + toklen); - if (ret < 0) - return ret; + prep->quotalen = datalen + toklen; token = kzalloc(sizeof(*token), GFP_KERNEL); if (!token) @@ -520,14 +523,14 @@ static int rxrpc_instantiate_xdr_rxk5(struct key *key, const __be32 *xdr, if (toklen != 0) goto inval; - /* attach the payload to the key */ - for (pptoken = (struct rxrpc_key_token **)&key->payload.data; + /* attach the payload */ + for (pptoken = (struct rxrpc_key_token **)&prep->payload[0]; *pptoken; pptoken = &(*pptoken)->next) continue; *pptoken = token; - if (token->kad->expiry < key->expiry) - key->expiry = token->kad->expiry; + if (token->kad->expiry < prep->expiry) + prep->expiry = token->kad->expiry; _leave(" = 0"); return 0; @@ -545,16 +548,17 @@ static int rxrpc_instantiate_xdr_rxk5(struct key *key, const __be32 *xdr, * attempt to parse the data as the XDR format * - the caller guarantees we have more than 7 words */ -static int rxrpc_instantiate_xdr(struct key *key, const void *data, size_t datalen) +static int rxrpc_preparse_xdr(struct key_preparsed_payload *prep) { - const __be32 *xdr = data, *token; + const __be32 *xdr = prep->data, *token; const char *cp; unsigned int len, tmp, loop, ntoken, toklen, sec_ix; + size_t datalen = prep->datalen; int ret; _enter(",{%x,%x,%x,%x},%zu", ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]), - datalen); + prep->datalen); if (datalen > AFSTOKEN_LENGTH_MAX) goto not_xdr; @@ -635,13 +639,13 @@ static int rxrpc_instantiate_xdr(struct key *key, const void *data, size_t datal switch (sec_ix) { case RXRPC_SECURITY_RXKAD: - ret = rxrpc_instantiate_xdr_rxkad(key, xdr, toklen); + ret = rxrpc_preparse_xdr_rxkad(prep, datalen, xdr, toklen); if (ret != 0) goto error; break; case RXRPC_SECURITY_RXK5: - ret = rxrpc_instantiate_xdr_rxk5(key, xdr, toklen); + ret = rxrpc_preparse_xdr_rxk5(prep, datalen, xdr, toklen); if (ret != 0) goto error; break; @@ -665,8 +669,9 @@ static int rxrpc_instantiate_xdr(struct key *key, const void *data, size_t datal } /* - * instantiate an rxrpc defined key - * data should be of the form: + * Preparse an rxrpc defined key. + * + * Data should be of the form: * OFFSET LEN CONTENT * 0 4 key interface version number * 4 2 security index (type) @@ -678,7 +683,7 @@ static int rxrpc_instantiate_xdr(struct key *key, const void *data, size_t datal * * if no data is provided, then a no-security key is made */ -static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep) +static int rxrpc_preparse(struct key_preparsed_payload *prep) { const struct rxrpc_key_data_v1 *v1; struct rxrpc_key_token *token, **pp; @@ -686,7 +691,7 @@ static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep u32 kver; int ret; - _enter("{%x},,%zu", key_serial(key), prep->datalen); + _enter("%zu", prep->datalen); /* handle a no-security key */ if (!prep->data && prep->datalen == 0) @@ -694,7 +699,7 @@ static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep /* determine if the XDR payload format is being used */ if (prep->datalen > 7 * 4) { - ret = rxrpc_instantiate_xdr(key, prep->data, prep->datalen); + ret = rxrpc_preparse_xdr(prep); if (ret != -EPROTO) return ret; } @@ -743,9 +748,7 @@ static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep goto error; plen = sizeof(*token->kad) + v1->ticket_length; - ret = key_payload_reserve(key, plen + sizeof(*token)); - if (ret < 0) - goto error; + prep->quotalen = plen + sizeof(*token); ret = -ENOMEM; token = kzalloc(sizeof(*token), GFP_KERNEL); @@ -762,15 +765,16 @@ static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep memcpy(&token->kad->session_key, &v1->session_key, 8); memcpy(&token->kad->ticket, v1->ticket, v1->ticket_length); - /* attach the data */ - key->type_data.x[0]++; + /* count the number of tokens attached */ + prep->type_data[0] = (void *)((unsigned long)prep->type_data[0] + 1); - pp = (struct rxrpc_key_token **)&key->payload.data; + /* attach the data */ + pp = (struct rxrpc_key_token **)&prep->payload[0]; while (*pp) pp = &(*pp)->next; *pp = token; - if (token->kad->expiry < key->expiry) - key->expiry = token->kad->expiry; + if (token->kad->expiry < prep->expiry) + prep->expiry = token->kad->expiry; token = NULL; ret = 0; @@ -781,20 +785,55 @@ static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep } /* - * instantiate a server secret key - * data should be a pointer to the 8-byte secret key + * Free token list. */ -static int rxrpc_instantiate_s(struct key *key, - struct key_preparsed_payload *prep) +static void rxrpc_free_token_list(struct rxrpc_key_token *token) +{ + struct rxrpc_key_token *next; + + for (; token; token = next) { + next = token->next; + switch (token->security_index) { + case RXRPC_SECURITY_RXKAD: + kfree(token->kad); + break; + case RXRPC_SECURITY_RXK5: + if (token->k5) + rxrpc_rxk5_free(token->k5); + break; + default: + printk(KERN_ERR "Unknown token type %x on rxrpc key\n", + token->security_index); + BUG(); + } + + kfree(token); + } +} + +/* + * Clean up preparse data. + */ +static void rxrpc_free_preparse(struct key_preparsed_payload *prep) +{ + rxrpc_free_token_list(prep->payload[0]); +} + +/* + * Preparse a server secret key. + * + * The data should be the 8-byte secret key. + */ +static int rxrpc_preparse_s(struct key_preparsed_payload *prep) { struct crypto_blkcipher *ci; - _enter("{%x},,%zu", key_serial(key), prep->datalen); + _enter("%zu", prep->datalen); if (prep->datalen != 8) return -EINVAL; - memcpy(&key->type_data, prep->data, 8); + memcpy(&prep->type_data, prep->data, 8); ci = crypto_alloc_blkcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(ci)) { @@ -805,36 +844,26 @@ static int rxrpc_instantiate_s(struct key *key, if (crypto_blkcipher_setkey(ci, prep->data, 8) < 0) BUG(); - key->payload.data = ci; + prep->payload[0] = ci; _leave(" = 0"); return 0; } +/* + * Clean up preparse data. + */ +static void rxrpc_free_preparse_s(struct key_preparsed_payload *prep) +{ + if (prep->payload[0]) + crypto_free_blkcipher(prep->payload[0]); +} + /* * dispose of the data dangling from the corpse of a rxrpc key */ static void rxrpc_destroy(struct key *key) { - struct rxrpc_key_token *token; - - while ((token = key->payload.data)) { - key->payload.data = token->next; - switch (token->security_index) { - case RXRPC_SECURITY_RXKAD: - kfree(token->kad); - break; - case RXRPC_SECURITY_RXK5: - if (token->k5) - rxrpc_rxk5_free(token->k5); - break; - default: - printk(KERN_ERR "Unknown token type %x on rxrpc key\n", - token->security_index); - BUG(); - } - - kfree(token); - } + rxrpc_free_token_list(key->payload.data); } /* diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 8137b27d641dad9080df66c829f8109472dbccc6..c2f91a0cf8899cb0d8d69fd06ebcc05405cf45f7 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -34,7 +34,9 @@ MODULE_LICENSE("GPL"); struct key_type key_type_big_key = { .name = "big_key", .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = big_key_instantiate, + .preparse = big_key_preparse, + .free_preparse = big_key_free_preparse, + .instantiate = generic_key_instantiate, .match = user_match, .revoke = big_key_revoke, .destroy = big_key_destroy, @@ -43,11 +45,11 @@ struct key_type key_type_big_key = { }; /* - * Instantiate a big key + * Preparse a big key */ -int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +int big_key_preparse(struct key_preparsed_payload *prep) { - struct path *path = (struct path *)&key->payload.data2; + struct path *path = (struct path *)&prep->payload; struct file *file; ssize_t written; size_t datalen = prep->datalen; @@ -58,11 +60,9 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) goto error; /* Set an arbitrary quota */ - ret = key_payload_reserve(key, 16); - if (ret < 0) - goto error; + prep->quotalen = 16; - key->type_data.x[1] = datalen; + prep->type_data[1] = (void *)(unsigned long)datalen; if (datalen > BIG_KEY_FILE_THRESHOLD) { /* Create a shmem file to store the data in. This will permit the data @@ -73,7 +73,7 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) file = shmem_kernel_file_setup("", datalen, 0); if (IS_ERR(file)) { ret = PTR_ERR(file); - goto err_quota; + goto error; } written = kernel_write(file, prep->data, prep->datalen, 0); @@ -93,23 +93,32 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) } else { /* Just store the data in a buffer */ void *data = kmalloc(datalen, GFP_KERNEL); - if (!data) { - ret = -ENOMEM; - goto err_quota; - } + if (!data) + return -ENOMEM; - key->payload.data = memcpy(data, prep->data, prep->datalen); + prep->payload[0] = memcpy(data, prep->data, prep->datalen); } return 0; err_fput: fput(file); -err_quota: - key_payload_reserve(key, 0); error: return ret; } +/* + * Clear preparsement. + */ +void big_key_free_preparse(struct key_preparsed_payload *prep) +{ + if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { + struct path *path = (struct path *)&prep->payload; + path_put(path); + } else { + kfree(prep->payload[0]); + } +} + /* * dispose of the links from a revoked keyring * - called with the key sem write-locked diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 5fe443d120afe81826902cc70efecff24ccee1bb..d252c5704f8a087005f345cff961ee2c1d1ad6be 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -811,7 +811,7 @@ static int encrypted_instantiate(struct key *key, goto out; } - rcu_assign_keypointer(key, epayload); + prep->payload[0] = epayload; out: kfree(datablob); return ret; diff --git a/security/keys/key.c b/security/keys/key.c index 2048a110e7f18f0bd1e78006a88dc7bc675c9994..b90a68c4e2c4277035b21f873af64efcd95bf782 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -437,6 +437,11 @@ static int __key_instantiate_and_link(struct key *key, /* disable the authorisation key */ if (authkey) key_revoke(authkey); + + if (prep->expiry != TIME_T_MAX) { + key->expiry = prep->expiry; + key_schedule_gc(prep->expiry + key_gc_delay); + } } } @@ -479,6 +484,7 @@ int key_instantiate_and_link(struct key *key, prep.data = data; prep.datalen = datalen; prep.quotalen = key->type->def_datalen; + prep.expiry = TIME_T_MAX; if (key->type->preparse) { ret = key->type->preparse(&prep); if (ret < 0) @@ -488,7 +494,7 @@ int key_instantiate_and_link(struct key *key, if (keyring) { ret = __key_link_begin(keyring, &key->index_key, &edit); if (ret < 0) - goto error_free_preparse; + goto error; } ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit); @@ -496,10 +502,9 @@ int key_instantiate_and_link(struct key *key, if (keyring) __key_link_end(keyring, &key->index_key, edit); -error_free_preparse: +error: if (key->type->preparse) key->type->free_preparse(&prep); -error: return ret; } @@ -811,11 +816,12 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, prep.datalen = plen; prep.quotalen = index_key.type->def_datalen; prep.trusted = flags & KEY_ALLOC_TRUSTED; + prep.expiry = TIME_T_MAX; if (index_key.type->preparse) { ret = index_key.type->preparse(&prep); if (ret < 0) { key_ref = ERR_PTR(ret); - goto error_put_type; + goto error_free_prep; } if (!index_key.description) index_key.description = prep.description; @@ -941,6 +947,7 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) prep.data = payload; prep.datalen = plen; prep.quotalen = key->type->def_datalen; + prep.expiry = TIME_T_MAX; if (key->type->preparse) { ret = key->type->preparse(&prep); if (ret < 0) @@ -956,9 +963,9 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) up_write(&key->sem); +error: if (key->type->preparse) key->type->free_preparse(&prep); -error: return ret; } EXPORT_SYMBOL(key_update); @@ -1023,6 +1030,38 @@ void key_invalidate(struct key *key) } EXPORT_SYMBOL(key_invalidate); +/** + * generic_key_instantiate - Simple instantiation of a key from preparsed data + * @key: The key to be instantiated + * @prep: The preparsed data to load. + * + * Instantiate a key from preparsed data. We assume we can just copy the data + * in directly and clear the old pointers. + * + * This can be pointed to directly by the key type instantiate op pointer. + */ +int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +{ + int ret; + + pr_devel("==>%s()\n", __func__); + + ret = key_payload_reserve(key, prep->quotalen); + if (ret == 0) { + key->type_data.p[0] = prep->type_data[0]; + key->type_data.p[1] = prep->type_data[1]; + rcu_assign_keypointer(key, prep->payload[0]); + key->payload.data2[1] = prep->payload[1]; + prep->type_data[0] = NULL; + prep->type_data[1] = NULL; + prep->payload[0] = NULL; + prep->payload[1] = NULL; + } + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(generic_key_instantiate); + /** * register_key_type - Register a type of key. * @ktype: The new key type. diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 9cf2575f0d97c8b237928f1e1ab46b4ad3895653..8314a7d2104df95ead666466213819504a31d5c2 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -73,6 +73,8 @@ static inline unsigned keyring_hash(const char *desc) * can be treated as ordinary keys in addition to having their own special * operations. */ +static int keyring_preparse(struct key_preparsed_payload *prep); +static void keyring_free_preparse(struct key_preparsed_payload *prep); static int keyring_instantiate(struct key *keyring, struct key_preparsed_payload *prep); static void keyring_revoke(struct key *keyring); @@ -84,6 +86,8 @@ static long keyring_read(const struct key *keyring, struct key_type key_type_keyring = { .name = "keyring", .def_datalen = 0, + .preparse = keyring_preparse, + .free_preparse = keyring_free_preparse, .instantiate = keyring_instantiate, .match = user_match, .revoke = keyring_revoke, @@ -122,6 +126,21 @@ static void keyring_publish_name(struct key *keyring) } } +/* + * Preparse a keyring payload + */ +static int keyring_preparse(struct key_preparsed_payload *prep) +{ + return prep->datalen != 0 ? -EINVAL : 0; +} + +/* + * Free a preparse of a user defined key payload + */ +static void keyring_free_preparse(struct key_preparsed_payload *prep) +{ +} + /* * Initialise a keyring. * @@ -130,17 +149,10 @@ static void keyring_publish_name(struct key *keyring) static int keyring_instantiate(struct key *keyring, struct key_preparsed_payload *prep) { - int ret; - - ret = -EINVAL; - if (prep->datalen == 0) { - assoc_array_init(&keyring->keys); - /* make the keyring available by name if it has one */ - keyring_publish_name(keyring); - ret = 0; - } - - return ret; + assoc_array_init(&keyring->keys); + /* make the keyring available by name if it has one */ + keyring_publish_name(keyring); + return 0; } /* diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 7495a93b4b9024dad78d526d17feb7d07f93016f..842e6f410d5052b6e3dad7c7d0ac8d6290293733 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -20,6 +20,8 @@ #include "internal.h" #include +static int request_key_auth_preparse(struct key_preparsed_payload *); +static void request_key_auth_free_preparse(struct key_preparsed_payload *); static int request_key_auth_instantiate(struct key *, struct key_preparsed_payload *); static void request_key_auth_describe(const struct key *, struct seq_file *); @@ -33,6 +35,8 @@ static long request_key_auth_read(const struct key *, char __user *, size_t); struct key_type key_type_request_key_auth = { .name = ".request_key_auth", .def_datalen = sizeof(struct request_key_auth), + .preparse = request_key_auth_preparse, + .free_preparse = request_key_auth_free_preparse, .instantiate = request_key_auth_instantiate, .describe = request_key_auth_describe, .revoke = request_key_auth_revoke, @@ -40,6 +44,15 @@ struct key_type key_type_request_key_auth = { .read = request_key_auth_read, }; +int request_key_auth_preparse(struct key_preparsed_payload *prep) +{ + return 0; +} + +void request_key_auth_free_preparse(struct key_preparsed_payload *prep) +{ +} + /* * Instantiate a request-key authorisation key. */ diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index faa2caeb593f8524a059e79d58e09bf430a1f992..eee340011f2bd54d7b12f3f28a25ec3f8396b5fd 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -27,7 +27,9 @@ static int logon_vet_description(const char *desc); struct key_type key_type_user = { .name = "user", .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .update = user_update, .match = user_match, .revoke = user_revoke, @@ -47,7 +49,9 @@ EXPORT_SYMBOL_GPL(key_type_user); struct key_type key_type_logon = { .name = "logon", .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .update = user_update, .match = user_match, .revoke = user_revoke, @@ -58,38 +62,37 @@ struct key_type key_type_logon = { EXPORT_SYMBOL_GPL(key_type_logon); /* - * instantiate a user defined key + * Preparse a user defined key payload */ -int user_instantiate(struct key *key, struct key_preparsed_payload *prep) +int user_preparse(struct key_preparsed_payload *prep) { struct user_key_payload *upayload; size_t datalen = prep->datalen; - int ret; - ret = -EINVAL; if (datalen <= 0 || datalen > 32767 || !prep->data) - goto error; - - ret = key_payload_reserve(key, datalen); - if (ret < 0) - goto error; + return -EINVAL; - ret = -ENOMEM; upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); if (!upayload) - goto error; + return -ENOMEM; /* attach the data */ + prep->quotalen = datalen; + prep->payload[0] = upayload; upayload->datalen = datalen; memcpy(upayload->data, prep->data, datalen); - rcu_assign_keypointer(key, upayload); - ret = 0; - -error: - return ret; + return 0; } +EXPORT_SYMBOL_GPL(user_preparse); -EXPORT_SYMBOL_GPL(user_instantiate); +/* + * Free a preparse of a user defined key payload + */ +void user_free_preparse(struct key_preparsed_payload *prep) +{ + kfree(prep->payload[0]); +} +EXPORT_SYMBOL_GPL(user_free_preparse); /* * update a user defined key