r19603: Make it easier to control the debug level of smbd.
[Samba.git] / source / heimdal / lib / gssapi / cfx.c
blobef7907c0de1d4140b2fa02426b896556177ccd8a
1 /*
2 * Copyright (c) 2003, PADL Software Pty Ltd.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
33 #include "gssapi_locl.h"
35 RCSID("$Id: cfx.c,v 1.19 2006/05/05 10:26:43 lha Exp $");
38 * Implementation of draft-ietf-krb-wg-gssapi-cfx-06.txt
41 #define CFXSentByAcceptor (1 << 0)
42 #define CFXSealed (1 << 1)
43 #define CFXAcceptorSubkey (1 << 2)
45 static krb5_error_code
46 wrap_length_cfx(krb5_crypto crypto,
47 int conf_req_flag,
48 size_t input_length,
49 size_t *output_length,
50 size_t *cksumsize,
51 uint16_t *padlength,
52 size_t *padsize)
54 krb5_error_code ret;
55 krb5_cksumtype type;
57 /* 16-byte header is always first */
58 *output_length = sizeof(gss_cfx_wrap_token_desc);
59 *padlength = 0;
61 ret = krb5_crypto_get_checksum_type(gssapi_krb5_context, crypto, &type);
62 if (ret) {
63 return ret;
66 ret = krb5_checksumsize(gssapi_krb5_context, type, cksumsize);
67 if (ret) {
68 return ret;
71 if (conf_req_flag) {
73 /* Header is concatenated with data before encryption */
74 input_length += sizeof(gss_cfx_wrap_token_desc);
76 ret = krb5_crypto_getpadsize(gssapi_krb5_context, crypto, padsize);
77 if (ret) {
78 return ret;
80 if (*padsize > 1) {
81 /* XXX check this */
82 *padlength = *padsize - (input_length % *padsize);
85 /* We add the pad ourselves (noted here for completeness only) */
86 input_length += *padlength;
88 *output_length += krb5_get_wrapped_length(gssapi_krb5_context,
89 crypto, input_length);
90 } else {
91 /* Checksum is concatenated with data */
92 *output_length += input_length + *cksumsize;
93 *padsize = 0;
96 assert(*output_length > input_length);
98 return 0;
101 OM_uint32 _gssapi_wrap_size_cfx(OM_uint32 *minor_status,
102 const gss_ctx_id_t context_handle,
103 int conf_req_flag,
104 gss_qop_t qop_req,
105 OM_uint32 req_input_size,
106 OM_uint32 *output_len,
107 OM_uint32 *padsize,
108 krb5_keyblock *key)
110 krb5_error_code ret;
111 krb5_crypto crypto;
112 uint16_t pad_length;
113 size_t pad_size;
114 size_t output_length, cksumsize;
116 ret = krb5_crypto_init(gssapi_krb5_context, key, 0, &crypto);
117 if (ret != 0) {
118 gssapi_krb5_set_error_string();
119 *minor_status = ret;
120 return GSS_S_FAILURE;
123 ret = wrap_length_cfx(crypto, conf_req_flag,
124 req_input_size,
125 &output_length, &cksumsize, &pad_length, &pad_size);
126 if (ret != 0) {
127 gssapi_krb5_set_error_string();
128 *minor_status = ret;
129 krb5_crypto_destroy(gssapi_krb5_context, crypto);
130 return GSS_S_FAILURE;
133 *output_len = output_length;
134 *padsize = pad_size;
136 krb5_crypto_destroy(gssapi_krb5_context, crypto);
138 return GSS_S_COMPLETE;
142 * Rotate "rrc" bytes to the front or back
145 static krb5_error_code
146 rrc_rotate(void *data, size_t len, uint16_t rrc, krb5_boolean unrotate)
148 u_char *tmp, buf[256];
149 size_t left;
151 if (len == 0)
152 return 0;
154 rrc %= len;
156 if (rrc == 0)
157 return 0;
159 left = len - rrc;
161 if (rrc <= sizeof(buf)) {
162 tmp = buf;
163 } else {
164 tmp = malloc(rrc);
165 if (tmp == NULL)
166 return ENOMEM;
169 if (unrotate) {
170 memcpy(tmp, data, rrc);
171 memmove(data, (u_char *)data + rrc, left);
172 memcpy((u_char *)data + left, tmp, rrc);
173 } else {
174 memcpy(tmp, (u_char *)data + left, rrc);
175 memmove((u_char *)data + rrc, data, left);
176 memcpy(data, tmp, rrc);
179 if (rrc > sizeof(buf))
180 free(tmp);
182 return 0;
185 OM_uint32 _gssapi_wrap_cfx(OM_uint32 *minor_status,
186 const gss_ctx_id_t context_handle,
187 int conf_req_flag,
188 gss_qop_t qop_req,
189 const gss_buffer_t input_message_buffer,
190 int *conf_state,
191 gss_buffer_t output_message_buffer,
192 krb5_keyblock *key)
194 krb5_crypto crypto;
195 gss_cfx_wrap_token token;
196 krb5_error_code ret;
197 unsigned usage;
198 krb5_data cipher;
199 size_t wrapped_len, cksumsize;
200 uint16_t padlength, rrc = 0;
201 int32_t seq_number;
202 size_t padsize;
203 u_char *p;
205 ret = krb5_crypto_init(gssapi_krb5_context, key, 0, &crypto);
206 if (ret != 0) {
207 gssapi_krb5_set_error_string();
208 *minor_status = ret;
209 return GSS_S_FAILURE;
212 ret = wrap_length_cfx(crypto, conf_req_flag,
213 input_message_buffer->length,
214 &wrapped_len, &cksumsize, &padlength, &padsize);
215 if (ret != 0) {
216 gssapi_krb5_set_error_string();
217 *minor_status = ret;
218 krb5_crypto_destroy(gssapi_krb5_context, crypto);
219 return GSS_S_FAILURE;
222 /* Always rotate encrypted token (if any) and checksum to header */
223 rrc = (conf_req_flag ? sizeof(*token) : 0) + (uint16_t)cksumsize;
225 output_message_buffer->length = wrapped_len;
226 output_message_buffer->value = malloc(output_message_buffer->length);
227 if (output_message_buffer->value == NULL) {
228 *minor_status = ENOMEM;
229 krb5_crypto_destroy(gssapi_krb5_context, crypto);
230 return GSS_S_FAILURE;
233 p = output_message_buffer->value;
234 token = (gss_cfx_wrap_token)p;
235 token->TOK_ID[0] = 0x05;
236 token->TOK_ID[1] = 0x04;
237 token->Flags = 0;
238 token->Filler = 0xFF;
239 if ((context_handle->more_flags & LOCAL) == 0)
240 token->Flags |= CFXSentByAcceptor;
241 if (context_handle->more_flags & ACCEPTOR_SUBKEY)
242 token->Flags |= CFXAcceptorSubkey;
243 if (conf_req_flag) {
245 * In Wrap tokens with confidentiality, the EC field is
246 * used to encode the size (in bytes) of the random filler.
248 token->Flags |= CFXSealed;
249 token->EC[0] = (padlength >> 8) & 0xFF;
250 token->EC[1] = (padlength >> 0) & 0xFF;
251 } else {
253 * In Wrap tokens without confidentiality, the EC field is
254 * used to encode the size (in bytes) of the trailing
255 * checksum.
257 * This is not used in the checksum calcuation itself,
258 * because the checksum length could potentially vary
259 * depending on the data length.
261 token->EC[0] = 0;
262 token->EC[1] = 0;
266 * In Wrap tokens that provide for confidentiality, the RRC
267 * field in the header contains the hex value 00 00 before
268 * encryption.
270 * In Wrap tokens that do not provide for confidentiality,
271 * both the EC and RRC fields in the appended checksum
272 * contain the hex value 00 00 for the purpose of calculating
273 * the checksum.
275 token->RRC[0] = 0;
276 token->RRC[1] = 0;
278 HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
279 krb5_auth_con_getlocalseqnumber(gssapi_krb5_context,
280 context_handle->auth_context,
281 &seq_number);
282 gssapi_encode_be_om_uint32(0, &token->SND_SEQ[0]);
283 gssapi_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]);
284 krb5_auth_con_setlocalseqnumber(gssapi_krb5_context,
285 context_handle->auth_context,
286 ++seq_number);
287 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
290 * If confidentiality is requested, the token header is
291 * appended to the plaintext before encryption; the resulting
292 * token is {"header" | encrypt(plaintext | pad | "header")}.
294 * If no confidentiality is requested, the checksum is
295 * calculated over the plaintext concatenated with the
296 * token header.
298 if (context_handle->more_flags & LOCAL) {
299 usage = KRB5_KU_USAGE_INITIATOR_SEAL;
300 } else {
301 usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
304 if (conf_req_flag) {
306 * Any necessary padding is added here to ensure that the
307 * encrypted token header is always at the end of the
308 * ciphertext.
310 * The specification does not require that the padding
311 * bytes are initialized.
313 p += sizeof(*token);
314 memcpy(p, input_message_buffer->value, input_message_buffer->length);
315 memset(p + input_message_buffer->length, 0xFF, padlength);
316 memcpy(p + input_message_buffer->length + padlength,
317 token, sizeof(*token));
319 ret = krb5_encrypt(gssapi_krb5_context, crypto,
320 usage, p,
321 input_message_buffer->length + padlength +
322 sizeof(*token),
323 &cipher);
324 if (ret != 0) {
325 gssapi_krb5_set_error_string();
326 *minor_status = ret;
327 krb5_crypto_destroy(gssapi_krb5_context, crypto);
328 gss_release_buffer(minor_status, output_message_buffer);
329 return GSS_S_FAILURE;
331 assert(sizeof(*token) + cipher.length == wrapped_len);
332 token->RRC[0] = (rrc >> 8) & 0xFF;
333 token->RRC[1] = (rrc >> 0) & 0xFF;
335 ret = rrc_rotate(cipher.data, cipher.length, rrc, FALSE);
336 if (ret != 0) {
337 gssapi_krb5_set_error_string();
338 *minor_status = ret;
339 krb5_crypto_destroy(gssapi_krb5_context, crypto);
340 gss_release_buffer(minor_status, output_message_buffer);
341 return GSS_S_FAILURE;
343 memcpy(p, cipher.data, cipher.length);
344 krb5_data_free(&cipher);
345 } else {
346 char *buf;
347 Checksum cksum;
349 buf = malloc(input_message_buffer->length + sizeof(*token));
350 if (buf == NULL) {
351 *minor_status = ENOMEM;
352 krb5_crypto_destroy(gssapi_krb5_context, crypto);
353 gss_release_buffer(minor_status, output_message_buffer);
354 return GSS_S_FAILURE;
356 memcpy(buf, input_message_buffer->value, input_message_buffer->length);
357 memcpy(buf + input_message_buffer->length, token, sizeof(*token));
359 ret = krb5_create_checksum(gssapi_krb5_context, crypto,
360 usage, 0, buf,
361 input_message_buffer->length +
362 sizeof(*token),
363 &cksum);
364 if (ret != 0) {
365 gssapi_krb5_set_error_string();
366 *minor_status = ret;
367 krb5_crypto_destroy(gssapi_krb5_context, crypto);
368 gss_release_buffer(minor_status, output_message_buffer);
369 free(buf);
370 return GSS_S_FAILURE;
373 free(buf);
375 assert(cksum.checksum.length == cksumsize);
376 token->EC[0] = (cksum.checksum.length >> 8) & 0xFF;
377 token->EC[1] = (cksum.checksum.length >> 0) & 0xFF;
378 token->RRC[0] = (rrc >> 8) & 0xFF;
379 token->RRC[1] = (rrc >> 0) & 0xFF;
381 p += sizeof(*token);
382 memcpy(p, input_message_buffer->value, input_message_buffer->length);
383 memcpy(p + input_message_buffer->length,
384 cksum.checksum.data, cksum.checksum.length);
386 ret = rrc_rotate(p,
387 input_message_buffer->length + cksum.checksum.length, rrc, FALSE);
388 if (ret != 0) {
389 gssapi_krb5_set_error_string();
390 *minor_status = ret;
391 krb5_crypto_destroy(gssapi_krb5_context, crypto);
392 gss_release_buffer(minor_status, output_message_buffer);
393 free_Checksum(&cksum);
394 return GSS_S_FAILURE;
396 free_Checksum(&cksum);
399 krb5_crypto_destroy(gssapi_krb5_context, crypto);
401 if (conf_state != NULL) {
402 *conf_state = conf_req_flag;
405 *minor_status = 0;
406 return GSS_S_COMPLETE;
409 OM_uint32 _gssapi_unwrap_cfx(OM_uint32 *minor_status,
410 const gss_ctx_id_t context_handle,
411 const gss_buffer_t input_message_buffer,
412 gss_buffer_t output_message_buffer,
413 int *conf_state,
414 gss_qop_t *qop_state,
415 krb5_keyblock *key)
417 krb5_crypto crypto;
418 gss_cfx_wrap_token token;
419 u_char token_flags;
420 krb5_error_code ret;
421 unsigned usage;
422 krb5_data data;
423 uint16_t ec, rrc;
424 OM_uint32 seq_number_lo, seq_number_hi;
425 size_t len;
426 u_char *p;
428 *minor_status = 0;
430 if (input_message_buffer->length < sizeof(*token)) {
431 return GSS_S_DEFECTIVE_TOKEN;
434 p = input_message_buffer->value;
436 token = (gss_cfx_wrap_token)p;
438 if (token->TOK_ID[0] != 0x05 || token->TOK_ID[1] != 0x04) {
439 return GSS_S_DEFECTIVE_TOKEN;
442 /* Ignore unknown flags */
443 token_flags = token->Flags &
444 (CFXSentByAcceptor | CFXSealed | CFXAcceptorSubkey);
446 if (token_flags & CFXSentByAcceptor) {
447 if ((context_handle->more_flags & LOCAL) == 0)
448 return GSS_S_DEFECTIVE_TOKEN;
451 if (context_handle->more_flags & ACCEPTOR_SUBKEY) {
452 if ((token_flags & CFXAcceptorSubkey) == 0)
453 return GSS_S_DEFECTIVE_TOKEN;
454 } else {
455 if (token_flags & CFXAcceptorSubkey)
456 return GSS_S_DEFECTIVE_TOKEN;
459 if (token->Filler != 0xFF) {
460 return GSS_S_DEFECTIVE_TOKEN;
463 if (conf_state != NULL) {
464 *conf_state = (token_flags & CFXSealed) ? 1 : 0;
467 ec = (token->EC[0] << 8) | token->EC[1];
468 rrc = (token->RRC[0] << 8) | token->RRC[1];
471 * Check sequence number
473 gssapi_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi);
474 gssapi_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo);
475 if (seq_number_hi) {
476 /* no support for 64-bit sequence numbers */
477 *minor_status = ERANGE;
478 return GSS_S_UNSEQ_TOKEN;
481 HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
482 ret = _gssapi_msg_order_check(context_handle->order, seq_number_lo);
483 if (ret != 0) {
484 *minor_status = 0;
485 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
486 gss_release_buffer(minor_status, output_message_buffer);
487 return ret;
489 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
492 * Decrypt and/or verify checksum
494 ret = krb5_crypto_init(gssapi_krb5_context, key, 0, &crypto);
495 if (ret != 0) {
496 gssapi_krb5_set_error_string();
497 *minor_status = ret;
498 return GSS_S_FAILURE;
501 if (context_handle->more_flags & LOCAL) {
502 usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
503 } else {
504 usage = KRB5_KU_USAGE_INITIATOR_SEAL;
507 p += sizeof(*token);
508 len = input_message_buffer->length;
509 len -= (p - (u_char *)input_message_buffer->value);
511 /* Rotate by RRC; bogus to do this in-place XXX */
512 *minor_status = rrc_rotate(p, len, rrc, TRUE);
513 if (*minor_status != 0) {
514 krb5_crypto_destroy(gssapi_krb5_context, crypto);
515 return GSS_S_FAILURE;
518 if (token_flags & CFXSealed) {
519 ret = krb5_decrypt(gssapi_krb5_context, crypto, usage,
520 p, len, &data);
521 if (ret != 0) {
522 gssapi_krb5_set_error_string();
523 *minor_status = ret;
524 krb5_crypto_destroy(gssapi_krb5_context, crypto);
525 return GSS_S_BAD_MIC;
528 /* Check that there is room for the pad and token header */
529 if (data.length < ec + sizeof(*token)) {
530 krb5_crypto_destroy(gssapi_krb5_context, crypto);
531 krb5_data_free(&data);
532 return GSS_S_DEFECTIVE_TOKEN;
534 p = data.data;
535 p += data.length - sizeof(*token);
537 /* RRC is unprotected; don't modify input buffer */
538 ((gss_cfx_wrap_token)p)->RRC[0] = token->RRC[0];
539 ((gss_cfx_wrap_token)p)->RRC[1] = token->RRC[1];
541 /* Check the integrity of the header */
542 if (memcmp(p, token, sizeof(*token)) != 0) {
543 krb5_crypto_destroy(gssapi_krb5_context, crypto);
544 krb5_data_free(&data);
545 return GSS_S_BAD_MIC;
548 output_message_buffer->value = data.data;
549 output_message_buffer->length = data.length - ec - sizeof(*token);
550 } else {
551 Checksum cksum;
553 /* Determine checksum type */
554 ret = krb5_crypto_get_checksum_type(gssapi_krb5_context,
555 crypto, &cksum.cksumtype);
556 if (ret != 0) {
557 gssapi_krb5_set_error_string();
558 *minor_status = ret;
559 krb5_crypto_destroy(gssapi_krb5_context, crypto);
560 return GSS_S_FAILURE;
563 cksum.checksum.length = ec;
565 /* Check we have at least as much data as the checksum */
566 if (len < cksum.checksum.length) {
567 *minor_status = ERANGE;
568 krb5_crypto_destroy(gssapi_krb5_context, crypto);
569 return GSS_S_BAD_MIC;
572 /* Length now is of the plaintext only, no checksum */
573 len -= cksum.checksum.length;
574 cksum.checksum.data = p + len;
576 output_message_buffer->length = len; /* for later */
577 output_message_buffer->value = malloc(len + sizeof(*token));
578 if (output_message_buffer->value == NULL) {
579 *minor_status = ENOMEM;
580 krb5_crypto_destroy(gssapi_krb5_context, crypto);
581 return GSS_S_FAILURE;
584 /* Checksum is over (plaintext-data | "header") */
585 memcpy(output_message_buffer->value, p, len);
586 memcpy((u_char *)output_message_buffer->value + len,
587 token, sizeof(*token));
589 /* EC is not included in checksum calculation */
590 token = (gss_cfx_wrap_token)((u_char *)output_message_buffer->value +
591 len);
592 token->EC[0] = 0;
593 token->EC[1] = 0;
594 token->RRC[0] = 0;
595 token->RRC[1] = 0;
597 ret = krb5_verify_checksum(gssapi_krb5_context, crypto,
598 usage,
599 output_message_buffer->value,
600 len + sizeof(*token),
601 &cksum);
602 if (ret != 0) {
603 gssapi_krb5_set_error_string();
604 *minor_status = ret;
605 krb5_crypto_destroy(gssapi_krb5_context, crypto);
606 gss_release_buffer(minor_status, output_message_buffer);
607 return GSS_S_BAD_MIC;
611 krb5_crypto_destroy(gssapi_krb5_context, crypto);
613 if (qop_state != NULL) {
614 *qop_state = GSS_C_QOP_DEFAULT;
617 *minor_status = 0;
618 return GSS_S_COMPLETE;
621 OM_uint32 _gssapi_mic_cfx(OM_uint32 *minor_status,
622 const gss_ctx_id_t context_handle,
623 gss_qop_t qop_req,
624 const gss_buffer_t message_buffer,
625 gss_buffer_t message_token,
626 krb5_keyblock *key)
628 krb5_crypto crypto;
629 gss_cfx_mic_token token;
630 krb5_error_code ret;
631 unsigned usage;
632 Checksum cksum;
633 u_char *buf;
634 size_t len;
635 int32_t seq_number;
637 ret = krb5_crypto_init(gssapi_krb5_context, key, 0, &crypto);
638 if (ret != 0) {
639 gssapi_krb5_set_error_string();
640 *minor_status = ret;
641 return GSS_S_FAILURE;
644 len = message_buffer->length + sizeof(*token);
645 buf = malloc(len);
646 if (buf == NULL) {
647 *minor_status = ENOMEM;
648 krb5_crypto_destroy(gssapi_krb5_context, crypto);
649 return GSS_S_FAILURE;
652 memcpy(buf, message_buffer->value, message_buffer->length);
654 token = (gss_cfx_mic_token)(buf + message_buffer->length);
655 token->TOK_ID[0] = 0x04;
656 token->TOK_ID[1] = 0x04;
657 token->Flags = 0;
658 if ((context_handle->more_flags & LOCAL) == 0)
659 token->Flags |= CFXSentByAcceptor;
660 if (context_handle->more_flags & ACCEPTOR_SUBKEY)
661 token->Flags |= CFXAcceptorSubkey;
662 memset(token->Filler, 0xFF, 5);
664 HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
665 krb5_auth_con_getlocalseqnumber(gssapi_krb5_context,
666 context_handle->auth_context,
667 &seq_number);
668 gssapi_encode_be_om_uint32(0, &token->SND_SEQ[0]);
669 gssapi_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]);
670 krb5_auth_con_setlocalseqnumber(gssapi_krb5_context,
671 context_handle->auth_context,
672 ++seq_number);
673 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
675 if (context_handle->more_flags & LOCAL) {
676 usage = KRB5_KU_USAGE_INITIATOR_SIGN;
677 } else {
678 usage = KRB5_KU_USAGE_ACCEPTOR_SIGN;
681 ret = krb5_create_checksum(gssapi_krb5_context, crypto,
682 usage, 0, buf, len, &cksum);
683 if (ret != 0) {
684 gssapi_krb5_set_error_string();
685 *minor_status = ret;
686 krb5_crypto_destroy(gssapi_krb5_context, crypto);
687 free(buf);
688 return GSS_S_FAILURE;
690 krb5_crypto_destroy(gssapi_krb5_context, crypto);
692 /* Determine MIC length */
693 message_token->length = sizeof(*token) + cksum.checksum.length;
694 message_token->value = malloc(message_token->length);
695 if (message_token->value == NULL) {
696 *minor_status = ENOMEM;
697 free_Checksum(&cksum);
698 free(buf);
699 return GSS_S_FAILURE;
702 /* Token is { "header" | get_mic("header" | plaintext-data) } */
703 memcpy(message_token->value, token, sizeof(*token));
704 memcpy((u_char *)message_token->value + sizeof(*token),
705 cksum.checksum.data, cksum.checksum.length);
707 free_Checksum(&cksum);
708 free(buf);
710 *minor_status = 0;
711 return GSS_S_COMPLETE;
714 OM_uint32 _gssapi_verify_mic_cfx(OM_uint32 *minor_status,
715 const gss_ctx_id_t context_handle,
716 const gss_buffer_t message_buffer,
717 const gss_buffer_t token_buffer,
718 gss_qop_t *qop_state,
719 krb5_keyblock *key)
721 krb5_crypto crypto;
722 gss_cfx_mic_token token;
723 u_char token_flags;
724 krb5_error_code ret;
725 unsigned usage;
726 OM_uint32 seq_number_lo, seq_number_hi;
727 u_char *buf, *p;
728 Checksum cksum;
730 *minor_status = 0;
732 if (token_buffer->length < sizeof(*token)) {
733 return GSS_S_DEFECTIVE_TOKEN;
736 p = token_buffer->value;
738 token = (gss_cfx_mic_token)p;
740 if (token->TOK_ID[0] != 0x04 || token->TOK_ID[1] != 0x04) {
741 return GSS_S_DEFECTIVE_TOKEN;
744 /* Ignore unknown flags */
745 token_flags = token->Flags & (CFXSentByAcceptor | CFXAcceptorSubkey);
747 if (token_flags & CFXSentByAcceptor) {
748 if ((context_handle->more_flags & LOCAL) == 0)
749 return GSS_S_DEFECTIVE_TOKEN;
751 if (context_handle->more_flags & ACCEPTOR_SUBKEY) {
752 if ((token_flags & CFXAcceptorSubkey) == 0)
753 return GSS_S_DEFECTIVE_TOKEN;
754 } else {
755 if (token_flags & CFXAcceptorSubkey)
756 return GSS_S_DEFECTIVE_TOKEN;
759 if (memcmp(token->Filler, "\xff\xff\xff\xff\xff", 5) != 0) {
760 return GSS_S_DEFECTIVE_TOKEN;
764 * Check sequence number
766 gssapi_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi);
767 gssapi_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo);
768 if (seq_number_hi) {
769 *minor_status = ERANGE;
770 return GSS_S_UNSEQ_TOKEN;
773 HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
774 ret = _gssapi_msg_order_check(context_handle->order, seq_number_lo);
775 if (ret != 0) {
776 *minor_status = 0;
777 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
778 return ret;
780 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
783 * Verify checksum
785 ret = krb5_crypto_init(gssapi_krb5_context, key, 0, &crypto);
786 if (ret != 0) {
787 gssapi_krb5_set_error_string();
788 *minor_status = ret;
789 return GSS_S_FAILURE;
792 ret = krb5_crypto_get_checksum_type(gssapi_krb5_context, crypto,
793 &cksum.cksumtype);
794 if (ret != 0) {
795 gssapi_krb5_set_error_string();
796 *minor_status = ret;
797 krb5_crypto_destroy(gssapi_krb5_context, crypto);
798 return GSS_S_FAILURE;
801 cksum.checksum.data = p + sizeof(*token);
802 cksum.checksum.length = token_buffer->length - sizeof(*token);
804 if (context_handle->more_flags & LOCAL) {
805 usage = KRB5_KU_USAGE_ACCEPTOR_SIGN;
806 } else {
807 usage = KRB5_KU_USAGE_INITIATOR_SIGN;
810 buf = malloc(message_buffer->length + sizeof(*token));
811 if (buf == NULL) {
812 *minor_status = ENOMEM;
813 krb5_crypto_destroy(gssapi_krb5_context, crypto);
814 return GSS_S_FAILURE;
816 memcpy(buf, message_buffer->value, message_buffer->length);
817 memcpy(buf + message_buffer->length, token, sizeof(*token));
819 ret = krb5_verify_checksum(gssapi_krb5_context, crypto,
820 usage,
821 buf,
822 sizeof(*token) + message_buffer->length,
823 &cksum);
824 if (ret != 0) {
825 gssapi_krb5_set_error_string();
826 *minor_status = ret;
827 krb5_crypto_destroy(gssapi_krb5_context, crypto);
828 free(buf);
829 return GSS_S_BAD_MIC;
832 free(buf);
834 if (qop_state != NULL) {
835 *qop_state = GSS_C_QOP_DEFAULT;
838 return GSS_S_COMPLETE;