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