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