More work on krb5 crypto merge
[mech_eap.orig] / util_crypt.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 2001, 2008 by the Massachusetts Institute of Technology.
34  * Copyright 1993 by OpenVision Technologies, Inc.
35  *
36  * Permission to use, copy, modify, distribute, and sell this software
37  * and its documentation for any purpose is hereby granted without fee,
38  * provided that the above copyright notice appears in all copies and
39  * that both that copyright notice and this permission notice appear in
40  * supporting documentation, and that the name of OpenVision not be used
41  * in advertising or publicity pertaining to distribution of the software
42  * without specific, written prior permission. OpenVision makes no
43  * representations about the suitability of this software for any
44  * purpose.  It is provided "as is" without express or implied warranty.
45  *
46  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
47  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
48  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
49  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
50  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
51  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
52  * PERFORMANCE OF THIS SOFTWARE.
53  */
54 /*
55  * Copyright (C) 1998 by the FundsXpress, INC.
56  *
57  * All rights reserved.
58  *
59  * Export of this software from the United States of America may require
60  * a specific license from the United States Government.  It is the
61  * responsibility of any person or organization contemplating export to
62  * obtain such a license before exporting.
63  *
64  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
65  * distribute this software and its documentation for any purpose and
66  * without fee is hereby granted, provided that the above copyright
67  * notice appear in all copies and that both that copyright notice and
68  * this permission notice appear in supporting documentation, and that
69  * the name of FundsXpress. not be used in advertising or publicity pertaining
70  * to distribution of the software without specific, written prior
71  * permission.  FundsXpress makes no representations about the suitability of
72  * this software for any purpose.  It is provided "as is" without express
73  * or implied warranty.
74  *
75  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
76  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
77  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
78  */
79
80 #include "gssapiP_eap.h"
81
82 /*
83  * DCE_STYLE indicates actual RRC is EC + RRC
84  * EC is extra rotate count for DCE_STYLE, pad length otherwise
85  * RRC is rotate count.
86  */
87 static krb5_error_code
88 mapIov(krb5_context context, int dce_style, size_t ec, size_t rrc,
89        krb5_enctype enctype, gss_iov_buffer_desc *iov,
90        int iov_count, krb5_crypto_iov **pkiov,
91        size_t *pkiov_count)
92 {
93     gss_iov_buffer_t header;
94     gss_iov_buffer_t trailer;
95     int i = 0, j;
96     size_t kiov_count;
97     krb5_crypto_iov *kiov;
98     unsigned int k5_headerlen = 0, k5_trailerlen = 0;
99     size_t gss_headerlen, gss_trailerlen;
100     krb5_error_code code;
101
102     *pkiov = NULL;
103     *pkiov_count = 0;
104
105     header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
106     assert(header != NULL);
107
108     trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
109     assert(trailer == NULL || rrc == 0);
110
111     code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER,
112                                 &k5_headerlen);
113     if (code != 0)
114         return code;
115
116     code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_TRAILER,
117                                 &k5_trailerlen);
118     if (code != 0)
119         return code;
120
121     /* Check header and trailer sizes */
122     gss_headerlen = 16 /* GSS-Header */ + k5_headerlen; /* Kerb-Header */
123     gss_trailerlen = ec + 16 /* E(GSS-Header) */ + k5_trailerlen; /* Kerb-Trailer */
124
125     /* If we're caller without a trailer, we must rotate by trailer length */
126     if (trailer == NULL) {
127         size_t actual_rrc = rrc;
128
129         if (dce_style)
130             actual_rrc += ec; /* compensate for Windows bug */
131
132         if (actual_rrc != gss_trailerlen)
133             return KRB5_BAD_MSIZE;
134
135         gss_headerlen += gss_trailerlen;
136         gss_trailerlen = 0;
137     } else {
138         if (trailer->buffer.length != gss_trailerlen)
139             return KRB5_BAD_MSIZE;
140     }
141
142     if (header->buffer.length != gss_headerlen)
143         return KRB5_BAD_MSIZE;
144
145     kiov_count = 3 + iov_count;
146     kiov = (krb5_crypto_iov *)GSSEAP_MALLOC(kiov_count * sizeof(krb5_crypto_iov));
147     if (kiov == NULL)
148         return ENOMEM;
149
150     /*
151      * The krb5 header is located at the end of the GSS header.
152      */
153     kiov[i].flags = KRB5_CRYPTO_TYPE_HEADER;
154     kiov[i].data.length = k5_headerlen;
155     kiov[i].data.data = (char *)header->buffer.value + header->buffer.length - k5_headerlen;
156     i++;
157
158     for (j = 0; j < iov_count; j++) {
159         kiov[i].flags = gssEapMapCryptoFlag(iov[j].type);
160         if (kiov[i].flags == KRB5_CRYPTO_TYPE_EMPTY)
161             continue;
162
163         kiov[i].data.length = iov[j].buffer.length;
164         kiov[i].data.data = (char *)iov[j].buffer.value;
165         i++;
166     }
167
168     /*
169      * The EC and encrypted GSS header are placed in the trailer, which may
170      * be rotated directly after the plaintext header if no trailer buffer
171      * is provided.
172      */
173     kiov[i].flags = KRB5_CRYPTO_TYPE_DATA;
174     kiov[i].data.length = ec + 16; /* E(Header) */
175     if (trailer == NULL)
176         kiov[i].data.data = (char *)header->buffer.value + 16;
177     else
178         kiov[i].data.data = (char *)trailer->buffer.value;
179     i++;
180
181     /*
182      * The krb5 trailer is placed after the encrypted copy of the
183      * krb5 header (which may be in the GSS header or trailer).
184      */
185     kiov[i].flags = KRB5_CRYPTO_TYPE_TRAILER;
186     kiov[i].data.length = k5_trailerlen;
187     kiov[i].data.data = kiov[i - 1].data.data + ec + 16; /* E(Header) */
188     i++;
189
190     *pkiov = kiov;
191     *pkiov_count = i;
192
193     return 0;
194 }
195
196 int
197 gssEapEncrypt(krb5_context context, int dce_style, size_t ec,
198               size_t rrc, krb5_keyblock *key, int usage, krb5_pointer iv,
199               gss_iov_buffer_desc *iov, int iov_count)
200 {
201     krb5_error_code code;
202     size_t blocksize;
203     krb5_data ivd, *pivd;
204     size_t kiov_count;
205     krb5_crypto_iov *kiov;
206
207     if (iv) {
208         code = krb5_c_block_size(context, KRB_KEYTYPE(key), &blocksize);
209         if (code)
210             return(code);
211
212         ivd.length = blocksize;
213         ivd.data = GSSEAP_MALLOC(ivd.length);
214         if (ivd.data == NULL)
215             return ENOMEM;
216         memcpy(ivd.data, iv, ivd.length);
217         pivd = &ivd;
218     } else {
219         pivd = NULL;
220     }
221
222     code = mapIov(context, dce_style, ec, rrc,
223                   KRB_KEYTYPE(key), iov, iov_count,
224                   &kiov, &kiov_count);
225     if (code == 0) {
226         code = krb5_c_encrypt_iov(context, key, usage, pivd, kiov, kiov_count);
227         GSSEAP_FREE(kiov);
228     }
229
230     if (pivd != NULL)
231         GSSEAP_FREE(pivd->data);
232
233     return code;
234 }
235
236 int
237 gssEapDecrypt(krb5_context context, int dce_style, size_t ec,
238               size_t rrc, krb5_keyblock *key, int usage, krb5_pointer iv,
239               gss_iov_buffer_desc *iov, int iov_count)
240 {
241     krb5_error_code code;
242     size_t blocksize;
243     krb5_data ivd, *pivd;
244     size_t kiov_count;
245     krb5_crypto_iov *kiov;
246
247     if (iv) {
248         code = krb5_c_block_size(context, KRB_KEYTYPE(key), &blocksize);
249         if (code)
250             return(code);
251
252         ivd.length = blocksize;
253         ivd.data = GSSEAP_MALLOC(ivd.length);
254         if (ivd.data == NULL)
255             return ENOMEM;
256         memcpy(ivd.data, iv, ivd.length);
257         pivd = &ivd;
258     } else {
259         pivd = NULL;
260     }
261
262     code = mapIov(context, dce_style, ec, rrc,
263                   KRB_KEYTYPE(key), iov, iov_count,
264                   &kiov, &kiov_count);
265     if (code == 0) {
266         code = krb5_c_decrypt_iov(context, key, usage, pivd, kiov, kiov_count);
267         GSSEAP_FREE(kiov);
268     }
269
270     if (pivd != NULL)
271         GSSEAP_FREE(pivd->data);
272
273     return code;
274 }
275
276 krb5_cryptotype
277 gssEapMapCryptoFlag(OM_uint32 type)
278 {
279     krb5_cryptotype ktype;
280
281     switch (GSS_IOV_BUFFER_TYPE(type)) {
282     case GSS_IOV_BUFFER_TYPE_DATA:
283     case GSS_IOV_BUFFER_TYPE_PADDING:
284         ktype = KRB5_CRYPTO_TYPE_DATA;
285         break;
286     case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
287         ktype = KRB5_CRYPTO_TYPE_SIGN_ONLY;
288         break;
289     default:
290         ktype = KRB5_CRYPTO_TYPE_EMPTY;
291         break;
292     }
293
294     return ktype;
295 }
296
297 gss_iov_buffer_t
298 gssEapLocateIov(gss_iov_buffer_desc *iov, int iov_count, OM_uint32 type)
299 {
300     int i;
301     gss_iov_buffer_t p = GSS_C_NO_IOV_BUFFER;
302
303     if (iov == GSS_C_NO_IOV_BUFFER)
304         return GSS_C_NO_IOV_BUFFER;
305
306     for (i = iov_count - 1; i >= 0; i--) {
307         if (GSS_IOV_BUFFER_TYPE(iov[i].type) == type) {
308             if (p == GSS_C_NO_IOV_BUFFER)
309                 p = &iov[i];
310             else
311                 return GSS_C_NO_IOV_BUFFER;
312         }
313     }
314
315     return p;
316 }
317
318 void
319 gssEapIovMessageLnegth(gss_iov_buffer_desc *iov,
320                        int iov_count,
321                        size_t *data_length_p,
322                        size_t *assoc_data_length_p)
323 {
324     int i;
325     size_t data_length = 0, assoc_data_length = 0;
326
327     assert(iov != GSS_C_NO_IOV_BUFFER);
328
329     *data_length_p = *assoc_data_length_p = 0;
330
331     for (i = 0; i < iov_count; i++) {
332         OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[i].type);
333
334         if (type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
335             assoc_data_length += iov[i].buffer.length;
336
337         if (type == GSS_IOV_BUFFER_TYPE_DATA ||
338             type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
339             data_length += iov[i].buffer.length;
340     }
341
342     *data_length_p = data_length;
343     *assoc_data_length_p = assoc_data_length;
344 }
345
346 void
347 gssEapReleaseIov(gss_iov_buffer_desc *iov, int iov_count)
348 {
349     int i;
350     OM_uint32 min_stat;
351
352     assert(iov != GSS_C_NO_IOV_BUFFER);
353
354     for (i = 0; i < iov_count; i++) {
355         if (iov[i].type & GSS_IOV_BUFFER_FLAG_ALLOCATED) {
356             gss_release_buffer(&min_stat, &iov[i].buffer);
357             iov[i].type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED);
358         }
359     }
360 }
361
362 int
363 gssEapIsIntegrityOnly(gss_iov_buffer_desc *iov, int iov_count)
364 {
365     int i;
366     krb5_boolean has_conf_data = FALSE;
367
368     assert(iov != GSS_C_NO_IOV_BUFFER);
369
370     for (i = 0; i < iov_count; i++) {
371         if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA) {
372             has_conf_data = TRUE;
373             break;
374         }
375     }
376
377     return (has_conf_data == FALSE);
378 }
379
380 int
381 gssEapAllocIov(gss_iov_buffer_t iov, size_t size)
382 {
383     assert(iov != GSS_C_NO_IOV_BUFFER);
384     assert(iov->type & GSS_IOV_BUFFER_FLAG_ALLOCATE);
385
386     iov->buffer.length = size;
387     iov->buffer.value = GSSEAP_MALLOC(size);
388     if (iov->buffer.value == NULL) {
389         iov->buffer.length = 0;
390         return ENOMEM;
391     }
392
393     iov->type |= GSS_IOV_BUFFER_FLAG_ALLOCATED;
394
395     return 0;
396 }