HEADS UP: move dictionary file to $prefix/etc/raddb/dictionary
[mech_eap.orig] / 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         major = acceptReadyEap(minor, ctx, cred);
516         if (GSS_ERROR(major))
517             goto cleanup;
518
519         ctx->state = GSSEAP_STATE_EXTENSIONS_REQ;
520     }
521
522     *minor = 0;
523     major = GSS_S_CONTINUE_NEEDED;
524
525 cleanup:
526     if (request != NULL)
527         rs_request_destroy(request);
528     if (req != NULL)
529         rs_packet_destroy(req);
530     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIATOR_EXTS) {
531         assert(major == GSS_S_CONTINUE_NEEDED);
532
533         rs_conn_destroy(ctx->acceptorCtx.radConn);
534         ctx->acceptorCtx.radConn = NULL;
535     }
536
537     return major;
538 }
539
540 static OM_uint32
541 eapGssSmAcceptExtensionsReq(OM_uint32 *minor,
542                             gss_ctx_id_t ctx,
543                             gss_cred_id_t cred,
544                             gss_buffer_t inputToken,
545                             gss_channel_bindings_t chanBindings,
546                             gss_buffer_t outputToken)
547 {
548     OM_uint32 major;
549
550     major = gssEapVerifyExtensions(minor, cred, ctx, chanBindings, inputToken);
551     if (GSS_ERROR(major))
552         return major;
553
554     outputToken->length = 0;
555     outputToken->value = NULL;
556
557     ctx->state = GSSEAP_STATE_EXTENSIONS_RESP;
558
559     *minor = 0;
560     return GSS_S_CONTINUE_NEEDED;
561 }
562
563 static OM_uint32
564 eapGssSmAcceptExtensionsResp(OM_uint32 *minor,
565                              gss_ctx_id_t ctx,
566                              gss_cred_id_t cred,
567                              gss_buffer_t inputToken,
568                              gss_channel_bindings_t chanBindings,
569                              gss_buffer_t outputToken)
570 {
571     OM_uint32 major;
572
573     major = gssEapMakeExtensions(minor, cred, ctx, chanBindings, outputToken);
574     if (GSS_ERROR(major))
575         return major;
576
577     ctx->state = GSSEAP_STATE_ESTABLISHED;
578
579     *minor = 0;
580     return GSS_S_COMPLETE;
581 }
582
583 static OM_uint32
584 eapGssSmAcceptEstablished(OM_uint32 *minor,
585                           gss_ctx_id_t ctx,
586                           gss_cred_id_t cred,
587                           gss_buffer_t inputToken,
588                           gss_channel_bindings_t chanBindings,
589                           gss_buffer_t outputToken)
590 {
591     /* Called with already established context */
592     *minor = GSSEAP_CONTEXT_ESTABLISHED;
593     return GSS_S_BAD_STATUS;
594 }
595
596 static OM_uint32
597 makeErrorToken(OM_uint32 *minor,
598                OM_uint32 majorStatus,
599                OM_uint32 minorStatus,
600                gss_buffer_t outputToken)
601 {
602     unsigned char errorData[8];
603     gss_buffer_desc errorBuffer;
604
605     assert(GSS_ERROR(majorStatus));
606
607     /*
608      * Only return error codes that the initiator could have caused,
609      * to avoid information leakage.
610      */
611     if (IS_RADIUS_ERROR(minorStatus)) {
612         /* Squash RADIUS error codes */
613         minorStatus = GSSEAP_RADIUS_PROT_FAILURE;
614     } else if (!IS_WIRE_ERROR(minorStatus)) {
615         /* Don't return non-wire error codes */
616         return GSS_S_COMPLETE;
617     }
618
619     minorStatus -= ERROR_TABLE_BASE_eapg;
620
621     store_uint32_be(majorStatus, &errorData[0]);
622     store_uint32_be(minorStatus, &errorData[4]);
623
624     errorBuffer.length = sizeof(errorData);
625     errorBuffer.value = errorData;
626
627     return duplicateBuffer(minor, &errorBuffer, outputToken);
628 }
629
630 static struct gss_eap_acceptor_sm {
631     enum gss_eap_token_type inputTokenType;
632     enum gss_eap_token_type outputTokenType;
633     OM_uint32 (*processToken)(OM_uint32 *,
634                               gss_ctx_id_t,
635                               gss_cred_id_t,
636                               gss_buffer_t,
637                               gss_channel_bindings_t,
638                               gss_buffer_t);
639 } eapGssAcceptorSm[] = {
640     { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,       eapGssSmAcceptIdentity           },
641     { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,       eapGssSmAcceptAuthenticate       },
642     { TOK_TYPE_EXT_REQ,     TOK_TYPE_NONE,          eapGssSmAcceptExtensionsReq      },
643     { TOK_TYPE_NONE,        TOK_TYPE_EXT_RESP,      eapGssSmAcceptExtensionsResp     },
644     { TOK_TYPE_NONE,        TOK_TYPE_NONE,          eapGssSmAcceptEstablished        },
645     { TOK_TYPE_NONE,        TOK_TYPE_CONTEXT_ERR,   NULL                             },
646 #ifdef GSSEAP_ENABLE_REAUTH
647     { TOK_TYPE_GSS_REAUTH,  TOK_TYPE_GSS_REAUTH,    eapGssSmAcceptGssReauth          },
648 #endif
649 };
650
651 OM_uint32
652 gss_accept_sec_context(OM_uint32 *minor,
653                        gss_ctx_id_t *context_handle,
654                        gss_cred_id_t cred,
655                        gss_buffer_t input_token,
656                        gss_channel_bindings_t input_chan_bindings,
657                        gss_name_t *src_name,
658                        gss_OID *mech_type,
659                        gss_buffer_t output_token,
660                        OM_uint32 *ret_flags,
661                        OM_uint32 *time_rec,
662                        gss_cred_id_t *delegated_cred_handle)
663 {
664     OM_uint32 major;
665     OM_uint32 tmpMajor, tmpMinor;
666     gss_ctx_id_t ctx = *context_handle;
667     struct gss_eap_acceptor_sm *sm = NULL;
668     gss_buffer_desc innerInputToken = GSS_C_EMPTY_BUFFER;
669     gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
670     enum gss_eap_token_type tokType;
671     int initialContextToken = 0;
672
673     *minor = 0;
674
675     output_token->length = 0;
676     output_token->value = NULL;
677
678     if (src_name != NULL)
679         *src_name = GSS_C_NO_NAME;
680
681     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
682         *minor = GSSEAP_TOK_TRUNC;
683         return GSS_S_DEFECTIVE_TOKEN;
684     }
685
686     if (ctx == GSS_C_NO_CONTEXT) {
687         major = gssEapAllocContext(minor, &ctx);
688         if (GSS_ERROR(major))
689             return major;
690
691         initialContextToken = 1;
692         *context_handle = ctx;
693     }
694
695     GSSEAP_MUTEX_LOCK(&ctx->mutex);
696
697     if (cred == GSS_C_NO_CREDENTIAL) {
698         if (ctx->defaultCred == GSS_C_NO_CREDENTIAL) {
699             major = gssEapAcquireCred(minor,
700                                       GSS_C_NO_NAME,
701                                       GSS_C_NO_BUFFER,
702                                       GSS_C_INDEFINITE,
703                                       GSS_C_NO_OID_SET,
704                                       GSS_C_ACCEPT,
705                                       &ctx->defaultCred,
706                                       NULL,
707                                       NULL);
708             if (GSS_ERROR(major))
709                 goto cleanup;
710         }
711
712         cred = ctx->defaultCred;
713     }
714
715     GSSEAP_MUTEX_LOCK(&cred->mutex);
716
717     sm = &eapGssAcceptorSm[ctx->state];
718
719     major = gssEapVerifyToken(minor, ctx, input_token,
720                               &tokType, &innerInputToken);
721     if (GSS_ERROR(major))
722         goto cleanup;
723
724     if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
725         *minor = GSSEAP_CRED_MECH_MISMATCH;
726         major = GSS_S_BAD_MECH;
727         goto cleanup;
728     }
729
730 #ifdef GSSEAP_ENABLE_REAUTH
731     /*
732      * If we're built with fast reauthentication support, it's valid
733      * for an initiator to send a GSS reauthentication token as its
734      * initial context token, causing us to short-circuit the state
735      * machine and process Kerberos GSS messages instead.
736      */
737     if (tokType == TOK_TYPE_GSS_REAUTH && initialContextToken) {
738         ctx->state = GSSEAP_STATE_KRB_REAUTH;
739     } else
740 #endif
741     if (tokType != sm->inputTokenType) {
742         *minor = GSSEAP_WRONG_TOK_ID;
743         major = GSS_S_DEFECTIVE_TOKEN;
744         goto cleanup;
745     }
746
747     do {
748         sm = &eapGssAcceptorSm[ctx->state];
749
750         major = (sm->processToken)(minor,
751                                    ctx,
752                                    cred,
753                                    &innerInputToken,
754                                    input_chan_bindings,
755                                    &innerOutputToken);
756         if (GSS_ERROR(major)) {
757             /* Possibly generate an error token */
758             tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputToken);
759             if (GSS_ERROR(tmpMajor)) {
760                 major = tmpMajor;
761                 goto cleanup;
762             }
763
764             sm = &eapGssAcceptorSm[GSSEAP_STATE_ERROR];
765             goto send_token;
766         }
767     } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.length == 0);
768
769     if (mech_type != NULL) {
770         if (!gssEapInternalizeOid(ctx->mechanismUsed, mech_type))
771             duplicateOid(&tmpMinor, ctx->mechanismUsed, mech_type);
772     }
773     if (ret_flags != NULL)
774         *ret_flags = ctx->gssFlags;
775     if (delegated_cred_handle != NULL)
776         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
777
778     if (major == GSS_S_COMPLETE) {
779         if (src_name != NULL && ctx->initiatorName != GSS_C_NO_NAME) {
780             major = gssEapDuplicateName(&tmpMinor, ctx->initiatorName, src_name);
781             if (GSS_ERROR(major))
782                 goto cleanup;
783         }
784         if (time_rec != NULL) {
785             major = gssEapContextTime(&tmpMinor, ctx, time_rec);
786             if (GSS_ERROR(major))
787                 goto cleanup;
788         }
789     }
790
791     assert(ctx->state == GSSEAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED);
792
793 send_token:
794     if (innerOutputToken.value != NULL) {
795         tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken,
796                                    sm->outputTokenType, output_token);
797         if (GSS_ERROR(tmpMajor)) {
798             major = tmpMajor;
799             *minor = tmpMinor;
800             goto cleanup;
801         }
802     }
803
804 cleanup:
805     if (cred != GSS_C_NO_CREDENTIAL)
806         GSSEAP_MUTEX_UNLOCK(&cred->mutex);
807     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
808
809     if (GSS_ERROR(major))
810         gssEapReleaseContext(&tmpMinor, context_handle);
811
812     gss_release_buffer(&tmpMinor, &innerOutputToken);
813
814     return major;
815 }
816
817 #ifdef GSSEAP_ENABLE_REAUTH
818 static OM_uint32
819 acceptReadyKrb(OM_uint32 *minor,
820                gss_ctx_id_t ctx,
821                gss_cred_id_t cred,
822                const gss_name_t initiator,
823                const gss_OID mech,
824                OM_uint32 timeRec)
825 {
826     OM_uint32 major;
827
828     major = gssEapGlueToMechName(minor, ctx, initiator, &ctx->initiatorName);
829     if (GSS_ERROR(major))
830         return major;
831
832     if (cred->name != GSS_C_NO_NAME) {
833         major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
834         if (GSS_ERROR(major))
835             return major;
836     }
837
838     major = gssEapReauthComplete(minor, ctx, cred, mech, timeRec);
839     if (GSS_ERROR(major))
840         return major;
841
842     ctx->state = GSSEAP_STATE_ESTABLISHED;
843
844     *minor = 0;
845     return GSS_S_COMPLETE;
846 }
847
848 static OM_uint32
849 eapGssSmAcceptGssReauth(OM_uint32 *minor,
850                         gss_ctx_id_t ctx,
851                         gss_cred_id_t cred,
852                         gss_buffer_t inputToken,
853                         gss_channel_bindings_t chanBindings,
854                         gss_buffer_t outputToken)
855 {
856     OM_uint32 major, tmpMinor;
857     gss_name_t krbInitiator = GSS_C_NO_NAME;
858     gss_OID mech = GSS_C_NO_OID;
859     OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE;
860
861     ctx->flags |= CTX_FLAG_KRB_REAUTH;
862
863     major = gssAcceptSecContext(minor,
864                                 &ctx->kerberosCtx,
865                                 cred->krbCred,
866                                 inputToken,
867                                 chanBindings,
868                                 &krbInitiator,
869                                 &mech,
870                                 outputToken,
871                                 &gssFlags,
872                                 &timeRec,
873                                 NULL);
874     if (major == GSS_S_COMPLETE) {
875         major = acceptReadyKrb(minor, ctx, cred,
876                                krbInitiator, mech, timeRec);
877     }
878
879     ctx->gssFlags = gssFlags;
880
881     gssReleaseName(&tmpMinor, &krbInitiator);
882
883     return major;
884 }
885 #endif /* GSSEAP_ENABLE_REAUTH */