d192d71f4a33ae973e9fd8de4349eb65a0c08c2f
[mech_eap.git] / accept_sec_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  * Establish a security context on the acceptor (server). These functions
35  * wrap around libradsec and (thus) talk to a RADIUS server or proxy.
36  */
37
38 #include "gssapiP_eap.h"
39
40 #ifdef GSSEAP_ENABLE_REAUTH
41 static OM_uint32
42 eapGssSmAcceptGssReauth(OM_uint32 *minor,
43                         gss_ctx_id_t ctx,
44                         gss_cred_id_t cred,
45                         gss_buffer_t inputToken,
46                         gss_channel_bindings_t chanBindings,
47                         gss_buffer_t outputToken);
48 #endif
49
50 /*
51  * Mark an acceptor context as ready for cryptographic operations
52  */
53 static OM_uint32
54 acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
55 {
56     OM_uint32 major, tmpMinor;
57     VALUE_PAIR *vp;
58     gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
59
60     /* Cache encryption type derived from selected mechanism OID */
61     major = gssEapOidToEnctype(minor, ctx->mechanismUsed,
62                                &ctx->encryptionType);
63     if (GSS_ERROR(major))
64         return major;
65
66     gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
67
68     major = gssEapRadiusGetRawAvp(minor, ctx->acceptorCtx.vps,
69                                   PW_USER_NAME, 0, &vp);
70     if (major == GSS_S_COMPLETE) {
71         nameBuf.length = vp->length;
72         nameBuf.value = vp->vp_strvalue;
73     } else {
74         ctx->gssFlags |= GSS_C_ANON_FLAG;
75     }
76
77     major = gssEapImportName(minor, &nameBuf,
78                              (ctx->gssFlags & GSS_C_ANON_FLAG) ?
79                                 GSS_C_NT_ANONYMOUS : GSS_C_NT_USER_NAME,
80                              &ctx->initiatorName);
81     if (GSS_ERROR(major))
82         return major;
83
84     major = gssEapRadiusGetRawAvp(minor, ctx->acceptorCtx.vps,
85                                   PW_MS_MPPE_SEND_KEY, VENDORPEC_MS, &vp);
86     if (GSS_ERROR(major)) {
87         *minor = GSSEAP_KEY_UNAVAILABLE;
88         return GSS_S_UNAVAILABLE;
89     }
90
91     major = gssEapDeriveRfc3961Key(minor,
92                                    vp->vp_octets,
93                                    vp->length,
94                                    ctx->encryptionType,
95                                    &ctx->rfc3961Key);
96     if (GSS_ERROR(major))
97         return major;
98
99     major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
100                                        &ctx->checksumType);
101     if (GSS_ERROR(major))
102         return major;
103
104     major = sequenceInit(minor,
105                          &ctx->seqState, ctx->recvSeq,
106                          ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
107                          ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
108                          TRUE);
109     if (GSS_ERROR(major))
110         return major;
111
112     major = gssEapCreateAttrContext(minor, cred, ctx,
113                                     &ctx->initiatorName->attrCtx,
114                                     &ctx->expiryTime);
115     if (GSS_ERROR(major))
116         return major;
117
118     *minor = 0;
119     return GSS_S_COMPLETE;
120 }
121
122 /*
123  * Emit a identity EAP request to force the initiator (peer) to identify
124  * itself.
125  */
126 static OM_uint32
127 eapGssSmAcceptIdentity(OM_uint32 *minor,
128                        gss_ctx_id_t ctx,
129                        gss_cred_id_t cred,
130                        gss_buffer_t inputToken,
131                        gss_channel_bindings_t chanBindings,
132                        gss_buffer_t outputToken)
133 {
134     OM_uint32 major;
135     struct wpabuf *reqData;
136     gss_buffer_desc pktBuffer;
137
138     if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
139         *minor = GSSEAP_WRONG_SIZE;
140         return GSS_S_DEFECTIVE_TOKEN;
141     }
142
143     assert(ctx->acceptorName == GSS_C_NO_NAME);
144
145     if (cred->name != GSS_C_NO_NAME) {
146         major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
147         if (GSS_ERROR(major))
148             return major;
149     }
150
151     reqData = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, 0,
152                             EAP_CODE_REQUEST, 0);
153     if (reqData == NULL) {
154         *minor = ENOMEM;
155         return GSS_S_FAILURE;
156     }
157
158     pktBuffer.length = wpabuf_len(reqData);
159     pktBuffer.value = (void *)wpabuf_head(reqData);
160
161     major = duplicateBuffer(minor, &pktBuffer, outputToken);
162     if (GSS_ERROR(major))
163         return major;
164
165     ctx->state = GSSEAP_STATE_AUTHENTICATE;
166
167     wpabuf_free(reqData);
168
169     *minor = 0;
170     return GSS_S_CONTINUE_NEEDED;
171 }
172
173 /*
174  * Returns TRUE if the input token contains an EAP identity response.
175  */
176 static int
177 isIdentityResponseP(gss_buffer_t inputToken)
178 {
179     struct wpabuf respData;
180
181     wpabuf_set(&respData, inputToken->value, inputToken->length);
182
183     return (eap_get_type(&respData) == EAP_TYPE_IDENTITY);
184 }
185
186 /*
187  * Save the asserted initiator identity from the EAP identity response.
188  */
189 static OM_uint32
190 importInitiatorIdentity(OM_uint32 *minor,
191                         gss_ctx_id_t ctx,
192                         gss_buffer_t inputToken)
193 {
194     OM_uint32 tmpMinor;
195     struct wpabuf respData;
196     const unsigned char *pos;
197     size_t len;
198     gss_buffer_desc nameBuf;
199
200     wpabuf_set(&respData, inputToken->value, inputToken->length);
201
202     pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
203                            &respData, &len);
204     if (pos == NULL) {
205         *minor = GSSEAP_PEER_BAD_MESSAGE;
206         return GSS_S_DEFECTIVE_TOKEN;
207     }
208
209     nameBuf.value = (void *)pos;
210     nameBuf.length = len;
211
212     gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
213
214     return gssEapImportName(minor, &nameBuf, GSS_C_NT_USER_NAME,
215                             &ctx->initiatorName);
216 }
217
218 /*
219  * Pass the asserted initiator identity to the authentication server.
220  */
221 static OM_uint32
222 setInitiatorIdentity(OM_uint32 *minor,
223                      gss_ctx_id_t ctx,
224                      VALUE_PAIR **vps)
225 {
226     OM_uint32 major, tmpMinor;
227     gss_buffer_desc nameBuf;
228
229     /*
230      * We should have got an EAP identity response, but if we didn't, then
231      * we will just avoid sending User-Name. Note that radsecproxy requires
232      * User-Name to be sent on every request (presumably so it can remain
233      * stateless).
234      */
235     if (ctx->initiatorName != GSS_C_NO_NAME) {
236         major = gssEapDisplayName(minor, ctx->initiatorName, &nameBuf, NULL);
237         if (GSS_ERROR(major))
238             return major;
239
240         major = gssEapRadiusAddAvp(minor, vps, PW_USER_NAME, 0, &nameBuf);
241         if (GSS_ERROR(major))
242             return major;
243
244         gss_release_buffer(&tmpMinor, &nameBuf);
245     }
246
247     *minor = 0;
248     return GSS_S_COMPLETE;
249 }
250
251 /*
252  * Pass the asserted acceptor identity to the authentication server.
253  */
254 static OM_uint32
255 setAcceptorIdentity(OM_uint32 *minor,
256                     gss_ctx_id_t ctx,
257                     VALUE_PAIR **vps)
258 {
259     OM_uint32 major;
260     gss_buffer_desc nameBuf;
261     krb5_context krbContext = NULL;
262     krb5_principal krbPrinc;
263     struct rs_context *rc = ctx->acceptorCtx.radContext;
264
265     assert(rc != NULL);
266
267     if (ctx->acceptorName == GSS_C_NO_NAME) {
268         *minor = 0;
269         return GSS_S_COMPLETE;
270     }
271
272     if ((ctx->acceptorName->flags & NAME_FLAG_SERVICE) == 0) {
273         *minor = GSSEAP_BAD_SERVICE_NAME;
274         return GSS_S_BAD_NAME;
275     }
276
277     GSSEAP_KRB_INIT(&krbContext);
278
279     krbPrinc = ctx->acceptorName->krbPrincipal;
280     assert(krbPrinc != NULL);
281     assert(KRB_PRINC_LENGTH(krbPrinc) >= 2);
282
283     /* Acceptor-Service-Name */
284     krbPrincComponentToGssBuffer(krbPrinc, 0, &nameBuf);
285
286     major = gssEapRadiusAddAvp(minor, vps,
287                                PW_GSS_ACCEPTOR_SERVICE_NAME,
288                                VENDORPEC_UKERNA,
289                                &nameBuf);
290     if (GSS_ERROR(major))
291         return major;
292
293     /* Acceptor-Host-Name */
294     krbPrincComponentToGssBuffer(krbPrinc, 1, &nameBuf);
295
296     major = gssEapRadiusAddAvp(minor, vps,
297                                PW_GSS_ACCEPTOR_HOST_NAME,
298                                VENDORPEC_UKERNA,
299                                &nameBuf);
300     if (GSS_ERROR(major))
301         return major;
302
303     if (KRB_PRINC_LENGTH(krbPrinc) > 2) {
304         /* Acceptor-Service-Specific */
305         krb5_principal_data ssiPrinc = *krbPrinc;
306         char *ssi;
307
308         KRB_PRINC_LENGTH(&ssiPrinc) -= 2;
309         KRB_PRINC_NAME(&ssiPrinc) += 2;
310
311         *minor = krb5_unparse_name_flags(krbContext, &ssiPrinc,
312                                          KRB5_PRINCIPAL_UNPARSE_NO_REALM, &ssi);
313         if (*minor != 0)
314             return GSS_S_FAILURE;
315
316         nameBuf.value = ssi;
317         nameBuf.length = strlen(ssi);
318
319         major = gssEapRadiusAddAvp(minor, vps,
320                                    PW_GSS_ACCEPTOR_SERVICE_SPECIFIC,
321                                    VENDORPEC_UKERNA,
322                                    &nameBuf);
323
324         if (GSS_ERROR(major)) {
325             krb5_free_unparsed_name(krbContext, ssi);
326             return major;
327         }
328         krb5_free_unparsed_name(krbContext, ssi);
329     }
330
331     krbPrincRealmToGssBuffer(krbPrinc, &nameBuf);
332     if (nameBuf.length != 0) {
333         /* Acceptor-Realm-Name */
334         major = gssEapRadiusAddAvp(minor, vps,
335                                    PW_GSS_ACCEPTOR_REALM_NAME,
336                                    VENDORPEC_UKERNA,
337                                    &nameBuf);
338         if (GSS_ERROR(major))
339             return major;
340     }
341
342     *minor = 0;
343     return GSS_S_COMPLETE;
344 }
345
346 /*
347  * Allocate a RadSec handle
348  */
349 static OM_uint32
350 createRadiusHandle(OM_uint32 *minor,
351                    gss_cred_id_t cred,
352                    gss_ctx_id_t ctx)
353 {
354     struct gss_eap_acceptor_ctx *actx = &ctx->acceptorCtx;
355     const char *configFile = RS_CONFIG_FILE;
356     const char *configStanza = "gss-eap";
357     struct rs_alloc_scheme ralloc;
358     struct rs_error *err;
359
360     assert(actx->radContext == NULL);
361     assert(actx->radConn == NULL);
362
363     if (rs_context_create(&actx->radContext, RS_DICT_FILE) != 0) {
364         *minor = GSSEAP_RADSEC_CONTEXT_FAILURE;
365         return GSS_S_FAILURE;
366     }
367
368     if (cred->radiusConfigFile != NULL)
369         configFile = cred->radiusConfigFile;
370     if (cred->radiusConfigStanza != NULL)
371         configStanza = cred->radiusConfigStanza;
372
373     ralloc.calloc  = GSSEAP_CALLOC;
374     ralloc.malloc  = GSSEAP_MALLOC;
375     ralloc.free    = GSSEAP_FREE;
376     ralloc.realloc = GSSEAP_REALLOC;
377
378     rs_context_set_alloc_scheme(actx->radContext, &ralloc);
379
380     if (rs_context_read_config(actx->radContext, configFile) != 0) {
381         err = rs_err_ctx_pop(actx->radContext);
382         goto fail;
383     }
384
385     if (rs_conn_create(actx->radContext, &actx->radConn, configStanza) != 0) {
386         err = rs_err_conn_pop(actx->radConn);
387         goto fail;
388     }
389
390     if (actx->radServer != NULL) {
391         if (rs_conn_select_peer(actx->radConn, actx->radServer) != 0) {
392             err = rs_err_conn_pop(actx->radConn);
393             goto fail;
394         }
395     }
396
397     *minor = 0;
398     return GSS_S_COMPLETE;
399
400 fail:
401     return gssEapRadiusMapError(minor, err);
402 }
403
404 /*
405  * Process a EAP response from the initiator.
406  */
407 static OM_uint32
408 eapGssSmAcceptAuthenticate(OM_uint32 *minor,
409                            gss_ctx_id_t ctx,
410                            gss_cred_id_t cred,
411                            gss_buffer_t inputToken,
412                            gss_channel_bindings_t chanBindings,
413                            gss_buffer_t outputToken)
414 {
415     OM_uint32 major, tmpMinor;
416     struct rs_connection *rconn;
417     struct rs_request *request = NULL;
418     struct rs_packet *req = NULL, *resp = NULL;
419     struct radius_packet *frreq, *frresp;
420
421     if (ctx->acceptorCtx.radContext == NULL) {
422         /* May be NULL from an imported partial context */
423         major = createRadiusHandle(minor, cred, ctx);
424         if (GSS_ERROR(major))
425             goto cleanup;
426     }
427
428     if (isIdentityResponseP(inputToken)) {
429         major = importInitiatorIdentity(minor, ctx, inputToken);
430         if (GSS_ERROR(major))
431             return major;
432     }
433
434     rconn = ctx->acceptorCtx.radConn;
435
436     if (rs_packet_create_authn_request(rconn, &req, NULL, NULL) != 0) {
437         major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
438         goto cleanup;
439     }
440     frreq = rs_packet_frpkt(req);
441
442     major = setInitiatorIdentity(minor, ctx, &frreq->vps);
443     if (GSS_ERROR(major))
444         goto cleanup;
445
446     major = setAcceptorIdentity(minor, ctx, &frreq->vps);
447     if (GSS_ERROR(major))
448         goto cleanup;
449
450     major = gssEapRadiusAddAvp(minor, &frreq->vps,
451                                PW_EAP_MESSAGE, 0, inputToken);
452     if (GSS_ERROR(major))
453         goto cleanup;
454
455     if (ctx->acceptorCtx.state.length != 0) {
456         major = gssEapRadiusAddAvp(minor, &frreq->vps, PW_STATE, 0,
457                                    &ctx->acceptorCtx.state);
458         if (GSS_ERROR(major))
459             goto cleanup;
460
461         gss_release_buffer(&tmpMinor, &ctx->acceptorCtx.state);
462     }
463
464     if (rs_request_create(rconn, &request) != 0) {
465         major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
466         goto cleanup;
467     }
468
469     rs_request_add_reqpkt(request, req);
470     req = NULL;
471
472     if (rs_request_send(request, &resp) != 0) {
473         major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
474         goto cleanup;
475     }
476
477     assert(resp != NULL);
478
479     frresp = rs_packet_frpkt(resp);
480     switch (frresp->code) {
481     case PW_AUTHENTICATION_ACK:
482     case PW_ACCESS_CHALLENGE:
483         major = GSS_S_CONTINUE_NEEDED;
484         break;
485     case PW_AUTHENTICATION_REJECT:
486         *minor = GSSEAP_RADIUS_AUTH_FAILURE;
487         major = GSS_S_DEFECTIVE_CREDENTIAL;
488         goto cleanup;
489         break;
490     default:
491         *minor = GSSEAP_UNKNOWN_RADIUS_CODE;
492         major = GSS_S_FAILURE;
493         goto cleanup;
494         break;
495     }
496
497     major = gssEapRadiusGetAvp(minor, frresp->vps, PW_EAP_MESSAGE, 0,
498                                outputToken, TRUE);
499     if (major == GSS_S_UNAVAILABLE && frresp->code == PW_ACCESS_CHALLENGE) {
500         *minor = GSSEAP_MISSING_EAP_REQUEST;
501         major = GSS_S_DEFECTIVE_TOKEN;
502         goto cleanup;
503     } else if (GSS_ERROR(major))
504         goto cleanup;
505
506     if (frresp->code == PW_ACCESS_CHALLENGE) {
507         major = gssEapRadiusGetAvp(minor, frresp->vps, PW_STATE, 0,
508                                    &ctx->acceptorCtx.state, TRUE);
509         if (GSS_ERROR(major) && *minor != GSSEAP_NO_SUCH_ATTR)
510             goto cleanup;
511     } else {
512         ctx->acceptorCtx.vps = frresp->vps;
513         frresp->vps = NULL;
514
515         rs_conn_destroy(ctx->acceptorCtx.radConn);
516         ctx->acceptorCtx.radConn = NULL;
517
518         major = acceptReadyEap(minor, ctx, cred);
519         if (GSS_ERROR(major))
520             goto cleanup;
521
522         ctx->state = GSSEAP_STATE_EXTENSIONS_REQ;
523     }
524
525     *minor = 0;
526     major = GSS_S_CONTINUE_NEEDED;
527
528 cleanup:
529     if (request != NULL)
530         rs_request_destroy(request);
531     if (req != NULL)
532         rs_packet_destroy(req);
533
534     return major;
535 }
536
537 static OM_uint32
538 eapGssSmAcceptExtensionsReq(OM_uint32 *minor,
539                             gss_ctx_id_t ctx,
540                             gss_cred_id_t cred,
541                             gss_buffer_t inputToken,
542                             gss_channel_bindings_t chanBindings,
543                             gss_buffer_t outputToken)
544 {
545     OM_uint32 major;
546
547     major = gssEapVerifyExtensions(minor, cred, ctx, chanBindings, inputToken);
548     if (GSS_ERROR(major))
549         return major;
550
551     outputToken->length = 0;
552     outputToken->value = NULL;
553
554     ctx->state = GSSEAP_STATE_EXTENSIONS_RESP;
555
556     *minor = 0;
557     return GSS_S_CONTINUE_NEEDED;
558 }
559
560 static OM_uint32
561 eapGssSmAcceptExtensionsResp(OM_uint32 *minor,
562                              gss_ctx_id_t ctx,
563                              gss_cred_id_t cred,
564                              gss_buffer_t inputToken,
565                              gss_channel_bindings_t chanBindings,
566                              gss_buffer_t outputToken)
567 {
568     OM_uint32 major;
569
570     major = gssEapMakeExtensions(minor, cred, ctx, chanBindings, outputToken);
571     if (GSS_ERROR(major))
572         return major;
573
574     ctx->state = GSSEAP_STATE_ESTABLISHED;
575
576     *minor = 0;
577     return GSS_S_COMPLETE;
578 }
579
580 static OM_uint32
581 eapGssSmAcceptEstablished(OM_uint32 *minor,
582                           gss_ctx_id_t ctx,
583                           gss_cred_id_t cred,
584                           gss_buffer_t inputToken,
585                           gss_channel_bindings_t chanBindings,
586                           gss_buffer_t outputToken)
587 {
588     /* Called with already established context */
589     *minor = GSSEAP_CONTEXT_ESTABLISHED;
590     return GSS_S_BAD_STATUS;
591 }
592
593 static OM_uint32
594 makeErrorToken(OM_uint32 *minor,
595                OM_uint32 majorStatus,
596                OM_uint32 minorStatus,
597                gss_buffer_t outputToken)
598 {
599     unsigned char errorData[8];
600     gss_buffer_desc errorBuffer;
601
602     assert(GSS_ERROR(majorStatus));
603
604     /*
605      * Only return error codes that the initiator could have caused,
606      * to avoid information leakage.
607      */
608     if (IS_RADIUS_ERROR(minorStatus)) {
609         /* Squash RADIUS error codes */
610         minorStatus = GSSEAP_RADIUS_PROT_FAILURE;
611     } else if (!IS_WIRE_ERROR(minorStatus)) {
612         /* Don't return non-wire error codes */
613         return GSS_S_COMPLETE;
614     }
615
616     minorStatus -= ERROR_TABLE_BASE_eapg;
617
618     store_uint32_be(majorStatus, &errorData[0]);
619     store_uint32_be(minorStatus, &errorData[4]);
620
621     errorBuffer.length = sizeof(errorData);
622     errorBuffer.value = errorData;
623
624     return duplicateBuffer(minor, &errorBuffer, outputToken);
625 }
626
627 static struct gss_eap_acceptor_sm {
628     enum gss_eap_token_type inputTokenType;
629     enum gss_eap_token_type outputTokenType;
630     OM_uint32 (*processToken)(OM_uint32 *,
631                               gss_ctx_id_t,
632                               gss_cred_id_t,
633                               gss_buffer_t,
634                               gss_channel_bindings_t,
635                               gss_buffer_t);
636 } eapGssAcceptorSm[] = {
637     { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,       eapGssSmAcceptIdentity           },
638     { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,       eapGssSmAcceptAuthenticate       },
639     { TOK_TYPE_EXT_REQ,     TOK_TYPE_NONE,          eapGssSmAcceptExtensionsReq      },
640     { TOK_TYPE_NONE,        TOK_TYPE_EXT_RESP,      eapGssSmAcceptExtensionsResp     },
641     { TOK_TYPE_NONE,        TOK_TYPE_NONE,          eapGssSmAcceptEstablished        },
642     { TOK_TYPE_NONE,        TOK_TYPE_CONTEXT_ERR,   NULL                             },
643 #ifdef GSSEAP_ENABLE_REAUTH
644     { TOK_TYPE_GSS_REAUTH,  TOK_TYPE_GSS_REAUTH,    eapGssSmAcceptGssReauth          },
645 #endif
646 };
647
648 OM_uint32
649 gss_accept_sec_context(OM_uint32 *minor,
650                        gss_ctx_id_t *context_handle,
651                        gss_cred_id_t cred,
652                        gss_buffer_t input_token,
653                        gss_channel_bindings_t input_chan_bindings,
654                        gss_name_t *src_name,
655                        gss_OID *mech_type,
656                        gss_buffer_t output_token,
657                        OM_uint32 *ret_flags,
658                        OM_uint32 *time_rec,
659                        gss_cred_id_t *delegated_cred_handle)
660 {
661     OM_uint32 major;
662     OM_uint32 tmpMajor, tmpMinor;
663     gss_ctx_id_t ctx = *context_handle;
664     struct gss_eap_acceptor_sm *sm = NULL;
665     gss_buffer_desc innerInputToken = GSS_C_EMPTY_BUFFER;
666     gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
667     enum gss_eap_token_type tokType;
668     int initialContextToken = 0;
669
670     *minor = 0;
671
672     output_token->length = 0;
673     output_token->value = NULL;
674
675     if (src_name != NULL)
676         *src_name = GSS_C_NO_NAME;
677
678     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
679         *minor = GSSEAP_TOK_TRUNC;
680         return GSS_S_DEFECTIVE_TOKEN;
681     }
682
683     if (ctx == GSS_C_NO_CONTEXT) {
684         major = gssEapAllocContext(minor, &ctx);
685         if (GSS_ERROR(major))
686             return major;
687
688         initialContextToken = 1;
689         *context_handle = ctx;
690     }
691
692     GSSEAP_MUTEX_LOCK(&ctx->mutex);
693
694     if (cred == GSS_C_NO_CREDENTIAL) {
695         if (ctx->defaultCred == GSS_C_NO_CREDENTIAL) {
696             major = gssEapAcquireCred(minor,
697                                       GSS_C_NO_NAME,
698                                       GSS_C_NO_BUFFER,
699                                       GSS_C_INDEFINITE,
700                                       GSS_C_NO_OID_SET,
701                                       GSS_C_ACCEPT,
702                                       &ctx->defaultCred,
703                                       NULL,
704                                       NULL);
705             if (GSS_ERROR(major))
706                 goto cleanup;
707         }
708
709         cred = ctx->defaultCred;
710     }
711
712     GSSEAP_MUTEX_LOCK(&cred->mutex);
713
714     sm = &eapGssAcceptorSm[ctx->state];
715
716     major = gssEapVerifyToken(minor, ctx, input_token,
717                               &tokType, &innerInputToken);
718     if (GSS_ERROR(major))
719         goto cleanup;
720
721     if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
722         *minor = GSSEAP_CRED_MECH_MISMATCH;
723         major = GSS_S_BAD_MECH;
724         goto cleanup;
725     }
726
727 #ifdef GSSEAP_ENABLE_REAUTH
728     /*
729      * If we're built with fast reauthentication support, it's valid
730      * for an initiator to send a GSS reauthentication token as its
731      * initial context token, causing us to short-circuit the state
732      * machine and process Kerberos GSS messages instead.
733      */
734     if (tokType == TOK_TYPE_GSS_REAUTH && initialContextToken) {
735         ctx->state = GSSEAP_STATE_KRB_REAUTH;
736     } else
737 #endif
738     if (tokType != sm->inputTokenType) {
739         *minor = GSSEAP_WRONG_TOK_ID;
740         major = GSS_S_DEFECTIVE_TOKEN;
741         goto cleanup;
742     }
743
744     do {
745         sm = &eapGssAcceptorSm[ctx->state];
746
747         major = (sm->processToken)(minor,
748                                    ctx,
749                                    cred,
750                                    &innerInputToken,
751                                    input_chan_bindings,
752                                    &innerOutputToken);
753         if (GSS_ERROR(major)) {
754             /* Possibly generate an error token */
755             tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputToken);
756             if (GSS_ERROR(tmpMajor)) {
757                 major = tmpMajor;
758                 goto cleanup;
759             }
760
761             sm = &eapGssAcceptorSm[GSSEAP_STATE_ERROR];
762             goto send_token;
763         }
764     } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.length == 0);
765
766     if (mech_type != NULL) {
767         if (!gssEapInternalizeOid(ctx->mechanismUsed, mech_type))
768             duplicateOid(&tmpMinor, ctx->mechanismUsed, mech_type);
769     }
770     if (ret_flags != NULL)
771         *ret_flags = ctx->gssFlags;
772     if (delegated_cred_handle != NULL)
773         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
774
775     if (major == GSS_S_COMPLETE) {
776         if (src_name != NULL && ctx->initiatorName != GSS_C_NO_NAME) {
777             major = gssEapDuplicateName(&tmpMinor, ctx->initiatorName, src_name);
778             if (GSS_ERROR(major))
779                 goto cleanup;
780         }
781         if (time_rec != NULL) {
782             major = gssEapContextTime(&tmpMinor, ctx, time_rec);
783             if (GSS_ERROR(major))
784                 goto cleanup;
785         }
786     }
787
788     assert(ctx->state == GSSEAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED);
789
790 send_token:
791     if (innerOutputToken.value != NULL) {
792         tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken,
793                                    sm->outputTokenType, output_token);
794         if (GSS_ERROR(tmpMajor)) {
795             major = tmpMajor;
796             *minor = tmpMinor;
797             goto cleanup;
798         }
799     }
800
801 cleanup:
802     if (cred != GSS_C_NO_CREDENTIAL)
803         GSSEAP_MUTEX_UNLOCK(&cred->mutex);
804     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
805
806     if (GSS_ERROR(major))
807         gssEapReleaseContext(&tmpMinor, context_handle);
808
809     gss_release_buffer(&tmpMinor, &innerOutputToken);
810
811     return major;
812 }
813
814 #ifdef GSSEAP_ENABLE_REAUTH
815 static OM_uint32
816 acceptReadyKrb(OM_uint32 *minor,
817                gss_ctx_id_t ctx,
818                gss_cred_id_t cred,
819                const gss_name_t initiator,
820                const gss_OID mech,
821                OM_uint32 timeRec)
822 {
823     OM_uint32 major;
824
825     major = gssEapGlueToMechName(minor, ctx, initiator, &ctx->initiatorName);
826     if (GSS_ERROR(major))
827         return major;
828
829     if (cred->name != GSS_C_NO_NAME) {
830         major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
831         if (GSS_ERROR(major))
832             return major;
833     }
834
835     major = gssEapReauthComplete(minor, ctx, cred, mech, timeRec);
836     if (GSS_ERROR(major))
837         return major;
838
839     ctx->state = GSSEAP_STATE_ESTABLISHED;
840
841     *minor = 0;
842     return GSS_S_COMPLETE;
843 }
844
845 static OM_uint32
846 eapGssSmAcceptGssReauth(OM_uint32 *minor,
847                         gss_ctx_id_t ctx,
848                         gss_cred_id_t cred,
849                         gss_buffer_t inputToken,
850                         gss_channel_bindings_t chanBindings,
851                         gss_buffer_t outputToken)
852 {
853     OM_uint32 major, tmpMinor;
854     gss_name_t krbInitiator = GSS_C_NO_NAME;
855     gss_OID mech = GSS_C_NO_OID;
856     OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE;
857
858     ctx->flags |= CTX_FLAG_KRB_REAUTH;
859
860     major = gssAcceptSecContext(minor,
861                                 &ctx->kerberosCtx,
862                                 cred->krbCred,
863                                 inputToken,
864                                 chanBindings,
865                                 &krbInitiator,
866                                 &mech,
867                                 outputToken,
868                                 &gssFlags,
869                                 &timeRec,
870                                 NULL);
871     if (major == GSS_S_COMPLETE) {
872         major = acceptReadyKrb(minor, ctx, cred,
873                                krbInitiator, mech, timeRec);
874     }
875
876     ctx->gssFlags = gssFlags;
877
878     gssReleaseName(&tmpMinor, &krbInitiator);
879
880     return major;
881 }
882 #endif /* GSSEAP_ENABLE_REAUTH */