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