add gsseap error table
[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 = GSSEAP_MISSING_REQUIRED_EXT;
291             major = GSS_S_UNAVAILABLE;
292             goto cleanup;
293         }
294     }
295
296     /* Check we processed all critical extensions */
297     for (i = 0; i < extensions->count; i++) {
298         if ((types[i] & EXT_FLAG_CRITICAL) &&
299             (types[i] & EXT_FLAG_VERIFIED) == 0) {
300             *minor = GSSEAP_CRIT_EXT_UNAVAILABLE;
301             major = GSS_S_UNAVAILABLE;
302             goto cleanup;
303         }
304     }
305
306     *minor = 0;
307     major = GSS_S_COMPLETE;
308
309 cleanup:
310     gss_release_buffer_set(&tmpMinor, &extensions);
311     if (types != NULL)
312         GSSEAP_FREE(types);
313
314     return major;
315 }
316
317 OM_uint32
318 gssEapVerifyExtensions(OM_uint32 *minor,
319                        gss_cred_id_t cred,
320                        gss_ctx_id_t ctx,
321                        gss_channel_bindings_t chanBindings,
322                        const gss_buffer_t buffer)
323 {
324     size_t nexts;
325     const struct gss_eap_extension_provider *exts;
326
327     if (CTX_IS_INITIATOR(ctx)) {
328         exts = eapGssAcceptExtensions;
329         nexts = sizeof(eapGssAcceptExtensions) / sizeof(eapGssAcceptExtensions[0]);
330     } else {
331         exts = eapGssInitExtensions;
332         nexts = sizeof(eapGssInitExtensions) / sizeof(eapGssInitExtensions[0]);
333     }
334
335     return verifyExtensions(minor, cred, ctx, exts, nexts, chanBindings, buffer);
336 }
337
338 static OM_uint32
339 encodeExtensions(OM_uint32 *minor,
340                  gss_buffer_set_t extensions,
341                  OM_uint32 *types,
342                  gss_buffer_t buffer)
343 {
344     OM_uint32 major, tmpMinor;
345     size_t required = 0, i;
346     unsigned char *p;
347
348     buffer->value = NULL;
349     buffer->length = 0;
350
351     if (extensions != GSS_C_NO_BUFFER_SET) {
352         for (i = 0; i < extensions->count; i++) {
353             required += 8 + extensions->elements[i].length;
354         }
355     }
356
357     /*
358      * We must always return a non-NULL token otherwise the calling state
359      * machine assumes we are finished. Hence care in case malloc(0) does
360      * return NULL.
361      */
362     buffer->value = GSSEAP_MALLOC(required ? required : 1);
363     if (buffer->value == NULL) {
364         *minor = ENOMEM;
365         major = GSS_S_FAILURE;
366         goto cleanup;
367     }
368
369     buffer->length = required;
370     p = (unsigned char *)buffer->value;
371
372     if (extensions != GSS_C_NO_BUFFER_SET) {
373         for (i = 0; i < extensions->count; i++) {
374             gss_buffer_t extension = &extensions->elements[i];
375
376             assert((types[i] & EXT_FLAG_VERIFIED) == 0); /* private flag */
377
378              /*
379               * Extensions are encoded as type-length-value, where the upper
380               * bit of the type indicates criticality.
381               */
382             store_uint32_be(types[i], &p[0]);
383             store_uint32_be(extension->length, &p[4]);
384             memcpy(&p[8], extension->value, extension->length);
385
386             p += 8 + extension->length;
387         }
388     }
389
390     assert(p == (unsigned char *)buffer->value + required);
391     assert(buffer->value != NULL);
392
393 cleanup:
394     if (GSS_ERROR(major)) {
395         gss_release_buffer(&tmpMinor, buffer);
396     }
397
398     return major;
399 }
400
401 static OM_uint32
402 decodeExtensions(OM_uint32 *minor,
403                  const gss_buffer_t buffer,
404                  gss_buffer_set_t *pExtensions,
405                  OM_uint32 **pTypes)
406 {
407     OM_uint32 major, tmpMinor;
408     gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET;
409     OM_uint32 *types = NULL;
410     unsigned char *p;
411     size_t remain;
412
413     *pExtensions = GSS_C_NO_BUFFER_SET;
414     *pTypes = NULL;
415
416     major = gss_create_empty_buffer_set(minor, &extensions);
417     if (GSS_ERROR(major))
418         goto cleanup;
419
420     if (buffer->length == 0) {
421         major = GSS_S_COMPLETE;
422         goto cleanup;
423     }
424
425     p = (unsigned char *)buffer->value;
426     remain = buffer->length;
427
428     do {
429         OM_uint32 *ntypes;
430         gss_buffer_desc extension;
431
432         if (remain < 8) {
433             major = GSS_S_DEFECTIVE_TOKEN;
434             goto cleanup;
435         }
436
437         ntypes = GSSEAP_REALLOC(types,
438                                 (extensions->count + 1) * sizeof(OM_uint32));
439         if (ntypes == NULL) {
440             *minor = ENOMEM;
441             major = GSS_S_FAILURE;
442             goto cleanup;
443         }
444         types = ntypes;
445
446         types[extensions->count] = load_uint32_be(&p[0]);
447         extension.length = load_uint32_be(&p[4]);
448
449         if (remain < 8 + extension.length) {
450             major = GSS_S_DEFECTIVE_TOKEN;
451             goto cleanup;
452         }
453         extension.value = &p[8];
454
455         major = gss_add_buffer_set_member(minor, &extension, &extensions);
456         if (GSS_ERROR(major))
457             goto cleanup;
458
459         p      += 8 + extension.length;
460         remain -= 8 + extension.length;
461     } while (remain != 0);
462
463 cleanup:
464     if (GSS_ERROR(major)) {
465         gss_release_buffer_set(&tmpMinor, &extensions);
466         if (types != NULL)
467             GSSEAP_FREE(types);
468     } else {
469         *pExtensions = extensions;
470         *pTypes = types;
471     }
472
473     return major;
474 }