xref: /freebsd/crypto/heimdal/lib/gssapi/krb5/cfx.c (revision f4b37ed0f8b307b1f3f0f630ca725d68f1dff30d)
1 /*
2  * Copyright (c) 2003, PADL Software Pty Ltd.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of PADL Software nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include "gsskrb5_locl.h"
34 
35 /*
36  * Implementation of RFC 4121
37  */
38 
39 #define CFXSentByAcceptor	(1 << 0)
40 #define CFXSealed		(1 << 1)
41 #define CFXAcceptorSubkey	(1 << 2)
42 
43 krb5_error_code
44 _gsskrb5cfx_wrap_length_cfx(krb5_context context,
45 			    krb5_crypto crypto,
46 			    int conf_req_flag,
47 			    int dce_style,
48 			    size_t input_length,
49 			    size_t *output_length,
50 			    size_t *cksumsize,
51 			    uint16_t *padlength)
52 {
53     krb5_error_code ret;
54     krb5_cksumtype type;
55 
56     /* 16-byte header is always first */
57     *output_length = sizeof(gss_cfx_wrap_token_desc);
58     *padlength = 0;
59 
60     ret = krb5_crypto_get_checksum_type(context, crypto, &type);
61     if (ret)
62 	return ret;
63 
64     ret = krb5_checksumsize(context, type, cksumsize);
65     if (ret)
66 	return ret;
67 
68     if (conf_req_flag) {
69 	size_t padsize;
70 
71 	/* Header is concatenated with data before encryption */
72 	input_length += sizeof(gss_cfx_wrap_token_desc);
73 
74 	if (dce_style) {
75 		ret = krb5_crypto_getblocksize(context, crypto, &padsize);
76 	} else {
77 		ret = krb5_crypto_getpadsize(context, crypto, &padsize);
78 	}
79 	if (ret) {
80 	    return ret;
81 	}
82 	if (padsize > 1) {
83 	    /* XXX check this */
84 	    *padlength = padsize - (input_length % padsize);
85 
86 	    /* We add the pad ourselves (noted here for completeness only) */
87 	    input_length += *padlength;
88 	}
89 
90 	*output_length += krb5_get_wrapped_length(context,
91 						  crypto, input_length);
92     } else {
93 	/* Checksum is concatenated with data */
94 	*output_length += input_length + *cksumsize;
95     }
96 
97     assert(*output_length > input_length);
98 
99     return 0;
100 }
101 
102 OM_uint32
103 _gssapi_wrap_size_cfx(OM_uint32 *minor_status,
104 		      const gsskrb5_ctx ctx,
105 		      krb5_context context,
106 		      int conf_req_flag,
107 		      gss_qop_t qop_req,
108 		      OM_uint32 req_output_size,
109 		      OM_uint32 *max_input_size)
110 {
111     krb5_error_code ret;
112 
113     *max_input_size = 0;
114 
115     /* 16-byte header is always first */
116     if (req_output_size < 16)
117 	return 0;
118     req_output_size -= 16;
119 
120     if (conf_req_flag) {
121 	size_t wrapped_size, sz;
122 
123 	wrapped_size = req_output_size + 1;
124 	do {
125 	    wrapped_size--;
126 	    sz = krb5_get_wrapped_length(context,
127 					 ctx->crypto, wrapped_size);
128 	} while (wrapped_size && sz > req_output_size);
129 	if (wrapped_size == 0)
130 	    return 0;
131 
132 	/* inner header */
133 	if (wrapped_size < 16)
134 	    return 0;
135 
136 	wrapped_size -= 16;
137 
138 	*max_input_size = wrapped_size;
139     } else {
140 	krb5_cksumtype type;
141 	size_t cksumsize;
142 
143 	ret = krb5_crypto_get_checksum_type(context, ctx->crypto, &type);
144 	if (ret)
145 	    return ret;
146 
147 	ret = krb5_checksumsize(context, type, &cksumsize);
148 	if (ret)
149 	    return ret;
150 
151 	if (req_output_size < cksumsize)
152 	    return 0;
153 
154 	/* Checksum is concatenated with data */
155 	*max_input_size = req_output_size - cksumsize;
156     }
157 
158     return 0;
159 }
160 
161 /*
162  * Rotate "rrc" bytes to the front or back
163  */
164 
165 static krb5_error_code
166 rrc_rotate(void *data, size_t len, uint16_t rrc, krb5_boolean unrotate)
167 {
168     u_char *tmp, buf[256];
169     size_t left;
170 
171     if (len == 0)
172 	return 0;
173 
174     rrc %= len;
175 
176     if (rrc == 0)
177 	return 0;
178 
179     left = len - rrc;
180 
181     if (rrc <= sizeof(buf)) {
182 	tmp = buf;
183     } else {
184 	tmp = malloc(rrc);
185 	if (tmp == NULL)
186 	    return ENOMEM;
187     }
188 
189     if (unrotate) {
190 	memcpy(tmp, data, rrc);
191 	memmove(data, (u_char *)data + rrc, left);
192 	memcpy((u_char *)data + left, tmp, rrc);
193     } else {
194 	memcpy(tmp, (u_char *)data + left, rrc);
195 	memmove((u_char *)data + rrc, data, left);
196 	memcpy(data, tmp, rrc);
197     }
198 
199     if (rrc > sizeof(buf))
200 	free(tmp);
201 
202     return 0;
203 }
204 
205 gss_iov_buffer_desc *
206 _gk_find_buffer(gss_iov_buffer_desc *iov, int iov_count, OM_uint32 type)
207 {
208     int i;
209 
210     for (i = 0; i < iov_count; i++)
211 	if (type == GSS_IOV_BUFFER_TYPE(iov[i].type))
212 	    return &iov[i];
213     return NULL;
214 }
215 
216 OM_uint32
217 _gk_allocate_buffer(OM_uint32 *minor_status, gss_iov_buffer_desc *buffer, size_t size)
218 {
219     if (buffer->type & GSS_IOV_BUFFER_FLAG_ALLOCATED) {
220 	if (buffer->buffer.length == size)
221 	    return GSS_S_COMPLETE;
222 	free(buffer->buffer.value);
223     }
224 
225     buffer->buffer.value = malloc(size);
226     buffer->buffer.length = size;
227     if (buffer->buffer.value == NULL) {
228 	*minor_status = ENOMEM;
229 	return GSS_S_FAILURE;
230     }
231     buffer->type |= GSS_IOV_BUFFER_FLAG_ALLOCATED;
232 
233     return GSS_S_COMPLETE;
234 }
235 
236 
237 OM_uint32
238 _gk_verify_buffers(OM_uint32 *minor_status,
239 		   const gsskrb5_ctx ctx,
240 		   const gss_iov_buffer_desc *header,
241 		   const gss_iov_buffer_desc *padding,
242 		   const gss_iov_buffer_desc *trailer)
243 {
244     if (header == NULL) {
245 	*minor_status = EINVAL;
246 	return GSS_S_FAILURE;
247     }
248 
249     if (IS_DCE_STYLE(ctx)) {
250 	/*
251 	 * In DCE style mode we reject having a padding or trailer buffer
252 	 */
253 	if (padding) {
254 	    *minor_status = EINVAL;
255 	    return GSS_S_FAILURE;
256 	}
257 	if (trailer) {
258 	    *minor_status = EINVAL;
259 	    return GSS_S_FAILURE;
260 	}
261     } else {
262 	/*
263 	 * In non-DCE style mode we require having a padding buffer
264 	 */
265 	if (padding == NULL) {
266 	    *minor_status = EINVAL;
267 	    return GSS_S_FAILURE;
268 	}
269     }
270 
271     *minor_status = 0;
272     return GSS_S_COMPLETE;
273 }
274 
275 #if 0
276 OM_uint32
277 _gssapi_wrap_cfx_iov(OM_uint32 *minor_status,
278 		     gsskrb5_ctx ctx,
279 		     krb5_context context,
280 		     int conf_req_flag,
281 		     int *conf_state,
282 		     gss_iov_buffer_desc *iov,
283 		     int iov_count)
284 {
285     OM_uint32 major_status, junk;
286     gss_iov_buffer_desc *header, *trailer, *padding;
287     size_t gsshsize, k5hsize;
288     size_t gsstsize, k5tsize;
289     size_t rrc = 0, ec = 0;
290     int i;
291     gss_cfx_wrap_token token;
292     krb5_error_code ret;
293     int32_t seq_number;
294     unsigned usage;
295     krb5_crypto_iov *data = NULL;
296 
297     header = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
298     if (header == NULL) {
299 	*minor_status = EINVAL;
300 	return GSS_S_FAILURE;
301     }
302 
303     padding = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
304     if (padding != NULL) {
305 	padding->buffer.length = 0;
306     }
307 
308     trailer = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
309 
310     major_status = _gk_verify_buffers(minor_status, ctx, header, padding, trailer);
311     if (major_status != GSS_S_COMPLETE) {
312 	    return major_status;
313     }
314 
315     if (conf_req_flag) {
316 	size_t k5psize = 0;
317 	size_t k5pbase = 0;
318 	size_t k5bsize = 0;
319 	size_t size = 0;
320 
321 	for (i = 0; i < iov_count; i++) {
322 	    switch (GSS_IOV_BUFFER_TYPE(iov[i].type)) {
323 	    case GSS_IOV_BUFFER_TYPE_DATA:
324 		size += iov[i].buffer.length;
325 		break;
326 	    default:
327 		break;
328 	    }
329 	}
330 
331 	size += sizeof(gss_cfx_wrap_token_desc);
332 
333 	*minor_status = krb5_crypto_length(context, ctx->crypto,
334 					   KRB5_CRYPTO_TYPE_HEADER,
335 					   &k5hsize);
336 	if (*minor_status)
337 	    return GSS_S_FAILURE;
338 
339 	*minor_status = krb5_crypto_length(context, ctx->crypto,
340 					   KRB5_CRYPTO_TYPE_TRAILER,
341 					   &k5tsize);
342 	if (*minor_status)
343 	    return GSS_S_FAILURE;
344 
345 	*minor_status = krb5_crypto_length(context, ctx->crypto,
346 					   KRB5_CRYPTO_TYPE_PADDING,
347 					   &k5pbase);
348 	if (*minor_status)
349 	    return GSS_S_FAILURE;
350 
351 	if (k5pbase > 1) {
352 	    k5psize = k5pbase - (size % k5pbase);
353 	} else {
354 	    k5psize = 0;
355 	}
356 
357 	if (k5psize == 0 && IS_DCE_STYLE(ctx)) {
358 	    *minor_status = krb5_crypto_getblocksize(context, ctx->crypto,
359 						     &k5bsize);
360 	    if (*minor_status)
361 		return GSS_S_FAILURE;
362 	    ec = k5bsize;
363 	} else {
364 	    ec = k5psize;
365 	}
366 
367 	gsshsize = sizeof(gss_cfx_wrap_token_desc) + k5hsize;
368 	gsstsize = sizeof(gss_cfx_wrap_token_desc) + ec + k5tsize;
369     } else {
370 	if (IS_DCE_STYLE(ctx)) {
371 	    *minor_status = EINVAL;
372 	    return GSS_S_FAILURE;
373 	}
374 
375 	k5hsize = 0;
376 	*minor_status = krb5_crypto_length(context, ctx->crypto,
377 					   KRB5_CRYPTO_TYPE_CHECKSUM,
378 					   &k5tsize);
379 	if (*minor_status)
380 	    return GSS_S_FAILURE;
381 
382 	gsshsize = sizeof(gss_cfx_wrap_token_desc);
383 	gsstsize = k5tsize;
384     }
385 
386     /*
387      *
388      */
389 
390     if (trailer == NULL) {
391 	rrc = gsstsize;
392 	if (IS_DCE_STYLE(ctx))
393 	    rrc -= ec;
394 	gsshsize += gsstsize;
395 	gsstsize = 0;
396     } else if (GSS_IOV_BUFFER_FLAGS(trailer->type) & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
397 	major_status = _gk_allocate_buffer(minor_status, trailer, gsstsize);
398 	if (major_status)
399 	    goto failure;
400     } else if (trailer->buffer.length < gsstsize) {
401 	*minor_status = KRB5_BAD_MSIZE;
402 	major_status = GSS_S_FAILURE;
403 	goto failure;
404     } else
405 	trailer->buffer.length = gsstsize;
406 
407     /*
408      *
409      */
410 
411     if (GSS_IOV_BUFFER_FLAGS(header->type) & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
412 	major_status = _gk_allocate_buffer(minor_status, header, gsshsize);
413 	if (major_status != GSS_S_COMPLETE)
414 	    goto failure;
415     } else if (header->buffer.length < gsshsize) {
416 	*minor_status = KRB5_BAD_MSIZE;
417 	major_status = GSS_S_FAILURE;
418 	goto failure;
419     } else
420 	header->buffer.length = gsshsize;
421 
422     token = (gss_cfx_wrap_token)header->buffer.value;
423 
424     token->TOK_ID[0] = 0x05;
425     token->TOK_ID[1] = 0x04;
426     token->Flags     = 0;
427     token->Filler    = 0xFF;
428 
429     if ((ctx->more_flags & LOCAL) == 0)
430 	token->Flags |= CFXSentByAcceptor;
431 
432     if (ctx->more_flags & ACCEPTOR_SUBKEY)
433 	token->Flags |= CFXAcceptorSubkey;
434 
435     if (ctx->more_flags & LOCAL)
436 	usage = KRB5_KU_USAGE_INITIATOR_SEAL;
437     else
438 	usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
439 
440     if (conf_req_flag) {
441 	/*
442 	 * In Wrap tokens with confidentiality, the EC field is
443 	 * used to encode the size (in bytes) of the random filler.
444 	 */
445 	token->Flags |= CFXSealed;
446 	token->EC[0] = (ec >> 8) & 0xFF;
447 	token->EC[1] = (ec >> 0) & 0xFF;
448 
449     } else {
450 	/*
451 	 * In Wrap tokens without confidentiality, the EC field is
452 	 * used to encode the size (in bytes) of the trailing
453 	 * checksum.
454 	 *
455 	 * This is not used in the checksum calcuation itself,
456 	 * because the checksum length could potentially vary
457 	 * depending on the data length.
458 	 */
459 	token->EC[0] = 0;
460 	token->EC[1] = 0;
461     }
462 
463     /*
464      * In Wrap tokens that provide for confidentiality, the RRC
465      * field in the header contains the hex value 00 00 before
466      * encryption.
467      *
468      * In Wrap tokens that do not provide for confidentiality,
469      * both the EC and RRC fields in the appended checksum
470      * contain the hex value 00 00 for the purpose of calculating
471      * the checksum.
472      */
473     token->RRC[0] = 0;
474     token->RRC[1] = 0;
475 
476     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
477     krb5_auth_con_getlocalseqnumber(context,
478 				    ctx->auth_context,
479 				    &seq_number);
480     _gsskrb5_encode_be_om_uint32(0,          &token->SND_SEQ[0]);
481     _gsskrb5_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]);
482     krb5_auth_con_setlocalseqnumber(context,
483 				    ctx->auth_context,
484 				    ++seq_number);
485     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
486 
487     data = calloc(iov_count + 3, sizeof(data[0]));
488     if (data == NULL) {
489 	*minor_status = ENOMEM;
490 	major_status = GSS_S_FAILURE;
491 	goto failure;
492     }
493 
494     if (conf_req_flag) {
495 	/*
496 	  plain packet:
497 
498 	  {"header" | encrypt(plaintext-data | ec-padding | E"header")}
499 
500 	  Expanded, this is with with RRC = 0:
501 
502 	  {"header" | krb5-header | plaintext-data | ec-padding | E"header" | krb5-trailer }
503 
504 	  In DCE-RPC mode == no trailer: RRC = gss "trailer" == length(ec-padding | E"header" | krb5-trailer)
505 
506 	  {"header" | ec-padding | E"header" | krb5-trailer | krb5-header | plaintext-data  }
507 	 */
508 
509 	i = 0;
510 	data[i].flags = KRB5_CRYPTO_TYPE_HEADER;
511 	data[i].data.data = ((uint8_t *)header->buffer.value) + header->buffer.length - k5hsize;
512 	data[i].data.length = k5hsize;
513 
514 	for (i = 1; i < iov_count + 1; i++) {
515 	    switch (GSS_IOV_BUFFER_TYPE(iov[i - 1].type)) {
516 	    case GSS_IOV_BUFFER_TYPE_DATA:
517 		data[i].flags = KRB5_CRYPTO_TYPE_DATA;
518 		break;
519 	    case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
520 		data[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
521 		break;
522 	    default:
523 		data[i].flags = KRB5_CRYPTO_TYPE_EMPTY;
524 		break;
525 	    }
526 	    data[i].data.length = iov[i - 1].buffer.length;
527 	    data[i].data.data = iov[i - 1].buffer.value;
528 	}
529 
530 	/*
531 	 * Any necessary padding is added here to ensure that the
532 	 * encrypted token header is always at the end of the
533 	 * ciphertext.
534 	 */
535 
536 	/* encrypted CFX header in trailer (or after the header if in
537 	   DCE mode). Copy in header into E"header"
538 	*/
539 	data[i].flags = KRB5_CRYPTO_TYPE_DATA;
540 	if (trailer)
541 	    data[i].data.data = trailer->buffer.value;
542 	else
543 	    data[i].data.data = ((uint8_t *)header->buffer.value) + sizeof(*token);
544 
545 	data[i].data.length = ec + sizeof(*token);
546 	memset(data[i].data.data, 0xFF, ec);
547 	memcpy(((uint8_t *)data[i].data.data) + ec, token, sizeof(*token));
548 	i++;
549 
550 	/* Kerberos trailer comes after the gss trailer */
551 	data[i].flags = KRB5_CRYPTO_TYPE_TRAILER;
552 	data[i].data.data = ((uint8_t *)data[i-1].data.data) + ec + sizeof(*token);
553 	data[i].data.length = k5tsize;
554 	i++;
555 
556 	ret = krb5_encrypt_iov_ivec(context, ctx->crypto, usage, data, i, NULL);
557 	if (ret != 0) {
558 	    *minor_status = ret;
559 	    major_status = GSS_S_FAILURE;
560 	    goto failure;
561 	}
562 
563 	if (rrc) {
564 	    token->RRC[0] = (rrc >> 8) & 0xFF;
565 	    token->RRC[1] = (rrc >> 0) & 0xFF;
566 	}
567 
568     } else {
569 	/*
570 	  plain packet:
571 
572 	  {data | "header" | gss-trailer (krb5 checksum)
573 
574 	  don't do RRC != 0
575 
576 	 */
577 
578 	for (i = 0; i < iov_count; i++) {
579 	    switch (GSS_IOV_BUFFER_TYPE(iov[i].type)) {
580 	    case GSS_IOV_BUFFER_TYPE_DATA:
581 		data[i].flags = KRB5_CRYPTO_TYPE_DATA;
582 		break;
583 	    case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
584 		data[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
585 		break;
586 	    default:
587 		data[i].flags = KRB5_CRYPTO_TYPE_EMPTY;
588 		break;
589 	    }
590 	    data[i].data.length = iov[i].buffer.length;
591 	    data[i].data.data = iov[i].buffer.value;
592 	}
593 
594 	data[i].flags = KRB5_CRYPTO_TYPE_DATA;
595 	data[i].data.data = header->buffer.value;
596 	data[i].data.length = sizeof(gss_cfx_wrap_token_desc);
597 	i++;
598 
599 	data[i].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
600 	if (trailer) {
601 		data[i].data.data = trailer->buffer.value;
602 	} else {
603 		data[i].data.data = (uint8_t *)header->buffer.value +
604 				     sizeof(gss_cfx_wrap_token_desc);
605 	}
606 	data[i].data.length = k5tsize;
607 	i++;
608 
609 	ret = krb5_create_checksum_iov(context, ctx->crypto, usage, data, i, NULL);
610 	if (ret) {
611 	    *minor_status = ret;
612 	    major_status = GSS_S_FAILURE;
613 	    goto failure;
614 	}
615 
616 	if (rrc) {
617 	    token->RRC[0] = (rrc >> 8) & 0xFF;
618 	    token->RRC[1] = (rrc >> 0) & 0xFF;
619 	}
620 
621 	token->EC[0] =  (k5tsize >> 8) & 0xFF;
622 	token->EC[1] =  (k5tsize >> 0) & 0xFF;
623     }
624 
625     if (conf_state != NULL)
626 	*conf_state = conf_req_flag;
627 
628     free(data);
629 
630     *minor_status = 0;
631     return GSS_S_COMPLETE;
632 
633  failure:
634     if (data)
635 	free(data);
636 
637     gss_release_iov_buffer(&junk, iov, iov_count);
638 
639     return major_status;
640 }
641 #endif
642 
643 /* This is slowpath */
644 static OM_uint32
645 unrotate_iov(OM_uint32 *minor_status, size_t rrc, gss_iov_buffer_desc *iov, int iov_count)
646 {
647     uint8_t *p, *q;
648     size_t len = 0, skip;
649     int i;
650 
651     for (i = 0; i < iov_count; i++)
652 	if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA ||
653 	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_PADDING ||
654 	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_TRAILER)
655 	    len += iov[i].buffer.length;
656 
657     p = malloc(len);
658     if (p == NULL) {
659 	*minor_status = ENOMEM;
660 	return GSS_S_FAILURE;
661     }
662     q = p;
663 
664     /* copy up */
665 
666     for (i = 0; i < iov_count; i++) {
667 	if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA ||
668 	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_PADDING ||
669 	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_TRAILER)
670 	{
671 	    memcpy(q, iov[i].buffer.value, iov[i].buffer.length);
672 	    q += iov[i].buffer.length;
673 	}
674     }
675     assert((size_t)(q - p) == len);
676 
677     /* unrotate first part */
678     q = p + rrc;
679     skip = rrc;
680     for (i = 0; i < iov_count; i++) {
681 	if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA ||
682 	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_PADDING ||
683 	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_TRAILER)
684 	{
685 	    if (iov[i].buffer.length <= skip) {
686 		skip -= iov[i].buffer.length;
687 	    } else {
688 		memcpy(((uint8_t *)iov[i].buffer.value) + skip, q, iov[i].buffer.length - skip);
689 		q += iov[i].buffer.length - skip;
690 		skip = 0;
691 	    }
692 	}
693     }
694     /* copy trailer */
695     q = p;
696     skip = rrc;
697     for (i = 0; i < iov_count; i++) {
698 	if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA ||
699 	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_PADDING ||
700 	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_TRAILER)
701 	{
702 	    memcpy(q, iov[i].buffer.value, min(iov[i].buffer.length, skip));
703 	    if (iov[i].buffer.length > skip)
704 		break;
705 	    skip -= iov[i].buffer.length;
706 	    q += iov[i].buffer.length;
707 	}
708     }
709     return GSS_S_COMPLETE;
710 }
711 
712 #if 0
713 
714 OM_uint32
715 _gssapi_unwrap_cfx_iov(OM_uint32 *minor_status,
716 		       gsskrb5_ctx ctx,
717 		       krb5_context context,
718 		       int *conf_state,
719 		       gss_qop_t *qop_state,
720 		       gss_iov_buffer_desc *iov,
721 		       int iov_count)
722 {
723     OM_uint32 seq_number_lo, seq_number_hi, major_status, junk;
724     gss_iov_buffer_desc *header, *trailer, *padding;
725     gss_cfx_wrap_token token, ttoken;
726     u_char token_flags;
727     krb5_error_code ret;
728     unsigned usage;
729     uint16_t ec, rrc;
730     krb5_crypto_iov *data = NULL;
731     int i, j;
732 
733     *minor_status = 0;
734 
735     header = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
736     if (header == NULL) {
737 	*minor_status = EINVAL;
738 	return GSS_S_FAILURE;
739     }
740 
741     if (header->buffer.length < sizeof(*token)) /* we check exact below */
742 	return GSS_S_DEFECTIVE_TOKEN;
743 
744     padding = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
745     if (padding != NULL && padding->buffer.length != 0) {
746 	*minor_status = EINVAL;
747 	return GSS_S_FAILURE;
748     }
749 
750     trailer = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
751 
752     major_status = _gk_verify_buffers(minor_status, ctx, header, padding, trailer);
753     if (major_status != GSS_S_COMPLETE) {
754 	    return major_status;
755     }
756 
757     token = (gss_cfx_wrap_token)header->buffer.value;
758 
759     if (token->TOK_ID[0] != 0x05 || token->TOK_ID[1] != 0x04)
760 	return GSS_S_DEFECTIVE_TOKEN;
761 
762     /* Ignore unknown flags */
763     token_flags = token->Flags &
764 	(CFXSentByAcceptor | CFXSealed | CFXAcceptorSubkey);
765 
766     if (token_flags & CFXSentByAcceptor) {
767 	if ((ctx->more_flags & LOCAL) == 0)
768 	    return GSS_S_DEFECTIVE_TOKEN;
769     }
770 
771     if (ctx->more_flags & ACCEPTOR_SUBKEY) {
772 	if ((token_flags & CFXAcceptorSubkey) == 0)
773 	    return GSS_S_DEFECTIVE_TOKEN;
774     } else {
775 	if (token_flags & CFXAcceptorSubkey)
776 	    return GSS_S_DEFECTIVE_TOKEN;
777     }
778 
779     if (token->Filler != 0xFF)
780 	return GSS_S_DEFECTIVE_TOKEN;
781 
782     if (conf_state != NULL)
783 	*conf_state = (token_flags & CFXSealed) ? 1 : 0;
784 
785     ec  = (token->EC[0]  << 8) | token->EC[1];
786     rrc = (token->RRC[0] << 8) | token->RRC[1];
787 
788     /*
789      * Check sequence number
790      */
791     _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi);
792     _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo);
793     if (seq_number_hi) {
794 	/* no support for 64-bit sequence numbers */
795 	*minor_status = ERANGE;
796 	return GSS_S_UNSEQ_TOKEN;
797     }
798 
799     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
800     ret = _gssapi_msg_order_check(ctx->order, seq_number_lo);
801     if (ret != 0) {
802 	*minor_status = 0;
803 	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
804 	return ret;
805     }
806     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
807 
808     /*
809      * Decrypt and/or verify checksum
810      */
811 
812     if (ctx->more_flags & LOCAL) {
813 	usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
814     } else {
815 	usage = KRB5_KU_USAGE_INITIATOR_SEAL;
816     }
817 
818     data = calloc(iov_count + 3, sizeof(data[0]));
819     if (data == NULL) {
820 	*minor_status = ENOMEM;
821 	major_status = GSS_S_FAILURE;
822 	goto failure;
823     }
824 
825     if (token_flags & CFXSealed) {
826 	size_t k5tsize, k5hsize;
827 
828 	krb5_crypto_length(context, ctx->crypto, KRB5_CRYPTO_TYPE_HEADER, &k5hsize);
829 	krb5_crypto_length(context, ctx->crypto, KRB5_CRYPTO_TYPE_TRAILER, &k5tsize);
830 
831 	/* Rotate by RRC; bogus to do this in-place XXX */
832 	/* Check RRC */
833 
834 	if (trailer == NULL) {
835 	    size_t gsstsize = k5tsize + sizeof(*token);
836 	    size_t gsshsize = k5hsize + sizeof(*token);
837 
838 	    if (rrc != gsstsize) {
839 		major_status = GSS_S_DEFECTIVE_TOKEN;
840 		goto failure;
841 	    }
842 
843 	    if (IS_DCE_STYLE(ctx))
844 		gsstsize += ec;
845 
846 	    gsshsize += gsstsize;
847 
848 	    if (header->buffer.length != gsshsize) {
849 		major_status = GSS_S_DEFECTIVE_TOKEN;
850 		goto failure;
851 	    }
852 	} else if (trailer->buffer.length != sizeof(*token) + k5tsize) {
853 	    major_status = GSS_S_DEFECTIVE_TOKEN;
854 	    goto failure;
855 	} else if (header->buffer.length != sizeof(*token) + k5hsize) {
856 	    major_status = GSS_S_DEFECTIVE_TOKEN;
857 	    goto failure;
858 	} else if (rrc != 0) {
859 	    /* go though slowpath */
860 	    major_status = unrotate_iov(minor_status, rrc, iov, iov_count);
861 	    if (major_status)
862 		goto failure;
863 	}
864 
865 	i = 0;
866 	data[i].flags = KRB5_CRYPTO_TYPE_HEADER;
867 	data[i].data.data = ((uint8_t *)header->buffer.value) + header->buffer.length - k5hsize;
868 	data[i].data.length = k5hsize;
869 	i++;
870 
871 	for (j = 0; j < iov_count; i++, j++) {
872 	    switch (GSS_IOV_BUFFER_TYPE(iov[j].type)) {
873 	    case GSS_IOV_BUFFER_TYPE_DATA:
874 		data[i].flags = KRB5_CRYPTO_TYPE_DATA;
875 		break;
876 	    case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
877 		data[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
878 		break;
879 	    default:
880 		data[i].flags = KRB5_CRYPTO_TYPE_EMPTY;
881 		break;
882 	    }
883 	    data[i].data.length = iov[j].buffer.length;
884 	    data[i].data.data = iov[j].buffer.value;
885 	}
886 
887 	/* encrypted CFX header in trailer (or after the header if in
888 	   DCE mode). Copy in header into E"header"
889 	*/
890 	data[i].flags = KRB5_CRYPTO_TYPE_DATA;
891 	if (trailer) {
892 	    data[i].data.data = trailer->buffer.value;
893 	} else {
894 	    data[i].data.data = ((uint8_t *)header->buffer.value) +
895 		header->buffer.length - k5hsize - k5tsize - ec- sizeof(*token);
896 	}
897 
898 	data[i].data.length = ec + sizeof(*token);
899 	ttoken = (gss_cfx_wrap_token)(((uint8_t *)data[i].data.data) + ec);
900 	i++;
901 
902 	/* Kerberos trailer comes after the gss trailer */
903 	data[i].flags = KRB5_CRYPTO_TYPE_TRAILER;
904 	data[i].data.data = ((uint8_t *)data[i-1].data.data) + ec + sizeof(*token);
905 	data[i].data.length = k5tsize;
906 	i++;
907 
908 	ret = krb5_decrypt_iov_ivec(context, ctx->crypto, usage, data, i, NULL);
909 	if (ret != 0) {
910 	    *minor_status = ret;
911 	    major_status = GSS_S_FAILURE;
912 	    goto failure;
913 	}
914 
915 	ttoken->RRC[0] = token->RRC[0];
916 	ttoken->RRC[1] = token->RRC[1];
917 
918 	/* Check the integrity of the header */
919 	if (ct_memcmp(ttoken, token, sizeof(*token)) != 0) {
920 	    major_status = GSS_S_BAD_MIC;
921 	    goto failure;
922 	}
923     } else {
924 	size_t gsstsize = ec;
925 	size_t gsshsize = sizeof(*token);
926 
927 	if (trailer == NULL) {
928 	    /* Check RRC */
929 	    if (rrc != gsstsize) {
930 	       *minor_status = EINVAL;
931 	       major_status = GSS_S_FAILURE;
932 	       goto failure;
933 	    }
934 
935 	    gsshsize += gsstsize;
936 	    gsstsize = 0;
937 	} else if (trailer->buffer.length != gsstsize) {
938 	    major_status = GSS_S_DEFECTIVE_TOKEN;
939 	    goto failure;
940 	} else if (rrc != 0) {
941 	    /* Check RRC */
942 	    *minor_status = EINVAL;
943 	    major_status = GSS_S_FAILURE;
944 	    goto failure;
945 	}
946 
947 	if (header->buffer.length != gsshsize) {
948 	    major_status = GSS_S_DEFECTIVE_TOKEN;
949 	    goto failure;
950 	}
951 
952 	for (i = 0; i < iov_count; i++) {
953 	    switch (GSS_IOV_BUFFER_TYPE(iov[i].type)) {
954 	    case GSS_IOV_BUFFER_TYPE_DATA:
955 		data[i].flags = KRB5_CRYPTO_TYPE_DATA;
956 		break;
957 	    case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
958 		data[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
959 		break;
960 	    default:
961 		data[i].flags = KRB5_CRYPTO_TYPE_EMPTY;
962 		break;
963 	    }
964 	    data[i].data.length = iov[i].buffer.length;
965 	    data[i].data.data = iov[i].buffer.value;
966 	}
967 
968 	data[i].flags = KRB5_CRYPTO_TYPE_DATA;
969 	data[i].data.data = header->buffer.value;
970 	data[i].data.length = sizeof(*token);
971 	i++;
972 
973 	data[i].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
974 	if (trailer) {
975 		data[i].data.data = trailer->buffer.value;
976 	} else {
977 		data[i].data.data = (uint8_t *)header->buffer.value +
978 				     sizeof(*token);
979 	}
980 	data[i].data.length = ec;
981 	i++;
982 
983 	token = (gss_cfx_wrap_token)header->buffer.value;
984 	token->EC[0]  = 0;
985 	token->EC[1]  = 0;
986 	token->RRC[0] = 0;
987 	token->RRC[1] = 0;
988 
989 	ret = krb5_verify_checksum_iov(context, ctx->crypto, usage, data, i, NULL);
990 	if (ret) {
991 	    *minor_status = ret;
992 	    major_status = GSS_S_FAILURE;
993 	    goto failure;
994 	}
995     }
996 
997     if (qop_state != NULL) {
998 	*qop_state = GSS_C_QOP_DEFAULT;
999     }
1000 
1001     free(data);
1002 
1003     *minor_status = 0;
1004     return GSS_S_COMPLETE;
1005 
1006  failure:
1007     if (data)
1008 	free(data);
1009 
1010     gss_release_iov_buffer(&junk, iov, iov_count);
1011 
1012     return major_status;
1013 }
1014 #endif
1015 
1016 OM_uint32
1017 _gssapi_wrap_iov_length_cfx(OM_uint32 *minor_status,
1018 			    gsskrb5_ctx ctx,
1019 			    krb5_context context,
1020 			    int conf_req_flag,
1021 			    gss_qop_t qop_req,
1022 			    int *conf_state,
1023 			    gss_iov_buffer_desc *iov,
1024 			    int iov_count)
1025 {
1026     OM_uint32 major_status;
1027     size_t size;
1028     int i;
1029     gss_iov_buffer_desc *header = NULL;
1030     gss_iov_buffer_desc *padding = NULL;
1031     gss_iov_buffer_desc *trailer = NULL;
1032     size_t gsshsize = 0;
1033     size_t gsstsize = 0;
1034     size_t k5hsize = 0;
1035     size_t k5tsize = 0;
1036 
1037     GSSAPI_KRB5_INIT (&context);
1038     *minor_status = 0;
1039 
1040     for (size = 0, i = 0; i < iov_count; i++) {
1041 	switch(GSS_IOV_BUFFER_TYPE(iov[i].type)) {
1042 	case GSS_IOV_BUFFER_TYPE_EMPTY:
1043 	    break;
1044 	case GSS_IOV_BUFFER_TYPE_DATA:
1045 	    size += iov[i].buffer.length;
1046 	    break;
1047 	case GSS_IOV_BUFFER_TYPE_HEADER:
1048 	    if (header != NULL) {
1049 		*minor_status = 0;
1050 		return GSS_S_FAILURE;
1051 	    }
1052 	    header = &iov[i];
1053 	    break;
1054 	case GSS_IOV_BUFFER_TYPE_TRAILER:
1055 	    if (trailer != NULL) {
1056 		*minor_status = 0;
1057 		return GSS_S_FAILURE;
1058 	    }
1059 	    trailer = &iov[i];
1060 	    break;
1061 	case GSS_IOV_BUFFER_TYPE_PADDING:
1062 	    if (padding != NULL) {
1063 		*minor_status = 0;
1064 		return GSS_S_FAILURE;
1065 	    }
1066 	    padding = &iov[i];
1067 	    break;
1068 	case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
1069 	    break;
1070 	default:
1071 	    *minor_status = EINVAL;
1072 	    return GSS_S_FAILURE;
1073 	}
1074     }
1075 
1076     major_status = _gk_verify_buffers(minor_status, ctx, header, padding, trailer);
1077     if (major_status != GSS_S_COMPLETE) {
1078 	    return major_status;
1079     }
1080 
1081     if (conf_req_flag) {
1082 	size_t k5psize = 0;
1083 	size_t k5pbase = 0;
1084 	size_t k5bsize = 0;
1085 	size_t ec = 0;
1086 
1087 	size += sizeof(gss_cfx_wrap_token_desc);
1088 
1089 	*minor_status = krb5_crypto_length(context, ctx->crypto,
1090 					   KRB5_CRYPTO_TYPE_HEADER,
1091 					   &k5hsize);
1092 	if (*minor_status)
1093 	    return GSS_S_FAILURE;
1094 
1095 	*minor_status = krb5_crypto_length(context, ctx->crypto,
1096 					   KRB5_CRYPTO_TYPE_TRAILER,
1097 					   &k5tsize);
1098 	if (*minor_status)
1099 	    return GSS_S_FAILURE;
1100 
1101 	*minor_status = krb5_crypto_length(context, ctx->crypto,
1102 					   KRB5_CRYPTO_TYPE_PADDING,
1103 					   &k5pbase);
1104 	if (*minor_status)
1105 	    return GSS_S_FAILURE;
1106 
1107 	if (k5pbase > 1) {
1108 	    k5psize = k5pbase - (size % k5pbase);
1109 	} else {
1110 	    k5psize = 0;
1111 	}
1112 
1113 	if (k5psize == 0 && IS_DCE_STYLE(ctx)) {
1114 	    *minor_status = krb5_crypto_getblocksize(context, ctx->crypto,
1115 						     &k5bsize);
1116 	    if (*minor_status)
1117 		return GSS_S_FAILURE;
1118 
1119 	    ec = k5bsize;
1120 	} else {
1121 	    ec = k5psize;
1122 	}
1123 
1124 	gsshsize = sizeof(gss_cfx_wrap_token_desc) + k5hsize;
1125 	gsstsize = sizeof(gss_cfx_wrap_token_desc) + ec + k5tsize;
1126     } else {
1127 	*minor_status = krb5_crypto_length(context, ctx->crypto,
1128 					   KRB5_CRYPTO_TYPE_CHECKSUM,
1129 					   &k5tsize);
1130 	if (*minor_status)
1131 	    return GSS_S_FAILURE;
1132 
1133 	gsshsize = sizeof(gss_cfx_wrap_token_desc);
1134 	gsstsize = k5tsize;
1135     }
1136 
1137     if (trailer != NULL) {
1138 	trailer->buffer.length = gsstsize;
1139     } else {
1140 	gsshsize += gsstsize;
1141     }
1142 
1143     header->buffer.length = gsshsize;
1144 
1145     if (padding) {
1146 	/* padding is done via EC and is contained in the header or trailer */
1147 	padding->buffer.length = 0;
1148     }
1149 
1150     if (conf_state) {
1151 	*conf_state = conf_req_flag;
1152     }
1153 
1154     return GSS_S_COMPLETE;
1155 }
1156 
1157 
1158 
1159 
1160 OM_uint32 _gssapi_wrap_cfx(OM_uint32 *minor_status,
1161 			   const gsskrb5_ctx ctx,
1162 			   krb5_context context,
1163 			   int conf_req_flag,
1164 			   const gss_buffer_t input_message_buffer,
1165 			   int *conf_state,
1166 			   gss_buffer_t output_message_buffer)
1167 {
1168     gss_cfx_wrap_token token;
1169     krb5_error_code ret;
1170     unsigned usage;
1171     krb5_data cipher;
1172     size_t wrapped_len, cksumsize;
1173     uint16_t padlength, rrc = 0;
1174     int32_t seq_number;
1175     u_char *p;
1176 
1177     ret = _gsskrb5cfx_wrap_length_cfx(context,
1178 				      ctx->crypto, conf_req_flag,
1179 				      IS_DCE_STYLE(ctx),
1180 				      input_message_buffer->length,
1181 				      &wrapped_len, &cksumsize, &padlength);
1182     if (ret != 0) {
1183 	*minor_status = ret;
1184 	return GSS_S_FAILURE;
1185     }
1186 
1187     /* Always rotate encrypted token (if any) and checksum to header */
1188     rrc = (conf_req_flag ? sizeof(*token) : 0) + (uint16_t)cksumsize;
1189 
1190     output_message_buffer->length = wrapped_len;
1191     output_message_buffer->value = malloc(output_message_buffer->length);
1192     if (output_message_buffer->value == NULL) {
1193 	*minor_status = ENOMEM;
1194 	return GSS_S_FAILURE;
1195     }
1196 
1197     p = output_message_buffer->value;
1198     token = (gss_cfx_wrap_token)p;
1199     token->TOK_ID[0] = 0x05;
1200     token->TOK_ID[1] = 0x04;
1201     token->Flags     = 0;
1202     token->Filler    = 0xFF;
1203     if ((ctx->more_flags & LOCAL) == 0)
1204 	token->Flags |= CFXSentByAcceptor;
1205     if (ctx->more_flags & ACCEPTOR_SUBKEY)
1206 	token->Flags |= CFXAcceptorSubkey;
1207     if (conf_req_flag) {
1208 	/*
1209 	 * In Wrap tokens with confidentiality, the EC field is
1210 	 * used to encode the size (in bytes) of the random filler.
1211 	 */
1212 	token->Flags |= CFXSealed;
1213 	token->EC[0] = (padlength >> 8) & 0xFF;
1214 	token->EC[1] = (padlength >> 0) & 0xFF;
1215     } else {
1216 	/*
1217 	 * In Wrap tokens without confidentiality, the EC field is
1218 	 * used to encode the size (in bytes) of the trailing
1219 	 * checksum.
1220 	 *
1221 	 * This is not used in the checksum calcuation itself,
1222 	 * because the checksum length could potentially vary
1223 	 * depending on the data length.
1224 	 */
1225 	token->EC[0] = 0;
1226 	token->EC[1] = 0;
1227     }
1228 
1229     /*
1230      * In Wrap tokens that provide for confidentiality, the RRC
1231      * field in the header contains the hex value 00 00 before
1232      * encryption.
1233      *
1234      * In Wrap tokens that do not provide for confidentiality,
1235      * both the EC and RRC fields in the appended checksum
1236      * contain the hex value 00 00 for the purpose of calculating
1237      * the checksum.
1238      */
1239     token->RRC[0] = 0;
1240     token->RRC[1] = 0;
1241 
1242     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
1243     krb5_auth_con_getlocalseqnumber(context,
1244 				    ctx->auth_context,
1245 				    &seq_number);
1246     _gsskrb5_encode_be_om_uint32(0,          &token->SND_SEQ[0]);
1247     _gsskrb5_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]);
1248     krb5_auth_con_setlocalseqnumber(context,
1249 				    ctx->auth_context,
1250 				    ++seq_number);
1251     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1252 
1253     /*
1254      * If confidentiality is requested, the token header is
1255      * appended to the plaintext before encryption; the resulting
1256      * token is {"header" | encrypt(plaintext | pad | "header")}.
1257      *
1258      * If no confidentiality is requested, the checksum is
1259      * calculated over the plaintext concatenated with the
1260      * token header.
1261      */
1262     if (ctx->more_flags & LOCAL) {
1263 	usage = KRB5_KU_USAGE_INITIATOR_SEAL;
1264     } else {
1265 	usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
1266     }
1267 
1268     if (conf_req_flag) {
1269 	/*
1270 	 * Any necessary padding is added here to ensure that the
1271 	 * encrypted token header is always at the end of the
1272 	 * ciphertext.
1273 	 *
1274 	 * The specification does not require that the padding
1275 	 * bytes are initialized.
1276 	 */
1277 	p += sizeof(*token);
1278 	memcpy(p, input_message_buffer->value, input_message_buffer->length);
1279 	memset(p + input_message_buffer->length, 0xFF, padlength);
1280 	memcpy(p + input_message_buffer->length + padlength,
1281 	       token, sizeof(*token));
1282 
1283 	ret = krb5_encrypt(context, ctx->crypto,
1284 			   usage, p,
1285 			   input_message_buffer->length + padlength +
1286 				sizeof(*token),
1287 			   &cipher);
1288 	if (ret != 0) {
1289 	    *minor_status = ret;
1290 	    _gsskrb5_release_buffer(minor_status, output_message_buffer);
1291 	    return GSS_S_FAILURE;
1292 	}
1293 	assert(sizeof(*token) + cipher.length == wrapped_len);
1294 	token->RRC[0] = (rrc >> 8) & 0xFF;
1295 	token->RRC[1] = (rrc >> 0) & 0xFF;
1296 
1297 	/*
1298 	 * this is really ugly, but needed against windows
1299 	 * for DCERPC, as windows rotates by EC+RRC.
1300 	 */
1301 	if (IS_DCE_STYLE(ctx)) {
1302 		ret = rrc_rotate(cipher.data, cipher.length, rrc+padlength, FALSE);
1303 	} else {
1304 		ret = rrc_rotate(cipher.data, cipher.length, rrc, FALSE);
1305 	}
1306 	if (ret != 0) {
1307 	    *minor_status = ret;
1308 	    _gsskrb5_release_buffer(minor_status, output_message_buffer);
1309 	    return GSS_S_FAILURE;
1310 	}
1311 	memcpy(p, cipher.data, cipher.length);
1312 	krb5_data_free(&cipher);
1313     } else {
1314 	char *buf;
1315 	Checksum cksum;
1316 
1317 	buf = malloc(input_message_buffer->length + sizeof(*token));
1318 	if (buf == NULL) {
1319 	    *minor_status = ENOMEM;
1320 	    _gsskrb5_release_buffer(minor_status, output_message_buffer);
1321 	    return GSS_S_FAILURE;
1322 	}
1323 	memcpy(buf, input_message_buffer->value, input_message_buffer->length);
1324 	memcpy(buf + input_message_buffer->length, token, sizeof(*token));
1325 
1326 	ret = krb5_create_checksum(context, ctx->crypto,
1327 				   usage, 0, buf,
1328 				   input_message_buffer->length +
1329 					sizeof(*token),
1330 				   &cksum);
1331 	if (ret != 0) {
1332 	    *minor_status = ret;
1333 	    _gsskrb5_release_buffer(minor_status, output_message_buffer);
1334 	    free(buf);
1335 	    return GSS_S_FAILURE;
1336 	}
1337 
1338 	free(buf);
1339 
1340 	assert(cksum.checksum.length == cksumsize);
1341 	token->EC[0] =  (cksum.checksum.length >> 8) & 0xFF;
1342 	token->EC[1] =  (cksum.checksum.length >> 0) & 0xFF;
1343 	token->RRC[0] = (rrc >> 8) & 0xFF;
1344 	token->RRC[1] = (rrc >> 0) & 0xFF;
1345 
1346 	p += sizeof(*token);
1347 	memcpy(p, input_message_buffer->value, input_message_buffer->length);
1348 	memcpy(p + input_message_buffer->length,
1349 	       cksum.checksum.data, cksum.checksum.length);
1350 
1351 	ret = rrc_rotate(p,
1352 	    input_message_buffer->length + cksum.checksum.length, rrc, FALSE);
1353 	if (ret != 0) {
1354 	    *minor_status = ret;
1355 	    _gsskrb5_release_buffer(minor_status, output_message_buffer);
1356 	    free_Checksum(&cksum);
1357 	    return GSS_S_FAILURE;
1358 	}
1359 	free_Checksum(&cksum);
1360     }
1361 
1362     if (conf_state != NULL) {
1363 	*conf_state = conf_req_flag;
1364     }
1365 
1366     *minor_status = 0;
1367     return GSS_S_COMPLETE;
1368 }
1369 
1370 OM_uint32 _gssapi_unwrap_cfx(OM_uint32 *minor_status,
1371 			     const gsskrb5_ctx ctx,
1372 			     krb5_context context,
1373 			     const gss_buffer_t input_message_buffer,
1374 			     gss_buffer_t output_message_buffer,
1375 			     int *conf_state,
1376 			     gss_qop_t *qop_state)
1377 {
1378     gss_cfx_wrap_token token;
1379     u_char token_flags;
1380     krb5_error_code ret;
1381     unsigned usage;
1382     krb5_data data;
1383     uint16_t ec, rrc;
1384     OM_uint32 seq_number_lo, seq_number_hi;
1385     size_t len;
1386     u_char *p;
1387 
1388     *minor_status = 0;
1389 
1390     if (input_message_buffer->length < sizeof(*token)) {
1391 	return GSS_S_DEFECTIVE_TOKEN;
1392     }
1393 
1394     p = input_message_buffer->value;
1395 
1396     token = (gss_cfx_wrap_token)p;
1397 
1398     if (token->TOK_ID[0] != 0x05 || token->TOK_ID[1] != 0x04) {
1399 	return GSS_S_DEFECTIVE_TOKEN;
1400     }
1401 
1402     /* Ignore unknown flags */
1403     token_flags = token->Flags &
1404 	(CFXSentByAcceptor | CFXSealed | CFXAcceptorSubkey);
1405 
1406     if (token_flags & CFXSentByAcceptor) {
1407 	if ((ctx->more_flags & LOCAL) == 0)
1408 	    return GSS_S_DEFECTIVE_TOKEN;
1409     }
1410 
1411     if (ctx->more_flags & ACCEPTOR_SUBKEY) {
1412 	if ((token_flags & CFXAcceptorSubkey) == 0)
1413 	    return GSS_S_DEFECTIVE_TOKEN;
1414     } else {
1415 	if (token_flags & CFXAcceptorSubkey)
1416 	    return GSS_S_DEFECTIVE_TOKEN;
1417     }
1418 
1419     if (token->Filler != 0xFF) {
1420 	return GSS_S_DEFECTIVE_TOKEN;
1421     }
1422 
1423     if (conf_state != NULL) {
1424 	*conf_state = (token_flags & CFXSealed) ? 1 : 0;
1425     }
1426 
1427     ec  = (token->EC[0]  << 8) | token->EC[1];
1428     rrc = (token->RRC[0] << 8) | token->RRC[1];
1429 
1430     /*
1431      * Check sequence number
1432      */
1433     _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi);
1434     _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo);
1435     if (seq_number_hi) {
1436 	/* no support for 64-bit sequence numbers */
1437 	*minor_status = ERANGE;
1438 	return GSS_S_UNSEQ_TOKEN;
1439     }
1440 
1441     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
1442     ret = _gssapi_msg_order_check(ctx->order, seq_number_lo);
1443     if (ret != 0) {
1444 	*minor_status = 0;
1445 	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1446 	_gsskrb5_release_buffer(minor_status, output_message_buffer);
1447 	return ret;
1448     }
1449     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1450 
1451     /*
1452      * Decrypt and/or verify checksum
1453      */
1454 
1455     if (ctx->more_flags & LOCAL) {
1456 	usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
1457     } else {
1458 	usage = KRB5_KU_USAGE_INITIATOR_SEAL;
1459     }
1460 
1461     p += sizeof(*token);
1462     len = input_message_buffer->length;
1463     len -= (p - (u_char *)input_message_buffer->value);
1464 
1465     if (token_flags & CFXSealed) {
1466 	/*
1467 	 * this is really ugly, but needed against windows
1468 	 * for DCERPC, as windows rotates by EC+RRC.
1469 	 */
1470 	if (IS_DCE_STYLE(ctx)) {
1471 		*minor_status = rrc_rotate(p, len, rrc+ec, TRUE);
1472 	} else {
1473 		*minor_status = rrc_rotate(p, len, rrc, TRUE);
1474 	}
1475 	if (*minor_status != 0) {
1476 	    return GSS_S_FAILURE;
1477 	}
1478 
1479 	ret = krb5_decrypt(context, ctx->crypto, usage,
1480 	    p, len, &data);
1481 	if (ret != 0) {
1482 	    *minor_status = ret;
1483 	    return GSS_S_BAD_MIC;
1484 	}
1485 
1486 	/* Check that there is room for the pad and token header */
1487 	if (data.length < ec + sizeof(*token)) {
1488 	    krb5_data_free(&data);
1489 	    return GSS_S_DEFECTIVE_TOKEN;
1490 	}
1491 	p = data.data;
1492 	p += data.length - sizeof(*token);
1493 
1494 	/* RRC is unprotected; don't modify input buffer */
1495 	((gss_cfx_wrap_token)p)->RRC[0] = token->RRC[0];
1496 	((gss_cfx_wrap_token)p)->RRC[1] = token->RRC[1];
1497 
1498 	/* Check the integrity of the header */
1499 	if (ct_memcmp(p, token, sizeof(*token)) != 0) {
1500 	    krb5_data_free(&data);
1501 	    return GSS_S_BAD_MIC;
1502 	}
1503 
1504 	output_message_buffer->value = data.data;
1505 	output_message_buffer->length = data.length - ec - sizeof(*token);
1506     } else {
1507 	Checksum cksum;
1508 
1509 	/* Rotate by RRC; bogus to do this in-place XXX */
1510 	*minor_status = rrc_rotate(p, len, rrc, TRUE);
1511 	if (*minor_status != 0) {
1512 	    return GSS_S_FAILURE;
1513 	}
1514 
1515 	/* Determine checksum type */
1516 	ret = krb5_crypto_get_checksum_type(context,
1517 					    ctx->crypto,
1518 					    &cksum.cksumtype);
1519 	if (ret != 0) {
1520 	    *minor_status = ret;
1521 	    return GSS_S_FAILURE;
1522 	}
1523 
1524 	cksum.checksum.length = ec;
1525 
1526 	/* Check we have at least as much data as the checksum */
1527 	if (len < cksum.checksum.length) {
1528 	    *minor_status = ERANGE;
1529 	    return GSS_S_BAD_MIC;
1530 	}
1531 
1532 	/* Length now is of the plaintext only, no checksum */
1533 	len -= cksum.checksum.length;
1534 	cksum.checksum.data = p + len;
1535 
1536 	output_message_buffer->length = len; /* for later */
1537 	output_message_buffer->value = malloc(len + sizeof(*token));
1538 	if (output_message_buffer->value == NULL) {
1539 	    *minor_status = ENOMEM;
1540 	    return GSS_S_FAILURE;
1541 	}
1542 
1543 	/* Checksum is over (plaintext-data | "header") */
1544 	memcpy(output_message_buffer->value, p, len);
1545 	memcpy((u_char *)output_message_buffer->value + len,
1546 	       token, sizeof(*token));
1547 
1548 	/* EC is not included in checksum calculation */
1549 	token = (gss_cfx_wrap_token)((u_char *)output_message_buffer->value +
1550 				     len);
1551 	token->EC[0]  = 0;
1552 	token->EC[1]  = 0;
1553 	token->RRC[0] = 0;
1554 	token->RRC[1] = 0;
1555 
1556 	ret = krb5_verify_checksum(context, ctx->crypto,
1557 				   usage,
1558 				   output_message_buffer->value,
1559 				   len + sizeof(*token),
1560 				   &cksum);
1561 	if (ret != 0) {
1562 	    *minor_status = ret;
1563 	    _gsskrb5_release_buffer(minor_status, output_message_buffer);
1564 	    return GSS_S_BAD_MIC;
1565 	}
1566     }
1567 
1568     if (qop_state != NULL) {
1569 	*qop_state = GSS_C_QOP_DEFAULT;
1570     }
1571 
1572     *minor_status = 0;
1573     return GSS_S_COMPLETE;
1574 }
1575 
1576 OM_uint32 _gssapi_mic_cfx(OM_uint32 *minor_status,
1577 			  const gsskrb5_ctx ctx,
1578 			  krb5_context context,
1579 			  gss_qop_t qop_req,
1580 			  const gss_buffer_t message_buffer,
1581 			  gss_buffer_t message_token)
1582 {
1583     gss_cfx_mic_token token;
1584     krb5_error_code ret;
1585     unsigned usage;
1586     Checksum cksum;
1587     u_char *buf;
1588     size_t len;
1589     int32_t seq_number;
1590 
1591     len = message_buffer->length + sizeof(*token);
1592     buf = malloc(len);
1593     if (buf == NULL) {
1594 	*minor_status = ENOMEM;
1595 	return GSS_S_FAILURE;
1596     }
1597 
1598     memcpy(buf, message_buffer->value, message_buffer->length);
1599 
1600     token = (gss_cfx_mic_token)(buf + message_buffer->length);
1601     token->TOK_ID[0] = 0x04;
1602     token->TOK_ID[1] = 0x04;
1603     token->Flags = 0;
1604     if ((ctx->more_flags & LOCAL) == 0)
1605 	token->Flags |= CFXSentByAcceptor;
1606     if (ctx->more_flags & ACCEPTOR_SUBKEY)
1607 	token->Flags |= CFXAcceptorSubkey;
1608     memset(token->Filler, 0xFF, 5);
1609 
1610     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
1611     krb5_auth_con_getlocalseqnumber(context,
1612 				    ctx->auth_context,
1613 				    &seq_number);
1614     _gsskrb5_encode_be_om_uint32(0,          &token->SND_SEQ[0]);
1615     _gsskrb5_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]);
1616     krb5_auth_con_setlocalseqnumber(context,
1617 				    ctx->auth_context,
1618 				    ++seq_number);
1619     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1620 
1621     if (ctx->more_flags & LOCAL) {
1622 	usage = KRB5_KU_USAGE_INITIATOR_SIGN;
1623     } else {
1624 	usage = KRB5_KU_USAGE_ACCEPTOR_SIGN;
1625     }
1626 
1627     ret = krb5_create_checksum(context, ctx->crypto,
1628 	usage, 0, buf, len, &cksum);
1629     if (ret != 0) {
1630 	*minor_status = ret;
1631 	free(buf);
1632 	return GSS_S_FAILURE;
1633     }
1634 
1635     /* Determine MIC length */
1636     message_token->length = sizeof(*token) + cksum.checksum.length;
1637     message_token->value = malloc(message_token->length);
1638     if (message_token->value == NULL) {
1639 	*minor_status = ENOMEM;
1640 	free_Checksum(&cksum);
1641 	free(buf);
1642 	return GSS_S_FAILURE;
1643     }
1644 
1645     /* Token is { "header" | get_mic("header" | plaintext-data) } */
1646     memcpy(message_token->value, token, sizeof(*token));
1647     memcpy((u_char *)message_token->value + sizeof(*token),
1648 	   cksum.checksum.data, cksum.checksum.length);
1649 
1650     free_Checksum(&cksum);
1651     free(buf);
1652 
1653     *minor_status = 0;
1654     return GSS_S_COMPLETE;
1655 }
1656 
1657 OM_uint32 _gssapi_verify_mic_cfx(OM_uint32 *minor_status,
1658 				 const gsskrb5_ctx ctx,
1659 				 krb5_context context,
1660 				 const gss_buffer_t message_buffer,
1661 				 const gss_buffer_t token_buffer,
1662 				 gss_qop_t *qop_state)
1663 {
1664     gss_cfx_mic_token token;
1665     u_char token_flags;
1666     krb5_error_code ret;
1667     unsigned usage;
1668     OM_uint32 seq_number_lo, seq_number_hi;
1669     u_char *buf, *p;
1670     Checksum cksum;
1671 
1672     *minor_status = 0;
1673 
1674     if (token_buffer->length < sizeof(*token)) {
1675 	return GSS_S_DEFECTIVE_TOKEN;
1676     }
1677 
1678     p = token_buffer->value;
1679 
1680     token = (gss_cfx_mic_token)p;
1681 
1682     if (token->TOK_ID[0] != 0x04 || token->TOK_ID[1] != 0x04) {
1683 	return GSS_S_DEFECTIVE_TOKEN;
1684     }
1685 
1686     /* Ignore unknown flags */
1687     token_flags = token->Flags & (CFXSentByAcceptor | CFXAcceptorSubkey);
1688 
1689     if (token_flags & CFXSentByAcceptor) {
1690 	if ((ctx->more_flags & LOCAL) == 0)
1691 	    return GSS_S_DEFECTIVE_TOKEN;
1692     }
1693     if (ctx->more_flags & ACCEPTOR_SUBKEY) {
1694 	if ((token_flags & CFXAcceptorSubkey) == 0)
1695 	    return GSS_S_DEFECTIVE_TOKEN;
1696     } else {
1697 	if (token_flags & CFXAcceptorSubkey)
1698 	    return GSS_S_DEFECTIVE_TOKEN;
1699     }
1700 
1701     if (ct_memcmp(token->Filler, "\xff\xff\xff\xff\xff", 5) != 0) {
1702 	return GSS_S_DEFECTIVE_TOKEN;
1703     }
1704 
1705     /*
1706      * Check sequence number
1707      */
1708     _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi);
1709     _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo);
1710     if (seq_number_hi) {
1711 	*minor_status = ERANGE;
1712 	return GSS_S_UNSEQ_TOKEN;
1713     }
1714 
1715     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
1716     ret = _gssapi_msg_order_check(ctx->order, seq_number_lo);
1717     if (ret != 0) {
1718 	*minor_status = 0;
1719 	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1720 	return ret;
1721     }
1722     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1723 
1724     /*
1725      * Verify checksum
1726      */
1727     ret = krb5_crypto_get_checksum_type(context, ctx->crypto,
1728 					&cksum.cksumtype);
1729     if (ret != 0) {
1730 	*minor_status = ret;
1731 	return GSS_S_FAILURE;
1732     }
1733 
1734     cksum.checksum.data = p + sizeof(*token);
1735     cksum.checksum.length = token_buffer->length - sizeof(*token);
1736 
1737     if (ctx->more_flags & LOCAL) {
1738 	usage = KRB5_KU_USAGE_ACCEPTOR_SIGN;
1739     } else {
1740 	usage = KRB5_KU_USAGE_INITIATOR_SIGN;
1741     }
1742 
1743     buf = malloc(message_buffer->length + sizeof(*token));
1744     if (buf == NULL) {
1745 	*minor_status = ENOMEM;
1746 	return GSS_S_FAILURE;
1747     }
1748     memcpy(buf, message_buffer->value, message_buffer->length);
1749     memcpy(buf + message_buffer->length, token, sizeof(*token));
1750 
1751     ret = krb5_verify_checksum(context, ctx->crypto,
1752 			       usage,
1753 			       buf,
1754 			       sizeof(*token) + message_buffer->length,
1755 			       &cksum);
1756     if (ret != 0) {
1757 	*minor_status = ret;
1758 	free(buf);
1759 	return GSS_S_BAD_MIC;
1760     }
1761 
1762     free(buf);
1763 
1764     if (qop_state != NULL) {
1765 	*qop_state = GSS_C_QOP_DEFAULT;
1766     }
1767 
1768     return GSS_S_COMPLETE;
1769 }
1770