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