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