994dded906253cb7854c408c982209c3b9d88466
[mech_eap.git] / wrap_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 unsigned char
59 rfc4121Flags(gss_ctx_id_t ctx, int receiving)
60 {
61     unsigned char flags;
62     int isAcceptor;
63
64     isAcceptor = !CTX_IS_INITIATOR(ctx);
65     if (receiving)
66         isAcceptor = !isAcceptor;
67
68     flags = 0;
69     if (isAcceptor)
70         flags |= TOK_FLAG_SENDER_IS_ACCEPTOR;
71
72     if ((ctx->flags & CTX_FLAG_KRB_REAUTH_GSS) &&
73         (ctx->gssFlags & GSS_C_MUTUAL_FLAG))
74         flags |= TOK_FLAG_ACCEPTOR_SUBKEY;
75
76     return flags;
77 }
78
79 OM_uint32
80 gssEapWrapOrGetMIC(OM_uint32 *minor,
81                    gss_ctx_id_t ctx,
82                    int conf_req_flag,
83                    int *conf_state,
84                    gss_iov_buffer_desc *iov,
85                    int iov_count,
86                    enum gss_eap_token_type toktype)
87 {
88     krb5_error_code code = 0;
89     gss_iov_buffer_t header;
90     gss_iov_buffer_t padding;
91     gss_iov_buffer_t trailer;
92     unsigned char flags;
93     unsigned char *outbuf = NULL;
94     unsigned char *tbuf = NULL;
95     int keyUsage;
96     size_t rrc = 0;
97     unsigned int gssHeaderLen, gssTrailerLen;
98     size_t dataLen, assocDataLen;
99     krb5_context krbContext;
100
101     if (ctx->encryptionType == ENCTYPE_NULL)
102         return GSS_S_UNAVAILABLE;
103
104     GSSEAP_KRB_INIT(&krbContext);
105
106     flags = rfc4121Flags(ctx, FALSE);
107
108     switch (toktype) {
109     case TOK_TYPE_WRAP:
110         keyUsage = CTX_IS_INITIATOR(ctx)
111                    ? KEY_USAGE_INITIATOR_SEAL
112                    : KEY_USAGE_ACCEPTOR_SEAL;
113         break;
114     case TOK_TYPE_GSS_CB:
115         keyUsage = KEY_USAGE_CHANNEL_BINDINGS;
116         break;
117     case TOK_TYPE_MIC:
118     default:
119         keyUsage = CTX_IS_INITIATOR(ctx)
120                    ? KEY_USAGE_INITIATOR_SIGN
121                    : KEY_USAGE_ACCEPTOR_SIGN;
122         break;
123     }
124
125     gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen);
126
127     header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
128     if (header == NULL) {
129         *minor = EINVAL;
130         return GSS_S_FAILURE;
131     }
132
133     padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
134     if (padding != NULL)
135         padding->buffer.length = 0;
136
137     trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
138
139     if (toktype == TOK_TYPE_WRAP && conf_req_flag) {
140         unsigned int krbHeaderLen, krbTrailerLen, krbPadLen;
141         size_t ec = 0;
142         size_t confDataLen = dataLen - assocDataLen;
143
144         code = krb5_c_crypto_length(krbContext, ctx->encryptionType,
145                                     KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen);
146         if (code != 0)
147             goto cleanup;
148
149         code = krb5_c_padding_length(krbContext, ctx->encryptionType,
150                                      confDataLen + 16 /* E(Header) */,
151                                      &krbPadLen);
152         if (code != 0)
153             goto cleanup;
154
155         if (krbPadLen == 0 && (ctx->gssFlags & GSS_C_DCE_STYLE)) {
156             /* Windows rejects AEAD tokens with non-zero EC */
157             code = krb5_c_block_size(krbContext, ctx->encryptionType, &ec);
158             if (code != 0)
159                 goto cleanup;
160         } else
161             ec = krbPadLen;
162
163         code = krb5_c_crypto_length(krbContext, ctx->encryptionType,
164                                     KRB5_CRYPTO_TYPE_TRAILER, &krbTrailerLen);
165         if (code != 0)
166             goto cleanup;
167
168         gssHeaderLen = 16 /* Header */ + krbHeaderLen;
169         gssTrailerLen = ec + 16 /* E(Header) */ + krbTrailerLen;
170
171         if (trailer == NULL) {
172             rrc = gssTrailerLen;
173             /* Workaround for Windows bug where it rotates by EC + RRC */
174             if (ctx->gssFlags & GSS_C_DCE_STYLE)
175                 rrc -= ec;
176             gssHeaderLen += gssTrailerLen;
177         }
178
179         if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
180             code = gssEapAllocIov(header, (size_t)gssHeaderLen);
181         } else if (header->buffer.length < gssHeaderLen)
182             code = KRB5_BAD_MSIZE;
183         if (code != 0)
184             goto cleanup;
185         outbuf = (unsigned char *)header->buffer.value;
186         header->buffer.length = (size_t)gssHeaderLen;
187
188         if (trailer != NULL) {
189             if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
190                 code = gssEapAllocIov(trailer, (size_t)gssTrailerLen);
191             else if (trailer->buffer.length < gssTrailerLen)
192                 code = KRB5_BAD_MSIZE;
193             if (code != 0)
194                 goto cleanup;
195             trailer->buffer.length = (size_t)gssTrailerLen;
196         }
197
198         /* TOK_ID */
199         store_uint16_be((uint16_t)toktype, outbuf);
200         /* flags */
201         outbuf[2] = flags
202                      | (conf_req_flag ? TOK_FLAG_WRAP_CONFIDENTIAL : 0);
203         /* filler */
204         outbuf[3] = 0xFF;
205         /* EC */
206         store_uint16_be(ec, outbuf + 4);
207         /* RRC */
208         store_uint16_be(0, outbuf + 6);
209         store_uint64_be(ctx->sendSeq, outbuf + 8);
210
211         /*
212          * EC | copy of header to be encrypted, located in
213          * (possibly rotated) trailer
214          */
215         if (trailer == NULL)
216             tbuf = (unsigned char *)header->buffer.value + 16; /* Header */
217         else
218             tbuf = (unsigned char *)trailer->buffer.value;
219
220         memset(tbuf, 0xFF, ec);
221         memcpy(tbuf + ec, header->buffer.value, 16);
222
223         code = gssEapEncrypt(krbContext,
224                              ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0),
225                              ec, rrc, &ctx->rfc3961Key,
226                              keyUsage, 0, iov, iov_count);
227         if (code != 0)
228             goto cleanup;
229
230         /* RRC */
231         store_uint16_be(rrc, outbuf + 6);
232
233         ctx->sendSeq++;
234     } else if (toktype == TOK_TYPE_WRAP && !conf_req_flag) {
235     wrap_with_checksum:
236
237         gssHeaderLen = 16;
238
239         code = krb5_c_crypto_length(krbContext, ctx->encryptionType,
240                                     KRB5_CRYPTO_TYPE_CHECKSUM,
241                                     &gssTrailerLen);
242         if (code != 0)
243             goto cleanup;
244
245         assert(gssTrailerLen <= 0xFFFF);
246
247         if (trailer == NULL) {
248             rrc = gssTrailerLen;
249             gssHeaderLen += gssTrailerLen;
250         }
251
252         if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
253             code = gssEapAllocIov(header, (size_t)gssHeaderLen);
254         else if (header->buffer.length < gssHeaderLen)
255             code = KRB5_BAD_MSIZE;
256         if (code != 0)
257             goto cleanup;
258         outbuf = (unsigned char *)header->buffer.value;
259         header->buffer.length = (size_t)gssHeaderLen;
260
261         if (trailer != NULL) {
262             if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
263                 code = gssEapAllocIov(trailer, (size_t)gssTrailerLen);
264             else if (trailer->buffer.length < gssTrailerLen)
265                 code = KRB5_BAD_MSIZE;
266             if (code != 0)
267                 goto cleanup;
268             trailer->buffer.length = (size_t)gssTrailerLen;
269         }
270
271         /* TOK_ID */
272         store_uint16_be((uint16_t)toktype, outbuf);
273         /* flags */
274         outbuf[2] = flags;
275         /* filler */
276         outbuf[3] = 0xFF;
277         if (toktype == TOK_TYPE_WRAP) {
278             /* Use 0 for checksum calculation, substitute
279              * checksum length later.
280              */
281             /* EC */
282             store_uint16_be(0, outbuf + 4);
283             /* RRC */
284             store_uint16_be(0, outbuf + 6);
285         } else {
286             /* MIC and DEL store 0xFF in EC and RRC */
287             store_uint16_be(0xFFFF, outbuf + 4);
288             store_uint16_be(0xFFFF, outbuf + 6);
289         }
290         store_uint64_be(ctx->sendSeq, outbuf + 8);
291
292         code = gssEapSign(krbContext, ctx->checksumType,
293                           rrc, &ctx->rfc3961Key, keyUsage,
294                           iov, iov_count);
295         if (code != 0)
296             goto cleanup;
297
298         if (toktype != TOK_TYPE_GSS_CB)
299             ctx->sendSeq++;
300
301         if (toktype == TOK_TYPE_WRAP) {
302             /* Fix up EC field */
303             store_uint16_be(gssTrailerLen, outbuf + 4);
304             /* Fix up RRC field */
305             store_uint16_be(rrc, outbuf + 6);
306         }
307     } else if (toktype == TOK_TYPE_MIC || toktype == TOK_TYPE_GSS_CB) {
308         trailer = NULL;
309         goto wrap_with_checksum;
310     } else if (toktype == TOK_TYPE_DELETE_CONTEXT) {
311         trailer = NULL;
312         goto wrap_with_checksum;
313     } else {
314         abort();
315     }
316
317     code = 0;
318
319 cleanup:
320     if (code != 0)
321         gssEapReleaseIov(iov, iov_count);
322
323     *minor = code;
324
325     return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
326 }
327
328 OM_uint32
329 gss_wrap_iov(OM_uint32 *minor,
330              gss_ctx_id_t ctx,
331              int conf_req_flag,
332              gss_qop_t qop_req,
333              int *conf_state,
334              gss_iov_buffer_desc *iov,
335              int iov_count)
336 {
337     if (!CTX_IS_ESTABLISHED(ctx))
338         return GSS_S_NO_CONTEXT;
339
340     return gssEapWrapOrGetMIC(minor, ctx, conf_req_flag, conf_state,
341                               iov, iov_count, TOK_TYPE_WRAP);
342 }