| 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| 2 | /* Common bits for GSSAPI-based RxRPC security. |
| 3 | * |
| 4 | * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. |
| 5 | * Written by David Howells (dhowells@redhat.com) |
| 6 | */ |
| 7 | |
| 8 | #include <crypto/krb5.h> |
| 9 | #include <crypto/skcipher.h> |
| 10 | #include <crypto/hash.h> |
| 11 | |
| 12 | /* |
| 13 | * Per-key number context. This is replaced when the connection is rekeyed. |
| 14 | */ |
| 15 | struct rxgk_context { |
| 16 | refcount_t usage; |
| 17 | unsigned int key_number; /* Rekeying number (goes in the rx header) */ |
| 18 | unsigned long flags; |
| 19 | #define RXGK_TK_NEEDS_REKEY 0 /* Set if this needs rekeying */ |
| 20 | unsigned long expiry; /* Expiration time of this key */ |
| 21 | long long bytes_remaining; /* Remaining Tx lifetime of this key */ |
| 22 | const struct krb5_enctype *krb5; /* RxGK encryption type */ |
| 23 | const struct rxgk_key *key; |
| 24 | |
| 25 | /* We need up to 7 keys derived from the transport key, but we don't |
| 26 | * actually need the transport key. Each key is derived by |
| 27 | * DK(TK,constant). |
| 28 | */ |
| 29 | struct crypto_aead *tx_enc; /* Transmission key */ |
| 30 | struct crypto_aead *rx_enc; /* Reception key */ |
| 31 | struct crypto_shash *tx_Kc; /* Transmission checksum key */ |
| 32 | struct crypto_shash *rx_Kc; /* Reception checksum key */ |
| 33 | struct crypto_aead *resp_enc; /* Response packet enc key */ |
| 34 | }; |
| 35 | |
| 36 | #define xdr_round_up(x) (round_up((x), sizeof(__be32))) |
| 37 | #define xdr_object_len(x) (4 + xdr_round_up(x)) |
| 38 | |
| 39 | /* |
| 40 | * rxgk_app.c |
| 41 | */ |
| 42 | int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb, |
| 43 | unsigned int ticket_offset, unsigned int ticket_len, |
| 44 | struct key **_key); |
| 45 | int (struct rxrpc_connection *conn, struct sk_buff *skb, |
| 46 | unsigned int token_offset, unsigned int token_len, |
| 47 | struct key **_key); |
| 48 | |
| 49 | /* |
| 50 | * rxgk_kdf.c |
| 51 | */ |
| 52 | void rxgk_put(struct rxgk_context *gk); |
| 53 | struct rxgk_context *rxgk_generate_transport_key(struct rxrpc_connection *conn, |
| 54 | const struct rxgk_key *key, |
| 55 | unsigned int key_number, |
| 56 | gfp_t gfp); |
| 57 | int rxgk_set_up_token_cipher(const struct krb5_buffer *server_key, |
| 58 | struct crypto_aead **token_key, |
| 59 | unsigned int enctype, |
| 60 | const struct krb5_enctype **_krb5, |
| 61 | gfp_t gfp); |
| 62 | |
| 63 | /* |
| 64 | * Apply decryption and checksumming functions to part of an skbuff. The |
| 65 | * offset and length are updated to reflect the actual content of the encrypted |
| 66 | * region. |
| 67 | */ |
| 68 | static inline |
| 69 | int rxgk_decrypt_skb(const struct krb5_enctype *krb5, |
| 70 | struct crypto_aead *aead, |
| 71 | struct sk_buff *skb, |
| 72 | unsigned int *_offset, unsigned int *_len, |
| 73 | int *_error_code) |
| 74 | { |
| 75 | struct scatterlist sg[16]; |
| 76 | size_t offset = 0, len = *_len; |
| 77 | int nr_sg, ret; |
| 78 | |
| 79 | sg_init_table(sg, ARRAY_SIZE(sg)); |
| 80 | nr_sg = skb_to_sgvec(skb, sg, offset: *_offset, len); |
| 81 | if (unlikely(nr_sg < 0)) |
| 82 | return nr_sg; |
| 83 | |
| 84 | ret = crypto_krb5_decrypt(krb5, aead, sg, nr_sg, |
| 85 | offset: &offset, len: &len); |
| 86 | switch (ret) { |
| 87 | case 0: |
| 88 | *_offset += offset; |
| 89 | *_len = len; |
| 90 | break; |
| 91 | case -EBADMSG: /* Checksum mismatch. */ |
| 92 | case -EPROTO: |
| 93 | *_error_code = RXGK_SEALEDINCON; |
| 94 | break; |
| 95 | case -EMSGSIZE: |
| 96 | *_error_code = RXGK_PACKETSHORT; |
| 97 | break; |
| 98 | case -ENOPKG: /* Would prefer RXGK_BADETYPE, but not available for YFS. */ |
| 99 | default: |
| 100 | *_error_code = RXGK_INCONSISTENCY; |
| 101 | break; |
| 102 | } |
| 103 | |
| 104 | return ret; |
| 105 | } |
| 106 | |
| 107 | /* |
| 108 | * Check the MIC on a region of an skbuff. The offset and length are updated |
| 109 | * to reflect the actual content of the secure region. |
| 110 | */ |
| 111 | static inline |
| 112 | int rxgk_verify_mic_skb(const struct krb5_enctype *krb5, |
| 113 | struct crypto_shash *shash, |
| 114 | const struct krb5_buffer *metadata, |
| 115 | struct sk_buff *skb, |
| 116 | unsigned int *_offset, unsigned int *_len, |
| 117 | u32 *_error_code) |
| 118 | { |
| 119 | struct scatterlist sg[16]; |
| 120 | size_t offset = 0, len = *_len; |
| 121 | int nr_sg, ret; |
| 122 | |
| 123 | sg_init_table(sg, ARRAY_SIZE(sg)); |
| 124 | nr_sg = skb_to_sgvec(skb, sg, offset: *_offset, len); |
| 125 | if (unlikely(nr_sg < 0)) |
| 126 | return nr_sg; |
| 127 | |
| 128 | ret = crypto_krb5_verify_mic(krb5, shash, metadata, sg, nr_sg, |
| 129 | offset: &offset, len: &len); |
| 130 | switch (ret) { |
| 131 | case 0: |
| 132 | *_offset += offset; |
| 133 | *_len = len; |
| 134 | break; |
| 135 | case -EBADMSG: /* Checksum mismatch */ |
| 136 | case -EPROTO: |
| 137 | *_error_code = RXGK_SEALEDINCON; |
| 138 | break; |
| 139 | case -EMSGSIZE: |
| 140 | *_error_code = RXGK_PACKETSHORT; |
| 141 | break; |
| 142 | case -ENOPKG: /* Would prefer RXGK_BADETYPE, but not available for YFS. */ |
| 143 | default: |
| 144 | *_error_code = RXGK_INCONSISTENCY; |
| 145 | break; |
| 146 | } |
| 147 | |
| 148 | return ret; |
| 149 | } |
| 150 | |