039cfdba65d67de4374d42a433f1d76ef89fab54
[mech_eap.git] / mech_eap / util_context.c
1 /*
2  * Copyright (c) 2011, 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 /*
34  * Utility routines for context handles.
35  */
36
37 #include "gssapiP_eap.h"
38
39 OM_uint32
40 gssEapAllocContext(OM_uint32 *minor,
41                    gss_ctx_id_t *pCtx)
42 {
43     OM_uint32 tmpMinor;
44     gss_ctx_id_t ctx;
45
46     GSSEAP_ASSERT(*pCtx == GSS_C_NO_CONTEXT);
47
48     ctx = (gss_ctx_id_t)GSSEAP_CALLOC(1, sizeof(*ctx));
49     if (ctx == NULL) {
50         *minor = ENOMEM;
51         return GSS_S_FAILURE;
52     }
53
54     if (GSSEAP_MUTEX_INIT(&ctx->mutex) != 0) {
55         *minor = GSSEAP_GET_LAST_ERROR();
56         gssEapReleaseContext(&tmpMinor, &ctx);
57         return GSS_S_FAILURE;
58     }
59
60     ctx->state = GSSEAP_STATE_INITIAL;
61     ctx->mechanismUsed = GSS_C_NO_OID;
62
63     /*
64      * Integrity, confidentiality, sequencing and replay detection are
65      * always available.  Regardless of what flags are requested in
66      * GSS_Init_sec_context, implementations MUST set the flag corresponding
67      * to these services in the output of GSS_Init_sec_context and
68      * GSS_Accept_sec_context.
69     */
70     ctx->gssFlags = GSS_C_TRANS_FLAG    |   /* exporting contexts */
71                     GSS_C_INTEG_FLAG    |   /* integrity */
72                     GSS_C_CONF_FLAG     |   /* confidentiality */
73                     GSS_C_SEQUENCE_FLAG |   /* sequencing */
74                     GSS_C_REPLAY_FLAG;      /* replay detection */
75
76     *pCtx = ctx;
77
78     return GSS_S_COMPLETE;
79 }
80
81 static void
82 releaseInitiatorContext(struct gss_eap_initiator_ctx *ctx)
83 {
84     eap_peer_sm_deinit(ctx->eap);
85 }
86
87 #ifdef GSSEAP_ENABLE_ACCEPTOR
88 static void
89 releaseAcceptorContext(struct gss_eap_acceptor_ctx *ctx)
90 {
91     OM_uint32 tmpMinor;
92
93     if (ctx->radConn != NULL)
94         rs_conn_destroy(ctx->radConn);
95     if (ctx->radContext != NULL)
96         rs_context_destroy(ctx->radContext);
97     if (ctx->radServer != NULL)
98         GSSEAP_FREE(ctx->radServer);
99     gss_release_buffer(&tmpMinor, &ctx->state);
100     if (ctx->vps != NULL)
101         gssEapRadiusFreeAvps(&tmpMinor, &ctx->vps);
102 }
103 #endif /* GSSEAP_ENABLE_ACCEPTOR */
104
105 OM_uint32
106 gssEapReleaseContext(OM_uint32 *minor,
107                      gss_ctx_id_t *pCtx)
108 {
109     OM_uint32 tmpMinor;
110     gss_ctx_id_t ctx = *pCtx;
111     krb5_context krbContext = NULL;
112
113     if (ctx == GSS_C_NO_CONTEXT) {
114         return GSS_S_COMPLETE;
115     }
116
117     gssEapKerberosInit(&tmpMinor, &krbContext);
118
119 #ifdef GSSEAP_ENABLE_REAUTH
120     if (ctx->flags & CTX_FLAG_KRB_REAUTH) {
121         gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
122     } else
123 #endif /* GSSEAP_ENABLE_REAUTH */
124     if (CTX_IS_INITIATOR(ctx)) {
125         releaseInitiatorContext(&ctx->initiatorCtx);
126     }
127 #ifdef GSSEAP_ENABLE_ACCEPTOR
128     else {
129         releaseAcceptorContext(&ctx->acceptorCtx);
130     }
131 #endif /* GSSEAP_ENABLE_ACCEPTOR */
132
133     krb5_free_keyblock_contents(krbContext, &ctx->rfc3961Key);
134     gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
135     gssEapReleaseName(&tmpMinor, &ctx->acceptorName);
136     gssEapReleaseOid(&tmpMinor, &ctx->mechanismUsed);
137     sequenceFree(&tmpMinor, &ctx->seqState);
138     gssEapReleaseCred(&tmpMinor, &ctx->cred);
139
140     GSSEAP_MUTEX_DESTROY(&ctx->mutex);
141
142     memset(ctx, 0, sizeof(*ctx));
143     GSSEAP_FREE(ctx);
144     *pCtx = GSS_C_NO_CONTEXT;
145
146     *minor = 0;
147     return GSS_S_COMPLETE;
148 }
149
150 OM_uint32
151 gssEapMakeToken(OM_uint32 *minor,
152                 gss_ctx_id_t ctx,
153                 const gss_buffer_t innerToken,
154                 enum gss_eap_token_type tokenType,
155                 gss_buffer_t outputToken)
156 {
157     unsigned char *p;
158
159     GSSEAP_ASSERT(ctx->mechanismUsed != GSS_C_NO_OID);
160
161     outputToken->length = tokenSize(ctx->mechanismUsed, innerToken->length);
162     outputToken->value = GSSEAP_MALLOC(outputToken->length);
163     if (outputToken->value == NULL) {
164         *minor = ENOMEM;
165         return GSS_S_FAILURE;
166     }
167
168     p = (unsigned char *)outputToken->value;
169     makeTokenHeader(ctx->mechanismUsed, innerToken->length, &p, tokenType);
170     memcpy(p, innerToken->value, innerToken->length);
171
172     *minor = 0;
173     return GSS_S_COMPLETE;
174 }
175
176 OM_uint32
177 gssEapVerifyToken(OM_uint32 *minor,
178                   gss_ctx_id_t ctx,
179                   const gss_buffer_t inputToken,
180                   enum gss_eap_token_type *actualToken,
181                   gss_buffer_t innerInputToken)
182 {
183     OM_uint32 major;
184     size_t bodySize;
185     unsigned char *p = (unsigned char *)inputToken->value;
186     gss_OID_desc oidBuf;
187     gss_OID oid;
188
189     if (ctx->mechanismUsed != GSS_C_NO_OID) {
190         oid = ctx->mechanismUsed;
191     } else {
192         oidBuf.elements = NULL;
193         oidBuf.length = 0;
194         oid = &oidBuf;
195     }
196
197     major = verifyTokenHeader(minor, oid, &bodySize, &p,
198                               inputToken->length, actualToken);
199     if (GSS_ERROR(major))
200         return major;
201
202     if (ctx->mechanismUsed == GSS_C_NO_OID) {
203         major = gssEapCanonicalizeOid(minor, oid, 0, &ctx->mechanismUsed);
204         if (GSS_ERROR(major))
205             return major;
206     }
207
208     innerInputToken->length = bodySize;
209     innerInputToken->value = p;
210
211     *minor = 0;
212     return GSS_S_COMPLETE;
213 }
214
215 OM_uint32
216 gssEapContextTime(OM_uint32 *minor,
217                   gss_const_ctx_id_t context_handle,
218                   OM_uint32 *time_rec)
219 {
220     *minor = 0;
221
222     if (context_handle->expiryTime == 0) {
223         *time_rec = GSS_C_INDEFINITE;
224     } else {
225         time_t now, lifetime;
226
227         time(&now);
228         lifetime = context_handle->expiryTime - now;
229         if (lifetime <= 0) {
230             *time_rec = 0;
231             return GSS_S_CONTEXT_EXPIRED;
232         }
233         *time_rec = lifetime;
234     }
235
236     return GSS_S_COMPLETE;
237 }
238
239 static OM_uint32
240 gssEapMakeOrVerifyTokenMIC(OM_uint32 *minor,
241                            gss_ctx_id_t ctx,
242                            gss_buffer_t tokenMIC,
243                            int verifyMIC)
244 {
245     OM_uint32 major;
246     size_t i = 0, j;
247     enum gss_eap_token_type tokType;
248     OM_uint32 micTokType;
249     unsigned char wireTokType[2];
250     unsigned char *innerTokTypes = NULL, *innerTokLengths = NULL;
251     const struct gss_eap_token_buffer_set *tokens;
252     ssize_t checksumIndex = -1;
253
254     krb5_keyusage usage;
255     krb5_error_code code = 0;
256     krb5_context krbContext;
257     krb5_crypto_iov *kiov = NULL;
258 #ifdef HAVE_HEIMDAL_VERSION
259     krb5_crypto krbCrypto = NULL;
260     krb5_cksumtype cksumType;
261 #endif
262     size_t kiovCount;
263
264     GSSEAP_KRB_INIT(&krbContext);
265
266     tokens = verifyMIC ? ctx->inputTokens : ctx->outputTokens;
267
268     GSSEAP_ASSERT(tokens != NULL);
269
270 #ifdef HAVE_HEIMDAL_VERSION
271     code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, ETYPE_NULL, &krbCrypto);
272     if (code != 0)
273         goto cleanup;
274 #endif
275
276     kiovCount = 2 + (3 * tokens->buffers.count) + 1;
277
278     if (verifyMIC) {
279         assert(tokens->buffers.count != 0);
280         kiovCount -= 3;
281     }
282
283     kiov = GSSEAP_CALLOC(kiovCount, sizeof(*kiov));
284     if (kiov == NULL) {
285         *minor = ENOMEM;
286         goto cleanup;
287     }
288
289     innerTokTypes = GSSEAP_MALLOC(4 * tokens->buffers.count);
290     if (innerTokTypes == NULL) {
291         *minor = ENOMEM;
292         goto cleanup;
293     }
294
295     innerTokLengths = GSSEAP_MALLOC(4 * tokens->buffers.count);
296     if (innerTokLengths == NULL) {
297         *minor = ENOMEM;
298         goto cleanup;
299     }
300
301     /* Mechanism OID */
302     GSSEAP_ASSERT(ctx->mechanismUsed != GSS_C_NO_OID);
303     kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
304     kiov[i].data.length = ctx->mechanismUsed->length;
305     kiov[i].data.data = ctx->mechanismUsed->elements;
306     i++;
307
308     /* Token type */
309     if (CTX_IS_INITIATOR(ctx) ^ verifyMIC) {
310         tokType = TOK_TYPE_INITIATOR_CONTEXT;
311         micTokType = ITOK_TYPE_INITIATOR_MIC;
312         usage = KEY_USAGE_GSSEAP_INITOKEN_MIC;
313     } else {
314         tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
315         micTokType = ITOK_TYPE_ACCEPTOR_MIC;
316         usage = KEY_USAGE_GSSEAP_ACCTOKEN_MIC;
317     }
318     store_uint16_be(tokType, wireTokType);
319
320     kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
321     kiov[i].data.length = sizeof(wireTokType);
322     kiov[i].data.data = (char *)wireTokType;
323     i++;
324
325     for (j = 0; j < tokens->buffers.count; j++) {
326         if (verifyMIC &&
327             (tokens->types[j] & ITOK_TYPE_MASK) == micTokType) {
328             continue;
329         }
330
331         kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
332         kiov[i].data.length = 4;
333         kiov[i].data.data = (char *)&innerTokTypes[j * 4];
334         store_uint32_be(tokens->types[j] & ~(ITOK_FLAG_VERIFIED),
335                         kiov[i].data.data);
336         i++;
337
338         kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
339         kiov[i].data.length = 4;
340         kiov[i].data.data = (char *)&innerTokLengths[j * 4];
341         store_uint32_be(tokens->buffers.elements[j].length,
342                         kiov[i].data.data);
343         i++;
344
345         kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
346         gssBufferToKrbData(&tokens->buffers.elements[j], &kiov[i].data);
347         i++;
348     }
349
350     kiov[i].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
351     if (verifyMIC) {
352         gssBufferToKrbData(tokenMIC, &kiov[i].data);
353     } else {
354         size_t checksumSize;
355
356         code = krb5_c_checksum_length(krbContext, ctx->checksumType,
357                                       &checksumSize);
358         if (code != 0)
359             goto cleanup;
360
361         kiov[i].data.data = GSSEAP_MALLOC(checksumSize);
362         if (kiov[i].data.data == NULL) {
363             code = ENOMEM;
364             goto cleanup;
365         }
366         kiov[i].data.length = checksumSize;
367         checksumIndex = i;
368     }
369     i++;
370     GSSEAP_ASSERT(i == kiovCount);
371
372 #ifdef HAVE_HEIMDAL_VERSION
373     cksumType = ctx->checksumType;
374
375     if (verifyMIC) {
376         code = krb5_verify_checksum_iov(krbContext, krbCrypto, usage,
377                                         kiov, i, &cksumType);
378     } else {
379         code = krb5_create_checksum_iov(krbContext, krbCrypto, usage,
380                                         kiov, i, &cksumType);
381     }
382 #else
383     if (verifyMIC) {
384         krb5_boolean kvalid = FALSE;
385
386         code = krb5_c_verify_checksum_iov(krbContext, ctx->checksumType,
387                                           &ctx->rfc3961Key,
388                                           usage, kiov, i, &kvalid);
389         if (code == 0 && !kvalid) {
390             code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
391         }
392     } else {
393         code = krb5_c_make_checksum_iov(krbContext, ctx->checksumType,
394                                         &ctx->rfc3961Key,
395                                         usage, kiov, i);
396     }
397 #endif /* HAVE_HEIMDAL_VERSION */
398
399     if (code == 0 && !verifyMIC) {
400         krbDataToGssBuffer(&kiov[checksumIndex].data, tokenMIC);
401         checksumIndex = -1;
402     }
403
404 cleanup:
405     if (checksumIndex != -1)
406         GSSEAP_FREE(kiov[checksumIndex].data.data);
407     if (kiov != NULL)
408         GSSEAP_FREE(kiov);
409     if (innerTokTypes != NULL)
410         GSSEAP_FREE(innerTokTypes);
411     if (innerTokLengths != NULL)
412         GSSEAP_FREE(innerTokLengths);
413 #ifdef HAVE_HEIMDAL_VERSION
414     if (krbCrypto != NULL)
415         krb5_crypto_destroy(krbContext, krbCrypto);
416 #endif
417
418     *minor = code;
419
420     switch (code) {
421     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
422         major = GSS_S_BAD_SIG;
423         break;
424     case 0:
425         major = GSS_S_COMPLETE;
426         break;
427     default:
428         major = GSS_S_FAILURE;
429         break;
430     }
431
432     return major;
433 }
434
435 OM_uint32
436 gssEapMakeTokenMIC(OM_uint32 *minor,
437                    gss_ctx_id_t ctx,
438                    gss_buffer_t tokenMIC)
439 {
440     tokenMIC->length = 0;
441     tokenMIC->value = NULL;
442
443     return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, FALSE);
444 }
445
446 OM_uint32
447 gssEapVerifyTokenMIC(OM_uint32 *minor,
448                      gss_ctx_id_t ctx,
449                      const gss_buffer_t tokenMIC)
450 {
451     return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, TRUE);
452 }