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