don't free RADIUS context before packet
[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 (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIATOR_EXTS) {
595         assert(major == GSS_S_CONTINUE_NEEDED);
596
597         rs_conn_destroy(ctx->acceptorCtx.radConn);
598         ctx->acceptorCtx.radConn = NULL;
599     }
600
601     return major;
602 }
603
604 static OM_uint32
605 eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
606                                  gss_cred_id_t cred,
607                                  gss_ctx_id_t ctx,
608                                  gss_name_t target,
609                                  gss_OID mech,
610                                  OM_uint32 reqFlags,
611                                  OM_uint32 timeReq,
612                                  gss_channel_bindings_t chanBindings,
613                                  gss_buffer_t inputToken,
614                                  gss_buffer_t outputToken,
615                                  OM_uint32 *smFlags)
616 {
617     OM_uint32 major, tmpMinor;
618     gss_iov_buffer_desc iov[2];
619
620     iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
621     iov[0].buffer.length = 0;
622     iov[0].buffer.value = NULL;
623
624     iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM;
625     iov[1].buffer = *inputToken;
626
627     major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
628                                     iov, 2, TOK_TYPE_WRAP);
629     if (GSS_ERROR(major))
630         return major;
631
632     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS &&
633         !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) {
634         major = GSS_S_BAD_BINDINGS;
635         *minor = GSSEAP_BINDINGS_MISMATCH;
636     } else {
637         major = GSS_S_CONTINUE_NEEDED;
638         *minor = 0;
639     }
640
641     gss_release_buffer(&tmpMinor, &iov[0].buffer);
642
643     return major;
644 }
645
646 #ifdef GSSEAP_ENABLE_REAUTH
647 static OM_uint32
648 eapGssSmAcceptReauthCreds(OM_uint32 *minor,
649                           gss_cred_id_t cred,
650                           gss_ctx_id_t ctx,
651                           gss_name_t target,
652                           gss_OID mech,
653                           OM_uint32 reqFlags,
654                           OM_uint32 timeReq,
655                           gss_channel_bindings_t chanBindings,
656                           gss_buffer_t inputToken,
657                           gss_buffer_t outputToken,
658                           OM_uint32 *smFlags)
659 {
660     OM_uint32 major;
661
662     /*
663      * If we're built with fast reauthentication enabled, then
664      * fabricate a ticket from the initiator to ourselves.
665      */
666     major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken);
667     if (major == GSS_S_UNAVAILABLE)
668         major = GSS_S_COMPLETE;
669     if (major == GSS_S_COMPLETE)
670         major = GSS_S_CONTINUE_NEEDED;
671
672     return major;
673 }
674 #endif
675
676 static OM_uint32
677 eapGssSmAcceptCompleteInitiatorExts(OM_uint32 *minor,
678                                     gss_cred_id_t cred,
679                                     gss_ctx_id_t ctx,
680                                     gss_name_t target,
681                                     gss_OID mech,
682                                     OM_uint32 reqFlags,
683                                     OM_uint32 timeReq,
684                                     gss_channel_bindings_t chanBindings,
685                                     gss_buffer_t inputToken,
686                                     gss_buffer_t outputToken,
687                                     OM_uint32 *smFlags)
688 {
689     GSSEAP_SM_TRANSITION_NEXT(ctx);
690
691     *minor = 0;
692
693     return GSS_S_CONTINUE_NEEDED;
694 }
695
696 static OM_uint32
697 eapGssSmAcceptCompleteAcceptorExts(OM_uint32 *minor,
698                                    gss_cred_id_t cred,
699                                    gss_ctx_id_t ctx,
700                                    gss_name_t target,
701                                    gss_OID mech,
702                                    OM_uint32 reqFlags,
703                                    OM_uint32 timeReq,
704                                    gss_channel_bindings_t chanBindings,
705                                    gss_buffer_t inputToken,
706                                    gss_buffer_t outputToken,
707                                    OM_uint32 *smFlags)
708 {
709     GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
710
711     *minor = 0;
712     *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
713
714     return GSS_S_COMPLETE;
715 }
716
717 static struct gss_eap_sm eapGssAcceptorSm[] = {
718     {
719         ITOK_TYPE_ACCEPTOR_NAME_REQ,
720         ITOK_TYPE_ACCEPTOR_NAME_RESP,
721         GSSEAP_STATE_INITIAL,
722         0,
723         eapGssSmAcceptAcceptorName
724     },
725 #ifdef GSSEAP_DEBUG
726     {
727         ITOK_TYPE_VENDOR_INFO,
728         ITOK_TYPE_NONE,
729         GSSEAP_STATE_INITIAL,
730         0,
731         eapGssSmAcceptVendorInfo,
732     },
733 #endif
734 #ifdef GSSEAP_ENABLE_REAUTH
735     {
736         ITOK_TYPE_REAUTH_REQ,
737         ITOK_TYPE_REAUTH_RESP,
738         GSSEAP_STATE_INITIAL,
739         0,
740         eapGssSmAcceptGssReauth,
741     },
742 #endif
743     {
744         ITOK_TYPE_NONE,
745         ITOK_TYPE_EAP_REQ,
746         GSSEAP_STATE_INITIAL,
747         SM_ITOK_FLAG_REQUIRED,
748         eapGssSmAcceptIdentity,
749     },
750     {
751         ITOK_TYPE_EAP_RESP,
752         ITOK_TYPE_EAP_REQ,
753         GSSEAP_STATE_AUTHENTICATE,
754         SM_ITOK_FLAG_REQUIRED,
755         eapGssSmAcceptAuthenticate
756     },
757     {
758         ITOK_TYPE_GSS_CHANNEL_BINDINGS,
759         ITOK_TYPE_NONE,
760         GSSEAP_STATE_INITIATOR_EXTS,
761         SM_ITOK_FLAG_REQUIRED,
762         eapGssSmAcceptGssChannelBindings,
763     },
764     {
765         ITOK_TYPE_NONE,
766         ITOK_TYPE_NONE,
767         GSSEAP_STATE_INITIATOR_EXTS,
768         0,
769         eapGssSmAcceptCompleteInitiatorExts,
770     },
771 #ifdef GSSEAP_ENABLE_REAUTH
772     {
773         ITOK_TYPE_NONE,
774         ITOK_TYPE_REAUTH_CREDS,
775         GSSEAP_STATE_ACCEPTOR_EXTS,
776         0,
777         eapGssSmAcceptReauthCreds,
778     },
779 #endif
780     {
781         ITOK_TYPE_NONE,
782         ITOK_TYPE_NONE,
783         GSSEAP_STATE_ACCEPTOR_EXTS,
784         0,
785         eapGssSmAcceptCompleteAcceptorExts
786     },
787 };
788
789 OM_uint32
790 gss_accept_sec_context(OM_uint32 *minor,
791                        gss_ctx_id_t *context_handle,
792                        gss_cred_id_t cred,
793                        gss_buffer_t input_token,
794                        gss_channel_bindings_t input_chan_bindings,
795                        gss_name_t *src_name,
796                        gss_OID *mech_type,
797                        gss_buffer_t output_token,
798                        OM_uint32 *ret_flags,
799                        OM_uint32 *time_rec,
800                        gss_cred_id_t *delegated_cred_handle)
801 {
802     OM_uint32 major, tmpMinor;
803     gss_ctx_id_t ctx = *context_handle;
804
805     *minor = 0;
806
807     output_token->length = 0;
808     output_token->value = NULL;
809
810     if (src_name != NULL)
811         *src_name = GSS_C_NO_NAME;
812
813     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
814         *minor = GSSEAP_TOK_TRUNC;
815         return GSS_S_DEFECTIVE_TOKEN;
816     }
817
818     if (ctx == GSS_C_NO_CONTEXT) {
819         major = gssEapAllocContext(minor, &ctx);
820         if (GSS_ERROR(major))
821             return major;
822
823         *context_handle = ctx;
824     }
825
826     GSSEAP_MUTEX_LOCK(&ctx->mutex);
827
828     if (cred == GSS_C_NO_CREDENTIAL) {
829         if (ctx->defaultCred == GSS_C_NO_CREDENTIAL) {
830             major = gssEapAcquireCred(minor,
831                                       GSS_C_NO_NAME,
832                                       GSS_C_NO_BUFFER,
833                                       GSS_C_INDEFINITE,
834                                       GSS_C_NO_OID_SET,
835                                       GSS_C_ACCEPT,
836                                       &ctx->defaultCred,
837                                       NULL,
838                                       NULL);
839             if (GSS_ERROR(major))
840                 goto cleanup;
841         }
842
843         cred = ctx->defaultCred;
844     }
845
846     GSSEAP_MUTEX_LOCK(&cred->mutex);
847
848     if (cred->name != GSS_C_NO_NAME) {
849         major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
850         if (GSS_ERROR(major))
851             goto cleanup;
852     }
853
854     major = gssEapSmStep(minor,
855                          cred,
856                          ctx,
857                          GSS_C_NO_NAME,
858                          GSS_C_NO_OID,
859                          0,
860                          GSS_C_INDEFINITE,
861                          input_chan_bindings,
862                          input_token,
863                          output_token,
864                          eapGssAcceptorSm,
865                          sizeof(eapGssAcceptorSm) / sizeof(eapGssAcceptorSm[0]));
866     if (GSS_ERROR(major))
867         goto cleanup;
868
869     if (mech_type != NULL) {
870         if (!gssEapInternalizeOid(ctx->mechanismUsed, mech_type))
871             duplicateOid(&tmpMinor, ctx->mechanismUsed, mech_type);
872     }
873     if (ret_flags != NULL)
874         *ret_flags = ctx->gssFlags;
875     if (delegated_cred_handle != NULL)
876         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
877
878     if (major == GSS_S_COMPLETE) {
879         if (src_name != NULL && ctx->initiatorName != GSS_C_NO_NAME) {
880             major = gssEapDuplicateName(&tmpMinor, ctx->initiatorName, src_name);
881             if (GSS_ERROR(major))
882                 goto cleanup;
883         }
884         if (time_rec != NULL) {
885             major = gssEapContextTime(&tmpMinor, ctx, time_rec);
886             if (GSS_ERROR(major))
887                 goto cleanup;
888         }
889     }
890
891     assert(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
892
893 cleanup:
894     if (cred != GSS_C_NO_CREDENTIAL)
895         GSSEAP_MUTEX_UNLOCK(&cred->mutex);
896     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
897
898     if (GSS_ERROR(major))
899         gssEapReleaseContext(&tmpMinor, context_handle);
900
901     return major;
902 }
903
904 #ifdef GSSEAP_ENABLE_REAUTH
905 static OM_uint32
906 acceptReadyKrb(OM_uint32 *minor,
907                gss_ctx_id_t ctx,
908                gss_cred_id_t cred,
909                const gss_name_t initiator,
910                const gss_OID mech,
911                OM_uint32 timeRec)
912 {
913     OM_uint32 major;
914
915     major = gssEapGlueToMechName(minor, ctx, initiator, &ctx->initiatorName);
916     if (GSS_ERROR(major))
917         return major;
918
919     major = gssEapReauthComplete(minor, ctx, cred, mech, timeRec);
920     if (GSS_ERROR(major))
921         return major;
922
923     *minor = 0;
924     return GSS_S_COMPLETE;
925 }
926
927 static OM_uint32
928 eapGssSmAcceptGssReauth(OM_uint32 *minor,
929                         gss_cred_id_t cred,
930                         gss_ctx_id_t ctx,
931                         gss_name_t target,
932                         gss_OID mech,
933                         OM_uint32 reqFlags,
934                         OM_uint32 timeReq,
935                         gss_channel_bindings_t chanBindings,
936                         gss_buffer_t inputToken,
937                         gss_buffer_t outputToken,
938                         OM_uint32 *smFlags)
939 {
940     OM_uint32 major, tmpMinor;
941     gss_name_t krbInitiator = GSS_C_NO_NAME;
942     OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE;
943
944     /*
945      * If we're built with fast reauthentication support, it's valid
946      * for an initiator to send a GSS reauthentication token as its
947      * initial context token, causing us to short-circuit the state
948      * machine and process Kerberos GSS messages instead.
949      */
950
951     ctx->flags |= CTX_FLAG_KRB_REAUTH;
952
953     major = gssAcceptSecContext(minor,
954                                 &ctx->kerberosCtx,
955                                 cred->krbCred,
956                                 inputToken,
957                                 chanBindings,
958                                 &krbInitiator,
959                                 &mech,
960                                 outputToken,
961                                 &gssFlags,
962                                 &timeRec,
963                                 NULL);
964     if (major == GSS_S_COMPLETE) {
965         major = acceptReadyKrb(minor, ctx, cred,
966                                krbInitiator, mech, timeRec);
967         if (major == GSS_S_COMPLETE) {
968             GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
969         }
970         ctx->gssFlags = gssFlags;
971     } else if (GSS_ERROR(major) &&
972         (*smFlags & SM_FLAG_INPUT_TOKEN_CRITICAL) == 0) {
973         /* pretend reauthentication attempt never happened */
974         gssDeleteSecContext(&tmpMinor, &ctx->kerberosCtx, GSS_C_NO_BUFFER);
975         ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
976         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
977         major = GSS_S_CONTINUE_NEEDED;
978     }
979
980     gssReleaseName(&tmpMinor, &krbInitiator);
981
982     return major;
983 }
984 #endif /* GSSEAP_ENABLE_REAUTH */