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