add GET_LAST_ERROR macro
[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 = 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
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
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
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     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     gss_iov_buffer_desc *iov = NULL;
247     size_t i = 0, j;
248     enum gss_eap_token_type tokType;
249     OM_uint32 micTokType;
250     unsigned char wireTokType[2];
251     unsigned char *innerTokTypes = NULL, *innerTokLengths = NULL;
252     const struct gss_eap_token_buffer_set *tokens;
253
254     tokens = verifyMIC ? ctx->inputTokens : ctx->outputTokens;
255
256     assert(tokens != NULL);
257
258     iov = GSSEAP_CALLOC(2 + (3 * tokens->buffers.count) + 1, sizeof(*iov));
259     if (iov == NULL) {
260         major = GSS_S_FAILURE;
261         *minor = ENOMEM;
262         goto cleanup;
263     }
264
265     innerTokTypes = GSSEAP_MALLOC(4 * tokens->buffers.count);
266     if (innerTokTypes == NULL) {
267         *minor = ENOMEM;
268         major = GSS_S_FAILURE;
269         goto cleanup;
270     }
271
272     innerTokLengths = GSSEAP_MALLOC(4 * tokens->buffers.count);
273     if (innerTokLengths == NULL) {
274         major = GSS_S_FAILURE;
275         *minor = ENOMEM;
276         goto cleanup;
277     }
278
279     /* Mechanism OID */
280     assert(ctx->mechanismUsed != GSS_C_NO_OID);
281     iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
282     iov[i].buffer.length = ctx->mechanismUsed->length;
283     iov[i].buffer.value = ctx->mechanismUsed->elements;
284     i++;
285
286     /* Token type */
287     if (CTX_IS_INITIATOR(ctx) ^ verifyMIC) {
288         tokType = TOK_TYPE_INITIATOR_CONTEXT;
289         micTokType = ITOK_TYPE_INITIATOR_MIC;
290     } else {
291         tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
292         micTokType = ITOK_TYPE_ACCEPTOR_MIC;
293     }
294     store_uint16_be(tokType, wireTokType);
295
296     iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
297     iov[i].buffer.length = sizeof(wireTokType);
298     iov[i].buffer.value = wireTokType;
299     i++;
300
301     for (j = 0; j < tokens->buffers.count; j++) {
302         if (verifyMIC &&
303             (tokens->types[j] & ITOK_TYPE_MASK) == micTokType)
304             continue; /* will use this slot for trailer */
305
306         iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
307         iov[i].buffer.length = 4;
308         iov[i].buffer.value = &innerTokTypes[j * 4];
309         store_uint32_be(tokens->types[j] & ~(ITOK_FLAG_VERIFIED),
310                         iov[i].buffer.value);
311         i++;
312
313         iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
314         iov[i].buffer.length = 4;
315         iov[i].buffer.value = &innerTokLengths[j * 4];
316         store_uint32_be(tokens->buffers.elements[j].length,
317                         iov[i].buffer.value);
318         i++;
319
320         iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
321         iov[i].buffer = tokens->buffers.elements[j];
322         i++;
323     }
324
325     if (verifyMIC) {
326         assert(tokenMIC->length >= 16);
327
328         assert(i < 2 + (3 * tokens->buffers.count));
329
330         iov[i].type = GSS_IOV_BUFFER_TYPE_HEADER;
331         iov[i].buffer.length = 16;
332         iov[i].buffer.value = tokenMIC->value;
333         i++;
334
335         iov[i].type = GSS_IOV_BUFFER_TYPE_TRAILER;
336         iov[i].buffer.length = tokenMIC->length - 16;
337         iov[i].buffer.value = (unsigned char *)tokenMIC->value + 16;
338         i++;
339
340         major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
341                                         iov, i, TOK_TYPE_MIC);
342     } else {
343         iov[i++].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
344         major = gssEapWrapOrGetMIC(minor, ctx, FALSE, NULL,
345                                    iov, i, TOK_TYPE_MIC);
346         if (!GSS_ERROR(major))
347             *tokenMIC = iov[i - 1].buffer;
348     }
349
350 cleanup:
351     if (iov != NULL)
352         gssEapReleaseIov(iov, tokens->buffers.count);
353     if (innerTokTypes != NULL)
354         GSSEAP_FREE(innerTokTypes);
355     if (innerTokLengths != NULL)
356         GSSEAP_FREE(innerTokLengths);
357
358     return major;
359 }
360
361 OM_uint32
362 gssEapMakeTokenMIC(OM_uint32 *minor,
363                    gss_ctx_id_t ctx,
364                    gss_buffer_t tokenMIC)
365 {
366     tokenMIC->length = 0;
367     tokenMIC->value = NULL;
368
369     return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, FALSE);
370 }
371
372 OM_uint32
373 gssEapVerifyTokenMIC(OM_uint32 *minor,
374                      gss_ctx_id_t ctx,
375                      const gss_buffer_t tokenMIC)
376 {
377     if (tokenMIC->length < 16) {
378         *minor = GSSEAP_TOK_TRUNC;
379         return GSS_S_BAD_SIG;
380     }
381
382     return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, TRUE);
383 }