32ca195bf03c257ca0eb2af9cae602d3b7af212f
[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     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 = errno;
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 static void
88 releaseAcceptorContext(struct gss_eap_acceptor_ctx *ctx)
89 {
90     OM_uint32 tmpMinor;
91
92     if (ctx->radConn != NULL)
93         rs_conn_destroy(ctx->radConn);
94     if (ctx->radContext != NULL)
95         rs_context_destroy(ctx->radContext);
96     if (ctx->radServer != NULL)
97         GSSEAP_FREE(ctx->radServer);
98     gss_release_buffer(&tmpMinor, &ctx->state);
99     if (ctx->vps != NULL)
100         gssEapRadiusFreeAvps(&tmpMinor, &ctx->vps);
101 }
102
103 OM_uint32
104 gssEapReleaseContext(OM_uint32 *minor,
105                      gss_ctx_id_t *pCtx)
106 {
107     OM_uint32 tmpMinor;
108     gss_ctx_id_t ctx = *pCtx;
109     krb5_context krbContext = NULL;
110
111     if (ctx == GSS_C_NO_CONTEXT) {
112         return GSS_S_COMPLETE;
113     }
114
115     gssEapKerberosInit(&tmpMinor, &krbContext);
116
117 #ifdef GSSEAP_ENABLE_REAUTH
118     if (ctx->flags & CTX_FLAG_KRB_REAUTH) {
119         gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
120     } else
121 #endif
122     if (CTX_IS_INITIATOR(ctx)) {
123         releaseInitiatorContext(&ctx->initiatorCtx);
124     } else {
125         releaseAcceptorContext(&ctx->acceptorCtx);
126     }
127
128     krb5_free_keyblock_contents(krbContext, &ctx->rfc3961Key);
129     gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
130     gssEapReleaseName(&tmpMinor, &ctx->acceptorName);
131     gssEapReleaseOid(&tmpMinor, &ctx->mechanismUsed);
132     sequenceFree(&tmpMinor, &ctx->seqState);
133     gssEapReleaseCred(&tmpMinor, &ctx->cred);
134
135     GSSEAP_MUTEX_DESTROY(&ctx->mutex);
136
137     memset(ctx, 0, sizeof(*ctx));
138     GSSEAP_FREE(ctx);
139     *pCtx = GSS_C_NO_CONTEXT;
140
141     *minor = 0;
142     return GSS_S_COMPLETE;
143 }
144
145 OM_uint32
146 gssEapMakeToken(OM_uint32 *minor,
147                 gss_ctx_id_t ctx,
148                 const gss_buffer_t innerToken,
149                 enum gss_eap_token_type tokenType,
150                 gss_buffer_t outputToken)
151 {
152     unsigned char *p;
153
154     assert(ctx->mechanismUsed != GSS_C_NO_OID);
155
156     outputToken->length = tokenSize(ctx->mechanismUsed, innerToken->length);
157     outputToken->value = GSSEAP_MALLOC(outputToken->length);
158     if (outputToken->value == NULL) {
159         *minor = ENOMEM;
160         return GSS_S_FAILURE;
161     }
162
163     p = (unsigned char *)outputToken->value;
164     makeTokenHeader(ctx->mechanismUsed, innerToken->length, &p, tokenType);
165     memcpy(p, innerToken->value, innerToken->length);
166
167     *minor = 0;
168     return GSS_S_COMPLETE;
169 }
170
171 OM_uint32
172 gssEapVerifyToken(OM_uint32 *minor,
173                   gss_ctx_id_t ctx,
174                   const gss_buffer_t inputToken,
175                   enum gss_eap_token_type *actualToken,
176                   gss_buffer_t innerInputToken)
177 {
178     OM_uint32 major;
179     size_t bodySize;
180     unsigned char *p = (unsigned char *)inputToken->value;
181     gss_OID_desc oidBuf;
182     gss_OID oid;
183
184     if (ctx->mechanismUsed != GSS_C_NO_OID) {
185         oid = ctx->mechanismUsed;
186     } else {
187         oidBuf.elements = NULL;
188         oidBuf.length = 0;
189         oid = &oidBuf;
190     }
191
192     major = verifyTokenHeader(minor, oid, &bodySize, &p,
193                               inputToken->length, actualToken);
194     if (GSS_ERROR(major))
195         return major;
196
197     if (ctx->mechanismUsed == GSS_C_NO_OID) {
198         major = gssEapCanonicalizeOid(minor, oid, 0, &ctx->mechanismUsed);
199         if (GSS_ERROR(major))
200             return major;
201     }
202
203     innerInputToken->length = bodySize;
204     innerInputToken->value = p;
205
206     *minor = 0;
207     return GSS_S_COMPLETE;
208 }
209
210 OM_uint32
211 gssEapContextTime(OM_uint32 *minor,
212                   gss_ctx_id_t context_handle,
213                   OM_uint32 *time_rec)
214 {
215     *minor = 0;
216
217     if (context_handle->expiryTime == 0) {
218         *time_rec = GSS_C_INDEFINITE;
219     } else {
220         time_t now, lifetime;
221
222         time(&now);
223         lifetime = context_handle->expiryTime - now;
224         if (lifetime <= 0) {
225             *time_rec = 0;
226             return GSS_S_CONTEXT_EXPIRED;
227         }
228         *time_rec = lifetime;
229     }
230
231     return GSS_S_COMPLETE;
232 }
233
234 static OM_uint32
235 gssEapMakeOrVerifyTokenMIC(OM_uint32 *minor,
236                            gss_ctx_id_t ctx,
237                            gss_buffer_t tokenMIC,
238                            int verifyMIC)
239 {
240     OM_uint32 major;
241     gss_iov_buffer_desc *iov = NULL;
242     size_t i = 0, j;
243     enum gss_eap_token_type tokType;
244     OM_uint32 micTokType;
245     unsigned char wireTokType[2];
246     unsigned char *innerTokTypes = NULL, *innerTokLengths = NULL;
247     const struct gss_eap_token_buffer_set *tokens;
248
249     tokens = verifyMIC ? ctx->inputTokens : ctx->outputTokens;
250
251     assert(tokens != NULL);
252
253     iov = GSSEAP_CALLOC(2 + (3 * tokens->buffers.count) + 1, sizeof(*iov));
254     if (iov == NULL) {
255         major = GSS_S_FAILURE;
256         *minor = ENOMEM;
257         goto cleanup;
258     }
259
260     innerTokTypes = GSSEAP_MALLOC(4 * tokens->buffers.count);
261     if (innerTokTypes == NULL) {
262         *minor = ENOMEM;
263         major = GSS_S_FAILURE;
264         goto cleanup;
265     }
266
267     innerTokLengths = GSSEAP_MALLOC(4 * tokens->buffers.count);
268     if (innerTokLengths == NULL) {
269         major = GSS_S_FAILURE;
270         *minor = ENOMEM;
271         goto cleanup;
272     }
273
274     /* Mechanism OID */
275     assert(ctx->mechanismUsed != GSS_C_NO_OID);
276     iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
277     iov[i].buffer.length = ctx->mechanismUsed->length;
278     iov[i].buffer.value = ctx->mechanismUsed->elements;
279     i++;
280
281     /* Token type */
282     if (CTX_IS_INITIATOR(ctx) ^ verifyMIC) {
283         tokType = TOK_TYPE_INITIATOR_CONTEXT;
284         micTokType = ITOK_TYPE_INITIATOR_MIC;
285     } else {
286         tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
287         micTokType = ITOK_TYPE_ACCEPTOR_MIC;
288     }
289     store_uint16_be(tokType, wireTokType);
290
291     iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
292     iov[i].buffer.length = sizeof(wireTokType);
293     iov[i].buffer.value = wireTokType;
294     i++;
295
296     for (j = 0; j < tokens->buffers.count; j++) {
297         if (verifyMIC &&
298             (tokens->types[j] & ITOK_TYPE_MASK) == micTokType)
299             continue; /* will use this slot for trailer */
300
301         iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
302         iov[i].buffer.length = 4;
303         iov[i].buffer.value = &innerTokTypes[j * 4];
304         store_uint32_be(tokens->types[j] & ~(ITOK_FLAG_VERIFIED),
305                         iov[i].buffer.value);
306         i++;
307
308         iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
309         iov[i].buffer.length = 4;
310         iov[i].buffer.value = &innerTokLengths[j * 4];
311         store_uint32_be(tokens->buffers.elements[j].length,
312                         iov[i].buffer.value);
313         i++;
314
315         iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
316         iov[i].buffer = tokens->buffers.elements[j];
317         i++;
318     }
319
320     if (verifyMIC) {
321         assert(tokenMIC->length >= 16);
322
323         assert(i < 2 + (3 * tokens->buffers.count));
324
325         iov[i].type = GSS_IOV_BUFFER_TYPE_HEADER;
326         iov[i].buffer.length = 16;
327         iov[i].buffer.value = tokenMIC->value;
328         i++;
329
330         iov[i].type = GSS_IOV_BUFFER_TYPE_TRAILER;
331         iov[i].buffer.length = tokenMIC->length - 16;
332         iov[i].buffer.value = (unsigned char *)tokenMIC->value + 16;
333         i++;
334
335         major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
336                                         iov, i, TOK_TYPE_MIC);
337     } else {
338         iov[i++].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
339         major = gssEapWrapOrGetMIC(minor, ctx, FALSE, NULL,
340                                    iov, i, TOK_TYPE_MIC);
341         if (!GSS_ERROR(major))
342             *tokenMIC = iov[i - 1].buffer;
343     }
344
345 cleanup:
346     if (iov != NULL)
347         gssEapReleaseIov(iov, tokens->buffers.count);
348     if (innerTokTypes != NULL)
349         GSSEAP_FREE(innerTokTypes);
350     if (innerTokLengths != NULL)
351         GSSEAP_FREE(innerTokLengths);
352
353     return major;
354 }
355
356 OM_uint32
357 gssEapMakeTokenMIC(OM_uint32 *minor,
358                    gss_ctx_id_t ctx,
359                    gss_buffer_t tokenMIC)
360 {
361     tokenMIC->length = 0;
362     tokenMIC->value = NULL;
363
364     return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, FALSE);
365 }
366
367 OM_uint32
368 gssEapVerifyTokenMIC(OM_uint32 *minor,
369                      gss_ctx_id_t ctx,
370                      const gss_buffer_t tokenMIC)
371 {
372     if (tokenMIC->length < 16) {
373         *minor = GSSEAP_TOK_TRUNC;
374         return GSS_S_BAD_SIG;
375     }
376
377     return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, TRUE);
378 }