cleanup
[mech_eap.orig] / util_exts.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 #include "gssapiP_eap.h"
34
35 static OM_uint32
36 encodeExtensions(OM_uint32 *minor,
37                  gss_buffer_set_t extensions,
38                  OM_uint32 *types,
39                  gss_buffer_t buffer);
40
41 static OM_uint32
42 decodeExtensions(OM_uint32 *minor,
43                  const gss_buffer_t buffer,
44                  gss_buffer_set_t *pExtensions,
45                  OM_uint32 **pTypes);
46
47 /*
48  * Initiator extensions
49  */
50 static OM_uint32
51 makeGssChannelBindings(OM_uint32 *minor,
52                        gss_cred_id_t cred,
53                        gss_ctx_id_t ctx,
54                        gss_channel_bindings_t chanBindings,
55                        gss_buffer_t outputToken)
56 {
57     OM_uint32 major;
58     gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
59
60     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
61         buffer = chanBindings->application_data;
62
63     major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
64                        &buffer, NULL, outputToken);
65     if (GSS_ERROR(major))
66         return major;
67
68     return GSS_S_COMPLETE;
69 }
70
71 static OM_uint32
72 verifyGssChannelBindings(OM_uint32 *minor,
73                          gss_cred_id_t cred,
74                          gss_ctx_id_t ctx,
75                          gss_channel_bindings_t chanBindings,
76                          gss_buffer_t inputToken)
77 {
78     OM_uint32 major, tmpMinor;
79     gss_iov_buffer_desc iov[2];
80
81     iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
82     iov[0].buffer.length = 0;
83     iov[0].buffer.value = NULL;
84
85     iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM;
86     iov[1].buffer = *inputToken;
87
88     major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
89                                     iov, 2, TOK_TYPE_WRAP);
90     if (GSS_ERROR(major))
91         return major;
92
93     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS &&
94         !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) {
95         major = GSS_S_BAD_BINDINGS;
96     } else {
97         major = GSS_S_COMPLETE;
98     }
99
100     gss_release_buffer(&tmpMinor, &iov[0].buffer);
101
102     return major;
103 }
104
105 static struct gss_eap_extension_provider
106 eapGssInitExtensions[] = {
107     {
108         EXT_TYPE_GSS_CHANNEL_BINDINGS,
109         1, /* critical */
110         1, /* required */
111         makeGssChannelBindings,
112         verifyGssChannelBindings
113     },
114 };
115
116 /*
117  * Acceptor extensions
118  */
119 static OM_uint32
120 makeReauthCreds(OM_uint32 *minor,
121                 gss_cred_id_t cred,
122                 gss_ctx_id_t ctx,
123                 gss_channel_bindings_t chanBindings,
124                 gss_buffer_t outputToken)
125 {
126     OM_uint32 major = GSS_S_UNAVAILABLE;
127
128 #ifdef GSSEAP_ENABLE_REAUTH
129     /*
130      * If we're built with fast reauthentication enabled, then
131      * fabricate a ticket from the initiator to ourselves.
132      */
133     major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken);
134 #endif
135
136     return major;
137 }
138
139 static OM_uint32
140 verifyReauthCreds(OM_uint32 *minor,
141                   gss_cred_id_t cred,
142                   gss_ctx_id_t ctx,
143                   gss_channel_bindings_t chanBindings,
144                   gss_buffer_t inputToken)
145 {
146 #ifdef GSSEAP_ENABLE_REAUTH
147     return gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
148 #else
149     return GSS_S_UNAVAILABLE;
150 #endif
151 }
152
153 static struct gss_eap_extension_provider
154 eapGssAcceptExtensions[] = {
155     {
156         EXT_TYPE_REAUTH_CREDS,
157         0, /* critical */
158         0, /* required */
159         makeReauthCreds,
160         verifyReauthCreds
161     },
162 };
163
164 OM_uint32
165 gssEapMakeExtensions(OM_uint32 *minor,
166                      gss_cred_id_t cred,
167                      gss_ctx_id_t ctx,
168                      gss_channel_bindings_t chanBindings,
169                      gss_buffer_t buffer)
170 {
171     OM_uint32 major, tmpMinor;
172     size_t i, j, nexts;
173     gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET;
174     OM_uint32 *types;
175     const struct gss_eap_extension_provider *exts;
176
177     if (CTX_IS_INITIATOR(ctx)) {
178         exts = eapGssInitExtensions;
179         nexts = sizeof(eapGssInitExtensions) / sizeof(eapGssInitExtensions[0]);
180     } else {
181         exts = eapGssAcceptExtensions;
182         nexts = sizeof(eapGssAcceptExtensions) / sizeof(eapGssAcceptExtensions[0]);
183     }
184
185     assert(buffer != GSS_C_NO_BUFFER);
186
187     buffer->length = 0;
188     buffer->value = NULL;
189
190     types = GSSEAP_CALLOC(nexts, sizeof(OM_uint32));
191     if (types == NULL) {
192         *minor = ENOMEM;
193         major = GSS_S_FAILURE;
194         goto cleanup;
195     }
196
197     for (i = 0, j = 0; i < nexts; i++) {
198         const struct gss_eap_extension_provider *ext = &exts[i];
199         gss_buffer_desc extension = GSS_C_EMPTY_BUFFER;
200
201         types[j] = ext->type;
202         if (ext->critical)
203             types[j] |= EXT_FLAG_CRITICAL;
204
205         major = ext->make(minor, cred, ctx, chanBindings, &extension);
206         if (GSS_ERROR(major)) {
207             if (ext->critical)
208                 goto cleanup;
209             else
210                 continue;
211         }
212
213         major = gss_add_buffer_set_member(minor, &extension, &extensions);
214         if (GSS_ERROR(major))
215             goto cleanup;
216
217         j++;
218     }
219
220     assert(j == (extensions == GSS_C_NO_BUFFER_SET ? 0 : extensions->count));
221
222     major = encodeExtensions(minor, extensions, types, buffer);
223     if (GSS_ERROR(major))
224         goto cleanup;
225
226 cleanup:
227     gss_release_buffer_set(&tmpMinor, &extensions);
228     if (types != NULL)
229         GSSEAP_FREE(types);
230
231     return major;
232 }
233
234 OM_uint32
235 gssEapVerifyExtensions(OM_uint32 *minor,
236                        gss_cred_id_t cred,
237                        gss_ctx_id_t ctx,
238                        gss_channel_bindings_t chanBindings,
239                        const gss_buffer_t buffer)
240 {
241     OM_uint32 major, tmpMinor;
242     gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET;
243     OM_uint32 *types = NULL;
244     size_t i, nexts;
245     const struct gss_eap_extension_provider *exts;
246
247     if (CTX_IS_INITIATOR(ctx)) {
248         exts = eapGssAcceptExtensions;
249         nexts = sizeof(eapGssAcceptExtensions) / sizeof(eapGssAcceptExtensions[0]);
250     } else {
251         exts = eapGssInitExtensions;
252         nexts = sizeof(eapGssInitExtensions) / sizeof(eapGssInitExtensions[0]);
253     }
254
255     major = decodeExtensions(minor, buffer, &extensions, &types);
256     if (GSS_ERROR(major))
257         goto cleanup;
258
259     for (i = 0; i < nexts; i++) {
260         const struct gss_eap_extension_provider *ext = &exts[i];
261         gss_buffer_t extension = GSS_C_NO_BUFFER;
262         size_t j;
263
264         for (j = 0; j < extensions->count; j++) {
265             if ((types[j] & EXT_TYPE_MASK) == ext->type) {
266                 extension = &extensions->elements[j];
267                 break;
268             }
269         }
270
271         if (extension != GSS_C_NO_BUFFER) {
272             /* Process extension and mark as verified */
273             major = ext->verify(minor, cred, ctx, chanBindings,
274                                 &extensions->elements[j]);
275             if (GSS_ERROR(major))
276                 goto cleanup;
277
278             types[j] |= EXT_FLAG_VERIFIED;
279         } else if (ext->required) {
280             /* Required extension missing */
281             *minor = ENOENT;
282             major = GSS_S_UNAVAILABLE;
283             gssEapSaveStatusInfo(*minor,
284                                  "Missing required GSS EAP extension %08x",
285                                  ext->type);
286             goto cleanup;
287         }
288     }
289
290     /* Check we processed all critical extensions */
291     for (i = 0; i < extensions->count; i++) {
292         if ((types[i] & EXT_FLAG_CRITICAL) &&
293             (types[i] & EXT_FLAG_VERIFIED) == 0) {
294             *minor = ENOSYS;
295             major = GSS_S_UNAVAILABLE;
296             gssEapSaveStatusInfo(*minor,
297                                  "Received unknown critical GSS EAP extension %08x",
298                                  (types[i] & EXT_TYPE_MASK));
299             goto cleanup;
300         }
301     }
302
303     *minor = 0;
304     major = GSS_S_COMPLETE;
305
306 cleanup:
307     gss_release_buffer_set(&tmpMinor, &extensions);
308     if (types != NULL)
309         GSSEAP_FREE(types);
310
311     return major;
312 }
313
314 static OM_uint32
315 encodeExtensions(OM_uint32 *minor,
316                  gss_buffer_set_t extensions,
317                  OM_uint32 *types,
318                  gss_buffer_t buffer)
319 {
320     OM_uint32 major, tmpMinor;
321     size_t required = 0, i;
322     unsigned char *p;
323
324     buffer->value = NULL;
325     buffer->length = 0;
326
327     if (extensions != GSS_C_NO_BUFFER_SET) {
328         for (i = 0; i < extensions->count; i++) {
329             required += 8 + extensions->elements[i].length;
330         }
331     }
332
333     /*
334      * We must always return a non-NULL token otherwise the calling state
335      * machine assumes we are finished. Hence care in case malloc(0) does
336      * return NULL.
337      */
338     buffer->value = GSSEAP_MALLOC(required ? required : 1);
339     if (buffer->value == NULL) {
340         *minor = ENOMEM;
341         major = GSS_S_FAILURE;
342         goto cleanup;
343     }
344
345     buffer->length = required;
346     p = (unsigned char *)buffer->value;
347
348     if (extensions != GSS_C_NO_BUFFER_SET) {
349         for (i = 0; i < extensions->count; i++) {
350             gss_buffer_t extension = &extensions->elements[i];
351
352             assert((types[i] & EXT_FLAG_VERIFIED) == 0); /* private flag */
353
354              /*
355               * Extensions are encoded as type-length-value, where the upper
356               * bit of the type indicates criticality.
357               */
358             store_uint32_be(types[i], &p[0]);
359             store_uint32_be(extension->length, &p[4]);
360             memcpy(&p[8], extension->value, extension->length);
361
362             p += 8 + extension->length;
363         }
364     }
365
366     assert(p == (unsigned char *)buffer->value + required);
367     assert(buffer->value != NULL);
368
369 cleanup:
370     if (GSS_ERROR(major)) {
371         gss_release_buffer(&tmpMinor, buffer);
372     }
373
374     return major;
375 }
376
377 static OM_uint32
378 decodeExtensions(OM_uint32 *minor,
379                  const gss_buffer_t buffer,
380                  gss_buffer_set_t *pExtensions,
381                  OM_uint32 **pTypes)
382 {
383     OM_uint32 major, tmpMinor;
384     gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET;
385     OM_uint32 *types = NULL;
386     unsigned char *p;
387     size_t remain;
388
389     *pExtensions = GSS_C_NO_BUFFER_SET;
390     *pTypes = NULL;
391
392     major = gss_create_empty_buffer_set(minor, &extensions);
393     if (GSS_ERROR(major))
394         goto cleanup;
395
396     if (buffer->length == 0) {
397         major = GSS_S_COMPLETE;
398         goto cleanup;
399     }
400
401     p = (unsigned char *)buffer->value;
402     remain = buffer->length;
403
404     do {
405         OM_uint32 *ntypes;
406         gss_buffer_desc extension;
407
408         if (remain < 8) {
409             major = GSS_S_DEFECTIVE_TOKEN;
410             goto cleanup;
411         }
412
413         ntypes = GSSEAP_REALLOC(types,
414                                 (extensions->count + 1) * sizeof(OM_uint32));
415         if (ntypes == NULL) {
416             *minor = ENOMEM;
417             major = GSS_S_FAILURE;
418             goto cleanup;
419         }
420         types = ntypes;
421
422         types[extensions->count] = load_uint32_be(&p[0]);
423         extension.length = load_uint32_be(&p[4]);
424
425         if (remain < 8 + extension.length) {
426             major = GSS_S_DEFECTIVE_TOKEN;
427             goto cleanup;
428         }
429         extension.value = &p[8];
430
431         major = gss_add_buffer_set_member(minor, &extension, &extensions);
432         if (GSS_ERROR(major))
433             goto cleanup;
434
435         p      += 8 + extension.length;
436         remain -= 8 + extension.length;
437     } while (remain != 0);
438
439 cleanup:
440     if (GSS_ERROR(major)) {
441         gss_release_buffer_set(&tmpMinor, &extensions);
442         if (types != NULL)
443             GSSEAP_FREE(types);
444     } else {
445         *pExtensions = extensions;
446         *pTypes = types;
447     }
448
449     return major;
450 }