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