use RFC3961 checksums for CB/exts MIC
[moonshot.git] / moonshot / 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_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;
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         major = GSS_S_FAILURE;
286         *minor = ENOMEM;
287         goto cleanup;
288     }
289
290     innerTokTypes = GSSEAP_MALLOC(4 * tokens->buffers.count);
291     if (innerTokTypes == NULL) {
292         *minor = ENOMEM;
293         major = GSS_S_FAILURE;
294         goto cleanup;
295     }
296
297     innerTokLengths = GSSEAP_MALLOC(4 * tokens->buffers.count);
298     if (innerTokLengths == NULL) {
299         major = GSS_S_FAILURE;
300         *minor = ENOMEM;
301         goto cleanup;
302     }
303
304     /* Mechanism OID */
305     GSSEAP_ASSERT(ctx->mechanismUsed != GSS_C_NO_OID);
306     kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
307     kiov[i].data.length = ctx->mechanismUsed->length;
308     kiov[i].data.data = ctx->mechanismUsed->elements;
309     i++;
310
311     /* Token type */
312     if (CTX_IS_INITIATOR(ctx) ^ verifyMIC) {
313         tokType = TOK_TYPE_INITIATOR_CONTEXT;
314         micTokType = ITOK_TYPE_INITIATOR_MIC;
315         usage = KEY_USAGE_GSSEAP_INITOKEN_MIC;
316     } else {
317         tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
318         micTokType = ITOK_TYPE_ACCEPTOR_MIC;
319         usage = KEY_USAGE_GSSEAP_ACCTOKEN_MIC;
320     }
321     store_uint16_be(tokType, wireTokType);
322
323     kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
324     kiov[i].data.length = sizeof(wireTokType);
325     kiov[i].data.data = (char *)wireTokType;
326     i++;
327
328     for (j = 0; j < tokens->buffers.count; j++) {
329         if (verifyMIC &&
330             (tokens->types[j] & ITOK_TYPE_MASK) == micTokType) {
331             continue;
332         }
333
334         kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
335         kiov[i].data.length = 4;
336         kiov[i].data.data = (char *)&innerTokTypes[j * 4];
337         store_uint32_be(tokens->types[j] & ~(ITOK_FLAG_VERIFIED),
338                         kiov[i].data.data);
339         i++;
340
341         kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
342         kiov[i].data.length = 4;
343         kiov[i].data.data = (char *)&innerTokLengths[j * 4];
344         store_uint32_be(tokens->buffers.elements[j].length,
345                         kiov[i].data.data);
346         i++;
347
348         kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
349         gssBufferToKrbData(&tokens->buffers.elements[j], &kiov[i].data);
350         i++;
351     }
352
353     kiov[i].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
354     if (verifyMIC) {
355         gssBufferToKrbData(tokenMIC, &kiov[i].data);
356     } else {
357         size_t checksumSize;
358
359         code = krb5_c_checksum_length(krbContext, ctx->checksumType,
360                                       &checksumSize);
361         if (code != 0)
362             goto cleanup;
363
364         kiov[i].data.data = GSSEAP_MALLOC(checksumSize);
365         if (kiov[i].data.data == NULL) {
366             code = ENOMEM;
367             goto cleanup;
368         }
369         kiov[i].data.length = checksumSize;
370         checksumIndex = i;
371     }
372     i++;
373     GSSEAP_ASSERT(i == kiovCount);
374
375 #ifdef HAVE_HEIMDAL_VERSION
376     cksumType = ctx->checksumType;
377
378     if (verifyMIC) {
379         code = krb5_verify_checksum_iov(krbContext, krbCrypto, usage,
380                                         kiov, i, &cksumType);
381     } else {
382         code = krb5_create_checksum_iov(krbContext, krbCrypto, usage,
383                                         kiov, i, &cksumType);
384     }
385 #else
386     if (verifyMIC) {
387         krb5_boolean kvalid = FALSE;
388
389         code = krb5_c_verify_checksum_iov(krbContext, ctx->checksumType,
390                                           &ctx->rfc3961Key,
391                                           usage, kiov, i, &kvalid);
392         if (code == 0 && !kvalid) {
393             code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
394         }
395     } else {
396         code = krb5_c_make_checksum_iov(krbContext, ctx->checksumType,
397                                         &ctx->rfc3961Key,
398                                         usage, kiov, i);
399     }
400 #endif /* HAVE_HEIMDAL_VERSION */
401
402     if (code == 0 && !verifyMIC) {
403         krbDataToGssBuffer(&kiov[checksumIndex].data, tokenMIC);
404         checksumIndex = -1;
405     }
406
407 cleanup:
408     if (checksumIndex != -1)
409         GSSEAP_FREE(kiov[checksumIndex].data.data);
410     if (kiov != NULL)
411         GSSEAP_FREE(kiov);
412     if (innerTokTypes != NULL)
413         GSSEAP_FREE(innerTokTypes);
414     if (innerTokLengths != NULL)
415         GSSEAP_FREE(innerTokLengths);
416 #ifdef HAVE_HEIMDAL_VERSION
417     if (krbCrypto != NULL)
418         krb5_crypto_destroy(krbContext, krbCrypto);
419 #endif
420
421     *minor = code;
422
423     switch (code) {
424     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
425         major = GSS_S_BAD_SIG;
426         break;
427     case 0:
428         major = GSS_S_COMPLETE;
429         break;
430     default:
431         major = GSS_S_FAILURE;
432         break;
433     }
434
435     return major;
436 }
437
438 OM_uint32
439 gssEapMakeTokenMIC(OM_uint32 *minor,
440                    gss_ctx_id_t ctx,
441                    gss_buffer_t tokenMIC)
442 {
443     tokenMIC->length = 0;
444     tokenMIC->value = NULL;
445
446     return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, FALSE);
447 }
448
449 OM_uint32
450 gssEapVerifyTokenMIC(OM_uint32 *minor,
451                      gss_ctx_id_t ctx,
452                      const gss_buffer_t tokenMIC)
453 {
454     return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, TRUE);
455 }