xref: /freebsd/crypto/krb5/src/lib/gssapi/krb5/unwrap.c (revision 621e0e7f27303452c2f5b5fcf93aaf72e2b036a6)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/gssapi/krb5/unwrap.c - krb5 gss_unwrap() implementation */
3 /*
4  * Copyright (C) 2024 by the Massachusetts Institute of Technology.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  *
14  * * Redistributions in binary form must reproduce the above copyright
15  *   notice, this list of conditions and the following disclaimer in
16  *   the documentation and/or other materials provided with the
17  *   distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30  * OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "gssapiP_krb5.h"
34 
35 /* The RFC 1964 token format is only used with DES3 and RC4, both of which use
36  * an 8-byte confounder. */
37 #define V1_CONFOUNDER_LEN 8
38 
39 #define V3_HEADER_LEN 16
40 
41 /* Perform raw decryption (unauthenticated, no change in length) from in into
42  * *out.  For RC4, use seqnum to derive the encryption key. */
43 static krb5_error_code
decrypt_v1(krb5_context context,uint16_t sealalg,krb5_key key,uint32_t seqnum,const uint8_t * in,size_t len,uint8_t ** out)44 decrypt_v1(krb5_context context, uint16_t sealalg, krb5_key key,
45            uint32_t seqnum, const uint8_t *in, size_t len, uint8_t **out)
46 {
47     krb5_error_code ret;
48     uint8_t bigend_seqnum[4], *plain = NULL;
49     krb5_keyblock *enc_key = NULL;
50     unsigned int i;
51 
52     *out = NULL;
53 
54     plain = malloc(len);
55     if (plain == NULL)
56         return ENOMEM;
57 
58     if (sealalg != SEAL_ALG_MICROSOFT_RC4) {
59         ret = kg_decrypt(context, key, KG_USAGE_SEAL, NULL, in, plain, len);
60         if (ret)
61             goto cleanup;
62     } else {
63         store_32_be(seqnum, bigend_seqnum);
64         ret = krb5_k_key_keyblock(context, key, &enc_key);
65         if (ret)
66             goto cleanup;
67         for (i = 0; i < enc_key->length; i++)
68             enc_key->contents[i] ^= 0xF0;
69         ret = kg_arcfour_docrypt(enc_key, 0, bigend_seqnum, 4, in, len, plain);
70         if (ret)
71             goto cleanup;
72     }
73 
74     *out = plain;
75     plain = NULL;
76 
77 cleanup:
78     free(plain);
79     krb5_free_keyblock(context, enc_key);
80     return ret;
81 }
82 
83 static OM_uint32
unwrap_v1(krb5_context context,OM_uint32 * minor_status,krb5_gss_ctx_id_rec * ctx,struct k5input * in,gss_buffer_t output_message,int * conf_state)84 unwrap_v1(krb5_context context, OM_uint32 *minor_status,
85           krb5_gss_ctx_id_rec *ctx, struct k5input *in,
86           gss_buffer_t output_message, int *conf_state)
87 {
88     krb5_error_code ret = 0;
89     OM_uint32 major;
90     uint8_t *decrypted = NULL;
91     const uint8_t *plain, *header, *seqbytes, *cksum;
92     int direction, bad_pad = 0;
93     size_t plainlen, cksum_len;
94     uint32_t seqnum;
95     uint16_t toktype, signalg, sealalg, filler;
96     uint8_t padlen;
97 
98     if (ctx->seq == NULL) {
99         /* ctx was established using a newer enctype, and cannot process RFC
100          * 1964 tokens. */
101         major = GSS_S_DEFECTIVE_TOKEN;
102         goto cleanup;
103     }
104 
105     /* Parse the header fields and fetch the checksum. */
106     header = in->ptr;
107     toktype = k5_input_get_uint16_be(in);
108     signalg = k5_input_get_uint16_le(in);
109     sealalg = k5_input_get_uint16_le(in);
110     filler = k5_input_get_uint16_le(in);
111     seqbytes = k5_input_get_bytes(in, 8);
112     cksum_len = (signalg == SGN_ALG_HMAC_SHA1_DES3_KD) ? 20 : 8;
113     cksum = k5_input_get_bytes(in, cksum_len);
114 
115     /* Validate the header fields, and ensure that there are enough bytes
116      * remaining for a confounder and padding length byte. */
117     if (in->status || in->len < V1_CONFOUNDER_LEN + 1 ||
118         toktype != KG_TOK_WRAP_MSG || filler != 0xFFFF ||
119         signalg != ctx->signalg ||
120         (sealalg != SEAL_ALG_NONE && sealalg != ctx->sealalg)) {
121         major = GSS_S_DEFECTIVE_TOKEN;
122         goto cleanup;
123     }
124 
125     ret = kg_get_seq_num(context, ctx->seq, cksum, seqbytes, &direction,
126                          &seqnum);
127     if (ret) {
128         major = GSS_S_BAD_SIG;
129         goto cleanup;
130     }
131 
132     /* Decrypt the ciphertext, or just accept the remaining bytes as the
133      * plaintext (still with a confounder and padding length byte). */
134     plain = in->ptr;
135     plainlen = in->len;
136     if (sealalg != SEAL_ALG_NONE) {
137         ret = decrypt_v1(context, sealalg, ctx->enc, seqnum, in->ptr, in->len,
138                          &decrypted);
139         if (ret) {
140             major = GSS_S_FAILURE;
141             goto cleanup;
142         }
143         plain = decrypted;
144     }
145 
146     padlen = plain[plainlen - 1];
147     if (plainlen - V1_CONFOUNDER_LEN < padlen) {
148         /* Don't error out yet, to avoid padding oracle attacks.  We will
149          * treat this as a checksum failure later on. */
150         padlen = 0;
151         bad_pad = 1;
152     }
153 
154     if (!kg_verify_checksum_v1(context, signalg, ctx->seq, KG_USAGE_SIGN,
155                                header, plain, plainlen, cksum, cksum_len) ||
156         bad_pad) {
157         major = GSS_S_BAD_SIG;
158         goto cleanup;
159     }
160 
161     if ((ctx->initiate && direction != 0xff) ||
162         (!ctx->initiate && direction != 0)) {
163         *minor_status = (OM_uint32)G_BAD_DIRECTION;
164         major = GSS_S_BAD_SIG;
165         goto cleanup;
166     }
167 
168     output_message->length = plainlen - V1_CONFOUNDER_LEN - padlen;
169     if (output_message->length > 0) {
170         output_message->value = gssalloc_malloc(output_message->length);
171         if (output_message->value == NULL) {
172             ret = ENOMEM;
173             major = GSS_S_FAILURE;
174             goto cleanup;
175         }
176         memcpy(output_message->value, plain + V1_CONFOUNDER_LEN,
177                output_message->length);
178     }
179 
180     if (conf_state != NULL)
181         *conf_state = (sealalg != SEAL_ALG_NONE);
182 
183     major = g_seqstate_check(ctx->seqstate, seqnum);
184 
185 cleanup:
186     free(decrypted);
187     *minor_status = ret;
188     return major;
189 }
190 
191 /* Return true if plain ends with an RFC 4121 header with the provided fields,
192  * and that plain contains at least ec additional bytes of padding. */
193 static krb5_boolean
verify_enc_header(krb5_data * plain,uint8_t flags,size_t ec,uint64_t seqnum)194 verify_enc_header(krb5_data *plain, uint8_t flags, size_t ec, uint64_t seqnum)
195 {
196     uint8_t *h;
197 
198     if (plain->length < V3_HEADER_LEN + ec)
199         return FALSE;
200     h = (uint8_t *)plain->data + plain->length - V3_HEADER_LEN;
201     return load_16_be(h) == KG2_TOK_WRAP_MSG && h[2] == flags &&
202         h[3] == 0xFF && load_16_be(h + 4) == ec &&
203         load_64_be(h + 8) == seqnum;
204 }
205 
206 /* Decrypt ctext, verify the encrypted header, and return the appropriately
207  * truncated plaintext in out, allocated with gssalloc_malloc(). */
208 static OM_uint32
decrypt_v3(krb5_context context,OM_uint32 * minor_status,krb5_key key,krb5_keyusage usage,const uint8_t * ctext,size_t len,uint8_t flags,size_t ec,uint64_t seqnum,gss_buffer_t out)209 decrypt_v3(krb5_context context, OM_uint32 *minor_status,
210            krb5_key key, krb5_keyusage usage, const uint8_t *ctext, size_t len,
211            uint8_t flags, size_t ec, uint64_t seqnum, gss_buffer_t out)
212 {
213     OM_uint32 major;
214     krb5_error_code ret;
215     krb5_enc_data cipher;
216     krb5_data plain;
217     uint8_t *buf = NULL;
218 
219     buf = gssalloc_malloc(len);
220     if (buf == NULL) {
221         *minor_status = ENOMEM;
222         return GSS_S_FAILURE;
223     }
224 
225     cipher.enctype = key->keyblock.enctype;
226     cipher.ciphertext = make_data((uint8_t *)ctext, len);
227     plain = make_data(buf, len);
228     ret = krb5_k_decrypt(context, key, usage, NULL, &cipher, &plain);
229     if (ret) {
230         *minor_status = ret;
231         major = GSS_S_BAD_SIG;
232         goto cleanup;
233     }
234 
235     if (!verify_enc_header(&plain, flags, ec, seqnum)) {
236         major = GSS_S_DEFECTIVE_TOKEN;
237         goto cleanup;
238     }
239 
240     out->length = plain.length - ec - 16;
241     out->value = buf;
242     buf = NULL;
243     if (out->length == 0) {
244         gssalloc_free(out->value);
245         out->value = NULL;
246     }
247 
248     major = GSS_S_COMPLETE;
249 
250 cleanup:
251     gssalloc_free(buf);
252     return major;
253 }
254 
255 /* Place a rotated copy of data in *storage and return it, or return data if no
256  * rotation is required.  Return null on allocation failure. */
257 static const uint8_t *
rotate_left(const uint8_t * data,size_t len,size_t rc,uint8_t ** storage)258 rotate_left(const uint8_t *data, size_t len, size_t rc, uint8_t **storage)
259 {
260     if (len == 0 || rc % len == 0)
261         return data;
262     rc %= len;
263 
264     *storage = malloc(len);
265     if (*storage == NULL)
266         return NULL;
267     memcpy(*storage, data + rc, len - rc);
268     memcpy(*storage + len - rc, data, rc);
269     return *storage;
270 }
271 
272 static OM_uint32
unwrap_v3(krb5_context context,OM_uint32 * minor_status,krb5_gss_ctx_id_rec * ctx,struct k5input * in,gss_buffer_t output_message,int * conf_state)273 unwrap_v3(krb5_context context, OM_uint32 *minor_status,
274           krb5_gss_ctx_id_rec *ctx, struct k5input *in,
275           gss_buffer_t output_message, int *conf_state)
276 {
277     OM_uint32 major;
278     krb5_error_code ret = 0;
279     krb5_keyusage usage;
280     krb5_key key;
281     krb5_cksumtype cksumtype;
282     size_t ec, rrc, cksumsize, plen, data_len;
283     uint64_t seqnum;
284     uint16_t toktype;
285     uint8_t flags, filler, *rotated = NULL;
286     const uint8_t *payload;
287 
288     toktype = k5_input_get_uint16_be(in);
289     flags = k5_input_get_byte(in);
290     filler = k5_input_get_byte(in);
291     ec = k5_input_get_uint16_be(in);
292     rrc = k5_input_get_uint16_be(in);
293     seqnum = k5_input_get_uint64_be(in);
294 
295     if (in->status || toktype != KG2_TOK_WRAP_MSG || filler != 0xFF) {
296         major = GSS_S_DEFECTIVE_TOKEN;
297         goto cleanup;
298     }
299 
300     if (!!(flags & FLAG_SENDER_IS_ACCEPTOR) != ctx->initiate) {
301         major = GSS_S_BAD_SIG;
302         *minor_status = (OM_uint32)G_BAD_DIRECTION;
303         goto cleanup;
304     }
305 
306     usage = ctx->initiate ? KG_USAGE_ACCEPTOR_SEAL : KG_USAGE_INITIATOR_SEAL;
307     if (ctx->have_acceptor_subkey && (flags & FLAG_ACCEPTOR_SUBKEY)) {
308         key = ctx->acceptor_subkey;
309         cksumtype = ctx->acceptor_subkey_cksumtype;
310     } else {
311         key = ctx->subkey;
312         cksumtype = ctx->cksumtype;
313     }
314     assert(key != NULL);
315 
316     payload = rotate_left(in->ptr, in->len, rrc, &rotated);
317     plen = in->len;
318     if (payload == NULL) {
319         major = GSS_S_FAILURE;
320         *minor_status = ENOMEM;
321         goto cleanup;
322     }
323     if (flags & FLAG_WRAP_CONFIDENTIAL) {
324         major = decrypt_v3(context, minor_status, key, usage, payload, plen,
325                            flags, ec, seqnum, output_message);
326         if (major != GSS_S_COMPLETE)
327             goto cleanup;
328     } else {
329         /* Divide the payload into data and checksum. */
330         ret = krb5_c_checksum_length(context, cksumtype, &cksumsize);
331         if (ret) {
332             major = GSS_S_FAILURE;
333             *minor_status = ret;
334             goto cleanup;
335         }
336         if (cksumsize > plen || ec != cksumsize) {
337             major = GSS_S_DEFECTIVE_TOKEN;
338             goto cleanup;
339         }
340         data_len = plen - cksumsize;
341 
342         if (!kg_verify_checksum_v3(context, key, usage, cksumtype,
343                                    KG2_TOK_WRAP_MSG, flags, seqnum,
344                                    payload, data_len,
345                                    payload + data_len, cksumsize)) {
346             major = GSS_S_BAD_SIG;
347             goto cleanup;
348         }
349 
350         output_message->length = data_len;
351         if (data_len > 0) {
352             output_message->value = gssalloc_malloc(data_len);
353             if (output_message->value == NULL) {
354                 major = GSS_S_FAILURE;
355                 *minor_status = ENOMEM;
356                 goto cleanup;
357             }
358             memcpy(output_message->value, payload, data_len);
359         }
360         output_message->length = data_len;
361     }
362 
363     if (conf_state != NULL)
364         *conf_state = !!(flags & FLAG_WRAP_CONFIDENTIAL);
365 
366     major = g_seqstate_check(ctx->seqstate, seqnum);
367 
368 cleanup:
369     free(rotated);
370     return major;
371 }
372 
373 OM_uint32 KRB5_CALLCONV
krb5_gss_unwrap(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_buffer_t input_message,gss_buffer_t output_message,int * conf_state,gss_qop_t * qop_state)374 krb5_gss_unwrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
375                 gss_buffer_t input_message, gss_buffer_t output_message,
376                 int *conf_state, gss_qop_t *qop_state)
377 {
378     krb5_gss_ctx_id_rec *ctx = (krb5_gss_ctx_id_rec *)context_handle;
379     uint16_t toktype;
380     OM_uint32 major;
381     struct k5input in, unwrapped;
382 
383     *minor_status = 0;
384     output_message->value = NULL;
385     output_message->length = 0;
386     if (qop_state != NULL)
387         *qop_state = GSS_C_QOP_DEFAULT;
388 
389     if (ctx->terminated || !ctx->established) {
390         *minor_status = KG_CTX_INCOMPLETE;
391         return GSS_S_NO_CONTEXT;
392     }
393 
394     k5_input_init(&in, input_message->value, input_message->length);
395     (void)g_verify_token_header(&in, ctx->mech_used);
396     unwrapped = in;
397 
398     toktype = k5_input_get_uint16_be(&in);
399     if (toktype == KG_TOK_WRAP_MSG) {
400         major = unwrap_v1(ctx->k5_context, minor_status, ctx, &unwrapped,
401                           output_message, conf_state);
402     } else if (toktype == KG2_TOK_WRAP_MSG) {
403         major = unwrap_v3(ctx->k5_context, minor_status, ctx, &unwrapped,
404                           output_message, conf_state);
405     } else {
406         *minor_status = (OM_uint32)G_BAD_TOK_HEADER;
407         major = GSS_S_DEFECTIVE_TOKEN;
408     }
409 
410     if (major)
411         save_error_info(*minor_status, ctx->k5_context);
412 
413     return major;
414 }
415