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