1178825Sdfr/*
2178825Sdfr * Copyright (c) 2003, PADL Software Pty Ltd.
3178825Sdfr * All rights reserved.
4178825Sdfr *
5178825Sdfr * Redistribution and use in source and binary forms, with or without
6178825Sdfr * modification, are permitted provided that the following conditions
7178825Sdfr * are met:
8178825Sdfr *
9178825Sdfr * 1. Redistributions of source code must retain the above copyright
10178825Sdfr *    notice, this list of conditions and the following disclaimer.
11178825Sdfr *
12178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
13178825Sdfr *    notice, this list of conditions and the following disclaimer in the
14178825Sdfr *    documentation and/or other materials provided with the distribution.
15178825Sdfr *
16178825Sdfr * 3. Neither the name of PADL Software nor the names of its contributors
17178825Sdfr *    may be used to endorse or promote products derived from this software
18178825Sdfr *    without specific prior written permission.
19178825Sdfr *
20178825Sdfr * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
21178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
24178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30178825Sdfr * SUCH DAMAGE.
31178825Sdfr */
32178825Sdfr
33233294Sstas#include "gsskrb5_locl.h"
34178825Sdfr
35178825Sdfr/*
36233294Sstas * Implementation of RFC 4121
37178825Sdfr */
38178825Sdfr
39178825Sdfr#define CFXSentByAcceptor	(1 << 0)
40178825Sdfr#define CFXSealed		(1 << 1)
41178825Sdfr#define CFXAcceptorSubkey	(1 << 2)
42178825Sdfr
43178825Sdfrkrb5_error_code
44178825Sdfr_gsskrb5cfx_wrap_length_cfx(krb5_context context,
45178825Sdfr			    krb5_crypto crypto,
46178825Sdfr			    int conf_req_flag,
47233294Sstas			    int dce_style,
48178825Sdfr			    size_t input_length,
49178825Sdfr			    size_t *output_length,
50178825Sdfr			    size_t *cksumsize,
51178825Sdfr			    uint16_t *padlength)
52178825Sdfr{
53178825Sdfr    krb5_error_code ret;
54178825Sdfr    krb5_cksumtype type;
55178825Sdfr
56178825Sdfr    /* 16-byte header is always first */
57178825Sdfr    *output_length = sizeof(gss_cfx_wrap_token_desc);
58178825Sdfr    *padlength = 0;
59178825Sdfr
60178825Sdfr    ret = krb5_crypto_get_checksum_type(context, crypto, &type);
61178825Sdfr    if (ret)
62178825Sdfr	return ret;
63178825Sdfr
64178825Sdfr    ret = krb5_checksumsize(context, type, cksumsize);
65178825Sdfr    if (ret)
66178825Sdfr	return ret;
67178825Sdfr
68178825Sdfr    if (conf_req_flag) {
69178825Sdfr	size_t padsize;
70178825Sdfr
71178825Sdfr	/* Header is concatenated with data before encryption */
72178825Sdfr	input_length += sizeof(gss_cfx_wrap_token_desc);
73178825Sdfr
74233294Sstas	if (dce_style) {
75233294Sstas		ret = krb5_crypto_getblocksize(context, crypto, &padsize);
76233294Sstas	} else {
77233294Sstas		ret = krb5_crypto_getpadsize(context, crypto, &padsize);
78233294Sstas	}
79178825Sdfr	if (ret) {
80178825Sdfr	    return ret;
81178825Sdfr	}
82178825Sdfr	if (padsize > 1) {
83178825Sdfr	    /* XXX check this */
84178825Sdfr	    *padlength = padsize - (input_length % padsize);
85178825Sdfr
86178825Sdfr	    /* We add the pad ourselves (noted here for completeness only) */
87178825Sdfr	    input_length += *padlength;
88178825Sdfr	}
89178825Sdfr
90178825Sdfr	*output_length += krb5_get_wrapped_length(context,
91178825Sdfr						  crypto, input_length);
92178825Sdfr    } else {
93178825Sdfr	/* Checksum is concatenated with data */
94178825Sdfr	*output_length += input_length + *cksumsize;
95178825Sdfr    }
96178825Sdfr
97178825Sdfr    assert(*output_length > input_length);
98178825Sdfr
99178825Sdfr    return 0;
100178825Sdfr}
101178825Sdfr
102233294SstasOM_uint32
103233294Sstas_gssapi_wrap_size_cfx(OM_uint32 *minor_status,
104233294Sstas		      const gsskrb5_ctx ctx,
105233294Sstas		      krb5_context context,
106233294Sstas		      int conf_req_flag,
107233294Sstas		      gss_qop_t qop_req,
108233294Sstas		      OM_uint32 req_output_size,
109233294Sstas		      OM_uint32 *max_input_size)
110178825Sdfr{
111178825Sdfr    krb5_error_code ret;
112178825Sdfr
113233294Sstas    *max_input_size = 0;
114178825Sdfr
115178825Sdfr    /* 16-byte header is always first */
116233294Sstas    if (req_output_size < 16)
117178825Sdfr	return 0;
118233294Sstas    req_output_size -= 16;
119178825Sdfr
120178825Sdfr    if (conf_req_flag) {
121178825Sdfr	size_t wrapped_size, sz;
122178825Sdfr
123233294Sstas	wrapped_size = req_output_size + 1;
124178825Sdfr	do {
125178825Sdfr	    wrapped_size--;
126233294Sstas	    sz = krb5_get_wrapped_length(context,
127233294Sstas					 ctx->crypto, wrapped_size);
128233294Sstas	} while (wrapped_size && sz > req_output_size);
129233294Sstas	if (wrapped_size == 0)
130178825Sdfr	    return 0;
131178825Sdfr
132178825Sdfr	/* inner header */
133233294Sstas	if (wrapped_size < 16)
134178825Sdfr	    return 0;
135233294Sstas
136178825Sdfr	wrapped_size -= 16;
137178825Sdfr
138233294Sstas	*max_input_size = wrapped_size;
139178825Sdfr    } else {
140178825Sdfr	krb5_cksumtype type;
141178825Sdfr	size_t cksumsize;
142178825Sdfr
143233294Sstas	ret = krb5_crypto_get_checksum_type(context, ctx->crypto, &type);
144178825Sdfr	if (ret)
145178825Sdfr	    return ret;
146178825Sdfr
147178825Sdfr	ret = krb5_checksumsize(context, type, &cksumsize);
148178825Sdfr	if (ret)
149178825Sdfr	    return ret;
150178825Sdfr
151233294Sstas	if (req_output_size < cksumsize)
152178825Sdfr	    return 0;
153178825Sdfr
154178825Sdfr	/* Checksum is concatenated with data */
155233294Sstas	*max_input_size = req_output_size - cksumsize;
156178825Sdfr    }
157178825Sdfr
158178825Sdfr    return 0;
159178825Sdfr}
160178825Sdfr
161178825Sdfr/*
162178825Sdfr * Rotate "rrc" bytes to the front or back
163178825Sdfr */
164178825Sdfr
165178825Sdfrstatic krb5_error_code
166178825Sdfrrrc_rotate(void *data, size_t len, uint16_t rrc, krb5_boolean unrotate)
167178825Sdfr{
168178825Sdfr    u_char *tmp, buf[256];
169178825Sdfr    size_t left;
170178825Sdfr
171178825Sdfr    if (len == 0)
172178825Sdfr	return 0;
173178825Sdfr
174178825Sdfr    rrc %= len;
175178825Sdfr
176178825Sdfr    if (rrc == 0)
177178825Sdfr	return 0;
178178825Sdfr
179178825Sdfr    left = len - rrc;
180178825Sdfr
181178825Sdfr    if (rrc <= sizeof(buf)) {
182178825Sdfr	tmp = buf;
183178825Sdfr    } else {
184178825Sdfr	tmp = malloc(rrc);
185233294Sstas	if (tmp == NULL)
186178825Sdfr	    return ENOMEM;
187178825Sdfr    }
188233294Sstas
189178825Sdfr    if (unrotate) {
190178825Sdfr	memcpy(tmp, data, rrc);
191178825Sdfr	memmove(data, (u_char *)data + rrc, left);
192178825Sdfr	memcpy((u_char *)data + left, tmp, rrc);
193178825Sdfr    } else {
194178825Sdfr	memcpy(tmp, (u_char *)data + left, rrc);
195178825Sdfr	memmove((u_char *)data + rrc, data, left);
196178825Sdfr	memcpy(data, tmp, rrc);
197178825Sdfr    }
198178825Sdfr
199233294Sstas    if (rrc > sizeof(buf))
200178825Sdfr	free(tmp);
201178825Sdfr
202178825Sdfr    return 0;
203178825Sdfr}
204178825Sdfr
205233294Sstasgss_iov_buffer_desc *
206233294Sstas_gk_find_buffer(gss_iov_buffer_desc *iov, int iov_count, OM_uint32 type)
207233294Sstas{
208233294Sstas    int i;
209233294Sstas
210233294Sstas    for (i = 0; i < iov_count; i++)
211233294Sstas	if (type == GSS_IOV_BUFFER_TYPE(iov[i].type))
212233294Sstas	    return &iov[i];
213233294Sstas    return NULL;
214233294Sstas}
215233294Sstas
216233294SstasOM_uint32
217233294Sstas_gk_allocate_buffer(OM_uint32 *minor_status, gss_iov_buffer_desc *buffer, size_t size)
218233294Sstas{
219233294Sstas    if (buffer->type & GSS_IOV_BUFFER_FLAG_ALLOCATED) {
220233294Sstas	if (buffer->buffer.length == size)
221233294Sstas	    return GSS_S_COMPLETE;
222233294Sstas	free(buffer->buffer.value);
223233294Sstas    }
224233294Sstas
225233294Sstas    buffer->buffer.value = malloc(size);
226233294Sstas    buffer->buffer.length = size;
227233294Sstas    if (buffer->buffer.value == NULL) {
228233294Sstas	*minor_status = ENOMEM;
229233294Sstas	return GSS_S_FAILURE;
230233294Sstas    }
231233294Sstas    buffer->type |= GSS_IOV_BUFFER_FLAG_ALLOCATED;
232233294Sstas
233233294Sstas    return GSS_S_COMPLETE;
234233294Sstas}
235233294Sstas
236233294Sstas
237233294SstasOM_uint32
238233294Sstas_gk_verify_buffers(OM_uint32 *minor_status,
239233294Sstas		   const gsskrb5_ctx ctx,
240233294Sstas		   const gss_iov_buffer_desc *header,
241233294Sstas		   const gss_iov_buffer_desc *padding,
242233294Sstas		   const gss_iov_buffer_desc *trailer)
243233294Sstas{
244233294Sstas    if (header == NULL) {
245233294Sstas	*minor_status = EINVAL;
246233294Sstas	return GSS_S_FAILURE;
247233294Sstas    }
248233294Sstas
249233294Sstas    if (IS_DCE_STYLE(ctx)) {
250233294Sstas	/*
251233294Sstas	 * In DCE style mode we reject having a padding or trailer buffer
252233294Sstas	 */
253233294Sstas	if (padding) {
254233294Sstas	    *minor_status = EINVAL;
255233294Sstas	    return GSS_S_FAILURE;
256233294Sstas	}
257233294Sstas	if (trailer) {
258233294Sstas	    *minor_status = EINVAL;
259233294Sstas	    return GSS_S_FAILURE;
260233294Sstas	}
261233294Sstas    } else {
262233294Sstas	/*
263233294Sstas	 * In non-DCE style mode we require having a padding buffer
264233294Sstas	 */
265233294Sstas	if (padding == NULL) {
266233294Sstas	    *minor_status = EINVAL;
267233294Sstas	    return GSS_S_FAILURE;
268233294Sstas	}
269233294Sstas    }
270233294Sstas
271233294Sstas    *minor_status = 0;
272233294Sstas    return GSS_S_COMPLETE;
273233294Sstas}
274233294Sstas
275233294Sstas#if 0
276233294SstasOM_uint32
277233294Sstas_gssapi_wrap_cfx_iov(OM_uint32 *minor_status,
278233294Sstas		     gsskrb5_ctx ctx,
279233294Sstas		     krb5_context context,
280233294Sstas		     int conf_req_flag,
281233294Sstas		     int *conf_state,
282233294Sstas		     gss_iov_buffer_desc *iov,
283233294Sstas		     int iov_count)
284233294Sstas{
285233294Sstas    OM_uint32 major_status, junk;
286233294Sstas    gss_iov_buffer_desc *header, *trailer, *padding;
287233294Sstas    size_t gsshsize, k5hsize;
288233294Sstas    size_t gsstsize, k5tsize;
289233294Sstas    size_t rrc = 0, ec = 0;
290233294Sstas    int i;
291233294Sstas    gss_cfx_wrap_token token;
292233294Sstas    krb5_error_code ret;
293233294Sstas    int32_t seq_number;
294233294Sstas    unsigned usage;
295233294Sstas    krb5_crypto_iov *data = NULL;
296233294Sstas
297233294Sstas    header = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
298233294Sstas    if (header == NULL) {
299233294Sstas	*minor_status = EINVAL;
300233294Sstas	return GSS_S_FAILURE;
301233294Sstas    }
302233294Sstas
303233294Sstas    padding = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
304233294Sstas    if (padding != NULL) {
305233294Sstas	padding->buffer.length = 0;
306233294Sstas    }
307233294Sstas
308233294Sstas    trailer = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
309233294Sstas
310233294Sstas    major_status = _gk_verify_buffers(minor_status, ctx, header, padding, trailer);
311233294Sstas    if (major_status != GSS_S_COMPLETE) {
312233294Sstas	    return major_status;
313233294Sstas    }
314233294Sstas
315233294Sstas    if (conf_req_flag) {
316233294Sstas	size_t k5psize = 0;
317233294Sstas	size_t k5pbase = 0;
318233294Sstas	size_t k5bsize = 0;
319233294Sstas	size_t size = 0;
320233294Sstas
321233294Sstas	for (i = 0; i < iov_count; i++) {
322233294Sstas	    switch (GSS_IOV_BUFFER_TYPE(iov[i].type)) {
323233294Sstas	    case GSS_IOV_BUFFER_TYPE_DATA:
324233294Sstas		size += iov[i].buffer.length;
325233294Sstas		break;
326233294Sstas	    default:
327233294Sstas		break;
328233294Sstas	    }
329233294Sstas	}
330233294Sstas
331233294Sstas	size += sizeof(gss_cfx_wrap_token_desc);
332233294Sstas
333233294Sstas	*minor_status = krb5_crypto_length(context, ctx->crypto,
334233294Sstas					   KRB5_CRYPTO_TYPE_HEADER,
335233294Sstas					   &k5hsize);
336233294Sstas	if (*minor_status)
337233294Sstas	    return GSS_S_FAILURE;
338233294Sstas
339233294Sstas	*minor_status = krb5_crypto_length(context, ctx->crypto,
340233294Sstas					   KRB5_CRYPTO_TYPE_TRAILER,
341233294Sstas					   &k5tsize);
342233294Sstas	if (*minor_status)
343233294Sstas	    return GSS_S_FAILURE;
344233294Sstas
345233294Sstas	*minor_status = krb5_crypto_length(context, ctx->crypto,
346233294Sstas					   KRB5_CRYPTO_TYPE_PADDING,
347233294Sstas					   &k5pbase);
348233294Sstas	if (*minor_status)
349233294Sstas	    return GSS_S_FAILURE;
350233294Sstas
351233294Sstas	if (k5pbase > 1) {
352233294Sstas	    k5psize = k5pbase - (size % k5pbase);
353233294Sstas	} else {
354233294Sstas	    k5psize = 0;
355233294Sstas	}
356233294Sstas
357233294Sstas	if (k5psize == 0 && IS_DCE_STYLE(ctx)) {
358233294Sstas	    *minor_status = krb5_crypto_getblocksize(context, ctx->crypto,
359233294Sstas						     &k5bsize);
360233294Sstas	    if (*minor_status)
361233294Sstas		return GSS_S_FAILURE;
362233294Sstas	    ec = k5bsize;
363233294Sstas	} else {
364233294Sstas	    ec = k5psize;
365233294Sstas	}
366233294Sstas
367233294Sstas	gsshsize = sizeof(gss_cfx_wrap_token_desc) + k5hsize;
368233294Sstas	gsstsize = sizeof(gss_cfx_wrap_token_desc) + ec + k5tsize;
369233294Sstas    } else {
370233294Sstas	if (IS_DCE_STYLE(ctx)) {
371233294Sstas	    *minor_status = EINVAL;
372233294Sstas	    return GSS_S_FAILURE;
373233294Sstas	}
374233294Sstas
375233294Sstas	k5hsize = 0;
376233294Sstas	*minor_status = krb5_crypto_length(context, ctx->crypto,
377233294Sstas					   KRB5_CRYPTO_TYPE_CHECKSUM,
378233294Sstas					   &k5tsize);
379233294Sstas	if (*minor_status)
380233294Sstas	    return GSS_S_FAILURE;
381233294Sstas
382233294Sstas	gsshsize = sizeof(gss_cfx_wrap_token_desc);
383233294Sstas	gsstsize = k5tsize;
384233294Sstas    }
385233294Sstas
386233294Sstas    /*
387233294Sstas     *
388233294Sstas     */
389233294Sstas
390233294Sstas    if (trailer == NULL) {
391233294Sstas	rrc = gsstsize;
392233294Sstas	if (IS_DCE_STYLE(ctx))
393233294Sstas	    rrc -= ec;
394233294Sstas	gsshsize += gsstsize;
395233294Sstas	gsstsize = 0;
396233294Sstas    } else if (GSS_IOV_BUFFER_FLAGS(trailer->type) & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
397233294Sstas	major_status = _gk_allocate_buffer(minor_status, trailer, gsstsize);
398233294Sstas	if (major_status)
399233294Sstas	    goto failure;
400233294Sstas    } else if (trailer->buffer.length < gsstsize) {
401233294Sstas	*minor_status = KRB5_BAD_MSIZE;
402233294Sstas	major_status = GSS_S_FAILURE;
403233294Sstas	goto failure;
404233294Sstas    } else
405233294Sstas	trailer->buffer.length = gsstsize;
406233294Sstas
407233294Sstas    /*
408233294Sstas     *
409233294Sstas     */
410233294Sstas
411233294Sstas    if (GSS_IOV_BUFFER_FLAGS(header->type) & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
412233294Sstas	major_status = _gk_allocate_buffer(minor_status, header, gsshsize);
413233294Sstas	if (major_status != GSS_S_COMPLETE)
414233294Sstas	    goto failure;
415233294Sstas    } else if (header->buffer.length < gsshsize) {
416233294Sstas	*minor_status = KRB5_BAD_MSIZE;
417233294Sstas	major_status = GSS_S_FAILURE;
418233294Sstas	goto failure;
419233294Sstas    } else
420233294Sstas	header->buffer.length = gsshsize;
421233294Sstas
422233294Sstas    token = (gss_cfx_wrap_token)header->buffer.value;
423233294Sstas
424233294Sstas    token->TOK_ID[0] = 0x05;
425233294Sstas    token->TOK_ID[1] = 0x04;
426233294Sstas    token->Flags     = 0;
427233294Sstas    token->Filler    = 0xFF;
428233294Sstas
429233294Sstas    if ((ctx->more_flags & LOCAL) == 0)
430233294Sstas	token->Flags |= CFXSentByAcceptor;
431233294Sstas
432233294Sstas    if (ctx->more_flags & ACCEPTOR_SUBKEY)
433233294Sstas	token->Flags |= CFXAcceptorSubkey;
434233294Sstas
435233294Sstas    if (ctx->more_flags & LOCAL)
436233294Sstas	usage = KRB5_KU_USAGE_INITIATOR_SEAL;
437233294Sstas    else
438233294Sstas	usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
439233294Sstas
440233294Sstas    if (conf_req_flag) {
441233294Sstas	/*
442233294Sstas	 * In Wrap tokens with confidentiality, the EC field is
443233294Sstas	 * used to encode the size (in bytes) of the random filler.
444233294Sstas	 */
445233294Sstas	token->Flags |= CFXSealed;
446233294Sstas	token->EC[0] = (ec >> 8) & 0xFF;
447233294Sstas	token->EC[1] = (ec >> 0) & 0xFF;
448233294Sstas
449233294Sstas    } else {
450233294Sstas	/*
451233294Sstas	 * In Wrap tokens without confidentiality, the EC field is
452233294Sstas	 * used to encode the size (in bytes) of the trailing
453233294Sstas	 * checksum.
454233294Sstas	 *
455233294Sstas	 * This is not used in the checksum calcuation itself,
456233294Sstas	 * because the checksum length could potentially vary
457233294Sstas	 * depending on the data length.
458233294Sstas	 */
459233294Sstas	token->EC[0] = 0;
460233294Sstas	token->EC[1] = 0;
461233294Sstas    }
462233294Sstas
463233294Sstas    /*
464233294Sstas     * In Wrap tokens that provide for confidentiality, the RRC
465233294Sstas     * field in the header contains the hex value 00 00 before
466233294Sstas     * encryption.
467233294Sstas     *
468233294Sstas     * In Wrap tokens that do not provide for confidentiality,
469233294Sstas     * both the EC and RRC fields in the appended checksum
470233294Sstas     * contain the hex value 00 00 for the purpose of calculating
471233294Sstas     * the checksum.
472233294Sstas     */
473233294Sstas    token->RRC[0] = 0;
474233294Sstas    token->RRC[1] = 0;
475233294Sstas
476233294Sstas    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
477233294Sstas    krb5_auth_con_getlocalseqnumber(context,
478233294Sstas				    ctx->auth_context,
479233294Sstas				    &seq_number);
480233294Sstas    _gsskrb5_encode_be_om_uint32(0,          &token->SND_SEQ[0]);
481233294Sstas    _gsskrb5_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]);
482233294Sstas    krb5_auth_con_setlocalseqnumber(context,
483233294Sstas				    ctx->auth_context,
484233294Sstas				    ++seq_number);
485233294Sstas    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
486233294Sstas
487233294Sstas    data = calloc(iov_count + 3, sizeof(data[0]));
488233294Sstas    if (data == NULL) {
489233294Sstas	*minor_status = ENOMEM;
490233294Sstas	major_status = GSS_S_FAILURE;
491233294Sstas	goto failure;
492233294Sstas    }
493233294Sstas
494233294Sstas    if (conf_req_flag) {
495233294Sstas	/*
496233294Sstas	  plain packet:
497233294Sstas
498233294Sstas	  {"header" | encrypt(plaintext-data | ec-padding | E"header")}
499233294Sstas
500233294Sstas	  Expanded, this is with with RRC = 0:
501233294Sstas
502233294Sstas	  {"header" | krb5-header | plaintext-data | ec-padding | E"header" | krb5-trailer }
503233294Sstas
504233294Sstas	  In DCE-RPC mode == no trailer: RRC = gss "trailer" == length(ec-padding | E"header" | krb5-trailer)
505233294Sstas
506233294Sstas	  {"header" | ec-padding | E"header" | krb5-trailer | krb5-header | plaintext-data  }
507233294Sstas	 */
508233294Sstas
509233294Sstas	i = 0;
510233294Sstas	data[i].flags = KRB5_CRYPTO_TYPE_HEADER;
511233294Sstas	data[i].data.data = ((uint8_t *)header->buffer.value) + header->buffer.length - k5hsize;
512233294Sstas	data[i].data.length = k5hsize;
513233294Sstas
514233294Sstas	for (i = 1; i < iov_count + 1; i++) {
515233294Sstas	    switch (GSS_IOV_BUFFER_TYPE(iov[i - 1].type)) {
516233294Sstas	    case GSS_IOV_BUFFER_TYPE_DATA:
517233294Sstas		data[i].flags = KRB5_CRYPTO_TYPE_DATA;
518233294Sstas		break;
519233294Sstas	    case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
520233294Sstas		data[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
521233294Sstas		break;
522233294Sstas	    default:
523233294Sstas		data[i].flags = KRB5_CRYPTO_TYPE_EMPTY;
524233294Sstas		break;
525233294Sstas	    }
526233294Sstas	    data[i].data.length = iov[i - 1].buffer.length;
527233294Sstas	    data[i].data.data = iov[i - 1].buffer.value;
528233294Sstas	}
529233294Sstas
530233294Sstas	/*
531233294Sstas	 * Any necessary padding is added here to ensure that the
532233294Sstas	 * encrypted token header is always at the end of the
533233294Sstas	 * ciphertext.
534233294Sstas	 */
535233294Sstas
536233294Sstas	/* encrypted CFX header in trailer (or after the header if in
537233294Sstas	   DCE mode). Copy in header into E"header"
538233294Sstas	*/
539233294Sstas	data[i].flags = KRB5_CRYPTO_TYPE_DATA;
540233294Sstas	if (trailer)
541233294Sstas	    data[i].data.data = trailer->buffer.value;
542233294Sstas	else
543233294Sstas	    data[i].data.data = ((uint8_t *)header->buffer.value) + sizeof(*token);
544233294Sstas
545233294Sstas	data[i].data.length = ec + sizeof(*token);
546233294Sstas	memset(data[i].data.data, 0xFF, ec);
547233294Sstas	memcpy(((uint8_t *)data[i].data.data) + ec, token, sizeof(*token));
548233294Sstas	i++;
549233294Sstas
550233294Sstas	/* Kerberos trailer comes after the gss trailer */
551233294Sstas	data[i].flags = KRB5_CRYPTO_TYPE_TRAILER;
552233294Sstas	data[i].data.data = ((uint8_t *)data[i-1].data.data) + ec + sizeof(*token);
553233294Sstas	data[i].data.length = k5tsize;
554233294Sstas	i++;
555233294Sstas
556233294Sstas	ret = krb5_encrypt_iov_ivec(context, ctx->crypto, usage, data, i, NULL);
557233294Sstas	if (ret != 0) {
558233294Sstas	    *minor_status = ret;
559233294Sstas	    major_status = GSS_S_FAILURE;
560233294Sstas	    goto failure;
561233294Sstas	}
562233294Sstas
563233294Sstas	if (rrc) {
564233294Sstas	    token->RRC[0] = (rrc >> 8) & 0xFF;
565233294Sstas	    token->RRC[1] = (rrc >> 0) & 0xFF;
566233294Sstas	}
567233294Sstas
568233294Sstas    } else {
569233294Sstas	/*
570233294Sstas	  plain packet:
571233294Sstas
572233294Sstas	  {data | "header" | gss-trailer (krb5 checksum)
573233294Sstas
574233294Sstas	  don't do RRC != 0
575233294Sstas
576233294Sstas	 */
577233294Sstas
578233294Sstas	for (i = 0; i < iov_count; i++) {
579233294Sstas	    switch (GSS_IOV_BUFFER_TYPE(iov[i].type)) {
580233294Sstas	    case GSS_IOV_BUFFER_TYPE_DATA:
581233294Sstas		data[i].flags = KRB5_CRYPTO_TYPE_DATA;
582233294Sstas		break;
583233294Sstas	    case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
584233294Sstas		data[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
585233294Sstas		break;
586233294Sstas	    default:
587233294Sstas		data[i].flags = KRB5_CRYPTO_TYPE_EMPTY;
588233294Sstas		break;
589233294Sstas	    }
590233294Sstas	    data[i].data.length = iov[i].buffer.length;
591233294Sstas	    data[i].data.data = iov[i].buffer.value;
592233294Sstas	}
593233294Sstas
594233294Sstas	data[i].flags = KRB5_CRYPTO_TYPE_DATA;
595233294Sstas	data[i].data.data = header->buffer.value;
596233294Sstas	data[i].data.length = sizeof(gss_cfx_wrap_token_desc);
597233294Sstas	i++;
598233294Sstas
599233294Sstas	data[i].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
600233294Sstas	if (trailer) {
601233294Sstas		data[i].data.data = trailer->buffer.value;
602233294Sstas	} else {
603233294Sstas		data[i].data.data = (uint8_t *)header->buffer.value +
604233294Sstas				     sizeof(gss_cfx_wrap_token_desc);
605233294Sstas	}
606233294Sstas	data[i].data.length = k5tsize;
607233294Sstas	i++;
608233294Sstas
609233294Sstas	ret = krb5_create_checksum_iov(context, ctx->crypto, usage, data, i, NULL);
610233294Sstas	if (ret) {
611233294Sstas	    *minor_status = ret;
612233294Sstas	    major_status = GSS_S_FAILURE;
613233294Sstas	    goto failure;
614233294Sstas	}
615233294Sstas
616233294Sstas	if (rrc) {
617233294Sstas	    token->RRC[0] = (rrc >> 8) & 0xFF;
618233294Sstas	    token->RRC[1] = (rrc >> 0) & 0xFF;
619233294Sstas	}
620233294Sstas
621233294Sstas	token->EC[0] =  (k5tsize >> 8) & 0xFF;
622233294Sstas	token->EC[1] =  (k5tsize >> 0) & 0xFF;
623233294Sstas    }
624233294Sstas
625233294Sstas    if (conf_state != NULL)
626233294Sstas	*conf_state = conf_req_flag;
627233294Sstas
628233294Sstas    free(data);
629233294Sstas
630233294Sstas    *minor_status = 0;
631233294Sstas    return GSS_S_COMPLETE;
632233294Sstas
633233294Sstas failure:
634233294Sstas    if (data)
635233294Sstas	free(data);
636233294Sstas
637233294Sstas    gss_release_iov_buffer(&junk, iov, iov_count);
638233294Sstas
639233294Sstas    return major_status;
640233294Sstas}
641233294Sstas#endif
642233294Sstas
643233294Sstas/* This is slowpath */
644233294Sstasstatic OM_uint32
645233294Sstasunrotate_iov(OM_uint32 *minor_status, size_t rrc, gss_iov_buffer_desc *iov, int iov_count)
646233294Sstas{
647233294Sstas    uint8_t *p, *q;
648233294Sstas    size_t len = 0, skip;
649233294Sstas    int i;
650233294Sstas
651233294Sstas    for (i = 0; i < iov_count; i++)
652233294Sstas	if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA ||
653233294Sstas	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_PADDING ||
654233294Sstas	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_TRAILER)
655233294Sstas	    len += iov[i].buffer.length;
656233294Sstas
657233294Sstas    p = malloc(len);
658233294Sstas    if (p == NULL) {
659233294Sstas	*minor_status = ENOMEM;
660233294Sstas	return GSS_S_FAILURE;
661233294Sstas    }
662233294Sstas    q = p;
663233294Sstas
664233294Sstas    /* copy up */
665233294Sstas
666233294Sstas    for (i = 0; i < iov_count; i++) {
667233294Sstas	if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA ||
668233294Sstas	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_PADDING ||
669233294Sstas	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_TRAILER)
670233294Sstas	{
671233294Sstas	    memcpy(q, iov[i].buffer.value, iov[i].buffer.length);
672233294Sstas	    q += iov[i].buffer.length;
673233294Sstas	}
674233294Sstas    }
675233294Sstas    assert((size_t)(q - p) == len);
676233294Sstas
677233294Sstas    /* unrotate first part */
678233294Sstas    q = p + rrc;
679233294Sstas    skip = rrc;
680233294Sstas    for (i = 0; i < iov_count; i++) {
681233294Sstas	if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA ||
682233294Sstas	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_PADDING ||
683233294Sstas	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_TRAILER)
684233294Sstas	{
685233294Sstas	    if (iov[i].buffer.length <= skip) {
686233294Sstas		skip -= iov[i].buffer.length;
687233294Sstas	    } else {
688233294Sstas		memcpy(((uint8_t *)iov[i].buffer.value) + skip, q, iov[i].buffer.length - skip);
689233294Sstas		q += iov[i].buffer.length - skip;
690233294Sstas		skip = 0;
691233294Sstas	    }
692233294Sstas	}
693233294Sstas    }
694233294Sstas    /* copy trailer */
695233294Sstas    q = p;
696233294Sstas    skip = rrc;
697233294Sstas    for (i = 0; i < iov_count; i++) {
698233294Sstas	if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA ||
699233294Sstas	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_PADDING ||
700233294Sstas	    GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_TRAILER)
701233294Sstas	{
702233294Sstas	    memcpy(q, iov[i].buffer.value, min(iov[i].buffer.length, skip));
703233294Sstas	    if (iov[i].buffer.length > skip)
704233294Sstas		break;
705233294Sstas	    skip -= iov[i].buffer.length;
706233294Sstas	    q += iov[i].buffer.length;
707233294Sstas	}
708233294Sstas    }
709233294Sstas    return GSS_S_COMPLETE;
710233294Sstas}
711233294Sstas
712233294Sstas#if 0
713233294Sstas
714233294SstasOM_uint32
715233294Sstas_gssapi_unwrap_cfx_iov(OM_uint32 *minor_status,
716233294Sstas		       gsskrb5_ctx ctx,
717233294Sstas		       krb5_context context,
718233294Sstas		       int *conf_state,
719233294Sstas		       gss_qop_t *qop_state,
720233294Sstas		       gss_iov_buffer_desc *iov,
721233294Sstas		       int iov_count)
722233294Sstas{
723233294Sstas    OM_uint32 seq_number_lo, seq_number_hi, major_status, junk;
724233294Sstas    gss_iov_buffer_desc *header, *trailer, *padding;
725233294Sstas    gss_cfx_wrap_token token, ttoken;
726233294Sstas    u_char token_flags;
727233294Sstas    krb5_error_code ret;
728233294Sstas    unsigned usage;
729233294Sstas    uint16_t ec, rrc;
730233294Sstas    krb5_crypto_iov *data = NULL;
731233294Sstas    int i, j;
732233294Sstas
733233294Sstas    *minor_status = 0;
734233294Sstas
735233294Sstas    header = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
736233294Sstas    if (header == NULL) {
737233294Sstas	*minor_status = EINVAL;
738233294Sstas	return GSS_S_FAILURE;
739233294Sstas    }
740233294Sstas
741233294Sstas    if (header->buffer.length < sizeof(*token)) /* we check exact below */
742233294Sstas	return GSS_S_DEFECTIVE_TOKEN;
743233294Sstas
744233294Sstas    padding = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
745233294Sstas    if (padding != NULL && padding->buffer.length != 0) {
746233294Sstas	*minor_status = EINVAL;
747233294Sstas	return GSS_S_FAILURE;
748233294Sstas    }
749233294Sstas
750233294Sstas    trailer = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
751233294Sstas
752233294Sstas    major_status = _gk_verify_buffers(minor_status, ctx, header, padding, trailer);
753233294Sstas    if (major_status != GSS_S_COMPLETE) {
754233294Sstas	    return major_status;
755233294Sstas    }
756233294Sstas
757233294Sstas    token = (gss_cfx_wrap_token)header->buffer.value;
758233294Sstas
759233294Sstas    if (token->TOK_ID[0] != 0x05 || token->TOK_ID[1] != 0x04)
760233294Sstas	return GSS_S_DEFECTIVE_TOKEN;
761233294Sstas
762233294Sstas    /* Ignore unknown flags */
763233294Sstas    token_flags = token->Flags &
764233294Sstas	(CFXSentByAcceptor | CFXSealed | CFXAcceptorSubkey);
765233294Sstas
766233294Sstas    if (token_flags & CFXSentByAcceptor) {
767233294Sstas	if ((ctx->more_flags & LOCAL) == 0)
768233294Sstas	    return GSS_S_DEFECTIVE_TOKEN;
769233294Sstas    }
770233294Sstas
771233294Sstas    if (ctx->more_flags & ACCEPTOR_SUBKEY) {
772233294Sstas	if ((token_flags & CFXAcceptorSubkey) == 0)
773233294Sstas	    return GSS_S_DEFECTIVE_TOKEN;
774233294Sstas    } else {
775233294Sstas	if (token_flags & CFXAcceptorSubkey)
776233294Sstas	    return GSS_S_DEFECTIVE_TOKEN;
777233294Sstas    }
778233294Sstas
779233294Sstas    if (token->Filler != 0xFF)
780233294Sstas	return GSS_S_DEFECTIVE_TOKEN;
781233294Sstas
782233294Sstas    if (conf_state != NULL)
783233294Sstas	*conf_state = (token_flags & CFXSealed) ? 1 : 0;
784233294Sstas
785233294Sstas    ec  = (token->EC[0]  << 8) | token->EC[1];
786233294Sstas    rrc = (token->RRC[0] << 8) | token->RRC[1];
787233294Sstas
788233294Sstas    /*
789233294Sstas     * Check sequence number
790233294Sstas     */
791233294Sstas    _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi);
792233294Sstas    _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo);
793233294Sstas    if (seq_number_hi) {
794233294Sstas	/* no support for 64-bit sequence numbers */
795233294Sstas	*minor_status = ERANGE;
796233294Sstas	return GSS_S_UNSEQ_TOKEN;
797233294Sstas    }
798233294Sstas
799233294Sstas    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
800233294Sstas    ret = _gssapi_msg_order_check(ctx->order, seq_number_lo);
801233294Sstas    if (ret != 0) {
802233294Sstas	*minor_status = 0;
803233294Sstas	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
804233294Sstas	return ret;
805233294Sstas    }
806233294Sstas    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
807233294Sstas
808233294Sstas    /*
809233294Sstas     * Decrypt and/or verify checksum
810233294Sstas     */
811233294Sstas
812233294Sstas    if (ctx->more_flags & LOCAL) {
813233294Sstas	usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
814233294Sstas    } else {
815233294Sstas	usage = KRB5_KU_USAGE_INITIATOR_SEAL;
816233294Sstas    }
817233294Sstas
818233294Sstas    data = calloc(iov_count + 3, sizeof(data[0]));
819233294Sstas    if (data == NULL) {
820233294Sstas	*minor_status = ENOMEM;
821233294Sstas	major_status = GSS_S_FAILURE;
822233294Sstas	goto failure;
823233294Sstas    }
824233294Sstas
825233294Sstas    if (token_flags & CFXSealed) {
826233294Sstas	size_t k5tsize, k5hsize;
827233294Sstas
828233294Sstas	krb5_crypto_length(context, ctx->crypto, KRB5_CRYPTO_TYPE_HEADER, &k5hsize);
829233294Sstas	krb5_crypto_length(context, ctx->crypto, KRB5_CRYPTO_TYPE_TRAILER, &k5tsize);
830233294Sstas
831233294Sstas	/* Rotate by RRC; bogus to do this in-place XXX */
832233294Sstas	/* Check RRC */
833233294Sstas
834233294Sstas	if (trailer == NULL) {
835233294Sstas	    size_t gsstsize = k5tsize + sizeof(*token);
836233294Sstas	    size_t gsshsize = k5hsize + sizeof(*token);
837233294Sstas
838233294Sstas	    if (rrc != gsstsize) {
839233294Sstas		major_status = GSS_S_DEFECTIVE_TOKEN;
840233294Sstas		goto failure;
841233294Sstas	    }
842233294Sstas
843233294Sstas	    if (IS_DCE_STYLE(ctx))
844233294Sstas		gsstsize += ec;
845233294Sstas
846233294Sstas	    gsshsize += gsstsize;
847233294Sstas
848233294Sstas	    if (header->buffer.length != gsshsize) {
849233294Sstas		major_status = GSS_S_DEFECTIVE_TOKEN;
850233294Sstas		goto failure;
851233294Sstas	    }
852233294Sstas	} else if (trailer->buffer.length != sizeof(*token) + k5tsize) {
853233294Sstas	    major_status = GSS_S_DEFECTIVE_TOKEN;
854233294Sstas	    goto failure;
855233294Sstas	} else if (header->buffer.length != sizeof(*token) + k5hsize) {
856233294Sstas	    major_status = GSS_S_DEFECTIVE_TOKEN;
857233294Sstas	    goto failure;
858233294Sstas	} else if (rrc != 0) {
859233294Sstas	    /* go though slowpath */
860233294Sstas	    major_status = unrotate_iov(minor_status, rrc, iov, iov_count);
861233294Sstas	    if (major_status)
862233294Sstas		goto failure;
863233294Sstas	}
864233294Sstas
865233294Sstas	i = 0;
866233294Sstas	data[i].flags = KRB5_CRYPTO_TYPE_HEADER;
867233294Sstas	data[i].data.data = ((uint8_t *)header->buffer.value) + header->buffer.length - k5hsize;
868233294Sstas	data[i].data.length = k5hsize;
869233294Sstas	i++;
870233294Sstas
871233294Sstas	for (j = 0; j < iov_count; i++, j++) {
872233294Sstas	    switch (GSS_IOV_BUFFER_TYPE(iov[j].type)) {
873233294Sstas	    case GSS_IOV_BUFFER_TYPE_DATA:
874233294Sstas		data[i].flags = KRB5_CRYPTO_TYPE_DATA;
875233294Sstas		break;
876233294Sstas	    case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
877233294Sstas		data[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
878233294Sstas		break;
879233294Sstas	    default:
880233294Sstas		data[i].flags = KRB5_CRYPTO_TYPE_EMPTY;
881233294Sstas		break;
882233294Sstas	    }
883233294Sstas	    data[i].data.length = iov[j].buffer.length;
884233294Sstas	    data[i].data.data = iov[j].buffer.value;
885233294Sstas	}
886233294Sstas
887233294Sstas	/* encrypted CFX header in trailer (or after the header if in
888233294Sstas	   DCE mode). Copy in header into E"header"
889233294Sstas	*/
890233294Sstas	data[i].flags = KRB5_CRYPTO_TYPE_DATA;
891233294Sstas	if (trailer) {
892233294Sstas	    data[i].data.data = trailer->buffer.value;
893233294Sstas	} else {
894233294Sstas	    data[i].data.data = ((uint8_t *)header->buffer.value) +
895233294Sstas		header->buffer.length - k5hsize - k5tsize - ec- sizeof(*token);
896233294Sstas	}
897233294Sstas
898233294Sstas	data[i].data.length = ec + sizeof(*token);
899233294Sstas	ttoken = (gss_cfx_wrap_token)(((uint8_t *)data[i].data.data) + ec);
900233294Sstas	i++;
901233294Sstas
902233294Sstas	/* Kerberos trailer comes after the gss trailer */
903233294Sstas	data[i].flags = KRB5_CRYPTO_TYPE_TRAILER;
904233294Sstas	data[i].data.data = ((uint8_t *)data[i-1].data.data) + ec + sizeof(*token);
905233294Sstas	data[i].data.length = k5tsize;
906233294Sstas	i++;
907233294Sstas
908233294Sstas	ret = krb5_decrypt_iov_ivec(context, ctx->crypto, usage, data, i, NULL);
909233294Sstas	if (ret != 0) {
910233294Sstas	    *minor_status = ret;
911233294Sstas	    major_status = GSS_S_FAILURE;
912233294Sstas	    goto failure;
913233294Sstas	}
914233294Sstas
915233294Sstas	ttoken->RRC[0] = token->RRC[0];
916233294Sstas	ttoken->RRC[1] = token->RRC[1];
917233294Sstas
918233294Sstas	/* Check the integrity of the header */
919233294Sstas	if (ct_memcmp(ttoken, token, sizeof(*token)) != 0) {
920233294Sstas	    major_status = GSS_S_BAD_MIC;
921233294Sstas	    goto failure;
922233294Sstas	}
923233294Sstas    } else {
924233294Sstas	size_t gsstsize = ec;
925233294Sstas	size_t gsshsize = sizeof(*token);
926233294Sstas
927233294Sstas	if (trailer == NULL) {
928233294Sstas	    /* Check RRC */
929233294Sstas	    if (rrc != gsstsize) {
930233294Sstas	       *minor_status = EINVAL;
931233294Sstas	       major_status = GSS_S_FAILURE;
932233294Sstas	       goto failure;
933233294Sstas	    }
934233294Sstas
935233294Sstas	    gsshsize += gsstsize;
936233294Sstas	    gsstsize = 0;
937233294Sstas	} else if (trailer->buffer.length != gsstsize) {
938233294Sstas	    major_status = GSS_S_DEFECTIVE_TOKEN;
939233294Sstas	    goto failure;
940233294Sstas	} else if (rrc != 0) {
941233294Sstas	    /* Check RRC */
942233294Sstas	    *minor_status = EINVAL;
943233294Sstas	    major_status = GSS_S_FAILURE;
944233294Sstas	    goto failure;
945233294Sstas	}
946233294Sstas
947233294Sstas	if (header->buffer.length != gsshsize) {
948233294Sstas	    major_status = GSS_S_DEFECTIVE_TOKEN;
949233294Sstas	    goto failure;
950233294Sstas	}
951233294Sstas
952233294Sstas	for (i = 0; i < iov_count; i++) {
953233294Sstas	    switch (GSS_IOV_BUFFER_TYPE(iov[i].type)) {
954233294Sstas	    case GSS_IOV_BUFFER_TYPE_DATA:
955233294Sstas		data[i].flags = KRB5_CRYPTO_TYPE_DATA;
956233294Sstas		break;
957233294Sstas	    case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
958233294Sstas		data[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
959233294Sstas		break;
960233294Sstas	    default:
961233294Sstas		data[i].flags = KRB5_CRYPTO_TYPE_EMPTY;
962233294Sstas		break;
963233294Sstas	    }
964233294Sstas	    data[i].data.length = iov[i].buffer.length;
965233294Sstas	    data[i].data.data = iov[i].buffer.value;
966233294Sstas	}
967233294Sstas
968233294Sstas	data[i].flags = KRB5_CRYPTO_TYPE_DATA;
969233294Sstas	data[i].data.data = header->buffer.value;
970233294Sstas	data[i].data.length = sizeof(*token);
971233294Sstas	i++;
972233294Sstas
973233294Sstas	data[i].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
974233294Sstas	if (trailer) {
975233294Sstas		data[i].data.data = trailer->buffer.value;
976233294Sstas	} else {
977233294Sstas		data[i].data.data = (uint8_t *)header->buffer.value +
978233294Sstas				     sizeof(*token);
979233294Sstas	}
980233294Sstas	data[i].data.length = ec;
981233294Sstas	i++;
982233294Sstas
983233294Sstas	token = (gss_cfx_wrap_token)header->buffer.value;
984233294Sstas	token->EC[0]  = 0;
985233294Sstas	token->EC[1]  = 0;
986233294Sstas	token->RRC[0] = 0;
987233294Sstas	token->RRC[1] = 0;
988233294Sstas
989233294Sstas	ret = krb5_verify_checksum_iov(context, ctx->crypto, usage, data, i, NULL);
990233294Sstas	if (ret) {
991233294Sstas	    *minor_status = ret;
992233294Sstas	    major_status = GSS_S_FAILURE;
993233294Sstas	    goto failure;
994233294Sstas	}
995233294Sstas    }
996233294Sstas
997233294Sstas    if (qop_state != NULL) {
998233294Sstas	*qop_state = GSS_C_QOP_DEFAULT;
999233294Sstas    }
1000233294Sstas
1001233294Sstas    free(data);
1002233294Sstas
1003233294Sstas    *minor_status = 0;
1004233294Sstas    return GSS_S_COMPLETE;
1005233294Sstas
1006233294Sstas failure:
1007233294Sstas    if (data)
1008233294Sstas	free(data);
1009233294Sstas
1010233294Sstas    gss_release_iov_buffer(&junk, iov, iov_count);
1011233294Sstas
1012233294Sstas    return major_status;
1013233294Sstas}
1014233294Sstas#endif
1015233294Sstas
1016233294SstasOM_uint32
1017233294Sstas_gssapi_wrap_iov_length_cfx(OM_uint32 *minor_status,
1018233294Sstas			    gsskrb5_ctx ctx,
1019233294Sstas			    krb5_context context,
1020233294Sstas			    int conf_req_flag,
1021233294Sstas			    gss_qop_t qop_req,
1022233294Sstas			    int *conf_state,
1023233294Sstas			    gss_iov_buffer_desc *iov,
1024233294Sstas			    int iov_count)
1025233294Sstas{
1026233294Sstas    OM_uint32 major_status;
1027233294Sstas    size_t size;
1028233294Sstas    int i;
1029233294Sstas    gss_iov_buffer_desc *header = NULL;
1030233294Sstas    gss_iov_buffer_desc *padding = NULL;
1031233294Sstas    gss_iov_buffer_desc *trailer = NULL;
1032233294Sstas    size_t gsshsize = 0;
1033233294Sstas    size_t gsstsize = 0;
1034233294Sstas    size_t k5hsize = 0;
1035233294Sstas    size_t k5tsize = 0;
1036233294Sstas
1037233294Sstas    GSSAPI_KRB5_INIT (&context);
1038233294Sstas    *minor_status = 0;
1039233294Sstas
1040233294Sstas    for (size = 0, i = 0; i < iov_count; i++) {
1041233294Sstas	switch(GSS_IOV_BUFFER_TYPE(iov[i].type)) {
1042233294Sstas	case GSS_IOV_BUFFER_TYPE_EMPTY:
1043233294Sstas	    break;
1044233294Sstas	case GSS_IOV_BUFFER_TYPE_DATA:
1045233294Sstas	    size += iov[i].buffer.length;
1046233294Sstas	    break;
1047233294Sstas	case GSS_IOV_BUFFER_TYPE_HEADER:
1048233294Sstas	    if (header != NULL) {
1049233294Sstas		*minor_status = 0;
1050233294Sstas		return GSS_S_FAILURE;
1051233294Sstas	    }
1052233294Sstas	    header = &iov[i];
1053233294Sstas	    break;
1054233294Sstas	case GSS_IOV_BUFFER_TYPE_TRAILER:
1055233294Sstas	    if (trailer != NULL) {
1056233294Sstas		*minor_status = 0;
1057233294Sstas		return GSS_S_FAILURE;
1058233294Sstas	    }
1059233294Sstas	    trailer = &iov[i];
1060233294Sstas	    break;
1061233294Sstas	case GSS_IOV_BUFFER_TYPE_PADDING:
1062233294Sstas	    if (padding != NULL) {
1063233294Sstas		*minor_status = 0;
1064233294Sstas		return GSS_S_FAILURE;
1065233294Sstas	    }
1066233294Sstas	    padding = &iov[i];
1067233294Sstas	    break;
1068233294Sstas	case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
1069233294Sstas	    break;
1070233294Sstas	default:
1071233294Sstas	    *minor_status = EINVAL;
1072233294Sstas	    return GSS_S_FAILURE;
1073233294Sstas	}
1074233294Sstas    }
1075233294Sstas
1076233294Sstas    major_status = _gk_verify_buffers(minor_status, ctx, header, padding, trailer);
1077233294Sstas    if (major_status != GSS_S_COMPLETE) {
1078233294Sstas	    return major_status;
1079233294Sstas    }
1080233294Sstas
1081233294Sstas    if (conf_req_flag) {
1082233294Sstas	size_t k5psize = 0;
1083233294Sstas	size_t k5pbase = 0;
1084233294Sstas	size_t k5bsize = 0;
1085233294Sstas	size_t ec = 0;
1086233294Sstas
1087233294Sstas	size += sizeof(gss_cfx_wrap_token_desc);
1088233294Sstas
1089233294Sstas	*minor_status = krb5_crypto_length(context, ctx->crypto,
1090233294Sstas					   KRB5_CRYPTO_TYPE_HEADER,
1091233294Sstas					   &k5hsize);
1092233294Sstas	if (*minor_status)
1093233294Sstas	    return GSS_S_FAILURE;
1094233294Sstas
1095233294Sstas	*minor_status = krb5_crypto_length(context, ctx->crypto,
1096233294Sstas					   KRB5_CRYPTO_TYPE_TRAILER,
1097233294Sstas					   &k5tsize);
1098233294Sstas	if (*minor_status)
1099233294Sstas	    return GSS_S_FAILURE;
1100233294Sstas
1101233294Sstas	*minor_status = krb5_crypto_length(context, ctx->crypto,
1102233294Sstas					   KRB5_CRYPTO_TYPE_PADDING,
1103233294Sstas					   &k5pbase);
1104233294Sstas	if (*minor_status)
1105233294Sstas	    return GSS_S_FAILURE;
1106233294Sstas
1107233294Sstas	if (k5pbase > 1) {
1108233294Sstas	    k5psize = k5pbase - (size % k5pbase);
1109233294Sstas	} else {
1110233294Sstas	    k5psize = 0;
1111233294Sstas	}
1112233294Sstas
1113233294Sstas	if (k5psize == 0 && IS_DCE_STYLE(ctx)) {
1114233294Sstas	    *minor_status = krb5_crypto_getblocksize(context, ctx->crypto,
1115233294Sstas						     &k5bsize);
1116233294Sstas	    if (*minor_status)
1117233294Sstas		return GSS_S_FAILURE;
1118233294Sstas
1119233294Sstas	    ec = k5bsize;
1120233294Sstas	} else {
1121233294Sstas	    ec = k5psize;
1122233294Sstas	}
1123233294Sstas
1124233294Sstas	gsshsize = sizeof(gss_cfx_wrap_token_desc) + k5hsize;
1125233294Sstas	gsstsize = sizeof(gss_cfx_wrap_token_desc) + ec + k5tsize;
1126233294Sstas    } else {
1127233294Sstas	*minor_status = krb5_crypto_length(context, ctx->crypto,
1128233294Sstas					   KRB5_CRYPTO_TYPE_CHECKSUM,
1129233294Sstas					   &k5tsize);
1130233294Sstas	if (*minor_status)
1131233294Sstas	    return GSS_S_FAILURE;
1132233294Sstas
1133233294Sstas	gsshsize = sizeof(gss_cfx_wrap_token_desc);
1134233294Sstas	gsstsize = k5tsize;
1135233294Sstas    }
1136233294Sstas
1137233294Sstas    if (trailer != NULL) {
1138233294Sstas	trailer->buffer.length = gsstsize;
1139233294Sstas    } else {
1140233294Sstas	gsshsize += gsstsize;
1141233294Sstas    }
1142233294Sstas
1143233294Sstas    header->buffer.length = gsshsize;
1144233294Sstas
1145233294Sstas    if (padding) {
1146233294Sstas	/* padding is done via EC and is contained in the header or trailer */
1147233294Sstas	padding->buffer.length = 0;
1148233294Sstas    }
1149233294Sstas
1150233294Sstas    if (conf_state) {
1151233294Sstas	*conf_state = conf_req_flag;
1152233294Sstas    }
1153233294Sstas
1154233294Sstas    return GSS_S_COMPLETE;
1155233294Sstas}
1156233294Sstas
1157233294Sstas
1158233294Sstas
1159233294Sstas
1160178825SdfrOM_uint32 _gssapi_wrap_cfx(OM_uint32 *minor_status,
1161233294Sstas			   const gsskrb5_ctx ctx,
1162178825Sdfr			   krb5_context context,
1163178825Sdfr			   int conf_req_flag,
1164178825Sdfr			   const gss_buffer_t input_message_buffer,
1165178825Sdfr			   int *conf_state,
1166233294Sstas			   gss_buffer_t output_message_buffer)
1167178825Sdfr{
1168178825Sdfr    gss_cfx_wrap_token token;
1169178825Sdfr    krb5_error_code ret;
1170178825Sdfr    unsigned usage;
1171178825Sdfr    krb5_data cipher;
1172178825Sdfr    size_t wrapped_len, cksumsize;
1173178825Sdfr    uint16_t padlength, rrc = 0;
1174178825Sdfr    int32_t seq_number;
1175178825Sdfr    u_char *p;
1176178825Sdfr
1177178825Sdfr    ret = _gsskrb5cfx_wrap_length_cfx(context,
1178233294Sstas				      ctx->crypto, conf_req_flag,
1179233294Sstas				      IS_DCE_STYLE(ctx),
1180178825Sdfr				      input_message_buffer->length,
1181178825Sdfr				      &wrapped_len, &cksumsize, &padlength);
1182178825Sdfr    if (ret != 0) {
1183178825Sdfr	*minor_status = ret;
1184178825Sdfr	return GSS_S_FAILURE;
1185178825Sdfr    }
1186178825Sdfr
1187178825Sdfr    /* Always rotate encrypted token (if any) and checksum to header */
1188178825Sdfr    rrc = (conf_req_flag ? sizeof(*token) : 0) + (uint16_t)cksumsize;
1189178825Sdfr
1190178825Sdfr    output_message_buffer->length = wrapped_len;
1191178825Sdfr    output_message_buffer->value = malloc(output_message_buffer->length);
1192178825Sdfr    if (output_message_buffer->value == NULL) {
1193178825Sdfr	*minor_status = ENOMEM;
1194178825Sdfr	return GSS_S_FAILURE;
1195178825Sdfr    }
1196178825Sdfr
1197178825Sdfr    p = output_message_buffer->value;
1198178825Sdfr    token = (gss_cfx_wrap_token)p;
1199178825Sdfr    token->TOK_ID[0] = 0x05;
1200178825Sdfr    token->TOK_ID[1] = 0x04;
1201178825Sdfr    token->Flags     = 0;
1202178825Sdfr    token->Filler    = 0xFF;
1203233294Sstas    if ((ctx->more_flags & LOCAL) == 0)
1204178825Sdfr	token->Flags |= CFXSentByAcceptor;
1205233294Sstas    if (ctx->more_flags & ACCEPTOR_SUBKEY)
1206178825Sdfr	token->Flags |= CFXAcceptorSubkey;
1207178825Sdfr    if (conf_req_flag) {
1208178825Sdfr	/*
1209178825Sdfr	 * In Wrap tokens with confidentiality, the EC field is
1210178825Sdfr	 * used to encode the size (in bytes) of the random filler.
1211178825Sdfr	 */
1212178825Sdfr	token->Flags |= CFXSealed;
1213178825Sdfr	token->EC[0] = (padlength >> 8) & 0xFF;
1214178825Sdfr	token->EC[1] = (padlength >> 0) & 0xFF;
1215178825Sdfr    } else {
1216178825Sdfr	/*
1217178825Sdfr	 * In Wrap tokens without confidentiality, the EC field is
1218178825Sdfr	 * used to encode the size (in bytes) of the trailing
1219178825Sdfr	 * checksum.
1220178825Sdfr	 *
1221178825Sdfr	 * This is not used in the checksum calcuation itself,
1222178825Sdfr	 * because the checksum length could potentially vary
1223178825Sdfr	 * depending on the data length.
1224178825Sdfr	 */
1225178825Sdfr	token->EC[0] = 0;
1226178825Sdfr	token->EC[1] = 0;
1227178825Sdfr    }
1228178825Sdfr
1229178825Sdfr    /*
1230178825Sdfr     * In Wrap tokens that provide for confidentiality, the RRC
1231178825Sdfr     * field in the header contains the hex value 00 00 before
1232178825Sdfr     * encryption.
1233178825Sdfr     *
1234178825Sdfr     * In Wrap tokens that do not provide for confidentiality,
1235178825Sdfr     * both the EC and RRC fields in the appended checksum
1236178825Sdfr     * contain the hex value 00 00 for the purpose of calculating
1237178825Sdfr     * the checksum.
1238178825Sdfr     */
1239178825Sdfr    token->RRC[0] = 0;
1240178825Sdfr    token->RRC[1] = 0;
1241178825Sdfr
1242233294Sstas    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
1243178825Sdfr    krb5_auth_con_getlocalseqnumber(context,
1244233294Sstas				    ctx->auth_context,
1245178825Sdfr				    &seq_number);
1246178825Sdfr    _gsskrb5_encode_be_om_uint32(0,          &token->SND_SEQ[0]);
1247178825Sdfr    _gsskrb5_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]);
1248178825Sdfr    krb5_auth_con_setlocalseqnumber(context,
1249233294Sstas				    ctx->auth_context,
1250178825Sdfr				    ++seq_number);
1251233294Sstas    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1252178825Sdfr
1253178825Sdfr    /*
1254178825Sdfr     * If confidentiality is requested, the token header is
1255178825Sdfr     * appended to the plaintext before encryption; the resulting
1256178825Sdfr     * token is {"header" | encrypt(plaintext | pad | "header")}.
1257178825Sdfr     *
1258178825Sdfr     * If no confidentiality is requested, the checksum is
1259178825Sdfr     * calculated over the plaintext concatenated with the
1260178825Sdfr     * token header.
1261178825Sdfr     */
1262233294Sstas    if (ctx->more_flags & LOCAL) {
1263178825Sdfr	usage = KRB5_KU_USAGE_INITIATOR_SEAL;
1264178825Sdfr    } else {
1265178825Sdfr	usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
1266178825Sdfr    }
1267178825Sdfr
1268178825Sdfr    if (conf_req_flag) {
1269178825Sdfr	/*
1270178825Sdfr	 * Any necessary padding is added here to ensure that the
1271178825Sdfr	 * encrypted token header is always at the end of the
1272178825Sdfr	 * ciphertext.
1273178825Sdfr	 *
1274178825Sdfr	 * The specification does not require that the padding
1275178825Sdfr	 * bytes are initialized.
1276178825Sdfr	 */
1277178825Sdfr	p += sizeof(*token);
1278178825Sdfr	memcpy(p, input_message_buffer->value, input_message_buffer->length);
1279178825Sdfr	memset(p + input_message_buffer->length, 0xFF, padlength);
1280178825Sdfr	memcpy(p + input_message_buffer->length + padlength,
1281178825Sdfr	       token, sizeof(*token));
1282178825Sdfr
1283233294Sstas	ret = krb5_encrypt(context, ctx->crypto,
1284178825Sdfr			   usage, p,
1285178825Sdfr			   input_message_buffer->length + padlength +
1286178825Sdfr				sizeof(*token),
1287178825Sdfr			   &cipher);
1288178825Sdfr	if (ret != 0) {
1289178825Sdfr	    *minor_status = ret;
1290178825Sdfr	    _gsskrb5_release_buffer(minor_status, output_message_buffer);
1291178825Sdfr	    return GSS_S_FAILURE;
1292178825Sdfr	}
1293178825Sdfr	assert(sizeof(*token) + cipher.length == wrapped_len);
1294233294Sstas	token->RRC[0] = (rrc >> 8) & 0xFF;
1295178825Sdfr	token->RRC[1] = (rrc >> 0) & 0xFF;
1296178825Sdfr
1297233294Sstas	/*
1298233294Sstas	 * this is really ugly, but needed against windows
1299233294Sstas	 * for DCERPC, as windows rotates by EC+RRC.
1300233294Sstas	 */
1301233294Sstas	if (IS_DCE_STYLE(ctx)) {
1302233294Sstas		ret = rrc_rotate(cipher.data, cipher.length, rrc+padlength, FALSE);
1303233294Sstas	} else {
1304233294Sstas		ret = rrc_rotate(cipher.data, cipher.length, rrc, FALSE);
1305233294Sstas	}
1306178825Sdfr	if (ret != 0) {
1307178825Sdfr	    *minor_status = ret;
1308178825Sdfr	    _gsskrb5_release_buffer(minor_status, output_message_buffer);
1309178825Sdfr	    return GSS_S_FAILURE;
1310178825Sdfr	}
1311178825Sdfr	memcpy(p, cipher.data, cipher.length);
1312178825Sdfr	krb5_data_free(&cipher);
1313178825Sdfr    } else {
1314178825Sdfr	char *buf;
1315178825Sdfr	Checksum cksum;
1316178825Sdfr
1317178825Sdfr	buf = malloc(input_message_buffer->length + sizeof(*token));
1318178825Sdfr	if (buf == NULL) {
1319178825Sdfr	    *minor_status = ENOMEM;
1320178825Sdfr	    _gsskrb5_release_buffer(minor_status, output_message_buffer);
1321178825Sdfr	    return GSS_S_FAILURE;
1322178825Sdfr	}
1323178825Sdfr	memcpy(buf, input_message_buffer->value, input_message_buffer->length);
1324178825Sdfr	memcpy(buf + input_message_buffer->length, token, sizeof(*token));
1325178825Sdfr
1326233294Sstas	ret = krb5_create_checksum(context, ctx->crypto,
1327233294Sstas				   usage, 0, buf,
1328178825Sdfr				   input_message_buffer->length +
1329233294Sstas					sizeof(*token),
1330178825Sdfr				   &cksum);
1331178825Sdfr	if (ret != 0) {
1332178825Sdfr	    *minor_status = ret;
1333178825Sdfr	    _gsskrb5_release_buffer(minor_status, output_message_buffer);
1334178825Sdfr	    free(buf);
1335178825Sdfr	    return GSS_S_FAILURE;
1336178825Sdfr	}
1337178825Sdfr
1338178825Sdfr	free(buf);
1339178825Sdfr
1340178825Sdfr	assert(cksum.checksum.length == cksumsize);
1341178825Sdfr	token->EC[0] =  (cksum.checksum.length >> 8) & 0xFF;
1342178825Sdfr	token->EC[1] =  (cksum.checksum.length >> 0) & 0xFF;
1343233294Sstas	token->RRC[0] = (rrc >> 8) & 0xFF;
1344178825Sdfr	token->RRC[1] = (rrc >> 0) & 0xFF;
1345178825Sdfr
1346178825Sdfr	p += sizeof(*token);
1347178825Sdfr	memcpy(p, input_message_buffer->value, input_message_buffer->length);
1348178825Sdfr	memcpy(p + input_message_buffer->length,
1349178825Sdfr	       cksum.checksum.data, cksum.checksum.length);
1350178825Sdfr
1351178825Sdfr	ret = rrc_rotate(p,
1352178825Sdfr	    input_message_buffer->length + cksum.checksum.length, rrc, FALSE);
1353178825Sdfr	if (ret != 0) {
1354178825Sdfr	    *minor_status = ret;
1355178825Sdfr	    _gsskrb5_release_buffer(minor_status, output_message_buffer);
1356178825Sdfr	    free_Checksum(&cksum);
1357178825Sdfr	    return GSS_S_FAILURE;
1358178825Sdfr	}
1359178825Sdfr	free_Checksum(&cksum);
1360178825Sdfr    }
1361178825Sdfr
1362178825Sdfr    if (conf_state != NULL) {
1363178825Sdfr	*conf_state = conf_req_flag;
1364178825Sdfr    }
1365178825Sdfr
1366178825Sdfr    *minor_status = 0;
1367178825Sdfr    return GSS_S_COMPLETE;
1368178825Sdfr}
1369178825Sdfr
1370178825SdfrOM_uint32 _gssapi_unwrap_cfx(OM_uint32 *minor_status,
1371233294Sstas			     const gsskrb5_ctx ctx,
1372178825Sdfr			     krb5_context context,
1373178825Sdfr			     const gss_buffer_t input_message_buffer,
1374178825Sdfr			     gss_buffer_t output_message_buffer,
1375178825Sdfr			     int *conf_state,
1376233294Sstas			     gss_qop_t *qop_state)
1377178825Sdfr{
1378178825Sdfr    gss_cfx_wrap_token token;
1379178825Sdfr    u_char token_flags;
1380178825Sdfr    krb5_error_code ret;
1381178825Sdfr    unsigned usage;
1382178825Sdfr    krb5_data data;
1383178825Sdfr    uint16_t ec, rrc;
1384178825Sdfr    OM_uint32 seq_number_lo, seq_number_hi;
1385178825Sdfr    size_t len;
1386178825Sdfr    u_char *p;
1387178825Sdfr
1388178825Sdfr    *minor_status = 0;
1389178825Sdfr
1390178825Sdfr    if (input_message_buffer->length < sizeof(*token)) {
1391178825Sdfr	return GSS_S_DEFECTIVE_TOKEN;
1392178825Sdfr    }
1393178825Sdfr
1394178825Sdfr    p = input_message_buffer->value;
1395178825Sdfr
1396178825Sdfr    token = (gss_cfx_wrap_token)p;
1397178825Sdfr
1398178825Sdfr    if (token->TOK_ID[0] != 0x05 || token->TOK_ID[1] != 0x04) {
1399178825Sdfr	return GSS_S_DEFECTIVE_TOKEN;
1400178825Sdfr    }
1401178825Sdfr
1402178825Sdfr    /* Ignore unknown flags */
1403178825Sdfr    token_flags = token->Flags &
1404178825Sdfr	(CFXSentByAcceptor | CFXSealed | CFXAcceptorSubkey);
1405178825Sdfr
1406178825Sdfr    if (token_flags & CFXSentByAcceptor) {
1407233294Sstas	if ((ctx->more_flags & LOCAL) == 0)
1408178825Sdfr	    return GSS_S_DEFECTIVE_TOKEN;
1409178825Sdfr    }
1410178825Sdfr
1411233294Sstas    if (ctx->more_flags & ACCEPTOR_SUBKEY) {
1412178825Sdfr	if ((token_flags & CFXAcceptorSubkey) == 0)
1413178825Sdfr	    return GSS_S_DEFECTIVE_TOKEN;
1414178825Sdfr    } else {
1415178825Sdfr	if (token_flags & CFXAcceptorSubkey)
1416178825Sdfr	    return GSS_S_DEFECTIVE_TOKEN;
1417178825Sdfr    }
1418178825Sdfr
1419178825Sdfr    if (token->Filler != 0xFF) {
1420178825Sdfr	return GSS_S_DEFECTIVE_TOKEN;
1421178825Sdfr    }
1422178825Sdfr
1423178825Sdfr    if (conf_state != NULL) {
1424178825Sdfr	*conf_state = (token_flags & CFXSealed) ? 1 : 0;
1425178825Sdfr    }
1426178825Sdfr
1427178825Sdfr    ec  = (token->EC[0]  << 8) | token->EC[1];
1428178825Sdfr    rrc = (token->RRC[0] << 8) | token->RRC[1];
1429178825Sdfr
1430178825Sdfr    /*
1431178825Sdfr     * Check sequence number
1432178825Sdfr     */
1433178825Sdfr    _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi);
1434178825Sdfr    _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo);
1435178825Sdfr    if (seq_number_hi) {
1436178825Sdfr	/* no support for 64-bit sequence numbers */
1437178825Sdfr	*minor_status = ERANGE;
1438178825Sdfr	return GSS_S_UNSEQ_TOKEN;
1439178825Sdfr    }
1440178825Sdfr
1441233294Sstas    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
1442233294Sstas    ret = _gssapi_msg_order_check(ctx->order, seq_number_lo);
1443178825Sdfr    if (ret != 0) {
1444178825Sdfr	*minor_status = 0;
1445233294Sstas	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1446178825Sdfr	_gsskrb5_release_buffer(minor_status, output_message_buffer);
1447178825Sdfr	return ret;
1448178825Sdfr    }
1449233294Sstas    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1450178825Sdfr
1451178825Sdfr    /*
1452178825Sdfr     * Decrypt and/or verify checksum
1453178825Sdfr     */
1454178825Sdfr
1455233294Sstas    if (ctx->more_flags & LOCAL) {
1456178825Sdfr	usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
1457178825Sdfr    } else {
1458178825Sdfr	usage = KRB5_KU_USAGE_INITIATOR_SEAL;
1459178825Sdfr    }
1460178825Sdfr
1461178825Sdfr    p += sizeof(*token);
1462178825Sdfr    len = input_message_buffer->length;
1463178825Sdfr    len -= (p - (u_char *)input_message_buffer->value);
1464178825Sdfr
1465233294Sstas    if (token_flags & CFXSealed) {
1466233294Sstas	/*
1467233294Sstas	 * this is really ugly, but needed against windows
1468233294Sstas	 * for DCERPC, as windows rotates by EC+RRC.
1469233294Sstas	 */
1470233294Sstas	if (IS_DCE_STYLE(ctx)) {
1471233294Sstas		*minor_status = rrc_rotate(p, len, rrc+ec, TRUE);
1472233294Sstas	} else {
1473233294Sstas		*minor_status = rrc_rotate(p, len, rrc, TRUE);
1474233294Sstas	}
1475233294Sstas	if (*minor_status != 0) {
1476233294Sstas	    return GSS_S_FAILURE;
1477233294Sstas	}
1478178825Sdfr
1479233294Sstas	ret = krb5_decrypt(context, ctx->crypto, usage,
1480178825Sdfr	    p, len, &data);
1481178825Sdfr	if (ret != 0) {
1482178825Sdfr	    *minor_status = ret;
1483178825Sdfr	    return GSS_S_BAD_MIC;
1484178825Sdfr	}
1485178825Sdfr
1486178825Sdfr	/* Check that there is room for the pad and token header */
1487178825Sdfr	if (data.length < ec + sizeof(*token)) {
1488178825Sdfr	    krb5_data_free(&data);
1489178825Sdfr	    return GSS_S_DEFECTIVE_TOKEN;
1490178825Sdfr	}
1491178825Sdfr	p = data.data;
1492178825Sdfr	p += data.length - sizeof(*token);
1493178825Sdfr
1494178825Sdfr	/* RRC is unprotected; don't modify input buffer */
1495178825Sdfr	((gss_cfx_wrap_token)p)->RRC[0] = token->RRC[0];
1496178825Sdfr	((gss_cfx_wrap_token)p)->RRC[1] = token->RRC[1];
1497178825Sdfr
1498178825Sdfr	/* Check the integrity of the header */
1499233294Sstas	if (ct_memcmp(p, token, sizeof(*token)) != 0) {
1500178825Sdfr	    krb5_data_free(&data);
1501178825Sdfr	    return GSS_S_BAD_MIC;
1502178825Sdfr	}
1503178825Sdfr
1504178825Sdfr	output_message_buffer->value = data.data;
1505178825Sdfr	output_message_buffer->length = data.length - ec - sizeof(*token);
1506178825Sdfr    } else {
1507178825Sdfr	Checksum cksum;
1508178825Sdfr
1509233294Sstas	/* Rotate by RRC; bogus to do this in-place XXX */
1510233294Sstas	*minor_status = rrc_rotate(p, len, rrc, TRUE);
1511233294Sstas	if (*minor_status != 0) {
1512233294Sstas	    return GSS_S_FAILURE;
1513233294Sstas	}
1514233294Sstas
1515178825Sdfr	/* Determine checksum type */
1516178825Sdfr	ret = krb5_crypto_get_checksum_type(context,
1517233294Sstas					    ctx->crypto,
1518233294Sstas					    &cksum.cksumtype);
1519178825Sdfr	if (ret != 0) {
1520178825Sdfr	    *minor_status = ret;
1521178825Sdfr	    return GSS_S_FAILURE;
1522178825Sdfr	}
1523178825Sdfr
1524178825Sdfr	cksum.checksum.length = ec;
1525178825Sdfr
1526178825Sdfr	/* Check we have at least as much data as the checksum */
1527178825Sdfr	if (len < cksum.checksum.length) {
1528178825Sdfr	    *minor_status = ERANGE;
1529178825Sdfr	    return GSS_S_BAD_MIC;
1530178825Sdfr	}
1531178825Sdfr
1532178825Sdfr	/* Length now is of the plaintext only, no checksum */
1533178825Sdfr	len -= cksum.checksum.length;
1534178825Sdfr	cksum.checksum.data = p + len;
1535178825Sdfr
1536178825Sdfr	output_message_buffer->length = len; /* for later */
1537178825Sdfr	output_message_buffer->value = malloc(len + sizeof(*token));
1538178825Sdfr	if (output_message_buffer->value == NULL) {
1539178825Sdfr	    *minor_status = ENOMEM;
1540178825Sdfr	    return GSS_S_FAILURE;
1541178825Sdfr	}
1542178825Sdfr
1543178825Sdfr	/* Checksum is over (plaintext-data | "header") */
1544178825Sdfr	memcpy(output_message_buffer->value, p, len);
1545233294Sstas	memcpy((u_char *)output_message_buffer->value + len,
1546178825Sdfr	       token, sizeof(*token));
1547178825Sdfr
1548178825Sdfr	/* EC is not included in checksum calculation */
1549178825Sdfr	token = (gss_cfx_wrap_token)((u_char *)output_message_buffer->value +
1550178825Sdfr				     len);
1551178825Sdfr	token->EC[0]  = 0;
1552178825Sdfr	token->EC[1]  = 0;
1553178825Sdfr	token->RRC[0] = 0;
1554178825Sdfr	token->RRC[1] = 0;
1555178825Sdfr
1556233294Sstas	ret = krb5_verify_checksum(context, ctx->crypto,
1557178825Sdfr				   usage,
1558178825Sdfr				   output_message_buffer->value,
1559178825Sdfr				   len + sizeof(*token),
1560178825Sdfr				   &cksum);
1561178825Sdfr	if (ret != 0) {
1562178825Sdfr	    *minor_status = ret;
1563178825Sdfr	    _gsskrb5_release_buffer(minor_status, output_message_buffer);
1564178825Sdfr	    return GSS_S_BAD_MIC;
1565178825Sdfr	}
1566178825Sdfr    }
1567178825Sdfr
1568178825Sdfr    if (qop_state != NULL) {
1569178825Sdfr	*qop_state = GSS_C_QOP_DEFAULT;
1570178825Sdfr    }
1571178825Sdfr
1572178825Sdfr    *minor_status = 0;
1573178825Sdfr    return GSS_S_COMPLETE;
1574178825Sdfr}
1575178825Sdfr
1576178825SdfrOM_uint32 _gssapi_mic_cfx(OM_uint32 *minor_status,
1577233294Sstas			  const gsskrb5_ctx ctx,
1578178825Sdfr			  krb5_context context,
1579178825Sdfr			  gss_qop_t qop_req,
1580178825Sdfr			  const gss_buffer_t message_buffer,
1581233294Sstas			  gss_buffer_t message_token)
1582178825Sdfr{
1583178825Sdfr    gss_cfx_mic_token token;
1584178825Sdfr    krb5_error_code ret;
1585178825Sdfr    unsigned usage;
1586178825Sdfr    Checksum cksum;
1587178825Sdfr    u_char *buf;
1588178825Sdfr    size_t len;
1589178825Sdfr    int32_t seq_number;
1590178825Sdfr
1591178825Sdfr    len = message_buffer->length + sizeof(*token);
1592178825Sdfr    buf = malloc(len);
1593178825Sdfr    if (buf == NULL) {
1594178825Sdfr	*minor_status = ENOMEM;
1595178825Sdfr	return GSS_S_FAILURE;
1596178825Sdfr    }
1597178825Sdfr
1598178825Sdfr    memcpy(buf, message_buffer->value, message_buffer->length);
1599178825Sdfr
1600178825Sdfr    token = (gss_cfx_mic_token)(buf + message_buffer->length);
1601178825Sdfr    token->TOK_ID[0] = 0x04;
1602178825Sdfr    token->TOK_ID[1] = 0x04;
1603178825Sdfr    token->Flags = 0;
1604233294Sstas    if ((ctx->more_flags & LOCAL) == 0)
1605178825Sdfr	token->Flags |= CFXSentByAcceptor;
1606233294Sstas    if (ctx->more_flags & ACCEPTOR_SUBKEY)
1607178825Sdfr	token->Flags |= CFXAcceptorSubkey;
1608178825Sdfr    memset(token->Filler, 0xFF, 5);
1609178825Sdfr
1610233294Sstas    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
1611178825Sdfr    krb5_auth_con_getlocalseqnumber(context,
1612233294Sstas				    ctx->auth_context,
1613178825Sdfr				    &seq_number);
1614178825Sdfr    _gsskrb5_encode_be_om_uint32(0,          &token->SND_SEQ[0]);
1615178825Sdfr    _gsskrb5_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]);
1616178825Sdfr    krb5_auth_con_setlocalseqnumber(context,
1617233294Sstas				    ctx->auth_context,
1618178825Sdfr				    ++seq_number);
1619233294Sstas    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1620178825Sdfr
1621233294Sstas    if (ctx->more_flags & LOCAL) {
1622178825Sdfr	usage = KRB5_KU_USAGE_INITIATOR_SIGN;
1623178825Sdfr    } else {
1624178825Sdfr	usage = KRB5_KU_USAGE_ACCEPTOR_SIGN;
1625178825Sdfr    }
1626178825Sdfr
1627233294Sstas    ret = krb5_create_checksum(context, ctx->crypto,
1628178825Sdfr	usage, 0, buf, len, &cksum);
1629178825Sdfr    if (ret != 0) {
1630178825Sdfr	*minor_status = ret;
1631178825Sdfr	free(buf);
1632178825Sdfr	return GSS_S_FAILURE;
1633178825Sdfr    }
1634178825Sdfr
1635178825Sdfr    /* Determine MIC length */
1636178825Sdfr    message_token->length = sizeof(*token) + cksum.checksum.length;
1637178825Sdfr    message_token->value = malloc(message_token->length);
1638178825Sdfr    if (message_token->value == NULL) {
1639178825Sdfr	*minor_status = ENOMEM;
1640178825Sdfr	free_Checksum(&cksum);
1641178825Sdfr	free(buf);
1642178825Sdfr	return GSS_S_FAILURE;
1643178825Sdfr    }
1644178825Sdfr
1645178825Sdfr    /* Token is { "header" | get_mic("header" | plaintext-data) } */
1646178825Sdfr    memcpy(message_token->value, token, sizeof(*token));
1647178825Sdfr    memcpy((u_char *)message_token->value + sizeof(*token),
1648178825Sdfr	   cksum.checksum.data, cksum.checksum.length);
1649178825Sdfr
1650178825Sdfr    free_Checksum(&cksum);
1651178825Sdfr    free(buf);
1652178825Sdfr
1653178825Sdfr    *minor_status = 0;
1654178825Sdfr    return GSS_S_COMPLETE;
1655178825Sdfr}
1656178825Sdfr
1657178825SdfrOM_uint32 _gssapi_verify_mic_cfx(OM_uint32 *minor_status,
1658233294Sstas				 const gsskrb5_ctx ctx,
1659178825Sdfr				 krb5_context context,
1660178825Sdfr				 const gss_buffer_t message_buffer,
1661178825Sdfr				 const gss_buffer_t token_buffer,
1662233294Sstas				 gss_qop_t *qop_state)
1663178825Sdfr{
1664178825Sdfr    gss_cfx_mic_token token;
1665178825Sdfr    u_char token_flags;
1666178825Sdfr    krb5_error_code ret;
1667178825Sdfr    unsigned usage;
1668178825Sdfr    OM_uint32 seq_number_lo, seq_number_hi;
1669178825Sdfr    u_char *buf, *p;
1670178825Sdfr    Checksum cksum;
1671178825Sdfr
1672178825Sdfr    *minor_status = 0;
1673178825Sdfr
1674178825Sdfr    if (token_buffer->length < sizeof(*token)) {
1675178825Sdfr	return GSS_S_DEFECTIVE_TOKEN;
1676178825Sdfr    }
1677178825Sdfr
1678178825Sdfr    p = token_buffer->value;
1679178825Sdfr
1680178825Sdfr    token = (gss_cfx_mic_token)p;
1681178825Sdfr
1682178825Sdfr    if (token->TOK_ID[0] != 0x04 || token->TOK_ID[1] != 0x04) {
1683178825Sdfr	return GSS_S_DEFECTIVE_TOKEN;
1684178825Sdfr    }
1685178825Sdfr
1686178825Sdfr    /* Ignore unknown flags */
1687178825Sdfr    token_flags = token->Flags & (CFXSentByAcceptor | CFXAcceptorSubkey);
1688178825Sdfr
1689178825Sdfr    if (token_flags & CFXSentByAcceptor) {
1690233294Sstas	if ((ctx->more_flags & LOCAL) == 0)
1691178825Sdfr	    return GSS_S_DEFECTIVE_TOKEN;
1692178825Sdfr    }
1693233294Sstas    if (ctx->more_flags & ACCEPTOR_SUBKEY) {
1694178825Sdfr	if ((token_flags & CFXAcceptorSubkey) == 0)
1695178825Sdfr	    return GSS_S_DEFECTIVE_TOKEN;
1696178825Sdfr    } else {
1697178825Sdfr	if (token_flags & CFXAcceptorSubkey)
1698178825Sdfr	    return GSS_S_DEFECTIVE_TOKEN;
1699178825Sdfr    }
1700178825Sdfr
1701233294Sstas    if (ct_memcmp(token->Filler, "\xff\xff\xff\xff\xff", 5) != 0) {
1702178825Sdfr	return GSS_S_DEFECTIVE_TOKEN;
1703178825Sdfr    }
1704178825Sdfr
1705178825Sdfr    /*
1706178825Sdfr     * Check sequence number
1707178825Sdfr     */
1708178825Sdfr    _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi);
1709178825Sdfr    _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo);
1710178825Sdfr    if (seq_number_hi) {
1711178825Sdfr	*minor_status = ERANGE;
1712178825Sdfr	return GSS_S_UNSEQ_TOKEN;
1713178825Sdfr    }
1714178825Sdfr
1715233294Sstas    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
1716233294Sstas    ret = _gssapi_msg_order_check(ctx->order, seq_number_lo);
1717178825Sdfr    if (ret != 0) {
1718178825Sdfr	*minor_status = 0;
1719233294Sstas	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1720178825Sdfr	return ret;
1721178825Sdfr    }
1722233294Sstas    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
1723178825Sdfr
1724178825Sdfr    /*
1725178825Sdfr     * Verify checksum
1726178825Sdfr     */
1727233294Sstas    ret = krb5_crypto_get_checksum_type(context, ctx->crypto,
1728178825Sdfr					&cksum.cksumtype);
1729178825Sdfr    if (ret != 0) {
1730178825Sdfr	*minor_status = ret;
1731178825Sdfr	return GSS_S_FAILURE;
1732178825Sdfr    }
1733178825Sdfr
1734178825Sdfr    cksum.checksum.data = p + sizeof(*token);
1735178825Sdfr    cksum.checksum.length = token_buffer->length - sizeof(*token);
1736178825Sdfr
1737233294Sstas    if (ctx->more_flags & LOCAL) {
1738178825Sdfr	usage = KRB5_KU_USAGE_ACCEPTOR_SIGN;
1739178825Sdfr    } else {
1740178825Sdfr	usage = KRB5_KU_USAGE_INITIATOR_SIGN;
1741178825Sdfr    }
1742178825Sdfr
1743178825Sdfr    buf = malloc(message_buffer->length + sizeof(*token));
1744178825Sdfr    if (buf == NULL) {
1745178825Sdfr	*minor_status = ENOMEM;
1746178825Sdfr	return GSS_S_FAILURE;
1747178825Sdfr    }
1748178825Sdfr    memcpy(buf, message_buffer->value, message_buffer->length);
1749178825Sdfr    memcpy(buf + message_buffer->length, token, sizeof(*token));
1750178825Sdfr
1751233294Sstas    ret = krb5_verify_checksum(context, ctx->crypto,
1752178825Sdfr			       usage,
1753178825Sdfr			       buf,
1754178825Sdfr			       sizeof(*token) + message_buffer->length,
1755178825Sdfr			       &cksum);
1756178825Sdfr    if (ret != 0) {
1757178825Sdfr	*minor_status = ret;
1758178825Sdfr	free(buf);
1759178825Sdfr	return GSS_S_BAD_MIC;
1760178825Sdfr    }
1761178825Sdfr
1762178825Sdfr    free(buf);
1763178825Sdfr
1764178825Sdfr    if (qop_state != NULL) {
1765178825Sdfr	*qop_state = GSS_C_QOP_DEFAULT;
1766178825Sdfr    }
1767178825Sdfr
1768178825Sdfr    return GSS_S_COMPLETE;
1769178825Sdfr}
1770