Fixes for Heimdal (macOS) builds from Stefan.
[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 #ifdef HAVE_HEIMDAL_VERSION
357         code = krb5_checksumsize(krbContext, ctx->checksumType,
358                                  &checksumSize);
359 #else
360         code = krb5_c_checksum_length(krbContext, ctx->checksumType,
361                                       &checksumSize);
362 #endif
363         if (code != 0)
364             goto cleanup;
365
366         kiov[i].data.data = GSSEAP_MALLOC(checksumSize);
367         if (kiov[i].data.data == NULL) {
368             code = ENOMEM;
369             goto cleanup;
370         }
371         kiov[i].data.length = checksumSize;
372         checksumIndex = i;
373     }
374     i++;
375     GSSEAP_ASSERT(i == kiovCount);
376
377 #ifdef HAVE_HEIMDAL_VERSION
378     cksumType = ctx->checksumType;
379
380     if (verifyMIC) {
381         code = krb5_verify_checksum_iov(krbContext, krbCrypto, usage,
382                                         kiov, i, &cksumType);
383     } else {
384         code = krb5_create_checksum_iov(krbContext, krbCrypto, usage,
385                                         kiov, i, &cksumType);
386     }
387 #else
388     if (verifyMIC) {
389         krb5_boolean kvalid = FALSE;
390
391         code = krb5_c_verify_checksum_iov(krbContext, ctx->checksumType,
392                                           &ctx->rfc3961Key,
393                                           usage, kiov, i, &kvalid);
394         if (code == 0 && !kvalid) {
395             code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
396         }
397     } else {
398         code = krb5_c_make_checksum_iov(krbContext, ctx->checksumType,
399                                         &ctx->rfc3961Key,
400                                         usage, kiov, i);
401     }
402 #endif /* HAVE_HEIMDAL_VERSION */
403
404     if (code == 0 && !verifyMIC) {
405         krbDataToGssBuffer(&kiov[checksumIndex].data, tokenMIC);
406         checksumIndex = -1;
407     }
408
409 cleanup:
410     if (checksumIndex != -1)
411         GSSEAP_FREE(kiov[checksumIndex].data.data);
412     if (kiov != NULL)
413         GSSEAP_FREE(kiov);
414     if (innerTokTypes != NULL)
415         GSSEAP_FREE(innerTokTypes);
416     if (innerTokLengths != NULL)
417         GSSEAP_FREE(innerTokLengths);
418 #ifdef HAVE_HEIMDAL_VERSION
419     if (krbCrypto != NULL)
420         krb5_crypto_destroy(krbContext, krbCrypto);
421 #endif
422
423     *minor = code;
424
425     switch (code) {
426     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
427         major = GSS_S_BAD_SIG;
428         break;
429     case 0:
430         major = GSS_S_COMPLETE;
431         break;
432     default:
433         major = GSS_S_FAILURE;
434         break;
435     }
436
437     return major;
438 }
439
440 OM_uint32
441 gssEapMakeTokenMIC(OM_uint32 *minor,
442                    gss_ctx_id_t ctx,
443                    gss_buffer_t tokenMIC)
444 {
445     tokenMIC->length = 0;
446     tokenMIC->value = NULL;
447
448     return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, FALSE);
449 }
450
451 OM_uint32
452 gssEapVerifyTokenMIC(OM_uint32 *minor,
453                      gss_ctx_id_t ctx,
454                      const gss_buffer_t tokenMIC)
455 {
456     return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, TRUE);
457 }