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