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