| /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
| /* Common bits for GSSAPI-based RxRPC security. | |
| * | |
| * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. | |
| * Written by David Howells (dhowells@redhat.com) | |
| */ | |
| #include <crypto/krb5.h> | |
| #include <crypto/skcipher.h> | |
| #include <crypto/hash.h> | |
| /* | |
| * Per-key number context. This is replaced when the connection is rekeyed. | |
| */ | |
| struct rxgk_context { | |
| refcount_t usage; | |
| unsigned int key_number; /* Rekeying number (goes in the rx header) */ | |
| unsigned long flags; | |
| #define RXGK_TK_NEEDS_REKEY 0 /* Set if this needs rekeying */ | |
| unsigned long expiry; /* Expiration time of this key */ | |
| long long bytes_remaining; /* Remaining Tx lifetime of this key */ | |
| const struct krb5_enctype *krb5; /* RxGK encryption type */ | |
| const struct rxgk_key *key; | |
| /* We need up to 7 keys derived from the transport key, but we don't | |
| * actually need the transport key. Each key is derived by | |
| * DK(TK,constant). | |
| */ | |
| struct crypto_aead *tx_enc; /* Transmission key */ | |
| struct crypto_aead *rx_enc; /* Reception key */ | |
| struct crypto_shash *tx_Kc; /* Transmission checksum key */ | |
| struct crypto_shash *rx_Kc; /* Reception checksum key */ | |
| struct crypto_aead *resp_enc; /* Response packet enc key */ | |
| }; | |
| #define xdr_round_up(x) (round_up((x), sizeof(__be32))) | |
| #define xdr_round_down(x) (round_down((x), sizeof(__be32))) | |
| #define xdr_object_len(x) (4 + xdr_round_up(x)) | |
| /* | |
| * rxgk_app.c | |
| */ | |
| int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb, | |
| void *ticket, unsigned int ticket_len, | |
| struct key **_key); | |
| int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb, | |
| void *token, unsigned int token_len, | |
| struct key **_key); | |
| /* | |
| * rxgk_kdf.c | |
| */ | |
| void rxgk_put(struct rxgk_context *gk); | |
| struct rxgk_context *rxgk_generate_transport_key(struct rxrpc_connection *conn, | |
| const struct rxgk_key *key, | |
| unsigned int key_number, | |
| gfp_t gfp); | |
| int rxgk_set_up_token_cipher(const struct krb5_buffer *server_key, | |
| struct crypto_aead **token_key, | |
| unsigned int enctype, | |
| const struct krb5_enctype **_krb5, | |
| gfp_t gfp); | |
| /* | |
| * Apply decryption and checksumming functions a flat data buffer. The data | |
| * point and length are updated to reflect the actual content of the encrypted | |
| * region. | |
| */ | |
| static inline int rxgk_decrypt(const struct krb5_enctype *krb5, | |
| struct crypto_aead *aead, | |
| void **_data, unsigned int *_len, | |
| int *_error_code) | |
| { | |
| struct scatterlist sg[1]; | |
| size_t offset = 0, len = *_len; | |
| int ret; | |
| sg_init_one(sg, *_data, len); | |
| ret = crypto_krb5_decrypt(krb5, aead, sg, 1, &offset, &len); | |
| switch (ret) { | |
| case 0: | |
| if (offset & 3) { | |
| *_error_code = RXGK_INCONSISTENCY; | |
| ret = -EPROTO; | |
| break; | |
| } | |
| *_data += offset; | |
| *_len = len; | |
| break; | |
| case -EBADMSG: /* Checksum mismatch. */ | |
| case -EPROTO: | |
| *_error_code = RXGK_SEALEDINCON; | |
| break; | |
| case -EMSGSIZE: | |
| *_error_code = RXGK_PACKETSHORT; | |
| break; | |
| case -ENOPKG: /* Would prefer RXGK_BADETYPE, but not available for YFS. */ | |
| default: | |
| *_error_code = RXGK_INCONSISTENCY; | |
| break; | |
| } | |
| return ret; | |
| } | |
| /* | |
| * Check the MIC on a flat buffer. The data pointer and length are updated to | |
| * reflect the actual content of the secure region. | |
| */ | |
| static inline | |
| int rxgk_verify_mic(const struct krb5_enctype *krb5, | |
| struct crypto_shash *shash, | |
| const struct krb5_buffer *metadata, | |
| void **_data, unsigned int *_len, | |
| u32 *_error_code) | |
| { | |
| struct scatterlist sg[1]; | |
| size_t offset = 0, len = *_len; | |
| int ret; | |
| sg_init_one(sg, *_data, len); | |
| ret = crypto_krb5_verify_mic(krb5, shash, metadata, sg, 1, &offset, &len); | |
| switch (ret) { | |
| case 0: | |
| *_data += offset; | |
| *_len = len; | |
| break; | |
| case -EBADMSG: /* Checksum mismatch */ | |
| case -EPROTO: | |
| *_error_code = RXGK_SEALEDINCON; | |
| break; | |
| case -EMSGSIZE: | |
| *_error_code = RXGK_PACKETSHORT; | |
| break; | |
| case -ENOPKG: /* Would prefer RXGK_BADETYPE, but not available for YFS. */ | |
| default: | |
| *_error_code = RXGK_INCONSISTENCY; | |
| break; | |
| } | |
| return ret; | |
| } |