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