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