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