s4:heimdal: import lorikeet-heimdal-200906080040 (commit 904d0124b46eed7a8ad6e5b73e89...
[samba.git] / source4 / heimdal / lib / gssapi / krb5 / cfx.c
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 RCSID("$Id$");
36
37 /*
38  * Implementation of draft-ietf-krb-wg-gssapi-cfx-06.txt
39  */
40
41 #define CFXSentByAcceptor       (1 << 0)
42 #define CFXSealed               (1 << 1)
43 #define CFXAcceptorSubkey       (1 << 2)
44
45 krb5_error_code
46 _gsskrb5cfx_wrap_length_cfx(const gsskrb5_ctx context_handle,
47                             krb5_context context,
48                             krb5_crypto crypto,
49                             int conf_req_flag,
50                             size_t input_length,
51                             size_t *output_length,
52                             size_t *cksumsize,
53                             uint16_t *padlength)
54 {
55     krb5_error_code ret;
56     krb5_cksumtype type;
57
58     /* 16-byte header is always first */
59     *output_length = sizeof(gss_cfx_wrap_token_desc);
60     *padlength = 0;
61
62     ret = krb5_crypto_get_checksum_type(context, crypto, &type);
63     if (ret)
64         return ret;
65
66     ret = krb5_checksumsize(context, type, cksumsize);
67     if (ret)
68         return ret;
69
70     if (conf_req_flag) {
71         size_t padsize;
72
73         /* Header is concatenated with data before encryption */
74         input_length += sizeof(gss_cfx_wrap_token_desc);
75
76         if (IS_DCE_STYLE(context_handle)) {
77                 ret = krb5_crypto_getblocksize(context, crypto, &padsize);
78         } else {
79                 ret = krb5_crypto_getpadsize(context, crypto, &padsize);
80         }
81         if (ret) {
82             return ret;
83         }
84         if (padsize > 1) {
85             /* XXX check this */
86             *padlength = padsize - (input_length % padsize);
87
88             /* We add the pad ourselves (noted here for completeness only) */
89             input_length += *padlength;
90         }
91
92         *output_length += krb5_get_wrapped_length(context,
93                                                   crypto, input_length);
94     } else {
95         /* Checksum is concatenated with data */
96         *output_length += input_length + *cksumsize;
97     }
98
99     assert(*output_length > input_length);
100
101     return 0;
102 }
103
104 OM_uint32 _gssapi_wrap_size_cfx(OM_uint32 *minor_status,
105                                 const gsskrb5_ctx ctx,
106                                 krb5_context context,
107                                 int conf_req_flag,
108                                 gss_qop_t qop_req,
109                                 OM_uint32 req_output_size,
110                                 OM_uint32 *max_input_size)
111 {
112     krb5_error_code ret;
113
114     *max_input_size = 0;
115
116     /* 16-byte header is always first */
117     if (req_output_size < 16)
118         return 0;
119     req_output_size -= 16;
120
121     if (conf_req_flag) {
122         size_t wrapped_size, sz;
123
124         wrapped_size = req_output_size + 1;
125         do {
126             wrapped_size--;
127             sz = krb5_get_wrapped_length(context,
128                                          ctx->crypto, wrapped_size);
129         } while (wrapped_size && sz > req_output_size);
130         if (wrapped_size == 0)
131             return 0;
132
133         /* inner header */
134         if (wrapped_size < 16)
135             return 0;
136
137         wrapped_size -= 16;
138
139         *max_input_size = wrapped_size;
140     } else {
141         krb5_cksumtype type;
142         size_t cksumsize;
143
144         ret = krb5_crypto_get_checksum_type(context, ctx->crypto, &type);
145         if (ret)
146             return ret;
147
148         ret = krb5_checksumsize(context, type, &cksumsize);
149         if (ret)
150             return ret;
151
152         if (req_output_size < cksumsize)
153             return 0;
154
155         /* Checksum is concatenated with data */
156         *max_input_size = req_output_size - cksumsize;
157     }
158
159     return 0;
160 }
161
162 /*
163  * Rotate "rrc" bytes to the front or back
164  */
165
166 static krb5_error_code
167 rrc_rotate(void *data, size_t len, uint16_t rrc, krb5_boolean unrotate)
168 {
169     u_char *tmp, buf[256];
170     size_t left;
171
172     if (len == 0)
173         return 0;
174
175     rrc %= len;
176
177     if (rrc == 0)
178         return 0;
179
180     left = len - rrc;
181
182     if (rrc <= sizeof(buf)) {
183         tmp = buf;
184     } else {
185         tmp = malloc(rrc);
186         if (tmp == NULL)
187             return ENOMEM;
188     }
189
190     if (unrotate) {
191         memcpy(tmp, data, rrc);
192         memmove(data, (u_char *)data + rrc, left);
193         memcpy((u_char *)data + left, tmp, rrc);
194     } else {
195         memcpy(tmp, (u_char *)data + left, rrc);
196         memmove((u_char *)data + rrc, data, left);
197         memcpy(data, tmp, rrc);
198     }
199
200     if (rrc > sizeof(buf))
201         free(tmp);
202
203     return 0;
204 }
205
206 OM_uint32 _gssapi_wrap_cfx(OM_uint32 *minor_status,
207                            const gsskrb5_ctx ctx,
208                            krb5_context context,
209                            int conf_req_flag,
210                            gss_qop_t qop_req,
211                            const gss_buffer_t input_message_buffer,
212                            int *conf_state,
213                            gss_buffer_t output_message_buffer)
214 {
215     gss_cfx_wrap_token token;
216     krb5_error_code ret;
217     unsigned usage;
218     krb5_data cipher;
219     size_t wrapped_len, cksumsize;
220     uint16_t padlength, rrc = 0;
221     int32_t seq_number;
222     u_char *p;
223
224     ret = _gsskrb5cfx_wrap_length_cfx(ctx, context,
225                                       ctx->crypto, conf_req_flag,
226                                       input_message_buffer->length,
227                                       &wrapped_len, &cksumsize, &padlength);
228     if (ret != 0) {
229         *minor_status = ret;
230         return GSS_S_FAILURE;
231     }
232
233     /* Always rotate encrypted token (if any) and checksum to header */
234     rrc = (conf_req_flag ? sizeof(*token) : 0) + (uint16_t)cksumsize;
235
236     output_message_buffer->length = wrapped_len;
237     output_message_buffer->value = malloc(output_message_buffer->length);
238     if (output_message_buffer->value == NULL) {
239         *minor_status = ENOMEM;
240         return GSS_S_FAILURE;
241     }
242
243     p = output_message_buffer->value;
244     token = (gss_cfx_wrap_token)p;
245     token->TOK_ID[0] = 0x05;
246     token->TOK_ID[1] = 0x04;
247     token->Flags     = 0;
248     token->Filler    = 0xFF;
249     if ((ctx->more_flags & LOCAL) == 0)
250         token->Flags |= CFXSentByAcceptor;
251     if (ctx->more_flags & ACCEPTOR_SUBKEY)
252         token->Flags |= CFXAcceptorSubkey;
253     if (conf_req_flag) {
254         /*
255          * In Wrap tokens with confidentiality, the EC field is
256          * used to encode the size (in bytes) of the random filler.
257          */
258         token->Flags |= CFXSealed;
259         token->EC[0] = (padlength >> 8) & 0xFF;
260         token->EC[1] = (padlength >> 0) & 0xFF;
261     } else {
262         /*
263          * In Wrap tokens without confidentiality, the EC field is
264          * used to encode the size (in bytes) of the trailing
265          * checksum.
266          *
267          * This is not used in the checksum calcuation itself,
268          * because the checksum length could potentially vary
269          * depending on the data length.
270          */
271         token->EC[0] = 0;
272         token->EC[1] = 0;
273     }
274
275     /*
276      * In Wrap tokens that provide for confidentiality, the RRC
277      * field in the header contains the hex value 00 00 before
278      * encryption.
279      *
280      * In Wrap tokens that do not provide for confidentiality,
281      * both the EC and RRC fields in the appended checksum
282      * contain the hex value 00 00 for the purpose of calculating
283      * the checksum.
284      */
285     token->RRC[0] = 0;
286     token->RRC[1] = 0;
287
288     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
289     krb5_auth_con_getlocalseqnumber(context,
290                                     ctx->auth_context,
291                                     &seq_number);
292     _gsskrb5_encode_be_om_uint32(0,          &token->SND_SEQ[0]);
293     _gsskrb5_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]);
294     krb5_auth_con_setlocalseqnumber(context,
295                                     ctx->auth_context,
296                                     ++seq_number);
297     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
298
299     /*
300      * If confidentiality is requested, the token header is
301      * appended to the plaintext before encryption; the resulting
302      * token is {"header" | encrypt(plaintext | pad | "header")}.
303      *
304      * If no confidentiality is requested, the checksum is
305      * calculated over the plaintext concatenated with the
306      * token header.
307      */
308     if (ctx->more_flags & LOCAL) {
309         usage = KRB5_KU_USAGE_INITIATOR_SEAL;
310     } else {
311         usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
312     }
313
314     if (conf_req_flag) {
315         /*
316          * Any necessary padding is added here to ensure that the
317          * encrypted token header is always at the end of the
318          * ciphertext.
319          *
320          * The specification does not require that the padding
321          * bytes are initialized.
322          */
323         p += sizeof(*token);
324         memcpy(p, input_message_buffer->value, input_message_buffer->length);
325         memset(p + input_message_buffer->length, 0xFF, padlength);
326         memcpy(p + input_message_buffer->length + padlength,
327                token, sizeof(*token));
328
329         ret = krb5_encrypt(context, ctx->crypto,
330                            usage, p,
331                            input_message_buffer->length + padlength +
332                                 sizeof(*token),
333                            &cipher);
334         if (ret != 0) {
335             *minor_status = ret;
336             _gsskrb5_release_buffer(minor_status, output_message_buffer);
337             return GSS_S_FAILURE;
338         }
339         assert(sizeof(*token) + cipher.length == wrapped_len);
340         token->RRC[0] = (rrc >> 8) & 0xFF;
341         token->RRC[1] = (rrc >> 0) & 0xFF;
342
343         /*
344          * this is really ugly, but needed against windows
345          * for DCERPC, as windows rotates by EC+RRC.
346          */
347         if (IS_DCE_STYLE(ctx)) {
348                 ret = rrc_rotate(cipher.data, cipher.length, rrc+padlength, FALSE);
349         } else {
350                 ret = rrc_rotate(cipher.data, cipher.length, rrc, FALSE);
351         }
352         if (ret != 0) {
353             *minor_status = ret;
354             _gsskrb5_release_buffer(minor_status, output_message_buffer);
355             return GSS_S_FAILURE;
356         }
357         memcpy(p, cipher.data, cipher.length);
358         krb5_data_free(&cipher);
359     } else {
360         char *buf;
361         Checksum cksum;
362
363         buf = malloc(input_message_buffer->length + sizeof(*token));
364         if (buf == NULL) {
365             *minor_status = ENOMEM;
366             _gsskrb5_release_buffer(minor_status, output_message_buffer);
367             return GSS_S_FAILURE;
368         }
369         memcpy(buf, input_message_buffer->value, input_message_buffer->length);
370         memcpy(buf + input_message_buffer->length, token, sizeof(*token));
371
372         ret = krb5_create_checksum(context, ctx->crypto,
373                                    usage, 0, buf,
374                                    input_message_buffer->length +
375                                         sizeof(*token),
376                                    &cksum);
377         if (ret != 0) {
378             *minor_status = ret;
379             _gsskrb5_release_buffer(minor_status, output_message_buffer);
380             free(buf);
381             return GSS_S_FAILURE;
382         }
383
384         free(buf);
385
386         assert(cksum.checksum.length == cksumsize);
387         token->EC[0] =  (cksum.checksum.length >> 8) & 0xFF;
388         token->EC[1] =  (cksum.checksum.length >> 0) & 0xFF;
389         token->RRC[0] = (rrc >> 8) & 0xFF;
390         token->RRC[1] = (rrc >> 0) & 0xFF;
391
392         p += sizeof(*token);
393         memcpy(p, input_message_buffer->value, input_message_buffer->length);
394         memcpy(p + input_message_buffer->length,
395                cksum.checksum.data, cksum.checksum.length);
396
397         ret = rrc_rotate(p,
398             input_message_buffer->length + cksum.checksum.length, rrc, FALSE);
399         if (ret != 0) {
400             *minor_status = ret;
401             _gsskrb5_release_buffer(minor_status, output_message_buffer);
402             free_Checksum(&cksum);
403             return GSS_S_FAILURE;
404         }
405         free_Checksum(&cksum);
406     }
407
408     if (conf_state != NULL) {
409         *conf_state = conf_req_flag;
410     }
411
412     *minor_status = 0;
413     return GSS_S_COMPLETE;
414 }
415
416 OM_uint32 _gssapi_unwrap_cfx(OM_uint32 *minor_status,
417                              const gsskrb5_ctx ctx,
418                              krb5_context context,
419                              const gss_buffer_t input_message_buffer,
420                              gss_buffer_t output_message_buffer,
421                              int *conf_state,
422                              gss_qop_t *qop_state)
423 {
424     gss_cfx_wrap_token token;
425     u_char token_flags;
426     krb5_error_code ret;
427     unsigned usage;
428     krb5_data data;
429     uint16_t ec, rrc;
430     OM_uint32 seq_number_lo, seq_number_hi;
431     size_t len;
432     u_char *p;
433
434     *minor_status = 0;
435
436     if (input_message_buffer->length < sizeof(*token)) {
437         return GSS_S_DEFECTIVE_TOKEN;
438     }
439
440     p = input_message_buffer->value;
441
442     token = (gss_cfx_wrap_token)p;
443
444     if (token->TOK_ID[0] != 0x05 || token->TOK_ID[1] != 0x04) {
445         return GSS_S_DEFECTIVE_TOKEN;
446     }
447
448     /* Ignore unknown flags */
449     token_flags = token->Flags &
450         (CFXSentByAcceptor | CFXSealed | CFXAcceptorSubkey);
451
452     if (token_flags & CFXSentByAcceptor) {
453         if ((ctx->more_flags & LOCAL) == 0)
454             return GSS_S_DEFECTIVE_TOKEN;
455     }
456
457     if (ctx->more_flags & ACCEPTOR_SUBKEY) {
458         if ((token_flags & CFXAcceptorSubkey) == 0)
459             return GSS_S_DEFECTIVE_TOKEN;
460     } else {
461         if (token_flags & CFXAcceptorSubkey)
462             return GSS_S_DEFECTIVE_TOKEN;
463     }
464
465     if (token->Filler != 0xFF) {
466         return GSS_S_DEFECTIVE_TOKEN;
467     }
468
469     if (conf_state != NULL) {
470         *conf_state = (token_flags & CFXSealed) ? 1 : 0;
471     }
472
473     ec  = (token->EC[0]  << 8) | token->EC[1];
474     rrc = (token->RRC[0] << 8) | token->RRC[1];
475
476     /*
477      * Check sequence number
478      */
479     _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi);
480     _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo);
481     if (seq_number_hi) {
482         /* no support for 64-bit sequence numbers */
483         *minor_status = ERANGE;
484         return GSS_S_UNSEQ_TOKEN;
485     }
486
487     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
488     ret = _gssapi_msg_order_check(ctx->order, seq_number_lo);
489     if (ret != 0) {
490         *minor_status = 0;
491         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
492         _gsskrb5_release_buffer(minor_status, output_message_buffer);
493         return ret;
494     }
495     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
496
497     /*
498      * Decrypt and/or verify checksum
499      */
500
501     if (ctx->more_flags & LOCAL) {
502         usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
503     } else {
504         usage = KRB5_KU_USAGE_INITIATOR_SEAL;
505     }
506
507     p += sizeof(*token);
508     len = input_message_buffer->length;
509     len -= (p - (u_char *)input_message_buffer->value);
510
511     if (token_flags & CFXSealed) {
512         /*
513          * this is really ugly, but needed against windows
514          * for DCERPC, as windows rotates by EC+RRC.
515          */
516         if (IS_DCE_STYLE(ctx)) {
517                 *minor_status = rrc_rotate(p, len, rrc+ec, TRUE);
518         } else {
519                 *minor_status = rrc_rotate(p, len, rrc, TRUE);
520         }
521         if (*minor_status != 0) {
522             return GSS_S_FAILURE;
523         }
524
525         ret = krb5_decrypt(context, ctx->crypto, usage,
526             p, len, &data);
527         if (ret != 0) {
528             *minor_status = ret;
529             return GSS_S_BAD_MIC;
530         }
531
532         /* Check that there is room for the pad and token header */
533         if (data.length < ec + sizeof(*token)) {
534             krb5_data_free(&data);
535             return GSS_S_DEFECTIVE_TOKEN;
536         }
537         p = data.data;
538         p += data.length - sizeof(*token);
539
540         /* RRC is unprotected; don't modify input buffer */
541         ((gss_cfx_wrap_token)p)->RRC[0] = token->RRC[0];
542         ((gss_cfx_wrap_token)p)->RRC[1] = token->RRC[1];
543
544         /* Check the integrity of the header */
545         if (memcmp(p, token, sizeof(*token)) != 0) {
546             krb5_data_free(&data);
547             return GSS_S_BAD_MIC;
548         }
549
550         output_message_buffer->value = data.data;
551         output_message_buffer->length = data.length - ec - sizeof(*token);
552     } else {
553         Checksum cksum;
554
555         /* Rotate by RRC; bogus to do this in-place XXX */
556         *minor_status = rrc_rotate(p, len, rrc, TRUE);
557         if (*minor_status != 0) {
558             return GSS_S_FAILURE;
559         }
560
561         /* Determine checksum type */
562         ret = krb5_crypto_get_checksum_type(context,
563                                             ctx->crypto,
564                                             &cksum.cksumtype);
565         if (ret != 0) {
566             *minor_status = ret;
567             return GSS_S_FAILURE;
568         }
569
570         cksum.checksum.length = ec;
571
572         /* Check we have at least as much data as the checksum */
573         if (len < cksum.checksum.length) {
574             *minor_status = ERANGE;
575             return GSS_S_BAD_MIC;
576         }
577
578         /* Length now is of the plaintext only, no checksum */
579         len -= cksum.checksum.length;
580         cksum.checksum.data = p + len;
581
582         output_message_buffer->length = len; /* for later */
583         output_message_buffer->value = malloc(len + sizeof(*token));
584         if (output_message_buffer->value == NULL) {
585             *minor_status = ENOMEM;
586             return GSS_S_FAILURE;
587         }
588
589         /* Checksum is over (plaintext-data | "header") */
590         memcpy(output_message_buffer->value, p, len);
591         memcpy((u_char *)output_message_buffer->value + len,
592                token, sizeof(*token));
593
594         /* EC is not included in checksum calculation */
595         token = (gss_cfx_wrap_token)((u_char *)output_message_buffer->value +
596                                      len);
597         token->EC[0]  = 0;
598         token->EC[1]  = 0;
599         token->RRC[0] = 0;
600         token->RRC[1] = 0;
601
602         ret = krb5_verify_checksum(context, ctx->crypto,
603                                    usage,
604                                    output_message_buffer->value,
605                                    len + sizeof(*token),
606                                    &cksum);
607         if (ret != 0) {
608             *minor_status = ret;
609             _gsskrb5_release_buffer(minor_status, output_message_buffer);
610             return GSS_S_BAD_MIC;
611         }
612     }
613
614     if (qop_state != NULL) {
615         *qop_state = GSS_C_QOP_DEFAULT;
616     }
617
618     *minor_status = 0;
619     return GSS_S_COMPLETE;
620 }
621
622 OM_uint32 _gssapi_mic_cfx(OM_uint32 *minor_status,
623                           const gsskrb5_ctx ctx,
624                           krb5_context context,
625                           gss_qop_t qop_req,
626                           const gss_buffer_t message_buffer,
627                           gss_buffer_t message_token)
628 {
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;
636
637     len = message_buffer->length + sizeof(*token);
638     buf = malloc(len);
639     if (buf == NULL) {
640         *minor_status = ENOMEM;
641         return GSS_S_FAILURE;
642     }
643
644     memcpy(buf, message_buffer->value, message_buffer->length);
645
646     token = (gss_cfx_mic_token)(buf + message_buffer->length);
647     token->TOK_ID[0] = 0x04;
648     token->TOK_ID[1] = 0x04;
649     token->Flags = 0;
650     if ((ctx->more_flags & LOCAL) == 0)
651         token->Flags |= CFXSentByAcceptor;
652     if (ctx->more_flags & ACCEPTOR_SUBKEY)
653         token->Flags |= CFXAcceptorSubkey;
654     memset(token->Filler, 0xFF, 5);
655
656     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
657     krb5_auth_con_getlocalseqnumber(context,
658                                     ctx->auth_context,
659                                     &seq_number);
660     _gsskrb5_encode_be_om_uint32(0,          &token->SND_SEQ[0]);
661     _gsskrb5_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]);
662     krb5_auth_con_setlocalseqnumber(context,
663                                     ctx->auth_context,
664                                     ++seq_number);
665     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
666
667     if (ctx->more_flags & LOCAL) {
668         usage = KRB5_KU_USAGE_INITIATOR_SIGN;
669     } else {
670         usage = KRB5_KU_USAGE_ACCEPTOR_SIGN;
671     }
672
673     ret = krb5_create_checksum(context, ctx->crypto,
674         usage, 0, buf, len, &cksum);
675     if (ret != 0) {
676         *minor_status = ret;
677         free(buf);
678         return GSS_S_FAILURE;
679     }
680
681     /* Determine MIC length */
682     message_token->length = sizeof(*token) + cksum.checksum.length;
683     message_token->value = malloc(message_token->length);
684     if (message_token->value == NULL) {
685         *minor_status = ENOMEM;
686         free_Checksum(&cksum);
687         free(buf);
688         return GSS_S_FAILURE;
689     }
690
691     /* Token is { "header" | get_mic("header" | plaintext-data) } */
692     memcpy(message_token->value, token, sizeof(*token));
693     memcpy((u_char *)message_token->value + sizeof(*token),
694            cksum.checksum.data, cksum.checksum.length);
695
696     free_Checksum(&cksum);
697     free(buf);
698
699     *minor_status = 0;
700     return GSS_S_COMPLETE;
701 }
702
703 OM_uint32 _gssapi_verify_mic_cfx(OM_uint32 *minor_status,
704                                  const gsskrb5_ctx ctx,
705                                  krb5_context context,
706                                  const gss_buffer_t message_buffer,
707                                  const gss_buffer_t token_buffer,
708                                  gss_qop_t *qop_state)
709 {
710     gss_cfx_mic_token token;
711     u_char token_flags;
712     krb5_error_code ret;
713     unsigned usage;
714     OM_uint32 seq_number_lo, seq_number_hi;
715     u_char *buf, *p;
716     Checksum cksum;
717
718     *minor_status = 0;
719
720     if (token_buffer->length < sizeof(*token)) {
721         return GSS_S_DEFECTIVE_TOKEN;
722     }
723
724     p = token_buffer->value;
725
726     token = (gss_cfx_mic_token)p;
727
728     if (token->TOK_ID[0] != 0x04 || token->TOK_ID[1] != 0x04) {
729         return GSS_S_DEFECTIVE_TOKEN;
730     }
731
732     /* Ignore unknown flags */
733     token_flags = token->Flags & (CFXSentByAcceptor | CFXAcceptorSubkey);
734
735     if (token_flags & CFXSentByAcceptor) {
736         if ((ctx->more_flags & LOCAL) == 0)
737             return GSS_S_DEFECTIVE_TOKEN;
738     }
739     if (ctx->more_flags & ACCEPTOR_SUBKEY) {
740         if ((token_flags & CFXAcceptorSubkey) == 0)
741             return GSS_S_DEFECTIVE_TOKEN;
742     } else {
743         if (token_flags & CFXAcceptorSubkey)
744             return GSS_S_DEFECTIVE_TOKEN;
745     }
746
747     if (memcmp(token->Filler, "\xff\xff\xff\xff\xff", 5) != 0) {
748         return GSS_S_DEFECTIVE_TOKEN;
749     }
750
751     /*
752      * Check sequence number
753      */
754     _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi);
755     _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo);
756     if (seq_number_hi) {
757         *minor_status = ERANGE;
758         return GSS_S_UNSEQ_TOKEN;
759     }
760
761     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
762     ret = _gssapi_msg_order_check(ctx->order, seq_number_lo);
763     if (ret != 0) {
764         *minor_status = 0;
765         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
766         return ret;
767     }
768     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
769
770     /*
771      * Verify checksum
772      */
773     ret = krb5_crypto_get_checksum_type(context, ctx->crypto,
774                                         &cksum.cksumtype);
775     if (ret != 0) {
776         *minor_status = ret;
777         return GSS_S_FAILURE;
778     }
779
780     cksum.checksum.data = p + sizeof(*token);
781     cksum.checksum.length = token_buffer->length - sizeof(*token);
782
783     if (ctx->more_flags & LOCAL) {
784         usage = KRB5_KU_USAGE_ACCEPTOR_SIGN;
785     } else {
786         usage = KRB5_KU_USAGE_INITIATOR_SIGN;
787     }
788
789     buf = malloc(message_buffer->length + sizeof(*token));
790     if (buf == NULL) {
791         *minor_status = ENOMEM;
792         return GSS_S_FAILURE;
793     }
794     memcpy(buf, message_buffer->value, message_buffer->length);
795     memcpy(buf + message_buffer->length, token, sizeof(*token));
796
797     ret = krb5_verify_checksum(context, ctx->crypto,
798                                usage,
799                                buf,
800                                sizeof(*token) + message_buffer->length,
801                                &cksum);
802     if (ret != 0) {
803         *minor_status = ret;
804         free(buf);
805         return GSS_S_BAD_MIC;
806     }
807
808     free(buf);
809
810     if (qop_state != NULL) {
811         *qop_state = GSS_C_QOP_DEFAULT;
812     }
813
814     return GSS_S_COMPLETE;
815 }