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