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