Change krbCred member to reauthCred to better clarify purpose
[moonshot.git] / moonshot / mech_eap / util_crypt.c
1 /*
2  * Copyright (c) 2011, 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 /*
81  * Message protection services: cryptography helpers.
82  */
83
84 #include "gssapiP_eap.h"
85
86 /*
87  * DCE_STYLE indicates actual RRC is EC + RRC
88  * EC is extra rotate count for DCE_STYLE, pad length otherwise
89  * RRC is rotate count.
90  */
91 static krb5_error_code
92 mapIov(krb5_context context, int dce_style, size_t ec, size_t rrc,
93 #ifdef HAVE_HEIMDAL_VERSION
94        krb5_crypto crypto,
95 #else
96        krb5_keyblock *crypto,
97 #endif
98        gss_iov_buffer_desc *iov,
99        int iov_count, krb5_crypto_iov **pkiov,
100        size_t *pkiov_count)
101 {
102     gss_iov_buffer_t header;
103     gss_iov_buffer_t trailer;
104     int i = 0, j;
105     size_t kiov_count;
106     krb5_crypto_iov *kiov;
107     size_t k5_headerlen = 0, k5_trailerlen = 0;
108     size_t gss_headerlen, gss_trailerlen;
109     krb5_error_code code;
110
111     *pkiov = NULL;
112     *pkiov_count = 0;
113
114     header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
115     assert(header != NULL);
116
117     trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
118     assert(trailer == NULL || rrc == 0);
119
120     code = krbCryptoLength(context, crypto, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen);
121     if (code != 0)
122         return code;
123
124     code = krbCryptoLength(context, crypto, KRB5_CRYPTO_TYPE_TRAILER, &k5_trailerlen);
125     if (code != 0)
126         return code;
127
128     /* Check header and trailer sizes */
129     gss_headerlen = 16 /* GSS-Header */ + k5_headerlen; /* Kerb-Header */
130     gss_trailerlen = ec + 16 /* E(GSS-Header) */ + k5_trailerlen; /* Kerb-Trailer */
131
132     /* If we're caller without a trailer, we must rotate by trailer length */
133     if (trailer == NULL) {
134         size_t actual_rrc = rrc;
135
136         if (dce_style)
137             actual_rrc += ec; /* compensate for Windows bug */
138
139         if (actual_rrc != gss_trailerlen)
140             return KRB5_BAD_MSIZE;
141
142         gss_headerlen += gss_trailerlen;
143         gss_trailerlen = 0;
144     } else {
145         if (trailer->buffer.length != gss_trailerlen)
146             return KRB5_BAD_MSIZE;
147     }
148
149     if (header->buffer.length != gss_headerlen)
150         return KRB5_BAD_MSIZE;
151
152     kiov_count = 3 + iov_count;
153     kiov = (krb5_crypto_iov *)GSSEAP_MALLOC(kiov_count * sizeof(krb5_crypto_iov));
154     if (kiov == NULL)
155         return ENOMEM;
156
157     /*
158      * The krb5 header is located at the end of the GSS header.
159      */
160     kiov[i].flags = KRB5_CRYPTO_TYPE_HEADER;
161     kiov[i].data.length = k5_headerlen;
162     kiov[i].data.data = (char *)header->buffer.value + header->buffer.length - k5_headerlen;
163     i++;
164
165     for (j = 0; j < iov_count; j++) {
166         kiov[i].flags = gssEapMapCryptoFlag(iov[j].type);
167         if (kiov[i].flags == KRB5_CRYPTO_TYPE_EMPTY)
168             continue;
169
170         kiov[i].data.length = iov[j].buffer.length;
171         kiov[i].data.data = (char *)iov[j].buffer.value;
172         i++;
173     }
174
175     /*
176      * The EC and encrypted GSS header are placed in the trailer, which may
177      * be rotated directly after the plaintext header if no trailer buffer
178      * is provided.
179      */
180     kiov[i].flags = KRB5_CRYPTO_TYPE_DATA;
181     kiov[i].data.length = ec + 16; /* E(Header) */
182     if (trailer == NULL)
183         kiov[i].data.data = (char *)header->buffer.value + 16;
184     else
185         kiov[i].data.data = (char *)trailer->buffer.value;
186     i++;
187
188     /*
189      * The krb5 trailer is placed after the encrypted copy of the
190      * krb5 header (which may be in the GSS header or trailer).
191      */
192     kiov[i].flags = KRB5_CRYPTO_TYPE_TRAILER;
193     kiov[i].data.length = k5_trailerlen;
194     kiov[i].data.data = kiov[i - 1].data.data + ec + 16; /* E(Header) */
195     i++;
196
197     *pkiov = kiov;
198     *pkiov_count = i;
199
200     return 0;
201 }
202
203 int
204 gssEapEncrypt(krb5_context context,
205               int dce_style,
206               size_t ec,
207               size_t rrc,
208 #ifdef HAVE_HEIMDAL_VERSION
209               krb5_crypto crypto,
210 #else
211               krb5_keyblock *crypto,
212 #endif
213               int usage,
214               gss_iov_buffer_desc *iov,
215               int iov_count)
216 {
217     krb5_error_code code;
218     size_t kiov_count;
219     krb5_crypto_iov *kiov = NULL;
220
221     code = mapIov(context, dce_style, ec, rrc, crypto,
222                   iov, iov_count, &kiov, &kiov_count);
223     if (code != 0)
224         goto cleanup;
225
226 #ifdef HAVE_HEIMDAL_VERSION
227     code = krb5_encrypt_iov_ivec(context, crypto, usage, kiov, kiov_count, NULL);
228 #else
229     code = krb5_c_encrypt_iov(context, crypto, usage, NULL, kiov, kiov_count);
230 #endif
231     if (code != 0)
232         goto cleanup;
233
234 cleanup:
235     if (kiov != NULL)
236         GSSEAP_FREE(kiov);
237
238     return code;
239 }
240
241 int
242 gssEapDecrypt(krb5_context context,
243               int dce_style,
244               size_t ec,
245               size_t rrc,
246 #ifdef HAVE_HEIMDAL_VERSION
247               krb5_crypto crypto,
248 #else
249               krb5_keyblock *crypto,
250 #endif
251               int usage,
252               gss_iov_buffer_desc *iov,
253               int iov_count)
254 {
255     krb5_error_code code;
256     size_t kiov_count;
257     krb5_crypto_iov *kiov;
258
259     code = mapIov(context, dce_style, ec, rrc, crypto,
260                   iov, iov_count, &kiov, &kiov_count);
261     if (code != 0)
262         goto cleanup;
263
264 #ifdef HAVE_HEIMDAL_VERSION
265     code = krb5_decrypt_iov_ivec(context, crypto, usage, kiov, kiov_count, NULL);
266 #else
267     code = krb5_c_decrypt_iov(context, crypto, usage, NULL, kiov, kiov_count);
268 #endif
269
270 cleanup:
271     if (kiov != NULL)
272         GSSEAP_FREE(kiov);
273
274     return code;
275 }
276
277 int
278 gssEapMapCryptoFlag(OM_uint32 type)
279 {
280     int ktype;
281
282     switch (GSS_IOV_BUFFER_TYPE(type)) {
283     case GSS_IOV_BUFFER_TYPE_DATA:
284     case GSS_IOV_BUFFER_TYPE_PADDING:
285         ktype = KRB5_CRYPTO_TYPE_DATA;
286         break;
287     case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
288         ktype = KRB5_CRYPTO_TYPE_SIGN_ONLY;
289         break;
290     default:
291         ktype = KRB5_CRYPTO_TYPE_EMPTY;
292         break;
293     }
294
295     return ktype;
296 }
297
298 gss_iov_buffer_t
299 gssEapLocateIov(gss_iov_buffer_desc *iov, int iov_count, OM_uint32 type)
300 {
301     int i;
302     gss_iov_buffer_t p = GSS_C_NO_IOV_BUFFER;
303
304     if (iov == GSS_C_NO_IOV_BUFFER)
305         return GSS_C_NO_IOV_BUFFER;
306
307     for (i = iov_count - 1; i >= 0; i--) {
308         if (GSS_IOV_BUFFER_TYPE(iov[i].type) == type) {
309             if (p == GSS_C_NO_IOV_BUFFER)
310                 p = &iov[i];
311             else
312                 return GSS_C_NO_IOV_BUFFER;
313         }
314     }
315
316     return p;
317 }
318
319 void
320 gssEapIovMessageLength(gss_iov_buffer_desc *iov,
321                        int iov_count,
322                        size_t *data_length_p,
323                        size_t *assoc_data_length_p)
324 {
325     int i;
326     size_t data_length = 0, assoc_data_length = 0;
327
328     assert(iov != GSS_C_NO_IOV_BUFFER);
329
330     *data_length_p = *assoc_data_length_p = 0;
331
332     for (i = 0; i < iov_count; i++) {
333         OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[i].type);
334
335         if (type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
336             assoc_data_length += iov[i].buffer.length;
337
338         if (type == GSS_IOV_BUFFER_TYPE_DATA ||
339             type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
340             data_length += iov[i].buffer.length;
341     }
342
343     *data_length_p = data_length;
344     *assoc_data_length_p = assoc_data_length;
345 }
346
347 void
348 gssEapReleaseIov(gss_iov_buffer_desc *iov, int iov_count)
349 {
350     int i;
351     OM_uint32 min_stat;
352
353     assert(iov != GSS_C_NO_IOV_BUFFER);
354
355     for (i = 0; i < iov_count; i++) {
356         if (iov[i].type & GSS_IOV_BUFFER_FLAG_ALLOCATED) {
357             gss_release_buffer(&min_stat, &iov[i].buffer);
358             iov[i].type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED);
359         }
360     }
361 }
362
363 int
364 gssEapIsIntegrityOnly(gss_iov_buffer_desc *iov, int iov_count)
365 {
366     int i;
367     krb5_boolean has_conf_data = FALSE;
368
369     assert(iov != GSS_C_NO_IOV_BUFFER);
370
371     for (i = 0; i < iov_count; i++) {
372         if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA) {
373             has_conf_data = TRUE;
374             break;
375         }
376     }
377
378     return (has_conf_data == FALSE);
379 }
380
381 int
382 gssEapAllocIov(gss_iov_buffer_t iov, size_t size)
383 {
384     assert(iov != GSS_C_NO_IOV_BUFFER);
385     assert(iov->type & GSS_IOV_BUFFER_FLAG_ALLOCATE);
386
387     iov->buffer.length = size;
388     iov->buffer.value = GSSEAP_MALLOC(size);
389     if (iov->buffer.value == NULL) {
390         iov->buffer.length = 0;
391         return ENOMEM;
392     }
393
394     iov->type |= GSS_IOV_BUFFER_FLAG_ALLOCATED;
395
396     return 0;
397 }