cleanup, plugin attr context
[mech_eap.git] / accept_sec_context.c
1 /*
2  * Copyright (c) 2010, 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 #include "gssapiP_eap.h"
34
35 #define RC_CONFIG_FILE      SYSCONFDIR "/radiusclient/radiusclient.conf"
36
37 /*
38  * Mark a context as ready for cryptographic operations
39  */
40 static OM_uint32
41 acceptReady(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
42 {
43     OM_uint32 major;
44     VALUE_PAIR *vp;
45     gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
46
47     /* Cache encryption type derived from selected mechanism OID */
48     major = gssEapOidToEnctype(minor, ctx->mechanismUsed,
49                                &ctx->encryptionType);
50     if (GSS_ERROR(major))
51         return major;
52
53     vp = rc_avpair_get(ctx->acceptorCtx.avps, PW_USER_NAME, 0);
54     if (vp != NULL) {
55         nameBuf.length = vp->lvalue;
56         nameBuf.value = vp->strvalue;
57     } else if (ctx->initiatorName == GSS_C_NO_NAME) {
58         ctx->gssFlags |= GSS_C_ANON_FLAG;
59     }
60
61     if (nameBuf.length != 0 || ctx->initiatorName == GSS_C_NO_NAME) {
62         major = gssEapImportName(minor, &nameBuf, GSS_C_NT_USER_NAME,
63                                  &ctx->initiatorName);
64         if (GSS_ERROR(major))
65             return major;
66     }
67
68     ctx->initiatorName->attrCtx = gssEapCreateAttrContext(cred, ctx);
69
70     vp = rc_avpair_get(ctx->acceptorCtx.avps, 0x01370010, 0);
71     if (ctx->encryptionType != ENCTYPE_NULL && vp != NULL) {
72         major = gssEapDeriveRfc3961Key(minor,
73                                        (unsigned char *)vp->strvalue,
74                                        vp->lvalue,
75                                        ctx->encryptionType,
76                                        &ctx->rfc3961Key);
77         if (GSS_ERROR(major))
78             return major;
79
80         major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
81                                            &ctx->checksumType);
82         if (GSS_ERROR(major))
83             return major;
84     } else {
85         /*
86          * draft-howlett-eap-gss says that integrity/confidentialty should
87          * always be advertised as available, but if we have no keying
88          * material it seems confusing to the caller to advertise this.
89          */
90         ctx->gssFlags &= ~(GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
91         ctx->encryptionType = ENCTYPE_NULL;
92     }
93
94     major = sequenceInit(minor,
95                          &ctx->seqState, ctx->recvSeq,
96                          ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
97                          ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
98                          TRUE);
99     if (GSS_ERROR(major))
100         return major;
101
102     return GSS_S_COMPLETE;
103 }
104
105 static OM_uint32
106 eapGssSmAcceptIdentity(OM_uint32 *minor,
107                        gss_ctx_id_t ctx,
108                        gss_cred_id_t cred,
109                        gss_buffer_t inputToken,
110                        gss_channel_bindings_t chanBindings,
111                        gss_buffer_t outputToken)
112 {
113     OM_uint32 major;
114     rc_handle *rh;
115     union {
116         struct eap_hdr pdu;
117         unsigned char data[5];
118     } pkt;
119     gss_buffer_desc pktBuffer;
120     char *config = RC_CONFIG_FILE;
121
122     if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0)
123         return GSS_S_DEFECTIVE_TOKEN;
124
125     assert(ctx->acceptorCtx.radHandle == NULL);
126
127     if (cred != GSS_C_NO_CREDENTIAL && cred->radiusConfigFile != NULL)
128         config = cred->radiusConfigFile;
129
130     rh = ctx->acceptorCtx.radHandle = rc_read_config(config);
131     if (rh == NULL) {
132         *minor = errno;
133         return GSS_S_FAILURE;
134     }
135
136     if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary")) != 0) {
137         *minor = errno;
138         return GSS_S_FAILURE;
139     }
140
141     if (ctx->acceptorName == GSS_C_NO_NAME &&
142         cred != GSS_C_NO_CREDENTIAL &&
143         cred->name != GSS_C_NO_NAME) {
144         major = gss_duplicate_name(minor, cred->name, &ctx->acceptorName);
145         if (GSS_ERROR(major))
146             return major;
147     }
148
149     pkt.pdu.code = EAP_CODE_REQUEST;
150     pkt.pdu.identifier = 0;
151     pkt.pdu.length = htons(sizeof(pkt.data));
152     pkt.data[4] = EAP_TYPE_IDENTITY;
153
154     pktBuffer.length = sizeof(pkt.data);
155     pktBuffer.value = pkt.data;
156
157     major = duplicateBuffer(minor, &pktBuffer, outputToken);
158     if (GSS_ERROR(major))
159         return major;
160
161     ctx->state = EAP_STATE_AUTHENTICATE;
162
163     return GSS_S_CONTINUE_NEEDED;
164 }
165
166 static OM_uint32
167 importInitiatorIdentity(OM_uint32 *minor,
168                         gss_ctx_id_t ctx,
169                         gss_buffer_t inputToken,
170                         gss_buffer_t nameBuf)
171 {
172     OM_uint32 major, tmpMinor;
173     struct eap_hdr *pdu = (struct eap_hdr *)inputToken->value;
174     unsigned char *pos = (unsigned char *)(pdu + 1);
175     gss_name_t name;
176
177     assert(pdu->code == EAP_CODE_RESPONSE);
178     assert(pos[0] == EAP_TYPE_IDENTITY);
179
180     nameBuf->value = pos + 1;
181     nameBuf->length = inputToken->length - sizeof(*pdu) - 1;
182
183     major = gssEapImportName(minor, nameBuf, GSS_C_NT_USER_NAME, &name);
184     if (GSS_ERROR(major))
185         return major;
186
187     gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
188     ctx->initiatorName = name;
189
190     return GSS_S_COMPLETE;
191 }
192
193 static OM_uint32
194 eapGssSmAcceptAuthenticate(OM_uint32 *minor,
195                            gss_ctx_id_t ctx,
196                            gss_cred_id_t cred,
197                            gss_buffer_t inputToken,
198                            gss_channel_bindings_t chanBindings,
199                            gss_buffer_t outputToken)
200 {
201     OM_uint32 major, tmpMinor;
202     int code;
203     VALUE_PAIR *send = NULL;
204     VALUE_PAIR *received = NULL;
205     rc_handle *rh = ctx->acceptorCtx.radHandle;
206     char msgBuffer[4096];
207     struct eap_hdr *pdu;
208     unsigned char *pos;
209     gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
210
211     pdu = (struct eap_hdr *)inputToken->value;
212     pos = (unsigned char *)(pdu + 1);
213
214     if (inputToken->length > sizeof(*pdu) &&
215         pdu->code == EAP_CODE_RESPONSE &&
216         pos[0] == EAP_TYPE_IDENTITY) {
217         major = importInitiatorIdentity(minor, ctx, inputToken, &nameBuf);
218         if (GSS_ERROR(major))
219             goto cleanup;
220
221         major = addAvpFromBuffer(minor, rh, &send, PW_USER_NAME, &nameBuf);
222         if (GSS_ERROR(major))
223             goto cleanup;
224     }
225
226     major = addAvpFromBuffer(minor, rh, &send, PW_EAP_MESSAGE, inputToken);
227     if (GSS_ERROR(major))
228         goto cleanup;
229
230     if (ctx->acceptorCtx.lastStatus == CHALLENGE_RC) {
231         major = addAvpFromBuffer(minor, rh, &send, PW_STATE,
232                                  &ctx->acceptorCtx.state);
233         if (GSS_ERROR(major))
234             goto cleanup;
235
236         gss_release_buffer(&tmpMinor, &ctx->acceptorCtx.state);
237     }
238
239     code = rc_auth(rh, 0, send, &received, msgBuffer);
240     switch (code) {
241     case OK_RC:
242     case CHALLENGE_RC:
243         major = GSS_S_CONTINUE_NEEDED;
244         break;
245     case TIMEOUT_RC:
246         major = GSS_S_UNAVAILABLE;
247         break;
248     case REJECT_RC:
249         major = GSS_S_DEFECTIVE_CREDENTIAL;
250         break;
251     default:
252         major = GSS_S_FAILURE;
253         goto cleanup;
254     }
255
256     if (GSS_ERROR(major))
257         goto cleanup;
258
259     ctx->acceptorCtx.lastStatus = code;
260
261     major = getBufferFromAvps(minor, received, PW_EAP_MESSAGE,
262                               outputToken, TRUE);
263     if ((major == GSS_S_UNAVAILABLE && code != OK_RC) ||
264         GSS_ERROR(major))
265         goto cleanup;
266
267     if (code == CHALLENGE_RC) {
268         major = getBufferFromAvps(minor, received, PW_STATE,
269                                   &ctx->acceptorCtx.state, TRUE);
270         if (major != GSS_S_UNAVAILABLE && GSS_ERROR(major))
271             goto cleanup;
272     } else {
273         ctx->acceptorCtx.avps = received;
274         received = NULL;
275
276         major = acceptReady(minor, ctx, cred);
277         if (GSS_ERROR(major))
278             goto cleanup;
279
280         ctx->state = EAP_STATE_GSS_CHANNEL_BINDINGS;
281     }
282
283     major = GSS_S_CONTINUE_NEEDED;
284
285 cleanup:
286     if (received != NULL)
287         rc_avpair_free(received);
288
289     return major;
290 }
291
292 static OM_uint32
293 eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
294                                  gss_ctx_id_t ctx,
295                                  gss_cred_id_t cred,
296                                  gss_buffer_t inputToken,
297                                  gss_channel_bindings_t chanBindings,
298                                  gss_buffer_t outputToken)
299 {
300     OM_uint32 major;
301     gss_iov_buffer_desc iov[2];
302
303     outputToken->length = 0;
304     outputToken->value = NULL;
305
306     if (chanBindings == GSS_C_NO_CHANNEL_BINDINGS) {
307         ctx->state = EAP_STATE_ESTABLISHED;
308         return GSS_S_COMPLETE;
309     }
310
311     if (inputToken->length < 14) {
312         return GSS_S_DEFECTIVE_TOKEN;
313     }
314
315     iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
316     iov[0].buffer.length = 0;
317     iov[0].buffer.value = NULL;
318
319     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
320         iov[0].buffer = chanBindings->application_data;
321
322     iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER;
323     iov[1].buffer.length = 16;
324     iov[1].buffer.value = (unsigned char *)inputToken->value - 2;
325
326     assert(load_uint16_be(iov[1].buffer.value) == TOK_TYPE_GSS_CB);
327
328     iov[2].type = GSS_IOV_BUFFER_TYPE_TRAILER;
329     iov[2].buffer.length = inputToken->length - 14;
330     iov[2].buffer.value = (unsigned char *)inputToken->value + 14;
331
332     major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
333                                     iov, 3, TOK_TYPE_GSS_CB);
334     if (major == GSS_S_COMPLETE) {
335         ctx->state = EAP_STATE_ESTABLISHED;
336     }
337
338 #if 0
339     gss_release_buffer(&tmpMinor, &iov[0].buffer);
340 #endif
341
342     return major;
343 }
344
345 static OM_uint32
346 eapGssSmAcceptEstablished(OM_uint32 *minor,
347                           gss_ctx_id_t ctx,
348                           gss_cred_id_t cred,
349                           gss_buffer_t inputToken,
350                           gss_channel_bindings_t chanBindings,
351                           gss_buffer_t outputToken)
352 {
353     /* Called with already established context */
354     *minor = EINVAL;
355     return GSS_S_BAD_STATUS;
356 }
357
358 static struct gss_eap_acceptor_sm {
359     enum gss_eap_token_type inputTokenType;
360     enum gss_eap_token_type outputTokenType;
361     OM_uint32 (*processToken)(OM_uint32 *,
362                               gss_ctx_id_t,
363                               gss_cred_id_t,
364                               gss_buffer_t,
365                               gss_channel_bindings_t,
366                               gss_buffer_t);
367 } eapGssAcceptorSm[] = {
368     { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  eapGssSmAcceptIdentity           },
369     { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  eapGssSmAcceptAuthenticate       },
370     { TOK_TYPE_GSS_CB,      TOK_TYPE_NONE,     eapGssSmAcceptGssChannelBindings },
371     { TOK_TYPE_NONE,        TOK_TYPE_NONE,     eapGssSmAcceptEstablished        },
372 };
373
374 OM_uint32
375 gss_accept_sec_context(OM_uint32 *minor,
376                        gss_ctx_id_t *context_handle,
377                        gss_cred_id_t cred,
378                        gss_buffer_t input_token,
379                        gss_channel_bindings_t input_chan_bindings,
380                        gss_name_t *src_name,
381                        gss_OID *mech_type,
382                        gss_buffer_t output_token,
383                        OM_uint32 *ret_flags,
384                        OM_uint32 *time_rec,
385                        gss_cred_id_t *delegated_cred_handle)
386 {
387     OM_uint32 major;
388     OM_uint32 tmpMajor, tmpMinor;
389     gss_ctx_id_t ctx = *context_handle;
390     struct gss_eap_acceptor_sm *sm = NULL;
391     gss_buffer_desc innerInputToken = GSS_C_EMPTY_BUFFER;
392     gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
393
394     *minor = 0;
395
396     output_token->length = 0;
397     output_token->value = NULL;
398
399     if (cred != GSS_C_NO_CREDENTIAL && !(cred->flags & CRED_FLAG_ACCEPT)) {
400         return GSS_S_NO_CRED;
401     }
402
403     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
404         return GSS_S_DEFECTIVE_TOKEN;
405     }
406
407     if (ctx == GSS_C_NO_CONTEXT) {
408         major = gssEapAllocContext(minor, &ctx);
409         if (GSS_ERROR(major))
410             return major;
411
412         *context_handle = ctx;
413     }
414
415     GSSEAP_MUTEX_LOCK(&ctx->mutex);
416
417     sm = &eapGssAcceptorSm[ctx->state];
418
419     major = gssEapVerifyToken(minor, ctx, input_token,
420                               sm->inputTokenType, &innerInputToken);
421     if (GSS_ERROR(major))
422         goto cleanup;
423
424     /* If credentials were provided, check they're usable with this mech */
425     if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
426         major = GSS_S_BAD_MECH;
427         goto cleanup;
428     }
429
430     do {
431         sm = &eapGssAcceptorSm[ctx->state];
432
433         major = (sm->processToken)(minor,
434                                    ctx,
435                                    cred,
436                                    &innerInputToken,
437                                    input_chan_bindings,
438                                    &innerOutputToken);
439         if (GSS_ERROR(major))
440             goto cleanup;
441     } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.length == 0);
442
443     if (mech_type != NULL) {
444         if (!gssEapInternalizeOid(ctx->mechanismUsed, mech_type))
445             duplicateOid(&tmpMinor, ctx->mechanismUsed, mech_type);
446     }
447     if (innerOutputToken.length != 0) {
448         tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken,
449                                    sm->outputTokenType, output_token);
450         if (GSS_ERROR(tmpMajor)) {
451             major = tmpMajor;
452             *minor = tmpMinor;
453             goto cleanup;
454         }
455     }
456     if (ret_flags != NULL)
457         *ret_flags = ctx->gssFlags;
458     if (delegated_cred_handle != NULL)
459         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
460
461     if (major == GSS_S_COMPLETE) {
462         if (src_name != NULL && ctx->initiatorName != GSS_C_NO_NAME) {
463             major = gss_duplicate_name(&tmpMinor, ctx->initiatorName, src_name);
464             if (GSS_ERROR(major))
465                 goto cleanup;
466         }
467         if (time_rec != NULL)
468             gss_context_time(&tmpMinor, ctx, time_rec);
469     }
470
471     assert(ctx->state == EAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED);
472
473 cleanup:
474     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
475
476     if (GSS_ERROR(major))
477         gssEapReleaseContext(&tmpMinor, context_handle);
478
479     gss_release_buffer(&tmpMinor, &innerOutputToken);
480
481     return major;
482 }
483