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