cregit-Linux how code gets into the kernel

Release 4.8 net/rxrpc/key.c

Directory: net/rxrpc
/* RxRPC key management
 *
 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *
 * RxRPC keys should have a description of describing their purpose:
 *      "afs@CAMBRIDGE.REDHAT.COM>
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <crypto/skcipher.h>
#include <linux/module.h>
#include <linux/net.h>
#include <linux/skbuff.h>
#include <linux/key-type.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <net/sock.h>
#include <net/af_rxrpc.h>
#include <keys/rxrpc-type.h>
#include <keys/user-type.h>
#include "ar-internal.h"

static int rxrpc_vet_description_s(const char *);
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 *);
static long rxrpc_read(const struct key *, char __user *, size_t);

/*
 * rxrpc defined keys take an arbitrary string as the description and an
 * arbitrary blob of data as the payload
 */

struct key_type key_type_rxrpc = {
	.name		= "rxrpc",
	.preparse	= rxrpc_preparse,
	.free_preparse	= rxrpc_free_preparse,
	.instantiate	= generic_key_instantiate,
	.destroy	= rxrpc_destroy,
	.describe	= rxrpc_describe,
	.read		= rxrpc_read,
};

EXPORT_SYMBOL(key_type_rxrpc);

/*
 * rxrpc server defined keys take "<serviceId>:<securityIndex>" as the
 * description and an 8-byte decryption key as the payload
 */

struct key_type key_type_rxrpc_s = {
	.name		= "rxrpc_s",
	.vet_description = rxrpc_vet_description_s,
	.preparse	= rxrpc_preparse_s,
	.free_preparse	= rxrpc_free_preparse_s,
	.instantiate	= generic_key_instantiate,
	.destroy	= rxrpc_destroy_s,
	.describe	= rxrpc_describe,
};

/*
 * Vet the description for an RxRPC server key
 */

static int rxrpc_vet_description_s(const char *desc) { unsigned long num; char *p; num = simple_strtoul(desc, &p, 10); if (*p != ':' || num > 65535) return -EINVAL; num = simple_strtoul(p + 1, &p, 10); if (*p || num < 1 || num > 255) return -EINVAL; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells80100.00%1100.00%
Total80100.00%1100.00%

/* * parse an RxKAD type XDR format token * - the caller guarantees we have at least 4 words */
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; _enter(",{%x,%x,%x,%x},%u", ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]), toklen); if (toklen <= 8 * 4) return -EKEYREJECTED; tktlen = ntohl(xdr[7]); _debug("tktlen: %x", tktlen); if (tktlen > AFSTOKEN_RK_TIX_MAX) return -EKEYREJECTED; if (toklen < 8 * 4 + tktlen) return -EKEYREJECTED; plen = sizeof(*token) + sizeof(*token->kad) + tktlen; prep->quotalen = datalen + plen; plen -= sizeof(*token); token = kzalloc(sizeof(*token), GFP_KERNEL); if (!token) return -ENOMEM; token->kad = kzalloc(plen, GFP_KERNEL); if (!token->kad) { kfree(token); return -ENOMEM; } token->security_index = RXRPC_SECURITY_RXKAD; token->kad->ticket_len = tktlen; token->kad->vice_id = ntohl(xdr[0]); token->kad->kvno = ntohl(xdr[1]); token->kad->start = ntohl(xdr[4]); token->kad->expiry = ntohl(xdr[5]); token->kad->primary_flag = ntohl(xdr[6]); memcpy(&token->kad->session_key, &xdr[2], 8); memcpy(&token->kad->ticket, &xdr[8], tktlen); _debug("SCIX: %u", token->security_index); _debug("TLEN: %u", token->kad->ticket_len); _debug("EXPY: %x", token->kad->expiry); _debug("KVNO: %u", token->kad->kvno); _debug("PRIM: %u", token->kad->primary_flag); _debug("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x", token->kad->session_key[0], token->kad->session_key[1], token->kad->session_key[2], token->kad->session_key[3], token->kad->session_key[4], token->kad->session_key[5], token->kad->session_key[6], token->kad->session_key[7]); if (token->kad->ticket_len >= 8) _debug("TCKT: %02x%02x%02x%02x%02x%02x%02x%02x", token->kad->ticket[0], token->kad->ticket[1], token->kad->ticket[2], token->kad->ticket[3], token->kad->ticket[4], token->kad->ticket[5], token->kad->ticket[6], token->kad->ticket[7]); /* count the number of tokens attached */ prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1); /* attach the data */ for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0]; *pptoken; pptoken = &(*pptoken)->next) continue; *pptoken = token; if (token->kad->expiry < prep->expiry) prep->expiry = token->kad->expiry; _leave(" = 0"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells64899.23%457.14%
nathaniel wesley filardonathaniel wesley filardo20.31%114.29%
anton blanchardanton blanchard20.31%114.29%
eric dumazeteric dumazet10.15%114.29%
Total653100.00%7100.00%


static void rxrpc_free_krb5_principal(struct krb5_principal *princ) { int loop; if (princ->name_parts) { for (loop = princ->n_name_parts - 1; loop >= 0; loop--) kfree(princ->name_parts[loop]); kfree(princ->name_parts); } kfree(princ->realm); }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells63100.00%2100.00%
Total63100.00%2100.00%


static void rxrpc_free_krb5_tagged(struct krb5_tagged_data *td) { kfree(td->data); }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells18100.00%2100.00%
Total18100.00%2100.00%

/* * free up an RxK5 token */
static void rxrpc_rxk5_free(struct rxk5_key *rxk5) { int loop; rxrpc_free_krb5_principal(&rxk5->client); rxrpc_free_krb5_principal(&rxk5->server); rxrpc_free_krb5_tagged(&rxk5->session); if (rxk5->addresses) { for (loop = rxk5->n_addresses - 1; loop >= 0; loop--) rxrpc_free_krb5_tagged(&rxk5->addresses[loop]); kfree(rxk5->addresses); } if (rxk5->authdata) { for (loop = rxk5->n_authdata - 1; loop >= 0; loop--) rxrpc_free_krb5_tagged(&rxk5->authdata[loop]); kfree(rxk5->authdata); } kfree(rxk5->ticket); kfree(rxk5->ticket2); kfree(rxk5); }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells143100.00%2100.00%
Total143100.00%2100.00%

/* * extract a krb5 principal */
static int rxrpc_krb5_decode_principal(struct krb5_principal *princ, const __be32 **_xdr, unsigned int *_toklen) { const __be32 *xdr = *_xdr; unsigned int toklen = *_toklen, n_parts, loop, tmp; /* there must be at least one name, and at least #names+1 length * words */ if (toklen <= 12) return -EINVAL; _enter(",{%x,%x,%x},%u", ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), toklen); n_parts = ntohl(*xdr++); toklen -= 4; if (n_parts <= 0 || n_parts > AFSTOKEN_K5_COMPONENTS_MAX) return -EINVAL; princ->n_name_parts = n_parts; if (toklen <= (n_parts + 1) * 4) return -EINVAL; princ->name_parts = kcalloc(n_parts, sizeof(char *), GFP_KERNEL); if (!princ->name_parts) return -ENOMEM; for (loop = 0; loop < n_parts; loop++) { if (toklen < 4) return -EINVAL; tmp = ntohl(*xdr++); toklen -= 4; if (tmp <= 0 || tmp > AFSTOKEN_STRING_MAX) return -EINVAL; if (tmp > toklen) return -EINVAL; princ->name_parts[loop] = kmalloc(tmp + 1, GFP_KERNEL); if (!princ->name_parts[loop]) return -ENOMEM; memcpy(princ->name_parts[loop], xdr, tmp); princ->name_parts[loop][tmp] = 0; tmp = (tmp + 3) & ~3; toklen -= tmp; xdr += tmp >> 2; } if (toklen < 4) return -EINVAL; tmp = ntohl(*xdr++); toklen -= 4; if (tmp <= 0 || tmp > AFSTOKEN_K5_REALM_MAX) return -EINVAL; if (tmp > toklen) return -EINVAL; princ->realm = kmalloc(tmp + 1, GFP_KERNEL); if (!princ->realm) return -ENOMEM; memcpy(princ->realm, xdr, tmp); princ->realm[tmp] = 0; tmp = (tmp + 3) & ~3; toklen -= tmp; xdr += tmp >> 2; _debug("%s/...@%s", princ->name_parts[0], princ->realm); *_xdr = xdr; *_toklen = toklen; _leave(" = 0 [toklen=%u]", toklen); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells44599.11%250.00%
axel linaxel lin20.45%125.00%
eric dumazeteric dumazet20.45%125.00%
Total449100.00%4100.00%

/* * extract a piece of krb5 tagged data */
static int rxrpc_krb5_decode_tagged_data(struct krb5_tagged_data *td, size_t max_data_size, const __be32 **_xdr, unsigned int *_toklen) { const __be32 *xdr = *_xdr; unsigned int toklen = *_toklen, len; /* there must be at least one tag and one length word */ if (toklen <= 8) return -EINVAL; _enter(",%zu,{%x,%x},%u", max_data_size, ntohl(xdr[0]), ntohl(xdr[1]), toklen); td->tag = ntohl(*xdr++); len = ntohl(*xdr++); toklen -= 8; if (len > max_data_size) return -EINVAL; td->data_len = len; if (len > 0) { td->data = kmemdup(xdr, len, GFP_KERNEL); if (!td->data) return -ENOMEM; len = (len + 3) & ~3; toklen -= len; xdr += len >> 2; } _debug("tag %x len %x", td->tag, td->data_len); *_xdr = xdr; *_toklen = toklen; _leave(" = 0 [toklen=%u]", toklen); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells19997.55%360.00%
thomas meyerthomas meyer31.47%120.00%
eric dumazeteric dumazet20.98%120.00%
Total204100.00%5100.00%

/* * extract an array of tagged data */
static int rxrpc_krb5_decode_tagged_array(struct krb5_tagged_data **_td, u8 *_n_elem, u8 max_n_elem, size_t max_elem_size, const __be32 **_xdr, unsigned int *_toklen) { struct krb5_tagged_data *td; const __be32 *xdr = *_xdr; unsigned int toklen = *_toklen, n_elem, loop; int ret; /* there must be at least one count */ if (toklen < 4) return -EINVAL; _enter(",,%u,%zu,{%x},%u", max_n_elem, max_elem_size, ntohl(xdr[0]), toklen); n_elem = ntohl(*xdr++); toklen -= 4; if (n_elem > max_n_elem) return -EINVAL; *_n_elem = n_elem; if (n_elem > 0) { if (toklen <= (n_elem + 1) * 4) return -EINVAL; _debug("n_elem %d", n_elem); td = kcalloc(n_elem, sizeof(struct krb5_tagged_data), GFP_KERNEL); if (!td) return -ENOMEM; *_td = td; for (loop = 0; loop < n_elem; loop++) { ret = rxrpc_krb5_decode_tagged_data(&td[loop], max_elem_size, &xdr, &toklen); if (ret < 0) return ret; } } *_xdr = xdr; *_toklen = toklen; _leave(" = 0 [toklen=%u]", toklen); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells23798.34%250.00%
eric dumazeteric dumazet20.83%125.00%
axel linaxel lin20.83%125.00%
Total241100.00%4100.00%

/* * extract a krb5 ticket */
static int rxrpc_krb5_decode_ticket(u8 **_ticket, u16 *_tktlen, const __be32 **_xdr, unsigned int *_toklen) { const __be32 *xdr = *_xdr; unsigned int toklen = *_toklen, len; /* there must be at least one length word */ if (toklen <= 4) return -EINVAL; _enter(",{%x},%u", ntohl(xdr[0]), toklen); len = ntohl(*xdr++); toklen -= 4; if (len > AFSTOKEN_K5_TIX_MAX) return -EINVAL; *_tktlen = len; _debug("ticket len %u", len); if (len > 0) { *_ticket = kmemdup(xdr, len, GFP_KERNEL); if (!*_ticket) return -ENOMEM; len = (len + 3) & ~3; toklen -= len; xdr += len >> 2; } *_xdr = xdr; *_toklen = toklen; _leave(" = 0 [toklen=%u]", toklen); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells17097.14%466.67%
thomas meyerthomas meyer31.71%116.67%
eric dumazeteric dumazet21.14%116.67%
Total175100.00%6100.00%

/* * parse an RxK5 type XDR format token * - the caller guarantees we have at least 4 words */
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; const __be32 *end_xdr = xdr + (toklen >> 2); int ret; _enter(",{%x,%x,%x,%x},%u", ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]), toklen); /* reserve some payload space for this subkey - the length of the token * is a reasonable approximation */ prep->quotalen = datalen + toklen; token = kzalloc(sizeof(*token), GFP_KERNEL); if (!token) return -ENOMEM; rxk5 = kzalloc(sizeof(*rxk5), GFP_KERNEL); if (!rxk5) { kfree(token); return -ENOMEM; } token->security_index = RXRPC_SECURITY_RXK5; token->k5 = rxk5; /* extract the principals */ ret = rxrpc_krb5_decode_principal(&rxk5->client, &xdr, &toklen); if (ret < 0) goto error; ret = rxrpc_krb5_decode_principal(&rxk5->server, &xdr, &toklen); if (ret < 0) goto error; /* extract the session key and the encoding type (the tag field -> * ENCTYPE_xxx) */ ret = rxrpc_krb5_decode_tagged_data(&rxk5->session, AFSTOKEN_DATA_MAX, &xdr, &toklen); if (ret < 0) goto error; if (toklen < 4 * 8 + 2 * 4) goto inval; rxk5->authtime = be64_to_cpup((const __be64 *) xdr); xdr += 2; rxk5->starttime = be64_to_cpup((const __be64 *) xdr); xdr += 2; rxk5->endtime = be64_to_cpup((const __be64 *) xdr); xdr += 2; rxk5->renew_till = be64_to_cpup((const __be64 *) xdr); xdr += 2; rxk5->is_skey = ntohl(*xdr++); rxk5->flags = ntohl(*xdr++); toklen -= 4 * 8 + 2 * 4; _debug("times: a=%llx s=%llx e=%llx rt=%llx", rxk5->authtime, rxk5->starttime, rxk5->endtime, rxk5->renew_till); _debug("is_skey=%x flags=%x", rxk5->is_skey, rxk5->flags); /* extract the permitted client addresses */ ret = rxrpc_krb5_decode_tagged_array(&rxk5->addresses, &rxk5->n_addresses, AFSTOKEN_K5_ADDRESSES_MAX, AFSTOKEN_DATA_MAX, &xdr, &toklen); if (ret < 0) goto error; ASSERTCMP((end_xdr - xdr) << 2, ==, toklen); /* extract the tickets */ ret = rxrpc_krb5_decode_ticket(&rxk5->ticket, &rxk5->ticket_len, &xdr, &toklen); if (ret < 0) goto error; ret = rxrpc_krb5_decode_ticket(&rxk5->ticket2, &rxk5->ticket2_len, &xdr, &toklen); if (ret < 0) goto error; ASSERTCMP((end_xdr - xdr) << 2, ==, toklen); /* extract the typed auth data */ ret = rxrpc_krb5_decode_tagged_array(&rxk5->authdata, &rxk5->n_authdata, AFSTOKEN_K5_AUTHDATA_MAX, AFSTOKEN_BDATALN_MAX, &xdr, &toklen); if (ret < 0) goto error; ASSERTCMP((end_xdr - xdr) << 2, ==, toklen); if (toklen != 0) goto inval; /* attach the payload */ for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0]; *pptoken; pptoken = &(*pptoken)->next) continue; *pptoken = token; if (token->kad->expiry < prep->expiry) prep->expiry = token->kad->expiry; _leave(" = 0"); return 0; inval: ret = -EINVAL; error: rxrpc_rxk5_free(rxk5);