cleanup, fix some uninitialised variable warnings
[mech_eap.git] / unwrap_iov.c
1 /*
2  * Copyright (c) 2010, 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 #include "gssapiP_eap.h"
57
58 /*
59  * Caller must provide TOKEN | DATA | PADDING | TRAILER, except
60  * for DCE in which case it can just provide TOKEN | DATA (must
61  * guarantee that DATA is padded)
62  */
63 OM_uint32
64 unwrapToken(OM_uint32 *minor,
65             gss_ctx_id_t ctx,
66             int *conf_state,
67             gss_qop_t *qop_state,
68             gss_iov_buffer_desc *iov,
69             int iov_count,
70             enum gss_eap_token_type toktype)
71 {
72     OM_uint32 code;
73     gss_iov_buffer_t header;
74     gss_iov_buffer_t padding;
75     gss_iov_buffer_t trailer;
76     unsigned char flags;
77     unsigned char *ptr = NULL;
78     int keyUsage;
79     size_t rrc, ec;
80     size_t dataLen, assocDataLen;
81     uint64_t seqnum;
82     int valid = 0;
83     int conf_flag = 0;
84     krb5_context krbContext;
85
86     GSSEAP_KRB_INIT(&krbContext);
87
88     *minor = 0;
89
90     if (qop_state != NULL)
91         *qop_state = GSS_C_QOP_DEFAULT;
92
93     header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
94     assert(header != NULL);
95
96     padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
97     if (padding != NULL && padding->buffer.length != 0) {
98         *minor = GSSEAP_BAD_PADDING_IOV;
99         return GSS_S_DEFECTIVE_TOKEN;
100     }
101
102     trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
103
104     flags = rfc4121Flags(ctx, TRUE);
105
106     if (toktype == TOK_TYPE_WRAP) {
107         keyUsage = !CTX_IS_INITIATOR(ctx)
108                    ? KEY_USAGE_INITIATOR_SEAL
109                    : KEY_USAGE_ACCEPTOR_SEAL;
110     } else {
111         keyUsage = !CTX_IS_INITIATOR(ctx)
112                    ? KEY_USAGE_INITIATOR_SIGN
113                    : KEY_USAGE_ACCEPTOR_SIGN;
114     }
115
116     gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen);
117
118     ptr = (unsigned char *)header->buffer.value;
119
120     if (header->buffer.length < 16) {
121         *minor = GSSEAP_TOK_TRUNC;
122         return GSS_S_DEFECTIVE_TOKEN;
123     }
124
125     if ((ptr[2] & flags) != flags) {
126         *minor = GSSEAP_BAD_DIRECTION;
127         return GSS_S_BAD_SIG;
128     }
129
130     if (toktype == TOK_TYPE_WRAP) {
131         unsigned int krbTrailerLen;
132
133         if (load_uint16_be(ptr) != TOK_TYPE_WRAP)
134             goto defective;
135         conf_flag = ((ptr[2] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0);
136         if (ptr[3] != 0xFF)
137             goto defective;
138         ec = load_uint16_be(ptr + 4);
139         rrc = load_uint16_be(ptr + 6);
140         seqnum = load_uint64_be(ptr + 8);
141
142         code = krb5_c_crypto_length(krbContext,
143                                     ctx->encryptionType,
144                                     conf_flag ? KRB5_CRYPTO_TYPE_TRAILER :
145                                     KRB5_CRYPTO_TYPE_CHECKSUM,
146                                     &krbTrailerLen);
147         if (code != 0) {
148             *minor = code;
149             return GSS_S_FAILURE;
150         }
151
152         /* Deal with RRC */
153         if (trailer == NULL) {
154             size_t desired_rrc = krbTrailerLen;
155
156             if (conf_flag) {
157                 desired_rrc += 16; /* E(Header) */
158
159                 if ((ctx->gssFlags & GSS_C_DCE_STYLE) == 0)
160                     desired_rrc += ec;
161             }
162
163             /* According to MS, we only need to deal with a fixed RRC for DCE */
164             if (rrc != desired_rrc)
165                 goto defective;
166         } else if (rrc != 0) {
167             goto defective;
168         }
169
170         if (conf_flag) {
171             unsigned char *althdr;
172
173             /* Decrypt */
174             code = gssEapDecrypt(krbContext,
175                                  ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0),
176                                  ec, rrc, &ctx->rfc3961Key,
177                                  keyUsage, 0, iov, iov_count);
178             if (code != 0) {
179                 *minor = code;
180                 return GSS_S_BAD_SIG;
181             }
182
183             /* Validate header integrity */
184             if (trailer == NULL)
185                 althdr = (unsigned char *)header->buffer.value + 16 + ec;
186             else
187                 althdr = (unsigned char *)trailer->buffer.value + ec;
188
189             if (load_uint16_be(althdr) != TOK_TYPE_WRAP
190                 || althdr[2] != ptr[2]
191                 || althdr[3] != ptr[3]
192                 || memcmp(althdr + 8, ptr + 8, 8) != 0) {
193                 *minor = GSSEAP_BAD_WRAP_TOKEN;
194                 return GSS_S_BAD_SIG;
195             }
196         } else {
197             /* Verify checksum: note EC is checksum size here, not padding */
198             if (ec != krbTrailerLen)
199                 goto defective;
200
201             /* Zero EC, RRC before computing checksum */
202             store_uint16_be(0, ptr + 4);
203             store_uint16_be(0, ptr + 6);
204
205             code = gssEapVerify(krbContext, ctx->checksumType, rrc,
206                                 &ctx->rfc3961Key, keyUsage,
207                                 iov, iov_count, &valid);
208             if (code != 0 || valid == FALSE) {
209                 *minor = code;
210                 return GSS_S_BAD_SIG;
211             }
212         }
213
214         code = sequenceCheck(minor, &ctx->seqState, seqnum);
215     } else if (toktype == TOK_TYPE_MIC) {
216         if (load_uint16_be(ptr) != toktype)
217             goto defective;
218
219     verify_mic_1:
220         if (ptr[3] != 0xFF)
221             goto defective;
222         seqnum = load_uint64_be(ptr + 8);
223
224         code = gssEapVerify(krbContext, ctx->checksumType, 0,
225                             &ctx->rfc3961Key, keyUsage,
226                             iov, iov_count, &valid);
227         if (code != 0 || valid == FALSE) {
228             *minor = code;
229             return GSS_S_BAD_SIG;
230         }
231         code = sequenceCheck(minor, &ctx->seqState, seqnum);
232     } else if (toktype == TOK_TYPE_DELETE_CONTEXT) {
233         if (load_uint16_be(ptr) != TOK_TYPE_DELETE_CONTEXT)
234             goto defective;
235         goto verify_mic_1;
236     } else {
237         goto defective;
238     }
239
240     *minor = 0;
241
242     if (conf_state != NULL)
243         *conf_state = conf_flag;
244
245     return code;
246
247 defective:
248     *minor = GSSEAP_BAD_WRAP_TOKEN;
249
250     return GSS_S_DEFECTIVE_TOKEN;
251 }
252
253 int
254 rotateLeft(void *ptr, size_t bufsiz, size_t rc)
255 {
256     void *tbuf;
257
258     if (bufsiz == 0)
259         return 0;
260     rc = rc % bufsiz;
261     if (rc == 0)
262         return 0;
263
264     tbuf = GSSEAP_MALLOC(rc);
265     if (tbuf == NULL)
266         return ENOMEM;
267
268     memcpy(tbuf, ptr, rc);
269     memmove(ptr, (char *)ptr + rc, bufsiz - rc);
270     memcpy((char *)ptr + bufsiz - rc, tbuf, rc);
271     GSSEAP_FREE(tbuf);
272
273     return 0;
274 }
275
276 /*
277  * Split a STREAM | SIGN_DATA | DATA into
278  *         HEADER | SIGN_DATA | DATA | PADDING | TRAILER
279  */
280 static OM_uint32
281 unwrapStream(OM_uint32 *minor,
282              gss_ctx_id_t ctx,
283              int *conf_state,
284              gss_qop_t *qop_state,
285              gss_iov_buffer_desc *iov,
286              int iov_count,
287              enum gss_eap_token_type toktype)
288 {
289     unsigned char *ptr;
290     OM_uint32 code = 0, major = GSS_S_FAILURE;
291     krb5_context krbContext;
292     int conf_req_flag, toktype2;
293     int i = 0, j;
294     gss_iov_buffer_desc *tiov = NULL;
295     gss_iov_buffer_t stream, data = NULL;
296     gss_iov_buffer_t theader, tdata = NULL, tpadding, ttrailer;
297
298     GSSEAP_KRB_INIT(&krbContext);
299
300     assert(toktype == TOK_TYPE_WRAP);
301
302     if (toktype != TOK_TYPE_WRAP) {
303         code = GSSEAP_WRONG_TOK_ID;
304         goto cleanup;
305     }
306
307     stream = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM);
308     assert(stream != NULL);
309
310     if (stream->buffer.length < 16) {
311         major = GSS_S_DEFECTIVE_TOKEN;
312         goto cleanup;
313     }
314
315     ptr = (unsigned char *)stream->buffer.value;
316     toktype2 = load_uint16_be(ptr);
317     ptr += 2;
318
319     tiov = (gss_iov_buffer_desc *)GSSEAP_CALLOC((size_t)iov_count + 2,
320                                                 sizeof(gss_iov_buffer_desc));
321     if (tiov == NULL) {
322         code = ENOMEM;
323         goto cleanup;
324     }
325
326     /* HEADER */
327     theader = &tiov[i++];
328     theader->type = GSS_IOV_BUFFER_TYPE_HEADER;
329     theader->buffer.value = stream->buffer.value;
330     theader->buffer.length = 16;
331
332     /* n[SIGN_DATA] | DATA | m[SIGN_DATA] */
333     for (j = 0; j < iov_count; j++) {
334         OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[j].type);
335
336         if (type == GSS_IOV_BUFFER_TYPE_DATA) {
337             if (data != NULL) {
338                 /* only a single DATA buffer can appear */
339                 code = GSSEAP_BAD_STREAM_IOV;
340                 goto cleanup;
341             }
342
343             data = &iov[j];
344             tdata = &tiov[i];
345         }
346         if (type == GSS_IOV_BUFFER_TYPE_DATA ||
347             type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
348             tiov[i++] = iov[j];
349     }
350
351     if (data == NULL) {
352         /* a single DATA buffer must be present */
353         code = GSSEAP_BAD_STREAM_IOV;
354         goto cleanup;
355     }
356
357     /* PADDING | TRAILER */
358     tpadding = &tiov[i++];
359     tpadding->type = GSS_IOV_BUFFER_TYPE_PADDING;
360     tpadding->buffer.length = 0;
361     tpadding->buffer.value = NULL;
362
363     ttrailer = &tiov[i++];
364     ttrailer->type = GSS_IOV_BUFFER_TYPE_TRAILER;
365
366     {
367         size_t ec, rrc;
368         unsigned int krbHeaderLen = 0;
369         unsigned int krbTrailerLen = 0;
370
371         conf_req_flag = ((ptr[0] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0);
372         ec = conf_req_flag ? load_uint16_be(ptr + 2) : 0;
373         rrc = load_uint16_be(ptr + 4);
374
375         if (rrc != 0) {
376             code = rotateLeft((unsigned char *)stream->buffer.value + 16,
377                               stream->buffer.length - 16, rrc);
378             if (code != 0)
379                 goto cleanup;
380             store_uint16_be(0, ptr + 4); /* set RRC to zero */
381         }
382
383         if (conf_req_flag) {
384             code = krb5_c_crypto_length(krbContext, ctx->encryptionType,
385                                         KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen);
386             if (code != 0)
387                 goto cleanup;
388             theader->buffer.length += krbHeaderLen; /* length validated later */
389         }
390
391         /* no PADDING for CFX, EC is used instead */
392         code = krb5_c_crypto_length(krbContext, ctx->encryptionType,
393                                     conf_req_flag
394                                       ? KRB5_CRYPTO_TYPE_TRAILER
395                                       : KRB5_CRYPTO_TYPE_CHECKSUM,
396                                     &krbTrailerLen);
397         if (code != 0)
398             goto cleanup;
399
400         ttrailer->buffer.length = ec + (conf_req_flag ? 16 : 0 /* E(Header) */) +
401                                   krbTrailerLen;
402         ttrailer->buffer.value = (unsigned char *)stream->buffer.value +
403             stream->buffer.length - ttrailer->buffer.length;
404     }
405
406     /* IOV: -----------0-------------+---1---+--2--+----------------3--------------*/
407     /* CFX: GSS-Header | Kerb-Header | Data  |     | EC | E(Header) | Kerb-Trailer */
408     /* GSS: -------GSS-HEADER--------+-DATA--+-PAD-+----------GSS-TRAILER----------*/
409
410     /* validate lengths */
411     if (stream->buffer.length < theader->buffer.length +
412         tpadding->buffer.length +
413         ttrailer->buffer.length) {
414         major = GSS_S_DEFECTIVE_TOKEN;
415         code = GSSEAP_TOK_TRUNC;
416         goto cleanup;
417     }
418
419     /* setup data */
420     tdata->buffer.length = stream->buffer.length - ttrailer->buffer.length -
421         tpadding->buffer.length - theader->buffer.length;
422
423     assert(data != NULL);
424
425     if (data->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
426         code = gssEapAllocIov(tdata, tdata->buffer.length);
427         if (code != 0)
428             goto cleanup;
429
430         memcpy(tdata->buffer.value,
431                (unsigned char *)stream->buffer.value + theader->buffer.length,
432                tdata->buffer.length);
433     } else {
434         tdata->buffer.value = (unsigned char *)stream->buffer.value +
435                               theader->buffer.length;
436     }
437
438     assert(i <= iov_count + 2);
439
440     major = unwrapToken(&code, ctx, conf_state, qop_state,
441                         tiov, i, toktype);
442     if (major == GSS_S_COMPLETE) {
443         *data = *tdata;
444     } else if (tdata->type & GSS_IOV_BUFFER_FLAG_ALLOCATED) {
445         OM_uint32 tmp;
446
447         gss_release_buffer(&tmp, &tdata->buffer);
448         tdata->type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED);
449     }
450
451 cleanup:
452     if (tiov != NULL)
453         GSSEAP_FREE(tiov);
454
455     *minor = code;
456
457     return major;
458 }
459
460 OM_uint32
461 gssEapUnwrapOrVerifyMIC(OM_uint32 *minor,
462                         gss_ctx_id_t ctx,
463                         int *conf_state,
464                         gss_qop_t *qop_state,
465                         gss_iov_buffer_desc *iov,
466                         int iov_count,
467                         enum gss_eap_token_type toktype)
468 {
469     OM_uint32 major;
470
471     if (ctx->encryptionType == ENCTYPE_NULL) {
472         *minor = GSSEAP_KEY_UNAVAILABLE;
473         return GSS_S_UNAVAILABLE;
474     }
475
476     if (gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM) != NULL) {
477         major = unwrapStream(minor, ctx, conf_state, qop_state,
478                              iov, iov_count, toktype);
479     } else {
480         major = unwrapToken(minor, ctx, conf_state, qop_state,
481                             iov, iov_count, toktype);
482     }
483
484     return major;
485 }
486
487 OM_uint32
488 gss_unwrap_iov(OM_uint32 *minor,
489                gss_ctx_id_t ctx,
490                int *conf_state,
491                gss_qop_t *qop_state,
492                gss_iov_buffer_desc *iov,
493                int iov_count)
494 {
495     OM_uint32 major;
496
497     if (ctx == GSS_C_NO_CONTEXT) {
498         *minor = EINVAL;
499         return GSS_S_NO_CONTEXT;
500     }
501
502     *minor = 0;
503
504     GSSEAP_MUTEX_LOCK(&ctx->mutex);
505
506     if (!CTX_IS_ESTABLISHED(ctx)) {
507         major = GSS_S_NO_CONTEXT;
508         *minor = GSSEAP_CONTEXT_INCOMPLETE;
509         goto cleanup;
510     }
511
512     major = gssEapUnwrapOrVerifyMIC(minor, ctx, conf_state, qop_state,
513                                     iov, iov_count, TOK_TYPE_WRAP);
514     if (GSS_ERROR(major))
515         goto cleanup;
516
517 cleanup:
518     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
519
520     return major;
521 }