1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/gssapi/krb5/k5sealiov.c */
3 /*
4 * Copyright 2008, 2009 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26
27 #include "k5-int.h"
28 #include "gssapiP_krb5.h"
29
30 static krb5_error_code
make_seal_token_v1_iov(krb5_context context,krb5_gss_ctx_id_rec * ctx,int conf_req_flag,int * conf_state,gss_iov_buffer_desc * iov,int iov_count,int toktype)31 make_seal_token_v1_iov(krb5_context context,
32 krb5_gss_ctx_id_rec *ctx,
33 int conf_req_flag,
34 int *conf_state,
35 gss_iov_buffer_desc *iov,
36 int iov_count,
37 int toktype)
38 {
39 krb5_error_code code = 0;
40 gss_iov_buffer_t header;
41 gss_iov_buffer_t padding;
42 gss_iov_buffer_t trailer;
43 krb5_checksum md5cksum;
44 krb5_checksum cksum;
45 size_t k5_headerlen = 0, k5_trailerlen = 0;
46 size_t data_length = 0, assoc_data_length = 0;
47 size_t tmsglen = 0, cnflen = 0, tlen;
48 uint8_t *metadata, *checksum, *confounder;
49 krb5_keyusage sign_usage = KG_USAGE_SIGN;
50 struct k5buf buf;
51
52 md5cksum.length = cksum.length = 0;
53 md5cksum.contents = cksum.contents = NULL;
54
55 header = kg_locate_header_iov(iov, iov_count, toktype);
56 if (header == NULL)
57 return EINVAL;
58
59 padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
60 if (padding == NULL && toktype == KG_TOK_WRAP_MSG &&
61 (ctx->gss_flags & GSS_C_DCE_STYLE) == 0)
62 return EINVAL;
63
64 trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
65 if (trailer != NULL)
66 trailer->buffer.length = 0;
67
68 /* Determine confounder length */
69 if (toktype == KG_TOK_WRAP_MSG) {
70 size_t k5_padlen = (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) ? 1 : 8;
71 size_t gss_padlen;
72 size_t conf_data_length;
73
74 cnflen = kg_confounder_size(context, ctx->enc->keyblock.enctype);
75
76 kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);
77 conf_data_length = cnflen + data_length - assoc_data_length;
78
79 if (k5_padlen == 1)
80 gss_padlen = 1; /* one byte to indicate one byte of padding */
81 else
82 gss_padlen = k5_padlen - (conf_data_length % k5_padlen);
83
84 if (ctx->gss_flags & GSS_C_DCE_STYLE) {
85 /* DCE will pad the actual data itself; padding buffer optional and will be zeroed */
86 gss_padlen = 0;
87
88 if (conf_data_length % k5_padlen)
89 code = KRB5_BAD_MSIZE;
90 } else if (padding->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
91 code = kg_allocate_iov(padding, gss_padlen);
92 } else if (padding->buffer.length < gss_padlen) {
93 code = KRB5_BAD_MSIZE;
94 }
95 if (code != 0)
96 goto cleanup;
97
98 /* Initialize padding buffer to pad itself */
99 if (padding != NULL) {
100 padding->buffer.length = gss_padlen;
101 memset(padding->buffer.value, (int)gss_padlen, gss_padlen);
102 }
103
104 if (ctx->gss_flags & GSS_C_DCE_STYLE)
105 tmsglen = cnflen; /* confounder length */
106 else
107 tmsglen = conf_data_length + padding->buffer.length;
108 }
109
110 /* Determine token size */
111 tlen = g_token_size(ctx->mech_used, 14 + ctx->cksum_size + tmsglen);
112
113 k5_headerlen = cnflen + tlen - tmsglen;
114
115 if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
116 code = kg_allocate_iov(header, k5_headerlen);
117 else if (header->buffer.length < k5_headerlen)
118 code = KRB5_BAD_MSIZE;
119 if (code != 0)
120 goto cleanup;
121
122 header->buffer.length = k5_headerlen;
123
124 k5_buf_init_fixed(&buf, header->buffer.value, k5_headerlen);
125 g_make_token_header(&buf, ctx->mech_used, 14 + ctx->cksum_size + tmsglen,
126 toktype);
127 metadata = k5_buf_get_space(&buf, 14);
128 checksum = k5_buf_get_space(&buf, ctx->cksum_size);
129 assert(metadata != NULL && checksum != NULL);
130
131 /* 0..1 SIGN_ALG */
132 store_16_le(ctx->signalg, &metadata[0]);
133
134 /* 2..3 SEAL_ALG or Filler */
135 if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
136 store_16_le(ctx->sealalg, &metadata[2]);
137 } else {
138 /* No seal */
139 metadata[2] = 0xFF;
140 metadata[3] = 0xFF;
141 }
142
143 /* 4..5 Filler */
144 metadata[4] = 0xFF;
145 metadata[5] = 0xFF;
146
147 /* pad the plaintext, encrypt if needed, and stick it in the token */
148
149 /* initialize the checksum */
150 switch (ctx->signalg) {
151 case SGN_ALG_HMAC_SHA1_DES3_KD:
152 md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3;
153 break;
154 case SGN_ALG_HMAC_MD5:
155 md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR;
156 if (toktype != KG_TOK_WRAP_MSG)
157 sign_usage = 15;
158 break;
159 default:
160 abort ();
161 }
162
163 code = krb5_c_checksum_length(context, md5cksum.checksum_type, &k5_trailerlen);
164 if (code != 0)
165 goto cleanup;
166 md5cksum.length = k5_trailerlen;
167
168 if (k5_headerlen != 0 && toktype == KG_TOK_WRAP_MSG) {
169 confounder = k5_buf_get_space(&buf, cnflen);
170 assert(confounder != NULL);
171 code = kg_make_confounder(context, ctx->enc->keyblock.enctype,
172 confounder);
173 if (code != 0)
174 goto cleanup;
175 }
176
177 /* compute the checksum */
178 code = kg_make_checksum_iov_v1(context, md5cksum.checksum_type,
179 ctx->cksum_size, ctx->seq, ctx->enc,
180 sign_usage, iov, iov_count, toktype,
181 &md5cksum);
182 if (code != 0)
183 goto cleanup;
184
185 switch (ctx->signalg) {
186 case SGN_ALG_HMAC_SHA1_DES3_KD:
187 assert(md5cksum.length == ctx->cksum_size);
188 memcpy(checksum, md5cksum.contents, md5cksum.length);
189 break;
190 case SGN_ALG_HMAC_MD5:
191 memcpy(checksum, md5cksum.contents, ctx->cksum_size);
192 break;
193 }
194
195 /* create the seq_num */
196 code = kg_make_seq_num(context, ctx->seq, ctx->initiate ? 0 : 0xFF,
197 (OM_uint32)ctx->seq_send, checksum, metadata + 6);
198 if (code != 0)
199 goto cleanup;
200
201 if (conf_req_flag) {
202 if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) {
203 unsigned char bigend_seqnum[4];
204 krb5_keyblock *enc_key;
205 size_t i;
206
207 store_32_be(ctx->seq_send, bigend_seqnum);
208
209 code = krb5_k_key_keyblock(context, ctx->enc, &enc_key);
210 if (code != 0)
211 goto cleanup;
212
213 assert(enc_key->length == 16);
214
215 for (i = 0; i < enc_key->length; i++)
216 ((char *)enc_key->contents)[i] ^= 0xF0;
217
218 code = kg_arcfour_docrypt_iov(context, enc_key, 0,
219 bigend_seqnum, 4,
220 iov, iov_count);
221 krb5_free_keyblock(context, enc_key);
222 } else {
223 code = kg_encrypt_iov(context, ctx->proto,
224 ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0),
225 0 /*EC*/, 0 /*RRC*/,
226 ctx->enc, KG_USAGE_SEAL, NULL,
227 iov, iov_count);
228 }
229 if (code != 0)
230 goto cleanup;
231 }
232
233 ctx->seq_send++;
234 ctx->seq_send &= 0xFFFFFFFFL;
235
236 code = 0;
237
238 if (conf_state != NULL)
239 *conf_state = conf_req_flag;
240
241 cleanup:
242 if (code != 0)
243 kg_release_iov(iov, iov_count);
244 krb5_free_checksum_contents(context, &md5cksum);
245
246 return code;
247 }
248
249 OM_uint32
kg_seal_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,int * conf_state,gss_iov_buffer_desc * iov,int iov_count,int toktype)250 kg_seal_iov(OM_uint32 *minor_status,
251 gss_ctx_id_t context_handle,
252 int conf_req_flag,
253 gss_qop_t qop_req,
254 int *conf_state,
255 gss_iov_buffer_desc *iov,
256 int iov_count,
257 int toktype)
258 {
259 krb5_gss_ctx_id_rec *ctx;
260 krb5_error_code code;
261 krb5_context context;
262
263 if (qop_req != 0) {
264 *minor_status = (OM_uint32)G_UNKNOWN_QOP;
265 return GSS_S_BAD_QOP;
266 }
267
268 ctx = (krb5_gss_ctx_id_rec *)context_handle;
269 if (ctx->terminated || !ctx->established) {
270 *minor_status = KG_CTX_INCOMPLETE;
271 return GSS_S_NO_CONTEXT;
272 }
273
274 if (conf_req_flag && kg_integ_only_iov(iov, iov_count)) {
275 /* may be more sensible to return an error here */
276 conf_req_flag = FALSE;
277 }
278
279 context = ctx->k5_context;
280 switch (ctx->proto) {
281 case 0:
282 code = make_seal_token_v1_iov(context, ctx, conf_req_flag,
283 conf_state, iov, iov_count, toktype);
284 break;
285 case 1:
286 code = gss_krb5int_make_seal_token_v3_iov(context, ctx, conf_req_flag,
287 conf_state, iov, iov_count, toktype);
288 break;
289 default:
290 code = G_UNKNOWN_QOP;
291 break;
292 }
293
294 if (code != 0) {
295 *minor_status = code;
296 save_error_info(*minor_status, context);
297 return GSS_S_FAILURE;
298 }
299
300 *minor_status = 0;
301
302 return GSS_S_COMPLETE;
303 }
304
305 #define INIT_IOV_DATA(_iov) do { (_iov)->buffer.value = NULL; \
306 (_iov)->buffer.length = 0; } \
307 while (0)
308
309 OM_uint32
kg_seal_iov_length(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,int * conf_state,gss_iov_buffer_desc * iov,int iov_count,int toktype)310 kg_seal_iov_length(OM_uint32 *minor_status,
311 gss_ctx_id_t context_handle,
312 int conf_req_flag,
313 gss_qop_t qop_req,
314 int *conf_state,
315 gss_iov_buffer_desc *iov,
316 int iov_count,
317 int toktype)
318 {
319 krb5_gss_ctx_id_rec *ctx;
320 gss_iov_buffer_t header, trailer, padding;
321 size_t data_length, assoc_data_length;
322 size_t gss_headerlen, gss_padlen, gss_trailerlen;
323 unsigned int k5_headerlen = 0, k5_trailerlen = 0, k5_padlen = 0;
324 krb5_error_code code;
325 krb5_context context;
326 int dce_or_mic;
327
328 if (qop_req != GSS_C_QOP_DEFAULT) {
329 *minor_status = (OM_uint32)G_UNKNOWN_QOP;
330 return GSS_S_BAD_QOP;
331 }
332
333 ctx = (krb5_gss_ctx_id_rec *)context_handle;
334 if (!ctx->established) {
335 *minor_status = KG_CTX_INCOMPLETE;
336 return GSS_S_NO_CONTEXT;
337 }
338
339 header = kg_locate_header_iov(iov, iov_count, toktype);
340 if (header == NULL) {
341 *minor_status = EINVAL;
342 return GSS_S_FAILURE;
343 }
344 INIT_IOV_DATA(header);
345
346 trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
347 if (trailer != NULL) {
348 INIT_IOV_DATA(trailer);
349 }
350
351 /* MIC tokens and DCE-style wrap tokens have similar length considerations:
352 * no padding, and the framing surrounds the header only, not the data. */
353 dce_or_mic = ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0 ||
354 toktype == KG_TOK_MIC_MSG);
355
356 /* For CFX, EC is used instead of padding, and is placed in header or trailer */
357 padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
358 if (padding == NULL) {
359 if (conf_req_flag && ctx->proto == 0 && !dce_or_mic) {
360 *minor_status = EINVAL;
361 return GSS_S_FAILURE;
362 }
363 } else {
364 INIT_IOV_DATA(padding);
365 }
366
367 kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);
368
369 if (conf_req_flag && kg_integ_only_iov(iov, iov_count))
370 conf_req_flag = FALSE;
371
372 context = ctx->k5_context;
373
374 gss_headerlen = gss_padlen = gss_trailerlen = 0;
375
376 if (ctx->proto == 1) {
377 krb5_key key;
378 krb5_enctype enctype;
379 size_t ec;
380
381 key = (ctx->have_acceptor_subkey) ? ctx->acceptor_subkey : ctx->subkey;
382 enctype = key->keyblock.enctype;
383
384 code = krb5_c_crypto_length(context, enctype,
385 conf_req_flag ?
386 KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM,
387 &k5_trailerlen);
388 if (code != 0) {
389 *minor_status = code;
390 return GSS_S_FAILURE;
391 }
392
393 if (conf_req_flag) {
394 code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen);
395 if (code != 0) {
396 *minor_status = code;
397 return GSS_S_FAILURE;
398 }
399 }
400
401 gss_headerlen = 16; /* Header */
402 if (conf_req_flag) {
403 gss_headerlen += k5_headerlen; /* Kerb-Header */
404 gss_trailerlen = 16 /* E(Header) */ + k5_trailerlen; /* Kerb-Trailer */
405
406 code = krb5_c_padding_length(context, enctype,
407 data_length - assoc_data_length + 16 /* E(Header) */, &k5_padlen);
408 if (code != 0) {
409 *minor_status = code;
410 return GSS_S_FAILURE;
411 }
412
413 if (k5_padlen == 0 && dce_or_mic) {
414 /* Windows rejects AEAD tokens with non-zero EC */
415 code = krb5_c_block_size(context, enctype, &ec);
416 if (code != 0) {
417 *minor_status = code;
418 return GSS_S_FAILURE;
419 }
420 } else
421 ec = k5_padlen;
422
423 gss_trailerlen += ec;
424 } else {
425 gss_trailerlen = k5_trailerlen; /* Kerb-Checksum */
426 }
427 } else if (!dce_or_mic) {
428 k5_padlen = (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) ? 1 : 8;
429
430 if (k5_padlen == 1)
431 gss_padlen = 1;
432 else
433 gss_padlen = k5_padlen - ((data_length - assoc_data_length) % k5_padlen);
434 }
435
436 data_length += gss_padlen;
437
438 if (ctx->proto == 0) {
439 /* Header | Checksum | Confounder | Data | Pad */
440 size_t data_size;
441
442 k5_headerlen = kg_confounder_size(context, ctx->enc->keyblock.enctype);
443
444 data_size = 14 /* Header */ + ctx->cksum_size + k5_headerlen;
445
446 if (!dce_or_mic)
447 data_size += data_length;
448
449 gss_headerlen = g_token_size(ctx->mech_used, data_size);
450
451 /* g_token_size() will include data_size as well as the overhead, so
452 * subtract data_length just to get the overhead (ie. token size) */
453 if (!dce_or_mic)
454 gss_headerlen -= data_length;
455 }
456
457 if (minor_status != NULL)
458 *minor_status = 0;
459
460 if (trailer == NULL)
461 gss_headerlen += gss_trailerlen;
462 else
463 trailer->buffer.length = gss_trailerlen;
464
465 assert(gss_padlen == 0 || padding != NULL);
466
467 if (padding != NULL)
468 padding->buffer.length = gss_padlen;
469
470 header->buffer.length = gss_headerlen;
471
472 if (conf_state != NULL)
473 *conf_state = conf_req_flag;
474
475 return GSS_S_COMPLETE;
476 }
477
478 OM_uint32 KRB5_CALLCONV
krb5_gss_wrap_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,int * conf_state,gss_iov_buffer_desc * iov,int iov_count)479 krb5_gss_wrap_iov(OM_uint32 *minor_status,
480 gss_ctx_id_t context_handle,
481 int conf_req_flag,
482 gss_qop_t qop_req,
483 int *conf_state,
484 gss_iov_buffer_desc *iov,
485 int iov_count)
486 {
487 OM_uint32 major_status;
488
489 major_status = kg_seal_iov(minor_status, context_handle, conf_req_flag,
490 qop_req, conf_state,
491 iov, iov_count, KG_TOK_WRAP_MSG);
492
493 return major_status;
494 }
495
496 OM_uint32 KRB5_CALLCONV
krb5_gss_wrap_iov_length(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,int * conf_state,gss_iov_buffer_desc * iov,int iov_count)497 krb5_gss_wrap_iov_length(OM_uint32 *minor_status,
498 gss_ctx_id_t context_handle,
499 int conf_req_flag,
500 gss_qop_t qop_req,
501 int *conf_state,
502 gss_iov_buffer_desc *iov,
503 int iov_count)
504 {
505 OM_uint32 major_status;
506
507 major_status = kg_seal_iov_length(minor_status, context_handle,
508 conf_req_flag, qop_req, conf_state, iov,
509 iov_count, KG_TOK_WRAP_MSG);
510 return major_status;
511 }
512
513 OM_uint32 KRB5_CALLCONV
krb5_gss_get_mic_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t qop_req,gss_iov_buffer_desc * iov,int iov_count)514 krb5_gss_get_mic_iov(OM_uint32 *minor_status,
515 gss_ctx_id_t context_handle,
516 gss_qop_t qop_req,
517 gss_iov_buffer_desc *iov,
518 int iov_count)
519 {
520 OM_uint32 major_status;
521
522 major_status = kg_seal_iov(minor_status, context_handle, FALSE,
523 qop_req, NULL,
524 iov, iov_count, KG_TOK_MIC_MSG);
525
526 return major_status;
527 }
528
529 OM_uint32 KRB5_CALLCONV
krb5_gss_get_mic_iov_length(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t qop_req,gss_iov_buffer_desc * iov,int iov_count)530 krb5_gss_get_mic_iov_length(OM_uint32 *minor_status,
531 gss_ctx_id_t context_handle,
532 gss_qop_t qop_req,
533 gss_iov_buffer_desc *iov,
534 int iov_count)
535 {
536 OM_uint32 major_status;
537
538 major_status = kg_seal_iov_length(minor_status, context_handle, FALSE,
539 qop_req, NULL, iov, iov_count,
540 KG_TOK_MIC_MSG);
541 return major_status;
542 }
543