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