do not ignore sequenceCheck() return value
[mech_eap.git] / mech_eap / unwrap_iov.c
1 /*
2  * Copyright (c) 2011, JANET(UK)
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 JANET(UK) 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND 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 THE COPYRIGHT HOLDER 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  * Copyright 2008 by the Massachusetts Institute of Technology.
34  * All Rights Reserved.
35  *
36  * Export of this software from the United States of America may
37  *   require a specific license from the United States Government.
38  *   It is the responsibility of any person or organization contemplating
39  *   export to obtain such a license before exporting.
40  *
41  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
42  * distribute this software and its documentation for any purpose and
43  * without fee is hereby granted, provided that the above copyright
44  * notice appear in all copies and that both that copyright notice and
45  * this permission notice appear in supporting documentation, and that
46  * the name of M.I.T. not be used in advertising or publicity pertaining
47  * to distribution of the software without specific, written prior
48  * permission.  Furthermore if you modify this software you must label
49  * your software as modified software and not distribute it in such a
50  * fashion that it might be confused with the original M.I.T. software.
51  * M.I.T. makes no representations about the suitability of
52  * this software for any purpose.  It is provided "as is" without express
53  * or implied warranty.
54  */
55
56 /*
57  * Message protection services: unwrap with scatter-gather API.
58  */
59
60 #include "gssapiP_eap.h"
61
62 /*
63  * Caller must provide TOKEN | DATA | PADDING | TRAILER, except
64  * for DCE in which case it can just provide TOKEN | DATA (must
65  * guarantee that DATA is padded)
66  */
67 OM_uint32
68 unwrapToken(OM_uint32 *minor,
69             gss_ctx_id_t ctx,
70 #ifdef HAVE_HEIMDAL_VERSION
71             krb5_crypto krbCrypto,
72 #else
73             krb5_keyblock *unused GSSEAP_UNUSED,
74 #endif
75             int *conf_state,
76             gss_qop_t *qop_state,
77             gss_iov_buffer_desc *iov,
78             int iov_count,
79             enum gss_eap_token_type toktype)
80 {
81     OM_uint32 major = GSS_S_FAILURE, code;
82     gss_iov_buffer_t header;
83     gss_iov_buffer_t padding;
84     gss_iov_buffer_t trailer;
85     unsigned char flags;
86     unsigned char *ptr = NULL;
87     int keyUsage;
88     size_t rrc, ec;
89     size_t dataLen, assocDataLen;
90     uint64_t seqnum;
91     int valid = 0;
92     int conf_flag = 0;
93     krb5_context krbContext;
94 #ifdef HAVE_HEIMDAL_VERSION
95     int freeCrypto = (krbCrypto == NULL);
96 #endif
97
98     GSSEAP_KRB_INIT(&krbContext);
99
100     *minor = 0;
101
102     if (qop_state != NULL)
103         *qop_state = GSS_C_QOP_DEFAULT;
104
105     header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
106     GSSEAP_ASSERT(header != NULL);
107
108     padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
109     if (padding != NULL && padding->buffer.length != 0) {
110         code = GSSEAP_BAD_PADDING_IOV;
111         major = GSS_S_DEFECTIVE_TOKEN;
112         goto cleanup;
113     }
114
115     trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
116
117     flags = rfc4121Flags(ctx, TRUE);
118
119     if (toktype == TOK_TYPE_WRAP) {
120         keyUsage = !CTX_IS_INITIATOR(ctx)
121                    ? KEY_USAGE_INITIATOR_SEAL
122                    : KEY_USAGE_ACCEPTOR_SEAL;
123     } else {
124         keyUsage = !CTX_IS_INITIATOR(ctx)
125                    ? KEY_USAGE_INITIATOR_SIGN
126                    : KEY_USAGE_ACCEPTOR_SIGN;
127     }
128
129     gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen);
130
131     ptr = (unsigned char *)header->buffer.value;
132
133     if (header->buffer.length < 16) {
134         code = GSSEAP_TOK_TRUNC;
135         major = GSS_S_DEFECTIVE_TOKEN;
136         goto cleanup;
137     }
138
139     if ((ptr[2] & flags) != flags) {
140         code = GSSEAP_BAD_DIRECTION;
141         major = GSS_S_BAD_SIG;
142         goto cleanup;
143     }
144
145 #ifdef HAVE_HEIMDAL_VERSION
146     if (krbCrypto == NULL) {
147         code = krb5_crypto_init(krbContext, &ctx->rfc3961Key,
148                                 ETYPE_NULL, &krbCrypto);
149         if (code != 0)
150             goto cleanup;
151     }
152 #endif
153
154     if (toktype == TOK_TYPE_WRAP) {
155         size_t krbTrailerLen;
156
157         if (load_uint16_be(ptr) != TOK_TYPE_WRAP)
158             goto defective;
159         conf_flag = ((ptr[2] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0);
160         if (ptr[3] != 0xFF)
161             goto defective;
162         ec = load_uint16_be(ptr + 4);
163         rrc = load_uint16_be(ptr + 6);
164         seqnum = load_uint64_be(ptr + 8);
165
166         code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
167                                conf_flag ? KRB5_CRYPTO_TYPE_TRAILER :
168                                            KRB5_CRYPTO_TYPE_CHECKSUM,
169                                &krbTrailerLen);
170         if (code != 0)
171             goto cleanup;
172
173         /* Deal with RRC */
174         if (trailer == NULL) {
175             size_t desired_rrc = krbTrailerLen;
176
177             if (conf_flag) {
178                 desired_rrc += 16; /* E(Header) */
179
180                 if ((ctx->gssFlags & GSS_C_DCE_STYLE) == 0)
181                     desired_rrc += ec;
182             }
183
184             /* According to MS, we only need to deal with a fixed RRC for DCE */
185             if (rrc != desired_rrc)
186                 goto defective;
187         } else if (rrc != 0) {
188             goto defective;
189         }
190
191         if (conf_flag) {
192             unsigned char *althdr;
193
194             /* Decrypt */
195             code = gssEapDecrypt(krbContext,
196                                  ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0),
197                                  ec, rrc, KRB_CRYPTO_CONTEXT(ctx), keyUsage,
198                                  iov, iov_count);
199             if (code != 0) {
200                 major = GSS_S_BAD_SIG;
201                 goto cleanup;
202             }
203
204             /* Validate header integrity */
205             if (trailer == NULL)
206                 althdr = (unsigned char *)header->buffer.value + 16 + ec;
207             else
208                 althdr = (unsigned char *)trailer->buffer.value + ec;
209
210             if (load_uint16_be(althdr) != TOK_TYPE_WRAP
211                 || althdr[2] != ptr[2]
212                 || althdr[3] != ptr[3]
213                 || memcmp(althdr + 8, ptr + 8, 8) != 0) {
214                 code = GSSEAP_BAD_WRAP_TOKEN;
215                 major = GSS_S_BAD_SIG;
216                 goto cleanup;
217             }
218         } else {
219             /* Verify checksum: note EC is checksum size here, not padding */
220             if (ec != krbTrailerLen)
221                 goto defective;
222
223             /* Zero EC, RRC before computing checksum */
224             store_uint16_be(0, ptr + 4);
225             store_uint16_be(0, ptr + 6);
226
227             code = gssEapVerify(krbContext, ctx->checksumType, rrc,
228                                 KRB_CRYPTO_CONTEXT(ctx), keyUsage,
229                                 iov, iov_count, &valid);
230             if (code != 0 || valid == FALSE) {
231                 major = GSS_S_BAD_SIG;
232                 goto cleanup;
233             }
234         }
235
236         major = sequenceCheck(&code, &ctx->seqState, seqnum);
237         if (GSS_ERROR(major))
238             goto cleanup;
239     } else if (toktype == TOK_TYPE_MIC) {
240         if (load_uint16_be(ptr) != toktype)
241             goto defective;
242
243     verify_mic_1:
244         if (ptr[3] != 0xFF)
245             goto defective;
246         seqnum = load_uint64_be(ptr + 8);
247
248         /*
249          * Although MIC tokens don't have a RRC, they are similarly
250          * composed of a header and a checksum. So the verify_mic()
251          * can be implemented with a single header buffer, fake the
252          * RRC to the putative trailer length if no trailer buffer.
253          */
254         code = gssEapVerify(krbContext, ctx->checksumType,
255                             trailer != NULL ? 0 : header->buffer.length - 16,
256                             KRB_CRYPTO_CONTEXT(ctx), keyUsage,
257                             iov, iov_count, &valid);
258         if (code != 0 || valid == FALSE) {
259             major = GSS_S_BAD_SIG;
260             goto cleanup;
261         }
262         major = sequenceCheck(&code, &ctx->seqState, seqnum);
263         if (GSS_ERROR(major))
264             goto cleanup;
265     } else if (toktype == TOK_TYPE_DELETE_CONTEXT) {
266         if (load_uint16_be(ptr) != TOK_TYPE_DELETE_CONTEXT)
267             goto defective;
268         goto verify_mic_1;
269     } else {
270         goto defective;
271     }
272
273     if (conf_state != NULL)
274         *conf_state = conf_flag;
275
276     code = 0;
277     major = GSS_S_COMPLETE;
278     goto cleanup;
279
280 defective:
281     code = GSSEAP_BAD_WRAP_TOKEN;
282     major = GSS_S_DEFECTIVE_TOKEN;
283
284 cleanup:
285     *minor = code;
286 #ifdef HAVE_HEIMDAL_VERSION
287     if (freeCrypto && krbCrypto != NULL)
288         krb5_crypto_destroy(krbContext, krbCrypto);
289 #endif
290
291     return major;
292 }
293
294 int
295 rotateLeft(void *ptr, size_t bufsiz, size_t rc)
296 {
297     void *tbuf;
298
299     if (bufsiz == 0)
300         return 0;
301     rc = rc % bufsiz;
302     if (rc == 0)
303         return 0;
304
305     tbuf = GSSEAP_MALLOC(rc);
306     if (tbuf == NULL)
307         return ENOMEM;
308
309     memcpy(tbuf, ptr, rc);
310     memmove(ptr, (char *)ptr + rc, bufsiz - rc);
311     memcpy((char *)ptr + bufsiz - rc, tbuf, rc);
312     GSSEAP_FREE(tbuf);
313
314     return 0;
315 }
316
317 /*
318  * Split a STREAM | SIGN_DATA | DATA into
319  *         HEADER | SIGN_DATA | DATA | PADDING | TRAILER
320  */
321 static OM_uint32
322 unwrapStream(OM_uint32 *minor,
323              gss_ctx_id_t ctx,
324              int *conf_state,
325              gss_qop_t *qop_state,
326              gss_iov_buffer_desc *iov,
327              int iov_count,
328              enum gss_eap_token_type toktype)
329 {
330     unsigned char *ptr;
331     OM_uint32 code = 0, major = GSS_S_FAILURE;
332     krb5_context krbContext;
333     int conf_req_flag;
334     int i = 0, j;
335     gss_iov_buffer_desc *tiov = NULL;
336     gss_iov_buffer_t stream, data = NULL;
337     gss_iov_buffer_t theader, tdata = NULL, tpadding, ttrailer;
338 #ifdef HAVE_HEIMDAL_VERSION
339     krb5_crypto krbCrypto = NULL;
340 #endif
341
342     GSSEAP_KRB_INIT(&krbContext);
343
344     GSSEAP_ASSERT(toktype == TOK_TYPE_WRAP);
345
346     if (toktype != TOK_TYPE_WRAP) {
347         code = GSSEAP_WRONG_TOK_ID;
348         goto cleanup;
349     }
350
351     stream = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM);
352     GSSEAP_ASSERT(stream != NULL);
353
354     if (stream->buffer.length < 16) {
355         major = GSS_S_DEFECTIVE_TOKEN;
356         goto cleanup;
357     }
358
359     ptr = (unsigned char *)stream->buffer.value;
360     ptr += 2; /* skip token type */
361
362     tiov = (gss_iov_buffer_desc *)GSSEAP_CALLOC((size_t)iov_count + 2,
363                                                 sizeof(gss_iov_buffer_desc));
364     if (tiov == NULL) {
365         code = ENOMEM;
366         goto cleanup;
367     }
368
369     /* HEADER */
370     theader = &tiov[i++];
371     theader->type = GSS_IOV_BUFFER_TYPE_HEADER;
372     theader->buffer.value = stream->buffer.value;
373     theader->buffer.length = 16;
374
375     /* n[SIGN_DATA] | DATA | m[SIGN_DATA] */
376     for (j = 0; j < iov_count; j++) {
377         OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[j].type);
378
379         if (type == GSS_IOV_BUFFER_TYPE_DATA) {
380             if (data != NULL) {
381                 /* only a single DATA buffer can appear */
382                 code = GSSEAP_BAD_STREAM_IOV;
383                 goto cleanup;
384             }
385
386             data = &iov[j];
387             tdata = &tiov[i];
388         }
389         if (type == GSS_IOV_BUFFER_TYPE_DATA ||
390             type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
391             tiov[i++] = iov[j];
392     }
393
394     if (data == NULL) {
395         /* a single DATA buffer must be present */
396         code = GSSEAP_BAD_STREAM_IOV;
397         goto cleanup;
398     }
399
400     /* PADDING | TRAILER */
401     tpadding = &tiov[i++];
402     tpadding->type = GSS_IOV_BUFFER_TYPE_PADDING;
403     tpadding->buffer.length = 0;
404     tpadding->buffer.value = NULL;
405
406     ttrailer = &tiov[i++];
407     ttrailer->type = GSS_IOV_BUFFER_TYPE_TRAILER;
408
409 #ifdef HAVE_HEIMDAL_VERSION
410     code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, ETYPE_NULL, &krbCrypto);
411     if (code != 0)
412         goto cleanup;
413 #endif
414
415     {
416         size_t ec, rrc;
417         size_t krbHeaderLen = 0;
418         size_t krbTrailerLen = 0;
419
420         conf_req_flag = ((ptr[0] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0);
421         ec = conf_req_flag ? load_uint16_be(ptr + 2) : 0;
422         rrc = load_uint16_be(ptr + 4);
423
424         if (rrc != 0) {
425             code = rotateLeft((unsigned char *)stream->buffer.value + 16,
426                               stream->buffer.length - 16, rrc);
427             if (code != 0)
428                 goto cleanup;
429             store_uint16_be(0, ptr + 4); /* set RRC to zero */
430         }
431
432         if (conf_req_flag) {
433             code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
434                                     KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen);
435             if (code != 0)
436                 goto cleanup;
437             theader->buffer.length += krbHeaderLen; /* length validated later */
438         }
439
440         /* no PADDING for CFX, EC is used instead */
441         code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
442                                conf_req_flag
443                                   ? KRB5_CRYPTO_TYPE_TRAILER
444                                   : KRB5_CRYPTO_TYPE_CHECKSUM,
445                                &krbTrailerLen);
446         if (code != 0)
447             goto cleanup;
448
449         ttrailer->buffer.length = ec + (conf_req_flag ? 16 : 0 /* E(Header) */) +
450                                   krbTrailerLen;
451         ttrailer->buffer.value = (unsigned char *)stream->buffer.value +
452             stream->buffer.length - ttrailer->buffer.length;
453     }
454
455     /* IOV: -----------0-------------+---1---+--2--+----------------3--------------*/
456     /* CFX: GSS-Header | Kerb-Header | Data  |     | EC | E(Header) | Kerb-Trailer */
457     /* GSS: -------GSS-HEADER--------+-DATA--+-PAD-+----------GSS-TRAILER----------*/
458
459     /* validate lengths */
460     if (stream->buffer.length < theader->buffer.length +
461         tpadding->buffer.length +
462         ttrailer->buffer.length) {
463         major = GSS_S_DEFECTIVE_TOKEN;
464         code = GSSEAP_TOK_TRUNC;
465         goto cleanup;
466     }
467
468     /* setup data */
469     tdata->buffer.length = stream->buffer.length - ttrailer->buffer.length -
470         tpadding->buffer.length - theader->buffer.length;
471
472     GSSEAP_ASSERT(data != NULL);
473
474     if (data->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
475         code = gssEapAllocIov(tdata, tdata->buffer.length);
476         if (code != 0)
477             goto cleanup;
478
479         memcpy(tdata->buffer.value,
480                (unsigned char *)stream->buffer.value + theader->buffer.length,
481                tdata->buffer.length);
482     } else {
483         tdata->buffer.value = (unsigned char *)stream->buffer.value +
484                               theader->buffer.length;
485     }
486
487     GSSEAP_ASSERT(i <= iov_count + 2);
488
489     major = unwrapToken(&code, ctx, KRB_CRYPTO_CONTEXT(ctx),
490                         conf_state, qop_state, tiov, i, toktype);
491     if (major == GSS_S_COMPLETE) {
492         *data = *tdata;
493     } else if (tdata->type & GSS_IOV_BUFFER_FLAG_ALLOCATED) {
494         OM_uint32 tmp;
495
496         gss_release_buffer(&tmp, &tdata->buffer);
497         tdata->type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED);
498     }
499
500 cleanup:
501     if (tiov != NULL)
502         GSSEAP_FREE(tiov);
503 #ifdef HAVE_HEIMDAL_VERSION
504     if (krbCrypto != NULL)
505         krb5_crypto_destroy(krbContext, krbCrypto);
506 #endif
507
508     *minor = code;
509
510     return major;
511 }
512
513 OM_uint32
514 gssEapUnwrapOrVerifyMIC(OM_uint32 *minor,
515                         gss_ctx_id_t ctx,
516                         int *conf_state,
517                         gss_qop_t *qop_state,
518                         gss_iov_buffer_desc *iov,
519                         int iov_count,
520                         enum gss_eap_token_type toktype)
521 {
522     OM_uint32 major;
523
524     if (ctx->encryptionType == ENCTYPE_NULL) {
525         *minor = GSSEAP_KEY_UNAVAILABLE;
526         return GSS_S_UNAVAILABLE;
527     }
528
529     if (gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM) != NULL) {
530         major = unwrapStream(minor, ctx, conf_state, qop_state,
531                              iov, iov_count, toktype);
532     } else {
533         major = unwrapToken(minor, ctx,
534                             NULL, /* krbCrypto */
535                             conf_state, qop_state,
536                             iov, iov_count, toktype);
537     }
538
539     return major;
540 }
541
542 OM_uint32 GSSAPI_CALLCONV
543 gss_unwrap_iov(OM_uint32 *minor,
544                gss_ctx_id_t ctx,
545                int *conf_state,
546                gss_qop_t *qop_state,
547                gss_iov_buffer_desc *iov,
548                int iov_count)
549 {
550     OM_uint32 major;
551
552     if (ctx == GSS_C_NO_CONTEXT) {
553         *minor = EINVAL;
554         return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
555     }
556
557     *minor = 0;
558
559     GSSEAP_MUTEX_LOCK(&ctx->mutex);
560
561     if (!CTX_IS_ESTABLISHED(ctx)) {
562         major = GSS_S_NO_CONTEXT;
563         *minor = GSSEAP_CONTEXT_INCOMPLETE;
564         goto cleanup;
565     }
566
567     major = gssEapUnwrapOrVerifyMIC(minor, ctx, conf_state, qop_state,
568                                     iov, iov_count, TOK_TYPE_WRAP);
569     if (GSS_ERROR(major))
570         goto cleanup;
571
572 cleanup:
573     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
574
575     return major;
576 }