18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* RxRPC key management 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. 58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * RxRPC keys should have a description of describing their purpose: 88c2ecf20Sopenharmony_ci * "afs@CAMBRIDGE.REDHAT.COM> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <crypto/skcipher.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/net.h> 168c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 178c2ecf20Sopenharmony_ci#include <linux/key-type.h> 188c2ecf20Sopenharmony_ci#include <linux/ctype.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <net/sock.h> 218c2ecf20Sopenharmony_ci#include <net/af_rxrpc.h> 228c2ecf20Sopenharmony_ci#include <keys/rxrpc-type.h> 238c2ecf20Sopenharmony_ci#include <keys/user-type.h> 248c2ecf20Sopenharmony_ci#include "ar-internal.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int rxrpc_vet_description_s(const char *); 278c2ecf20Sopenharmony_cistatic int rxrpc_preparse(struct key_preparsed_payload *); 288c2ecf20Sopenharmony_cistatic int rxrpc_preparse_s(struct key_preparsed_payload *); 298c2ecf20Sopenharmony_cistatic void rxrpc_free_preparse(struct key_preparsed_payload *); 308c2ecf20Sopenharmony_cistatic void rxrpc_free_preparse_s(struct key_preparsed_payload *); 318c2ecf20Sopenharmony_cistatic void rxrpc_destroy(struct key *); 328c2ecf20Sopenharmony_cistatic void rxrpc_destroy_s(struct key *); 338c2ecf20Sopenharmony_cistatic void rxrpc_describe(const struct key *, struct seq_file *); 348c2ecf20Sopenharmony_cistatic long rxrpc_read(const struct key *, char *, size_t); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * rxrpc defined keys take an arbitrary string as the description and an 388c2ecf20Sopenharmony_ci * arbitrary blob of data as the payload 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistruct key_type key_type_rxrpc = { 418c2ecf20Sopenharmony_ci .name = "rxrpc", 428c2ecf20Sopenharmony_ci .flags = KEY_TYPE_NET_DOMAIN, 438c2ecf20Sopenharmony_ci .preparse = rxrpc_preparse, 448c2ecf20Sopenharmony_ci .free_preparse = rxrpc_free_preparse, 458c2ecf20Sopenharmony_ci .instantiate = generic_key_instantiate, 468c2ecf20Sopenharmony_ci .destroy = rxrpc_destroy, 478c2ecf20Sopenharmony_ci .describe = rxrpc_describe, 488c2ecf20Sopenharmony_ci .read = rxrpc_read, 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(key_type_rxrpc); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci * rxrpc server defined keys take "<serviceId>:<securityIndex>" as the 548c2ecf20Sopenharmony_ci * description and an 8-byte decryption key as the payload 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_cistruct key_type key_type_rxrpc_s = { 578c2ecf20Sopenharmony_ci .name = "rxrpc_s", 588c2ecf20Sopenharmony_ci .flags = KEY_TYPE_NET_DOMAIN, 598c2ecf20Sopenharmony_ci .vet_description = rxrpc_vet_description_s, 608c2ecf20Sopenharmony_ci .preparse = rxrpc_preparse_s, 618c2ecf20Sopenharmony_ci .free_preparse = rxrpc_free_preparse_s, 628c2ecf20Sopenharmony_ci .instantiate = generic_key_instantiate, 638c2ecf20Sopenharmony_ci .destroy = rxrpc_destroy_s, 648c2ecf20Sopenharmony_ci .describe = rxrpc_describe, 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* 688c2ecf20Sopenharmony_ci * Vet the description for an RxRPC server key 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_cistatic int rxrpc_vet_description_s(const char *desc) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci unsigned long num; 738c2ecf20Sopenharmony_ci char *p; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci num = simple_strtoul(desc, &p, 10); 768c2ecf20Sopenharmony_ci if (*p != ':' || num > 65535) 778c2ecf20Sopenharmony_ci return -EINVAL; 788c2ecf20Sopenharmony_ci num = simple_strtoul(p + 1, &p, 10); 798c2ecf20Sopenharmony_ci if (*p || num < 1 || num > 255) 808c2ecf20Sopenharmony_ci return -EINVAL; 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* 858c2ecf20Sopenharmony_ci * parse an RxKAD type XDR format token 868c2ecf20Sopenharmony_ci * - the caller guarantees we have at least 4 words 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_cistatic int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep, 898c2ecf20Sopenharmony_ci size_t datalen, 908c2ecf20Sopenharmony_ci const __be32 *xdr, unsigned int toklen) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct rxrpc_key_token *token, **pptoken; 938c2ecf20Sopenharmony_ci time64_t expiry; 948c2ecf20Sopenharmony_ci size_t plen; 958c2ecf20Sopenharmony_ci u32 tktlen; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci _enter(",{%x,%x,%x,%x},%u", 988c2ecf20Sopenharmony_ci ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]), 998c2ecf20Sopenharmony_ci toklen); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (toklen <= 8 * 4) 1028c2ecf20Sopenharmony_ci return -EKEYREJECTED; 1038c2ecf20Sopenharmony_ci tktlen = ntohl(xdr[7]); 1048c2ecf20Sopenharmony_ci _debug("tktlen: %x", tktlen); 1058c2ecf20Sopenharmony_ci if (tktlen > AFSTOKEN_RK_TIX_MAX) 1068c2ecf20Sopenharmony_ci return -EKEYREJECTED; 1078c2ecf20Sopenharmony_ci if (toklen < 8 * 4 + tktlen) 1088c2ecf20Sopenharmony_ci return -EKEYREJECTED; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci plen = sizeof(*token) + sizeof(*token->kad) + tktlen; 1118c2ecf20Sopenharmony_ci prep->quotalen = datalen + plen; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci plen -= sizeof(*token); 1148c2ecf20Sopenharmony_ci token = kzalloc(sizeof(*token), GFP_KERNEL); 1158c2ecf20Sopenharmony_ci if (!token) 1168c2ecf20Sopenharmony_ci return -ENOMEM; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci token->kad = kzalloc(plen, GFP_KERNEL); 1198c2ecf20Sopenharmony_ci if (!token->kad) { 1208c2ecf20Sopenharmony_ci kfree(token); 1218c2ecf20Sopenharmony_ci return -ENOMEM; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci token->security_index = RXRPC_SECURITY_RXKAD; 1258c2ecf20Sopenharmony_ci token->kad->ticket_len = tktlen; 1268c2ecf20Sopenharmony_ci token->kad->vice_id = ntohl(xdr[0]); 1278c2ecf20Sopenharmony_ci token->kad->kvno = ntohl(xdr[1]); 1288c2ecf20Sopenharmony_ci token->kad->start = ntohl(xdr[4]); 1298c2ecf20Sopenharmony_ci token->kad->expiry = ntohl(xdr[5]); 1308c2ecf20Sopenharmony_ci token->kad->primary_flag = ntohl(xdr[6]); 1318c2ecf20Sopenharmony_ci memcpy(&token->kad->session_key, &xdr[2], 8); 1328c2ecf20Sopenharmony_ci memcpy(&token->kad->ticket, &xdr[8], tktlen); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci _debug("SCIX: %u", token->security_index); 1358c2ecf20Sopenharmony_ci _debug("TLEN: %u", token->kad->ticket_len); 1368c2ecf20Sopenharmony_ci _debug("EXPY: %x", token->kad->expiry); 1378c2ecf20Sopenharmony_ci _debug("KVNO: %u", token->kad->kvno); 1388c2ecf20Sopenharmony_ci _debug("PRIM: %u", token->kad->primary_flag); 1398c2ecf20Sopenharmony_ci _debug("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x", 1408c2ecf20Sopenharmony_ci token->kad->session_key[0], token->kad->session_key[1], 1418c2ecf20Sopenharmony_ci token->kad->session_key[2], token->kad->session_key[3], 1428c2ecf20Sopenharmony_ci token->kad->session_key[4], token->kad->session_key[5], 1438c2ecf20Sopenharmony_ci token->kad->session_key[6], token->kad->session_key[7]); 1448c2ecf20Sopenharmony_ci if (token->kad->ticket_len >= 8) 1458c2ecf20Sopenharmony_ci _debug("TCKT: %02x%02x%02x%02x%02x%02x%02x%02x", 1468c2ecf20Sopenharmony_ci token->kad->ticket[0], token->kad->ticket[1], 1478c2ecf20Sopenharmony_ci token->kad->ticket[2], token->kad->ticket[3], 1488c2ecf20Sopenharmony_ci token->kad->ticket[4], token->kad->ticket[5], 1498c2ecf20Sopenharmony_ci token->kad->ticket[6], token->kad->ticket[7]); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* count the number of tokens attached */ 1528c2ecf20Sopenharmony_ci prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* attach the data */ 1558c2ecf20Sopenharmony_ci for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0]; 1568c2ecf20Sopenharmony_ci *pptoken; 1578c2ecf20Sopenharmony_ci pptoken = &(*pptoken)->next) 1588c2ecf20Sopenharmony_ci continue; 1598c2ecf20Sopenharmony_ci *pptoken = token; 1608c2ecf20Sopenharmony_ci expiry = rxrpc_u32_to_time64(token->kad->expiry); 1618c2ecf20Sopenharmony_ci if (expiry < prep->expiry) 1628c2ecf20Sopenharmony_ci prep->expiry = expiry; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci _leave(" = 0"); 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic void rxrpc_free_krb5_principal(struct krb5_principal *princ) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci int loop; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (princ->name_parts) { 1738c2ecf20Sopenharmony_ci for (loop = princ->n_name_parts - 1; loop >= 0; loop--) 1748c2ecf20Sopenharmony_ci kfree(princ->name_parts[loop]); 1758c2ecf20Sopenharmony_ci kfree(princ->name_parts); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci kfree(princ->realm); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void rxrpc_free_krb5_tagged(struct krb5_tagged_data *td) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci kfree(td->data); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* 1868c2ecf20Sopenharmony_ci * free up an RxK5 token 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_cistatic void rxrpc_rxk5_free(struct rxk5_key *rxk5) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci int loop; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci rxrpc_free_krb5_principal(&rxk5->client); 1938c2ecf20Sopenharmony_ci rxrpc_free_krb5_principal(&rxk5->server); 1948c2ecf20Sopenharmony_ci rxrpc_free_krb5_tagged(&rxk5->session); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (rxk5->addresses) { 1978c2ecf20Sopenharmony_ci for (loop = rxk5->n_addresses - 1; loop >= 0; loop--) 1988c2ecf20Sopenharmony_ci rxrpc_free_krb5_tagged(&rxk5->addresses[loop]); 1998c2ecf20Sopenharmony_ci kfree(rxk5->addresses); 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci if (rxk5->authdata) { 2028c2ecf20Sopenharmony_ci for (loop = rxk5->n_authdata - 1; loop >= 0; loop--) 2038c2ecf20Sopenharmony_ci rxrpc_free_krb5_tagged(&rxk5->authdata[loop]); 2048c2ecf20Sopenharmony_ci kfree(rxk5->authdata); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci kfree(rxk5->ticket); 2088c2ecf20Sopenharmony_ci kfree(rxk5->ticket2); 2098c2ecf20Sopenharmony_ci kfree(rxk5); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/* 2138c2ecf20Sopenharmony_ci * extract a krb5 principal 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_cistatic int rxrpc_krb5_decode_principal(struct krb5_principal *princ, 2168c2ecf20Sopenharmony_ci const __be32 **_xdr, 2178c2ecf20Sopenharmony_ci unsigned int *_toklen) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci const __be32 *xdr = *_xdr; 2208c2ecf20Sopenharmony_ci unsigned int toklen = *_toklen, n_parts, loop, tmp, paddedlen; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* there must be at least one name, and at least #names+1 length 2238c2ecf20Sopenharmony_ci * words */ 2248c2ecf20Sopenharmony_ci if (toklen <= 12) 2258c2ecf20Sopenharmony_ci return -EINVAL; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci _enter(",{%x,%x,%x},%u", 2288c2ecf20Sopenharmony_ci ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), toklen); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci n_parts = ntohl(*xdr++); 2318c2ecf20Sopenharmony_ci toklen -= 4; 2328c2ecf20Sopenharmony_ci if (n_parts <= 0 || n_parts > AFSTOKEN_K5_COMPONENTS_MAX) 2338c2ecf20Sopenharmony_ci return -EINVAL; 2348c2ecf20Sopenharmony_ci princ->n_name_parts = n_parts; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (toklen <= (n_parts + 1) * 4) 2378c2ecf20Sopenharmony_ci return -EINVAL; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci princ->name_parts = kcalloc(n_parts, sizeof(char *), GFP_KERNEL); 2408c2ecf20Sopenharmony_ci if (!princ->name_parts) 2418c2ecf20Sopenharmony_ci return -ENOMEM; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci for (loop = 0; loop < n_parts; loop++) { 2448c2ecf20Sopenharmony_ci if (toklen < 4) 2458c2ecf20Sopenharmony_ci return -EINVAL; 2468c2ecf20Sopenharmony_ci tmp = ntohl(*xdr++); 2478c2ecf20Sopenharmony_ci toklen -= 4; 2488c2ecf20Sopenharmony_ci if (tmp <= 0 || tmp > AFSTOKEN_STRING_MAX) 2498c2ecf20Sopenharmony_ci return -EINVAL; 2508c2ecf20Sopenharmony_ci paddedlen = (tmp + 3) & ~3; 2518c2ecf20Sopenharmony_ci if (paddedlen > toklen) 2528c2ecf20Sopenharmony_ci return -EINVAL; 2538c2ecf20Sopenharmony_ci princ->name_parts[loop] = kmalloc(tmp + 1, GFP_KERNEL); 2548c2ecf20Sopenharmony_ci if (!princ->name_parts[loop]) 2558c2ecf20Sopenharmony_ci return -ENOMEM; 2568c2ecf20Sopenharmony_ci memcpy(princ->name_parts[loop], xdr, tmp); 2578c2ecf20Sopenharmony_ci princ->name_parts[loop][tmp] = 0; 2588c2ecf20Sopenharmony_ci toklen -= paddedlen; 2598c2ecf20Sopenharmony_ci xdr += paddedlen >> 2; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (toklen < 4) 2638c2ecf20Sopenharmony_ci return -EINVAL; 2648c2ecf20Sopenharmony_ci tmp = ntohl(*xdr++); 2658c2ecf20Sopenharmony_ci toklen -= 4; 2668c2ecf20Sopenharmony_ci if (tmp <= 0 || tmp > AFSTOKEN_K5_REALM_MAX) 2678c2ecf20Sopenharmony_ci return -EINVAL; 2688c2ecf20Sopenharmony_ci paddedlen = (tmp + 3) & ~3; 2698c2ecf20Sopenharmony_ci if (paddedlen > toklen) 2708c2ecf20Sopenharmony_ci return -EINVAL; 2718c2ecf20Sopenharmony_ci princ->realm = kmalloc(tmp + 1, GFP_KERNEL); 2728c2ecf20Sopenharmony_ci if (!princ->realm) 2738c2ecf20Sopenharmony_ci return -ENOMEM; 2748c2ecf20Sopenharmony_ci memcpy(princ->realm, xdr, tmp); 2758c2ecf20Sopenharmony_ci princ->realm[tmp] = 0; 2768c2ecf20Sopenharmony_ci toklen -= paddedlen; 2778c2ecf20Sopenharmony_ci xdr += paddedlen >> 2; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci _debug("%s/...@%s", princ->name_parts[0], princ->realm); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci *_xdr = xdr; 2828c2ecf20Sopenharmony_ci *_toklen = toklen; 2838c2ecf20Sopenharmony_ci _leave(" = 0 [toklen=%u]", toklen); 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/* 2888c2ecf20Sopenharmony_ci * extract a piece of krb5 tagged data 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_cistatic int rxrpc_krb5_decode_tagged_data(struct krb5_tagged_data *td, 2918c2ecf20Sopenharmony_ci size_t max_data_size, 2928c2ecf20Sopenharmony_ci const __be32 **_xdr, 2938c2ecf20Sopenharmony_ci unsigned int *_toklen) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci const __be32 *xdr = *_xdr; 2968c2ecf20Sopenharmony_ci unsigned int toklen = *_toklen, len, paddedlen; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* there must be at least one tag and one length word */ 2998c2ecf20Sopenharmony_ci if (toklen <= 8) 3008c2ecf20Sopenharmony_ci return -EINVAL; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci _enter(",%zu,{%x,%x},%u", 3038c2ecf20Sopenharmony_ci max_data_size, ntohl(xdr[0]), ntohl(xdr[1]), toklen); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci td->tag = ntohl(*xdr++); 3068c2ecf20Sopenharmony_ci len = ntohl(*xdr++); 3078c2ecf20Sopenharmony_ci toklen -= 8; 3088c2ecf20Sopenharmony_ci if (len > max_data_size) 3098c2ecf20Sopenharmony_ci return -EINVAL; 3108c2ecf20Sopenharmony_ci paddedlen = (len + 3) & ~3; 3118c2ecf20Sopenharmony_ci if (paddedlen > toklen) 3128c2ecf20Sopenharmony_ci return -EINVAL; 3138c2ecf20Sopenharmony_ci td->data_len = len; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (len > 0) { 3168c2ecf20Sopenharmony_ci td->data = kmemdup(xdr, len, GFP_KERNEL); 3178c2ecf20Sopenharmony_ci if (!td->data) 3188c2ecf20Sopenharmony_ci return -ENOMEM; 3198c2ecf20Sopenharmony_ci toklen -= paddedlen; 3208c2ecf20Sopenharmony_ci xdr += paddedlen >> 2; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci _debug("tag %x len %x", td->tag, td->data_len); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci *_xdr = xdr; 3268c2ecf20Sopenharmony_ci *_toklen = toklen; 3278c2ecf20Sopenharmony_ci _leave(" = 0 [toklen=%u]", toklen); 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci/* 3328c2ecf20Sopenharmony_ci * extract an array of tagged data 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_cistatic int rxrpc_krb5_decode_tagged_array(struct krb5_tagged_data **_td, 3358c2ecf20Sopenharmony_ci u8 *_n_elem, 3368c2ecf20Sopenharmony_ci u8 max_n_elem, 3378c2ecf20Sopenharmony_ci size_t max_elem_size, 3388c2ecf20Sopenharmony_ci const __be32 **_xdr, 3398c2ecf20Sopenharmony_ci unsigned int *_toklen) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct krb5_tagged_data *td; 3428c2ecf20Sopenharmony_ci const __be32 *xdr = *_xdr; 3438c2ecf20Sopenharmony_ci unsigned int toklen = *_toklen, n_elem, loop; 3448c2ecf20Sopenharmony_ci int ret; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* there must be at least one count */ 3478c2ecf20Sopenharmony_ci if (toklen < 4) 3488c2ecf20Sopenharmony_ci return -EINVAL; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci _enter(",,%u,%zu,{%x},%u", 3518c2ecf20Sopenharmony_ci max_n_elem, max_elem_size, ntohl(xdr[0]), toklen); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci n_elem = ntohl(*xdr++); 3548c2ecf20Sopenharmony_ci toklen -= 4; 3558c2ecf20Sopenharmony_ci if (n_elem > max_n_elem) 3568c2ecf20Sopenharmony_ci return -EINVAL; 3578c2ecf20Sopenharmony_ci *_n_elem = n_elem; 3588c2ecf20Sopenharmony_ci if (n_elem > 0) { 3598c2ecf20Sopenharmony_ci if (toklen <= (n_elem + 1) * 4) 3608c2ecf20Sopenharmony_ci return -EINVAL; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci _debug("n_elem %d", n_elem); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci td = kcalloc(n_elem, sizeof(struct krb5_tagged_data), 3658c2ecf20Sopenharmony_ci GFP_KERNEL); 3668c2ecf20Sopenharmony_ci if (!td) 3678c2ecf20Sopenharmony_ci return -ENOMEM; 3688c2ecf20Sopenharmony_ci *_td = td; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci for (loop = 0; loop < n_elem; loop++) { 3718c2ecf20Sopenharmony_ci ret = rxrpc_krb5_decode_tagged_data(&td[loop], 3728c2ecf20Sopenharmony_ci max_elem_size, 3738c2ecf20Sopenharmony_ci &xdr, &toklen); 3748c2ecf20Sopenharmony_ci if (ret < 0) 3758c2ecf20Sopenharmony_ci return ret; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci *_xdr = xdr; 3808c2ecf20Sopenharmony_ci *_toklen = toklen; 3818c2ecf20Sopenharmony_ci _leave(" = 0 [toklen=%u]", toklen); 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci/* 3868c2ecf20Sopenharmony_ci * extract a krb5 ticket 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_cistatic int rxrpc_krb5_decode_ticket(u8 **_ticket, u16 *_tktlen, 3898c2ecf20Sopenharmony_ci const __be32 **_xdr, unsigned int *_toklen) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci const __be32 *xdr = *_xdr; 3928c2ecf20Sopenharmony_ci unsigned int toklen = *_toklen, len, paddedlen; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* there must be at least one length word */ 3958c2ecf20Sopenharmony_ci if (toklen <= 4) 3968c2ecf20Sopenharmony_ci return -EINVAL; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci _enter(",{%x},%u", ntohl(xdr[0]), toklen); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci len = ntohl(*xdr++); 4018c2ecf20Sopenharmony_ci toklen -= 4; 4028c2ecf20Sopenharmony_ci if (len > AFSTOKEN_K5_TIX_MAX) 4038c2ecf20Sopenharmony_ci return -EINVAL; 4048c2ecf20Sopenharmony_ci paddedlen = (len + 3) & ~3; 4058c2ecf20Sopenharmony_ci if (paddedlen > toklen) 4068c2ecf20Sopenharmony_ci return -EINVAL; 4078c2ecf20Sopenharmony_ci *_tktlen = len; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci _debug("ticket len %u", len); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (len > 0) { 4128c2ecf20Sopenharmony_ci *_ticket = kmemdup(xdr, len, GFP_KERNEL); 4138c2ecf20Sopenharmony_ci if (!*_ticket) 4148c2ecf20Sopenharmony_ci return -ENOMEM; 4158c2ecf20Sopenharmony_ci toklen -= paddedlen; 4168c2ecf20Sopenharmony_ci xdr += paddedlen >> 2; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci *_xdr = xdr; 4208c2ecf20Sopenharmony_ci *_toklen = toklen; 4218c2ecf20Sopenharmony_ci _leave(" = 0 [toklen=%u]", toklen); 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci/* 4268c2ecf20Sopenharmony_ci * parse an RxK5 type XDR format token 4278c2ecf20Sopenharmony_ci * - the caller guarantees we have at least 4 words 4288c2ecf20Sopenharmony_ci */ 4298c2ecf20Sopenharmony_cistatic int rxrpc_preparse_xdr_rxk5(struct key_preparsed_payload *prep, 4308c2ecf20Sopenharmony_ci size_t datalen, 4318c2ecf20Sopenharmony_ci const __be32 *xdr, unsigned int toklen) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct rxrpc_key_token *token, **pptoken; 4348c2ecf20Sopenharmony_ci struct rxk5_key *rxk5; 4358c2ecf20Sopenharmony_ci const __be32 *end_xdr = xdr + (toklen >> 2); 4368c2ecf20Sopenharmony_ci time64_t expiry; 4378c2ecf20Sopenharmony_ci int ret; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci _enter(",{%x,%x,%x,%x},%u", 4408c2ecf20Sopenharmony_ci ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]), 4418c2ecf20Sopenharmony_ci toklen); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* reserve some payload space for this subkey - the length of the token 4448c2ecf20Sopenharmony_ci * is a reasonable approximation */ 4458c2ecf20Sopenharmony_ci prep->quotalen = datalen + toklen; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci token = kzalloc(sizeof(*token), GFP_KERNEL); 4488c2ecf20Sopenharmony_ci if (!token) 4498c2ecf20Sopenharmony_ci return -ENOMEM; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci rxk5 = kzalloc(sizeof(*rxk5), GFP_KERNEL); 4528c2ecf20Sopenharmony_ci if (!rxk5) { 4538c2ecf20Sopenharmony_ci kfree(token); 4548c2ecf20Sopenharmony_ci return -ENOMEM; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci token->security_index = RXRPC_SECURITY_RXK5; 4588c2ecf20Sopenharmony_ci token->k5 = rxk5; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* extract the principals */ 4618c2ecf20Sopenharmony_ci ret = rxrpc_krb5_decode_principal(&rxk5->client, &xdr, &toklen); 4628c2ecf20Sopenharmony_ci if (ret < 0) 4638c2ecf20Sopenharmony_ci goto error; 4648c2ecf20Sopenharmony_ci ret = rxrpc_krb5_decode_principal(&rxk5->server, &xdr, &toklen); 4658c2ecf20Sopenharmony_ci if (ret < 0) 4668c2ecf20Sopenharmony_ci goto error; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* extract the session key and the encoding type (the tag field -> 4698c2ecf20Sopenharmony_ci * ENCTYPE_xxx) */ 4708c2ecf20Sopenharmony_ci ret = rxrpc_krb5_decode_tagged_data(&rxk5->session, AFSTOKEN_DATA_MAX, 4718c2ecf20Sopenharmony_ci &xdr, &toklen); 4728c2ecf20Sopenharmony_ci if (ret < 0) 4738c2ecf20Sopenharmony_ci goto error; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (toklen < 4 * 8 + 2 * 4) 4768c2ecf20Sopenharmony_ci goto inval; 4778c2ecf20Sopenharmony_ci rxk5->authtime = be64_to_cpup((const __be64 *) xdr); 4788c2ecf20Sopenharmony_ci xdr += 2; 4798c2ecf20Sopenharmony_ci rxk5->starttime = be64_to_cpup((const __be64 *) xdr); 4808c2ecf20Sopenharmony_ci xdr += 2; 4818c2ecf20Sopenharmony_ci rxk5->endtime = be64_to_cpup((const __be64 *) xdr); 4828c2ecf20Sopenharmony_ci xdr += 2; 4838c2ecf20Sopenharmony_ci rxk5->renew_till = be64_to_cpup((const __be64 *) xdr); 4848c2ecf20Sopenharmony_ci xdr += 2; 4858c2ecf20Sopenharmony_ci rxk5->is_skey = ntohl(*xdr++); 4868c2ecf20Sopenharmony_ci rxk5->flags = ntohl(*xdr++); 4878c2ecf20Sopenharmony_ci toklen -= 4 * 8 + 2 * 4; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci _debug("times: a=%llx s=%llx e=%llx rt=%llx", 4908c2ecf20Sopenharmony_ci rxk5->authtime, rxk5->starttime, rxk5->endtime, 4918c2ecf20Sopenharmony_ci rxk5->renew_till); 4928c2ecf20Sopenharmony_ci _debug("is_skey=%x flags=%x", rxk5->is_skey, rxk5->flags); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* extract the permitted client addresses */ 4958c2ecf20Sopenharmony_ci ret = rxrpc_krb5_decode_tagged_array(&rxk5->addresses, 4968c2ecf20Sopenharmony_ci &rxk5->n_addresses, 4978c2ecf20Sopenharmony_ci AFSTOKEN_K5_ADDRESSES_MAX, 4988c2ecf20Sopenharmony_ci AFSTOKEN_DATA_MAX, 4998c2ecf20Sopenharmony_ci &xdr, &toklen); 5008c2ecf20Sopenharmony_ci if (ret < 0) 5018c2ecf20Sopenharmony_ci goto error; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci ASSERTCMP((end_xdr - xdr) << 2, ==, toklen); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* extract the tickets */ 5068c2ecf20Sopenharmony_ci ret = rxrpc_krb5_decode_ticket(&rxk5->ticket, &rxk5->ticket_len, 5078c2ecf20Sopenharmony_ci &xdr, &toklen); 5088c2ecf20Sopenharmony_ci if (ret < 0) 5098c2ecf20Sopenharmony_ci goto error; 5108c2ecf20Sopenharmony_ci ret = rxrpc_krb5_decode_ticket(&rxk5->ticket2, &rxk5->ticket2_len, 5118c2ecf20Sopenharmony_ci &xdr, &toklen); 5128c2ecf20Sopenharmony_ci if (ret < 0) 5138c2ecf20Sopenharmony_ci goto error; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci ASSERTCMP((end_xdr - xdr) << 2, ==, toklen); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* extract the typed auth data */ 5188c2ecf20Sopenharmony_ci ret = rxrpc_krb5_decode_tagged_array(&rxk5->authdata, 5198c2ecf20Sopenharmony_ci &rxk5->n_authdata, 5208c2ecf20Sopenharmony_ci AFSTOKEN_K5_AUTHDATA_MAX, 5218c2ecf20Sopenharmony_ci AFSTOKEN_BDATALN_MAX, 5228c2ecf20Sopenharmony_ci &xdr, &toklen); 5238c2ecf20Sopenharmony_ci if (ret < 0) 5248c2ecf20Sopenharmony_ci goto error; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci ASSERTCMP((end_xdr - xdr) << 2, ==, toklen); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (toklen != 0) 5298c2ecf20Sopenharmony_ci goto inval; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* attach the payload */ 5328c2ecf20Sopenharmony_ci for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0]; 5338c2ecf20Sopenharmony_ci *pptoken; 5348c2ecf20Sopenharmony_ci pptoken = &(*pptoken)->next) 5358c2ecf20Sopenharmony_ci continue; 5368c2ecf20Sopenharmony_ci *pptoken = token; 5378c2ecf20Sopenharmony_ci expiry = rxrpc_u32_to_time64(token->k5->endtime); 5388c2ecf20Sopenharmony_ci if (expiry < prep->expiry) 5398c2ecf20Sopenharmony_ci prep->expiry = expiry; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci _leave(" = 0"); 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ciinval: 5458c2ecf20Sopenharmony_ci ret = -EINVAL; 5468c2ecf20Sopenharmony_cierror: 5478c2ecf20Sopenharmony_ci rxrpc_rxk5_free(rxk5); 5488c2ecf20Sopenharmony_ci kfree(token); 5498c2ecf20Sopenharmony_ci _leave(" = %d", ret); 5508c2ecf20Sopenharmony_ci return ret; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci/* 5548c2ecf20Sopenharmony_ci * attempt to parse the data as the XDR format 5558c2ecf20Sopenharmony_ci * - the caller guarantees we have more than 7 words 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_cistatic int rxrpc_preparse_xdr(struct key_preparsed_payload *prep) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci const __be32 *xdr = prep->data, *token; 5608c2ecf20Sopenharmony_ci const char *cp; 5618c2ecf20Sopenharmony_ci unsigned int len, paddedlen, loop, ntoken, toklen, sec_ix; 5628c2ecf20Sopenharmony_ci size_t datalen = prep->datalen; 5638c2ecf20Sopenharmony_ci int ret; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci _enter(",{%x,%x,%x,%x},%zu", 5668c2ecf20Sopenharmony_ci ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]), 5678c2ecf20Sopenharmony_ci prep->datalen); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (datalen > AFSTOKEN_LENGTH_MAX) 5708c2ecf20Sopenharmony_ci goto not_xdr; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* XDR is an array of __be32's */ 5738c2ecf20Sopenharmony_ci if (datalen & 3) 5748c2ecf20Sopenharmony_ci goto not_xdr; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* the flags should be 0 (the setpag bit must be handled by 5778c2ecf20Sopenharmony_ci * userspace) */ 5788c2ecf20Sopenharmony_ci if (ntohl(*xdr++) != 0) 5798c2ecf20Sopenharmony_ci goto not_xdr; 5808c2ecf20Sopenharmony_ci datalen -= 4; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* check the cell name */ 5838c2ecf20Sopenharmony_ci len = ntohl(*xdr++); 5848c2ecf20Sopenharmony_ci if (len < 1 || len > AFSTOKEN_CELL_MAX) 5858c2ecf20Sopenharmony_ci goto not_xdr; 5868c2ecf20Sopenharmony_ci datalen -= 4; 5878c2ecf20Sopenharmony_ci paddedlen = (len + 3) & ~3; 5888c2ecf20Sopenharmony_ci if (paddedlen > datalen) 5898c2ecf20Sopenharmony_ci goto not_xdr; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci cp = (const char *) xdr; 5928c2ecf20Sopenharmony_ci for (loop = 0; loop < len; loop++) 5938c2ecf20Sopenharmony_ci if (!isprint(cp[loop])) 5948c2ecf20Sopenharmony_ci goto not_xdr; 5958c2ecf20Sopenharmony_ci for (; loop < paddedlen; loop++) 5968c2ecf20Sopenharmony_ci if (cp[loop]) 5978c2ecf20Sopenharmony_ci goto not_xdr; 5988c2ecf20Sopenharmony_ci _debug("cellname: [%u/%u] '%*.*s'", 5998c2ecf20Sopenharmony_ci len, paddedlen, len, len, (const char *) xdr); 6008c2ecf20Sopenharmony_ci datalen -= paddedlen; 6018c2ecf20Sopenharmony_ci xdr += paddedlen >> 2; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* get the token count */ 6048c2ecf20Sopenharmony_ci if (datalen < 12) 6058c2ecf20Sopenharmony_ci goto not_xdr; 6068c2ecf20Sopenharmony_ci ntoken = ntohl(*xdr++); 6078c2ecf20Sopenharmony_ci datalen -= 4; 6088c2ecf20Sopenharmony_ci _debug("ntoken: %x", ntoken); 6098c2ecf20Sopenharmony_ci if (ntoken < 1 || ntoken > AFSTOKEN_MAX) 6108c2ecf20Sopenharmony_ci goto not_xdr; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* check each token wrapper */ 6138c2ecf20Sopenharmony_ci token = xdr; 6148c2ecf20Sopenharmony_ci loop = ntoken; 6158c2ecf20Sopenharmony_ci do { 6168c2ecf20Sopenharmony_ci if (datalen < 8) 6178c2ecf20Sopenharmony_ci goto not_xdr; 6188c2ecf20Sopenharmony_ci toklen = ntohl(*xdr++); 6198c2ecf20Sopenharmony_ci sec_ix = ntohl(*xdr); 6208c2ecf20Sopenharmony_ci datalen -= 4; 6218c2ecf20Sopenharmony_ci _debug("token: [%x/%zx] %x", toklen, datalen, sec_ix); 6228c2ecf20Sopenharmony_ci paddedlen = (toklen + 3) & ~3; 6238c2ecf20Sopenharmony_ci if (toklen < 20 || toklen > datalen || paddedlen > datalen) 6248c2ecf20Sopenharmony_ci goto not_xdr; 6258c2ecf20Sopenharmony_ci datalen -= paddedlen; 6268c2ecf20Sopenharmony_ci xdr += paddedlen >> 2; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci } while (--loop > 0); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci _debug("remainder: %zu", datalen); 6318c2ecf20Sopenharmony_ci if (datalen != 0) 6328c2ecf20Sopenharmony_ci goto not_xdr; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* okay: we're going to assume it's valid XDR format 6358c2ecf20Sopenharmony_ci * - we ignore the cellname, relying on the key to be correctly named 6368c2ecf20Sopenharmony_ci */ 6378c2ecf20Sopenharmony_ci do { 6388c2ecf20Sopenharmony_ci xdr = token; 6398c2ecf20Sopenharmony_ci toklen = ntohl(*xdr++); 6408c2ecf20Sopenharmony_ci token = xdr + ((toklen + 3) >> 2); 6418c2ecf20Sopenharmony_ci sec_ix = ntohl(*xdr++); 6428c2ecf20Sopenharmony_ci toklen -= 4; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci _debug("TOKEN type=%u [%p-%p]", sec_ix, xdr, token); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci switch (sec_ix) { 6478c2ecf20Sopenharmony_ci case RXRPC_SECURITY_RXKAD: 6488c2ecf20Sopenharmony_ci ret = rxrpc_preparse_xdr_rxkad(prep, datalen, xdr, toklen); 6498c2ecf20Sopenharmony_ci if (ret != 0) 6508c2ecf20Sopenharmony_ci goto error; 6518c2ecf20Sopenharmony_ci break; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci case RXRPC_SECURITY_RXK5: 6548c2ecf20Sopenharmony_ci ret = rxrpc_preparse_xdr_rxk5(prep, datalen, xdr, toklen); 6558c2ecf20Sopenharmony_ci if (ret != 0) 6568c2ecf20Sopenharmony_ci goto error; 6578c2ecf20Sopenharmony_ci break; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci default: 6608c2ecf20Sopenharmony_ci ret = -EPROTONOSUPPORT; 6618c2ecf20Sopenharmony_ci goto error; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci } while (--ntoken > 0); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci _leave(" = 0"); 6678c2ecf20Sopenharmony_ci return 0; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cinot_xdr: 6708c2ecf20Sopenharmony_ci _leave(" = -EPROTO"); 6718c2ecf20Sopenharmony_ci return -EPROTO; 6728c2ecf20Sopenharmony_cierror: 6738c2ecf20Sopenharmony_ci _leave(" = %d", ret); 6748c2ecf20Sopenharmony_ci return ret; 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci/* 6788c2ecf20Sopenharmony_ci * Preparse an rxrpc defined key. 6798c2ecf20Sopenharmony_ci * 6808c2ecf20Sopenharmony_ci * Data should be of the form: 6818c2ecf20Sopenharmony_ci * OFFSET LEN CONTENT 6828c2ecf20Sopenharmony_ci * 0 4 key interface version number 6838c2ecf20Sopenharmony_ci * 4 2 security index (type) 6848c2ecf20Sopenharmony_ci * 6 2 ticket length 6858c2ecf20Sopenharmony_ci * 8 4 key expiry time (time_t) 6868c2ecf20Sopenharmony_ci * 12 4 kvno 6878c2ecf20Sopenharmony_ci * 16 8 session key 6888c2ecf20Sopenharmony_ci * 24 [len] ticket 6898c2ecf20Sopenharmony_ci * 6908c2ecf20Sopenharmony_ci * if no data is provided, then a no-security key is made 6918c2ecf20Sopenharmony_ci */ 6928c2ecf20Sopenharmony_cistatic int rxrpc_preparse(struct key_preparsed_payload *prep) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci const struct rxrpc_key_data_v1 *v1; 6958c2ecf20Sopenharmony_ci struct rxrpc_key_token *token, **pp; 6968c2ecf20Sopenharmony_ci time64_t expiry; 6978c2ecf20Sopenharmony_ci size_t plen; 6988c2ecf20Sopenharmony_ci u32 kver; 6998c2ecf20Sopenharmony_ci int ret; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci _enter("%zu", prep->datalen); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci /* handle a no-security key */ 7048c2ecf20Sopenharmony_ci if (!prep->data && prep->datalen == 0) 7058c2ecf20Sopenharmony_ci return 0; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci /* determine if the XDR payload format is being used */ 7088c2ecf20Sopenharmony_ci if (prep->datalen > 7 * 4) { 7098c2ecf20Sopenharmony_ci ret = rxrpc_preparse_xdr(prep); 7108c2ecf20Sopenharmony_ci if (ret != -EPROTO) 7118c2ecf20Sopenharmony_ci return ret; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci /* get the key interface version number */ 7158c2ecf20Sopenharmony_ci ret = -EINVAL; 7168c2ecf20Sopenharmony_ci if (prep->datalen <= 4 || !prep->data) 7178c2ecf20Sopenharmony_ci goto error; 7188c2ecf20Sopenharmony_ci memcpy(&kver, prep->data, sizeof(kver)); 7198c2ecf20Sopenharmony_ci prep->data += sizeof(kver); 7208c2ecf20Sopenharmony_ci prep->datalen -= sizeof(kver); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci _debug("KEY I/F VERSION: %u", kver); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci ret = -EKEYREJECTED; 7258c2ecf20Sopenharmony_ci if (kver != 1) 7268c2ecf20Sopenharmony_ci goto error; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* deal with a version 1 key */ 7298c2ecf20Sopenharmony_ci ret = -EINVAL; 7308c2ecf20Sopenharmony_ci if (prep->datalen < sizeof(*v1)) 7318c2ecf20Sopenharmony_ci goto error; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci v1 = prep->data; 7348c2ecf20Sopenharmony_ci if (prep->datalen != sizeof(*v1) + v1->ticket_length) 7358c2ecf20Sopenharmony_ci goto error; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci _debug("SCIX: %u", v1->security_index); 7388c2ecf20Sopenharmony_ci _debug("TLEN: %u", v1->ticket_length); 7398c2ecf20Sopenharmony_ci _debug("EXPY: %x", v1->expiry); 7408c2ecf20Sopenharmony_ci _debug("KVNO: %u", v1->kvno); 7418c2ecf20Sopenharmony_ci _debug("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x", 7428c2ecf20Sopenharmony_ci v1->session_key[0], v1->session_key[1], 7438c2ecf20Sopenharmony_ci v1->session_key[2], v1->session_key[3], 7448c2ecf20Sopenharmony_ci v1->session_key[4], v1->session_key[5], 7458c2ecf20Sopenharmony_ci v1->session_key[6], v1->session_key[7]); 7468c2ecf20Sopenharmony_ci if (v1->ticket_length >= 8) 7478c2ecf20Sopenharmony_ci _debug("TCKT: %02x%02x%02x%02x%02x%02x%02x%02x", 7488c2ecf20Sopenharmony_ci v1->ticket[0], v1->ticket[1], 7498c2ecf20Sopenharmony_ci v1->ticket[2], v1->ticket[3], 7508c2ecf20Sopenharmony_ci v1->ticket[4], v1->ticket[5], 7518c2ecf20Sopenharmony_ci v1->ticket[6], v1->ticket[7]); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci ret = -EPROTONOSUPPORT; 7548c2ecf20Sopenharmony_ci if (v1->security_index != RXRPC_SECURITY_RXKAD) 7558c2ecf20Sopenharmony_ci goto error; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci plen = sizeof(*token->kad) + v1->ticket_length; 7588c2ecf20Sopenharmony_ci prep->quotalen = plen + sizeof(*token); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci ret = -ENOMEM; 7618c2ecf20Sopenharmony_ci token = kzalloc(sizeof(*token), GFP_KERNEL); 7628c2ecf20Sopenharmony_ci if (!token) 7638c2ecf20Sopenharmony_ci goto error; 7648c2ecf20Sopenharmony_ci token->kad = kzalloc(plen, GFP_KERNEL); 7658c2ecf20Sopenharmony_ci if (!token->kad) 7668c2ecf20Sopenharmony_ci goto error_free; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci token->security_index = RXRPC_SECURITY_RXKAD; 7698c2ecf20Sopenharmony_ci token->kad->ticket_len = v1->ticket_length; 7708c2ecf20Sopenharmony_ci token->kad->expiry = v1->expiry; 7718c2ecf20Sopenharmony_ci token->kad->kvno = v1->kvno; 7728c2ecf20Sopenharmony_ci memcpy(&token->kad->session_key, &v1->session_key, 8); 7738c2ecf20Sopenharmony_ci memcpy(&token->kad->ticket, v1->ticket, v1->ticket_length); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* count the number of tokens attached */ 7768c2ecf20Sopenharmony_ci prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* attach the data */ 7798c2ecf20Sopenharmony_ci pp = (struct rxrpc_key_token **)&prep->payload.data[0]; 7808c2ecf20Sopenharmony_ci while (*pp) 7818c2ecf20Sopenharmony_ci pp = &(*pp)->next; 7828c2ecf20Sopenharmony_ci *pp = token; 7838c2ecf20Sopenharmony_ci expiry = rxrpc_u32_to_time64(token->kad->expiry); 7848c2ecf20Sopenharmony_ci if (expiry < prep->expiry) 7858c2ecf20Sopenharmony_ci prep->expiry = expiry; 7868c2ecf20Sopenharmony_ci token = NULL; 7878c2ecf20Sopenharmony_ci ret = 0; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cierror_free: 7908c2ecf20Sopenharmony_ci kfree(token); 7918c2ecf20Sopenharmony_cierror: 7928c2ecf20Sopenharmony_ci return ret; 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci/* 7968c2ecf20Sopenharmony_ci * Free token list. 7978c2ecf20Sopenharmony_ci */ 7988c2ecf20Sopenharmony_cistatic void rxrpc_free_token_list(struct rxrpc_key_token *token) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci struct rxrpc_key_token *next; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci for (; token; token = next) { 8038c2ecf20Sopenharmony_ci next = token->next; 8048c2ecf20Sopenharmony_ci switch (token->security_index) { 8058c2ecf20Sopenharmony_ci case RXRPC_SECURITY_RXKAD: 8068c2ecf20Sopenharmony_ci kfree(token->kad); 8078c2ecf20Sopenharmony_ci break; 8088c2ecf20Sopenharmony_ci case RXRPC_SECURITY_RXK5: 8098c2ecf20Sopenharmony_ci if (token->k5) 8108c2ecf20Sopenharmony_ci rxrpc_rxk5_free(token->k5); 8118c2ecf20Sopenharmony_ci break; 8128c2ecf20Sopenharmony_ci default: 8138c2ecf20Sopenharmony_ci pr_err("Unknown token type %x on rxrpc key\n", 8148c2ecf20Sopenharmony_ci token->security_index); 8158c2ecf20Sopenharmony_ci BUG(); 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci kfree(token); 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci/* 8238c2ecf20Sopenharmony_ci * Clean up preparse data. 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_cistatic void rxrpc_free_preparse(struct key_preparsed_payload *prep) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci rxrpc_free_token_list(prep->payload.data[0]); 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci/* 8318c2ecf20Sopenharmony_ci * Preparse a server secret key. 8328c2ecf20Sopenharmony_ci * 8338c2ecf20Sopenharmony_ci * The data should be the 8-byte secret key. 8348c2ecf20Sopenharmony_ci */ 8358c2ecf20Sopenharmony_cistatic int rxrpc_preparse_s(struct key_preparsed_payload *prep) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci struct crypto_skcipher *ci; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci _enter("%zu", prep->datalen); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (prep->datalen != 8) 8428c2ecf20Sopenharmony_ci return -EINVAL; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci memcpy(&prep->payload.data[2], prep->data, 8); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci ci = crypto_alloc_skcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC); 8478c2ecf20Sopenharmony_ci if (IS_ERR(ci)) { 8488c2ecf20Sopenharmony_ci _leave(" = %ld", PTR_ERR(ci)); 8498c2ecf20Sopenharmony_ci return PTR_ERR(ci); 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (crypto_skcipher_setkey(ci, prep->data, 8) < 0) 8538c2ecf20Sopenharmony_ci BUG(); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci prep->payload.data[0] = ci; 8568c2ecf20Sopenharmony_ci _leave(" = 0"); 8578c2ecf20Sopenharmony_ci return 0; 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci/* 8618c2ecf20Sopenharmony_ci * Clean up preparse data. 8628c2ecf20Sopenharmony_ci */ 8638c2ecf20Sopenharmony_cistatic void rxrpc_free_preparse_s(struct key_preparsed_payload *prep) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci if (prep->payload.data[0]) 8668c2ecf20Sopenharmony_ci crypto_free_skcipher(prep->payload.data[0]); 8678c2ecf20Sopenharmony_ci} 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci/* 8708c2ecf20Sopenharmony_ci * dispose of the data dangling from the corpse of a rxrpc key 8718c2ecf20Sopenharmony_ci */ 8728c2ecf20Sopenharmony_cistatic void rxrpc_destroy(struct key *key) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci rxrpc_free_token_list(key->payload.data[0]); 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci/* 8788c2ecf20Sopenharmony_ci * dispose of the data dangling from the corpse of a rxrpc key 8798c2ecf20Sopenharmony_ci */ 8808c2ecf20Sopenharmony_cistatic void rxrpc_destroy_s(struct key *key) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci if (key->payload.data[0]) { 8838c2ecf20Sopenharmony_ci crypto_free_skcipher(key->payload.data[0]); 8848c2ecf20Sopenharmony_ci key->payload.data[0] = NULL; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci} 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci/* 8898c2ecf20Sopenharmony_ci * describe the rxrpc key 8908c2ecf20Sopenharmony_ci */ 8918c2ecf20Sopenharmony_cistatic void rxrpc_describe(const struct key *key, struct seq_file *m) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci seq_puts(m, key->description); 8948c2ecf20Sopenharmony_ci} 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci/* 8978c2ecf20Sopenharmony_ci * grab the security key for a socket 8988c2ecf20Sopenharmony_ci */ 8998c2ecf20Sopenharmony_ciint rxrpc_request_key(struct rxrpc_sock *rx, sockptr_t optval, int optlen) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci struct key *key; 9028c2ecf20Sopenharmony_ci char *description; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci _enter(""); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (optlen <= 0 || optlen > PAGE_SIZE - 1 || rx->securities) 9078c2ecf20Sopenharmony_ci return -EINVAL; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci description = memdup_sockptr_nul(optval, optlen); 9108c2ecf20Sopenharmony_ci if (IS_ERR(description)) 9118c2ecf20Sopenharmony_ci return PTR_ERR(description); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci key = request_key_net(&key_type_rxrpc, description, sock_net(&rx->sk), NULL); 9148c2ecf20Sopenharmony_ci if (IS_ERR(key)) { 9158c2ecf20Sopenharmony_ci kfree(description); 9168c2ecf20Sopenharmony_ci _leave(" = %ld", PTR_ERR(key)); 9178c2ecf20Sopenharmony_ci return PTR_ERR(key); 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci rx->key = key; 9218c2ecf20Sopenharmony_ci kfree(description); 9228c2ecf20Sopenharmony_ci _leave(" = 0 [key %x]", key->serial); 9238c2ecf20Sopenharmony_ci return 0; 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci/* 9278c2ecf20Sopenharmony_ci * grab the security keyring for a server socket 9288c2ecf20Sopenharmony_ci */ 9298c2ecf20Sopenharmony_ciint rxrpc_server_keyring(struct rxrpc_sock *rx, sockptr_t optval, int optlen) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci struct key *key; 9328c2ecf20Sopenharmony_ci char *description; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci _enter(""); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if (optlen <= 0 || optlen > PAGE_SIZE - 1) 9378c2ecf20Sopenharmony_ci return -EINVAL; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci description = memdup_sockptr_nul(optval, optlen); 9408c2ecf20Sopenharmony_ci if (IS_ERR(description)) 9418c2ecf20Sopenharmony_ci return PTR_ERR(description); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci key = request_key(&key_type_keyring, description, NULL); 9448c2ecf20Sopenharmony_ci if (IS_ERR(key)) { 9458c2ecf20Sopenharmony_ci kfree(description); 9468c2ecf20Sopenharmony_ci _leave(" = %ld", PTR_ERR(key)); 9478c2ecf20Sopenharmony_ci return PTR_ERR(key); 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci rx->securities = key; 9518c2ecf20Sopenharmony_ci kfree(description); 9528c2ecf20Sopenharmony_ci _leave(" = 0 [key %x]", key->serial); 9538c2ecf20Sopenharmony_ci return 0; 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci/* 9578c2ecf20Sopenharmony_ci * generate a server data key 9588c2ecf20Sopenharmony_ci */ 9598c2ecf20Sopenharmony_ciint rxrpc_get_server_data_key(struct rxrpc_connection *conn, 9608c2ecf20Sopenharmony_ci const void *session_key, 9618c2ecf20Sopenharmony_ci time64_t expiry, 9628c2ecf20Sopenharmony_ci u32 kvno) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci const struct cred *cred = current_cred(); 9658c2ecf20Sopenharmony_ci struct key *key; 9668c2ecf20Sopenharmony_ci int ret; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci struct { 9698c2ecf20Sopenharmony_ci u32 kver; 9708c2ecf20Sopenharmony_ci struct rxrpc_key_data_v1 v1; 9718c2ecf20Sopenharmony_ci } data; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci _enter(""); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci key = key_alloc(&key_type_rxrpc, "x", 9768c2ecf20Sopenharmony_ci GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0, 9778c2ecf20Sopenharmony_ci KEY_ALLOC_NOT_IN_QUOTA, NULL); 9788c2ecf20Sopenharmony_ci if (IS_ERR(key)) { 9798c2ecf20Sopenharmony_ci _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key)); 9808c2ecf20Sopenharmony_ci return -ENOMEM; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci _debug("key %d", key_serial(key)); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci data.kver = 1; 9868c2ecf20Sopenharmony_ci data.v1.security_index = RXRPC_SECURITY_RXKAD; 9878c2ecf20Sopenharmony_ci data.v1.ticket_length = 0; 9888c2ecf20Sopenharmony_ci data.v1.expiry = rxrpc_time64_to_u32(expiry); 9898c2ecf20Sopenharmony_ci data.v1.kvno = 0; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci memcpy(&data.v1.session_key, session_key, sizeof(data.v1.session_key)); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci ret = key_instantiate_and_link(key, &data, sizeof(data), NULL, NULL); 9948c2ecf20Sopenharmony_ci if (ret < 0) 9958c2ecf20Sopenharmony_ci goto error; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci conn->params.key = key; 9988c2ecf20Sopenharmony_ci _leave(" = 0 [%d]", key_serial(key)); 9998c2ecf20Sopenharmony_ci return 0; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_cierror: 10028c2ecf20Sopenharmony_ci key_revoke(key); 10038c2ecf20Sopenharmony_ci key_put(key); 10048c2ecf20Sopenharmony_ci _leave(" = -ENOMEM [ins %d]", ret); 10058c2ecf20Sopenharmony_ci return -ENOMEM; 10068c2ecf20Sopenharmony_ci} 10078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rxrpc_get_server_data_key); 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci/** 10108c2ecf20Sopenharmony_ci * rxrpc_get_null_key - Generate a null RxRPC key 10118c2ecf20Sopenharmony_ci * @keyname: The name to give the key. 10128c2ecf20Sopenharmony_ci * 10138c2ecf20Sopenharmony_ci * Generate a null RxRPC key that can be used to indicate anonymous security is 10148c2ecf20Sopenharmony_ci * required for a particular domain. 10158c2ecf20Sopenharmony_ci */ 10168c2ecf20Sopenharmony_cistruct key *rxrpc_get_null_key(const char *keyname) 10178c2ecf20Sopenharmony_ci{ 10188c2ecf20Sopenharmony_ci const struct cred *cred = current_cred(); 10198c2ecf20Sopenharmony_ci struct key *key; 10208c2ecf20Sopenharmony_ci int ret; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci key = key_alloc(&key_type_rxrpc, keyname, 10238c2ecf20Sopenharmony_ci GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 10248c2ecf20Sopenharmony_ci KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, NULL); 10258c2ecf20Sopenharmony_ci if (IS_ERR(key)) 10268c2ecf20Sopenharmony_ci return key; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci ret = key_instantiate_and_link(key, NULL, 0, NULL, NULL); 10298c2ecf20Sopenharmony_ci if (ret < 0) { 10308c2ecf20Sopenharmony_ci key_revoke(key); 10318c2ecf20Sopenharmony_ci key_put(key); 10328c2ecf20Sopenharmony_ci return ERR_PTR(ret); 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci return key; 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rxrpc_get_null_key); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci/* 10408c2ecf20Sopenharmony_ci * read the contents of an rxrpc key 10418c2ecf20Sopenharmony_ci * - this returns the result in XDR form 10428c2ecf20Sopenharmony_ci */ 10438c2ecf20Sopenharmony_cistatic long rxrpc_read(const struct key *key, 10448c2ecf20Sopenharmony_ci char *buffer, size_t buflen) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci const struct rxrpc_key_token *token; 10478c2ecf20Sopenharmony_ci const struct krb5_principal *princ; 10488c2ecf20Sopenharmony_ci size_t size; 10498c2ecf20Sopenharmony_ci __be32 *xdr, *oldxdr; 10508c2ecf20Sopenharmony_ci u32 cnlen, toksize, ntoks, tok, zero; 10518c2ecf20Sopenharmony_ci u16 toksizes[AFSTOKEN_MAX]; 10528c2ecf20Sopenharmony_ci int loop; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci _enter(""); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci /* we don't know what form we should return non-AFS keys in */ 10578c2ecf20Sopenharmony_ci if (memcmp(key->description, "afs@", 4) != 0) 10588c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10598c2ecf20Sopenharmony_ci cnlen = strlen(key->description + 4); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci#define RND(X) (((X) + 3) & ~3) 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci /* AFS keys we return in XDR form, so we need to work out the size of 10648c2ecf20Sopenharmony_ci * the XDR */ 10658c2ecf20Sopenharmony_ci size = 2 * 4; /* flags, cellname len */ 10668c2ecf20Sopenharmony_ci size += RND(cnlen); /* cellname */ 10678c2ecf20Sopenharmony_ci size += 1 * 4; /* token count */ 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci ntoks = 0; 10708c2ecf20Sopenharmony_ci for (token = key->payload.data[0]; token; token = token->next) { 10718c2ecf20Sopenharmony_ci toksize = 4; /* sec index */ 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci switch (token->security_index) { 10748c2ecf20Sopenharmony_ci case RXRPC_SECURITY_RXKAD: 10758c2ecf20Sopenharmony_ci toksize += 8 * 4; /* viceid, kvno, key*2, begin, 10768c2ecf20Sopenharmony_ci * end, primary, tktlen */ 10778c2ecf20Sopenharmony_ci toksize += RND(token->kad->ticket_len); 10788c2ecf20Sopenharmony_ci break; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci case RXRPC_SECURITY_RXK5: 10818c2ecf20Sopenharmony_ci princ = &token->k5->client; 10828c2ecf20Sopenharmony_ci toksize += 4 + princ->n_name_parts * 4; 10838c2ecf20Sopenharmony_ci for (loop = 0; loop < princ->n_name_parts; loop++) 10848c2ecf20Sopenharmony_ci toksize += RND(strlen(princ->name_parts[loop])); 10858c2ecf20Sopenharmony_ci toksize += 4 + RND(strlen(princ->realm)); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci princ = &token->k5->server; 10888c2ecf20Sopenharmony_ci toksize += 4 + princ->n_name_parts * 4; 10898c2ecf20Sopenharmony_ci for (loop = 0; loop < princ->n_name_parts; loop++) 10908c2ecf20Sopenharmony_ci toksize += RND(strlen(princ->name_parts[loop])); 10918c2ecf20Sopenharmony_ci toksize += 4 + RND(strlen(princ->realm)); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci toksize += 8 + RND(token->k5->session.data_len); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci toksize += 4 * 8 + 2 * 4; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci toksize += 4 + token->k5->n_addresses * 8; 10988c2ecf20Sopenharmony_ci for (loop = 0; loop < token->k5->n_addresses; loop++) 10998c2ecf20Sopenharmony_ci toksize += RND(token->k5->addresses[loop].data_len); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci toksize += 4 + RND(token->k5->ticket_len); 11028c2ecf20Sopenharmony_ci toksize += 4 + RND(token->k5->ticket2_len); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci toksize += 4 + token->k5->n_authdata * 8; 11058c2ecf20Sopenharmony_ci for (loop = 0; loop < token->k5->n_authdata; loop++) 11068c2ecf20Sopenharmony_ci toksize += RND(token->k5->authdata[loop].data_len); 11078c2ecf20Sopenharmony_ci break; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci default: /* we have a ticket we can't encode */ 11108c2ecf20Sopenharmony_ci pr_err("Unsupported key token type (%u)\n", 11118c2ecf20Sopenharmony_ci token->security_index); 11128c2ecf20Sopenharmony_ci return -ENOPKG; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci _debug("token[%u]: toksize=%u", ntoks, toksize); 11168c2ecf20Sopenharmony_ci ASSERTCMP(toksize, <=, AFSTOKEN_LENGTH_MAX); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci toksizes[ntoks++] = toksize; 11198c2ecf20Sopenharmony_ci size += toksize + 4; /* each token has a length word */ 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci#undef RND 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci if (!buffer || buflen < size) 11258c2ecf20Sopenharmony_ci return size; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci xdr = (__be32 *)buffer; 11288c2ecf20Sopenharmony_ci zero = 0; 11298c2ecf20Sopenharmony_ci#define ENCODE(x) \ 11308c2ecf20Sopenharmony_ci do { \ 11318c2ecf20Sopenharmony_ci *xdr++ = htonl(x); \ 11328c2ecf20Sopenharmony_ci } while(0) 11338c2ecf20Sopenharmony_ci#define ENCODE_DATA(l, s) \ 11348c2ecf20Sopenharmony_ci do { \ 11358c2ecf20Sopenharmony_ci u32 _l = (l); \ 11368c2ecf20Sopenharmony_ci ENCODE(l); \ 11378c2ecf20Sopenharmony_ci memcpy(xdr, (s), _l); \ 11388c2ecf20Sopenharmony_ci if (_l & 3) \ 11398c2ecf20Sopenharmony_ci memcpy((u8 *)xdr + _l, &zero, 4 - (_l & 3)); \ 11408c2ecf20Sopenharmony_ci xdr += (_l + 3) >> 2; \ 11418c2ecf20Sopenharmony_ci } while(0) 11428c2ecf20Sopenharmony_ci#define ENCODE_BYTES(l, s) \ 11438c2ecf20Sopenharmony_ci do { \ 11448c2ecf20Sopenharmony_ci u32 _l = (l); \ 11458c2ecf20Sopenharmony_ci memcpy(xdr, (s), _l); \ 11468c2ecf20Sopenharmony_ci if (_l & 3) \ 11478c2ecf20Sopenharmony_ci memcpy((u8 *)xdr + _l, &zero, 4 - (_l & 3)); \ 11488c2ecf20Sopenharmony_ci xdr += (_l + 3) >> 2; \ 11498c2ecf20Sopenharmony_ci } while(0) 11508c2ecf20Sopenharmony_ci#define ENCODE64(x) \ 11518c2ecf20Sopenharmony_ci do { \ 11528c2ecf20Sopenharmony_ci __be64 y = cpu_to_be64(x); \ 11538c2ecf20Sopenharmony_ci memcpy(xdr, &y, 8); \ 11548c2ecf20Sopenharmony_ci xdr += 8 >> 2; \ 11558c2ecf20Sopenharmony_ci } while(0) 11568c2ecf20Sopenharmony_ci#define ENCODE_STR(s) \ 11578c2ecf20Sopenharmony_ci do { \ 11588c2ecf20Sopenharmony_ci const char *_s = (s); \ 11598c2ecf20Sopenharmony_ci ENCODE_DATA(strlen(_s), _s); \ 11608c2ecf20Sopenharmony_ci } while(0) 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci ENCODE(0); /* flags */ 11638c2ecf20Sopenharmony_ci ENCODE_DATA(cnlen, key->description + 4); /* cellname */ 11648c2ecf20Sopenharmony_ci ENCODE(ntoks); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci tok = 0; 11678c2ecf20Sopenharmony_ci for (token = key->payload.data[0]; token; token = token->next) { 11688c2ecf20Sopenharmony_ci toksize = toksizes[tok++]; 11698c2ecf20Sopenharmony_ci ENCODE(toksize); 11708c2ecf20Sopenharmony_ci oldxdr = xdr; 11718c2ecf20Sopenharmony_ci ENCODE(token->security_index); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci switch (token->security_index) { 11748c2ecf20Sopenharmony_ci case RXRPC_SECURITY_RXKAD: 11758c2ecf20Sopenharmony_ci ENCODE(token->kad->vice_id); 11768c2ecf20Sopenharmony_ci ENCODE(token->kad->kvno); 11778c2ecf20Sopenharmony_ci ENCODE_BYTES(8, token->kad->session_key); 11788c2ecf20Sopenharmony_ci ENCODE(token->kad->start); 11798c2ecf20Sopenharmony_ci ENCODE(token->kad->expiry); 11808c2ecf20Sopenharmony_ci ENCODE(token->kad->primary_flag); 11818c2ecf20Sopenharmony_ci ENCODE_DATA(token->kad->ticket_len, token->kad->ticket); 11828c2ecf20Sopenharmony_ci break; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci case RXRPC_SECURITY_RXK5: 11858c2ecf20Sopenharmony_ci princ = &token->k5->client; 11868c2ecf20Sopenharmony_ci ENCODE(princ->n_name_parts); 11878c2ecf20Sopenharmony_ci for (loop = 0; loop < princ->n_name_parts; loop++) 11888c2ecf20Sopenharmony_ci ENCODE_STR(princ->name_parts[loop]); 11898c2ecf20Sopenharmony_ci ENCODE_STR(princ->realm); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci princ = &token->k5->server; 11928c2ecf20Sopenharmony_ci ENCODE(princ->n_name_parts); 11938c2ecf20Sopenharmony_ci for (loop = 0; loop < princ->n_name_parts; loop++) 11948c2ecf20Sopenharmony_ci ENCODE_STR(princ->name_parts[loop]); 11958c2ecf20Sopenharmony_ci ENCODE_STR(princ->realm); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci ENCODE(token->k5->session.tag); 11988c2ecf20Sopenharmony_ci ENCODE_DATA(token->k5->session.data_len, 11998c2ecf20Sopenharmony_ci token->k5->session.data); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci ENCODE64(token->k5->authtime); 12028c2ecf20Sopenharmony_ci ENCODE64(token->k5->starttime); 12038c2ecf20Sopenharmony_ci ENCODE64(token->k5->endtime); 12048c2ecf20Sopenharmony_ci ENCODE64(token->k5->renew_till); 12058c2ecf20Sopenharmony_ci ENCODE(token->k5->is_skey); 12068c2ecf20Sopenharmony_ci ENCODE(token->k5->flags); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci ENCODE(token->k5->n_addresses); 12098c2ecf20Sopenharmony_ci for (loop = 0; loop < token->k5->n_addresses; loop++) { 12108c2ecf20Sopenharmony_ci ENCODE(token->k5->addresses[loop].tag); 12118c2ecf20Sopenharmony_ci ENCODE_DATA(token->k5->addresses[loop].data_len, 12128c2ecf20Sopenharmony_ci token->k5->addresses[loop].data); 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci ENCODE_DATA(token->k5->ticket_len, token->k5->ticket); 12168c2ecf20Sopenharmony_ci ENCODE_DATA(token->k5->ticket2_len, token->k5->ticket2); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci ENCODE(token->k5->n_authdata); 12198c2ecf20Sopenharmony_ci for (loop = 0; loop < token->k5->n_authdata; loop++) { 12208c2ecf20Sopenharmony_ci ENCODE(token->k5->authdata[loop].tag); 12218c2ecf20Sopenharmony_ci ENCODE_DATA(token->k5->authdata[loop].data_len, 12228c2ecf20Sopenharmony_ci token->k5->authdata[loop].data); 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci break; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci default: 12278c2ecf20Sopenharmony_ci pr_err("Unsupported key token type (%u)\n", 12288c2ecf20Sopenharmony_ci token->security_index); 12298c2ecf20Sopenharmony_ci return -ENOPKG; 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci ASSERTCMP((unsigned long)xdr - (unsigned long)oldxdr, ==, 12338c2ecf20Sopenharmony_ci toksize); 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci#undef ENCODE_STR 12378c2ecf20Sopenharmony_ci#undef ENCODE_DATA 12388c2ecf20Sopenharmony_ci#undef ENCODE64 12398c2ecf20Sopenharmony_ci#undef ENCODE 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci ASSERTCMP(tok, ==, ntoks); 12428c2ecf20Sopenharmony_ci ASSERTCMP((char __user *) xdr - buffer, ==, size); 12438c2ecf20Sopenharmony_ci _leave(" = %zu", size); 12448c2ecf20Sopenharmony_ci return size; 12458c2ecf20Sopenharmony_ci} 1246