comment out dumping code
[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) != 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_context_init_freeradius_dict(actx->radContext, NULL) != 0) {
447         err = rs_err_ctx_pop(actx->radContext);
448         goto fail;
449     }
450
451     if (rs_conn_create(actx->radContext, &actx->radConn, configStanza) != 0) {
452         err = rs_err_conn_pop(actx->radConn);
453         goto fail;
454     }
455
456     if (actx->radServer != NULL) {
457         if (rs_conn_select_peer(actx->radConn, actx->radServer) != 0) {
458             err = rs_err_conn_pop(actx->radConn);
459             goto fail;
460         }
461     }
462
463     *minor = 0;
464     return GSS_S_COMPLETE;
465
466 fail:
467     return gssEapRadiusMapError(minor, err);
468 }
469
470 /*
471  * Process a EAP response from the initiator.
472  */
473 static OM_uint32
474 eapGssSmAcceptAuthenticate(OM_uint32 *minor,
475                            gss_cred_id_t cred,
476                            gss_ctx_id_t ctx,
477                            gss_name_t target GSSEAP_UNUSED,
478                            gss_OID mech GSSEAP_UNUSED,
479                            OM_uint32 reqFlags GSSEAP_UNUSED,
480                            OM_uint32 timeReq GSSEAP_UNUSED,
481                            gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
482                            gss_buffer_t inputToken,
483                            gss_buffer_t outputToken,
484                            OM_uint32 *smFlags)
485 {
486     OM_uint32 major, tmpMinor;
487     struct rs_connection *rconn;
488     struct rs_request *request = NULL;
489     struct rs_packet *req = NULL, *resp = NULL;
490     struct radius_packet *frreq, *frresp;
491
492     if (ctx->acceptorCtx.radContext == NULL) {
493         /* May be NULL from an imported partial context */
494         major = createRadiusHandle(minor, cred, ctx);
495         if (GSS_ERROR(major))
496             goto cleanup;
497     }
498
499     if (isIdentityResponseP(inputToken)) {
500         major = importInitiatorIdentity(minor, ctx, inputToken);
501         if (GSS_ERROR(major))
502             return major;
503     }
504
505     rconn = ctx->acceptorCtx.radConn;
506
507     if (rs_packet_create_authn_request(rconn, &req, NULL, NULL) != 0) {
508         major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
509         goto cleanup;
510     }
511     frreq = rs_packet_frpkt(req);
512
513     major = setInitiatorIdentity(minor, ctx, &frreq->vps);
514     if (GSS_ERROR(major))
515         goto cleanup;
516
517     major = setAcceptorIdentity(minor, ctx, &frreq->vps);
518     if (GSS_ERROR(major))
519         goto cleanup;
520
521     major = gssEapRadiusAddAvp(minor, &frreq->vps,
522                                PW_EAP_MESSAGE, 0, inputToken);
523     if (GSS_ERROR(major))
524         goto cleanup;
525
526     if (ctx->acceptorCtx.state.length != 0) {
527         major = gssEapRadiusAddAvp(minor, &frreq->vps, PW_STATE, 0,
528                                    &ctx->acceptorCtx.state);
529         if (GSS_ERROR(major))
530             goto cleanup;
531
532         gss_release_buffer(&tmpMinor, &ctx->acceptorCtx.state);
533     }
534
535     if (rs_request_create(rconn, &request) != 0) {
536         major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
537         goto cleanup;
538     }
539
540     rs_request_add_reqpkt(request, req);
541     req = NULL;
542
543     if (rs_request_send(request, &resp) != 0) {
544         major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
545         goto cleanup;
546     }
547
548     assert(resp != NULL);
549
550     frresp = rs_packet_frpkt(resp);
551     switch (frresp->code) {
552     case PW_ACCESS_CHALLENGE:
553     case PW_AUTHENTICATION_ACK:
554         break;
555     case PW_AUTHENTICATION_REJECT:
556         *minor = GSSEAP_RADIUS_AUTH_FAILURE;
557         major = GSS_S_DEFECTIVE_CREDENTIAL;
558         goto cleanup;
559         break;
560     default:
561         *minor = GSSEAP_UNKNOWN_RADIUS_CODE;
562         major = GSS_S_FAILURE;
563         goto cleanup;
564         break;
565     }
566
567     major = gssEapRadiusGetAvp(minor, frresp->vps, PW_EAP_MESSAGE, 0,
568                                outputToken, TRUE);
569     if (major == GSS_S_UNAVAILABLE && frresp->code == PW_ACCESS_CHALLENGE) {
570         *minor = GSSEAP_MISSING_EAP_REQUEST;
571         major = GSS_S_DEFECTIVE_TOKEN;
572         goto cleanup;
573     } else if (GSS_ERROR(major))
574         goto cleanup;
575
576     if (frresp->code == PW_ACCESS_CHALLENGE) {
577         major = gssEapRadiusGetAvp(minor, frresp->vps, PW_STATE, 0,
578                                    &ctx->acceptorCtx.state, TRUE);
579         if (GSS_ERROR(major) && *minor != GSSEAP_NO_SUCH_ATTR)
580             goto cleanup;
581     } else {
582         ctx->acceptorCtx.vps = frresp->vps;
583         frresp->vps = NULL;
584
585         major = acceptReadyEap(minor, ctx, cred);
586         if (GSS_ERROR(major))
587             goto cleanup;
588
589         GSSEAP_SM_TRANSITION_NEXT(ctx);
590     }
591
592     major = GSS_S_CONTINUE_NEEDED;
593     *minor = 0;
594     *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
595
596 cleanup:
597     if (request != NULL)
598         rs_request_destroy(request);
599     if (req != NULL)
600         rs_packet_destroy(req);
601     if (resp != NULL)
602         rs_packet_destroy(resp);
603     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIATOR_EXTS) {
604         assert(major == GSS_S_CONTINUE_NEEDED);
605
606         rs_conn_destroy(ctx->acceptorCtx.radConn);
607         ctx->acceptorCtx.radConn = NULL;
608     }
609
610     return major;
611 }
612
613 static OM_uint32
614 eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
615                                  gss_cred_id_t cred GSSEAP_UNUSED,
616                                  gss_ctx_id_t ctx,
617                                  gss_name_t target GSSEAP_UNUSED,
618                                  gss_OID mech GSSEAP_UNUSED,
619                                  OM_uint32 reqFlags GSSEAP_UNUSED,
620                                  OM_uint32 timeReq GSSEAP_UNUSED,
621                                  gss_channel_bindings_t chanBindings,
622                                  gss_buffer_t inputToken,
623                                  gss_buffer_t outputToken GSSEAP_UNUSED,
624                                  OM_uint32 *smFlags GSSEAP_UNUSED)
625 {
626     OM_uint32 major, tmpMinor;
627     gss_iov_buffer_desc iov[2];
628
629     iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
630     iov[0].buffer.length = 0;
631     iov[0].buffer.value = NULL;
632
633     iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM;
634     iov[1].buffer = *inputToken;
635
636     major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
637                                     iov, 2, TOK_TYPE_WRAP);
638     if (GSS_ERROR(major))
639         return major;
640
641     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS &&
642         !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) {
643         major = GSS_S_BAD_BINDINGS;
644         *minor = GSSEAP_BINDINGS_MISMATCH;
645     } else {
646         major = GSS_S_CONTINUE_NEEDED;
647         *minor = 0;
648     }
649
650     gss_release_buffer(&tmpMinor, &iov[0].buffer);
651
652     return major;
653 }
654
655 #ifdef GSSEAP_ENABLE_REAUTH
656 static OM_uint32
657 eapGssSmAcceptReauthCreds(OM_uint32 *minor,
658                           gss_cred_id_t cred,
659                           gss_ctx_id_t ctx,
660                           gss_name_t target GSSEAP_UNUSED,
661                           gss_OID mech GSSEAP_UNUSED,
662                           OM_uint32 reqFlags GSSEAP_UNUSED,
663                           OM_uint32 timeReq GSSEAP_UNUSED,
664                           gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
665                           gss_buffer_t inputToken GSSEAP_UNUSED,
666                           gss_buffer_t outputToken,
667                           OM_uint32 *smFlags GSSEAP_UNUSED)
668 {
669     OM_uint32 major;
670
671     /*
672      * If we're built with fast reauthentication enabled, then
673      * fabricate a ticket from the initiator to ourselves.
674      */
675     major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken);
676     if (major == GSS_S_UNAVAILABLE)
677         major = GSS_S_COMPLETE;
678     if (major == GSS_S_COMPLETE)
679         major = GSS_S_CONTINUE_NEEDED;
680
681     return major;
682 }
683 #endif
684
685 static OM_uint32
686 eapGssSmAcceptCompleteInitiatorExts(OM_uint32 *minor,
687                                     gss_cred_id_t cred GSSEAP_UNUSED,
688                                     gss_ctx_id_t ctx,
689                                     gss_name_t target GSSEAP_UNUSED,
690                                     gss_OID mech GSSEAP_UNUSED,
691                                     OM_uint32 reqFlags GSSEAP_UNUSED,
692                                     OM_uint32 timeReq GSSEAP_UNUSED,
693                                     gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
694                                     gss_buffer_t inputToken GSSEAP_UNUSED,
695                                     gss_buffer_t outputToken GSSEAP_UNUSED,
696                                     OM_uint32 *smFlags GSSEAP_UNUSED)
697 {
698     GSSEAP_SM_TRANSITION_NEXT(ctx);
699
700     *minor = 0;
701
702     return GSS_S_CONTINUE_NEEDED;
703 }
704
705 static OM_uint32
706 eapGssSmAcceptCompleteAcceptorExts(OM_uint32 *minor,
707                                    gss_cred_id_t cred GSSEAP_UNUSED,
708                                    gss_ctx_id_t ctx,
709                                    gss_name_t target GSSEAP_UNUSED,
710                                    gss_OID mech GSSEAP_UNUSED,
711                                    OM_uint32 reqFlags GSSEAP_UNUSED,
712                                    OM_uint32 timeReq GSSEAP_UNUSED,
713                                    gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
714                                    gss_buffer_t inputToken GSSEAP_UNUSED,
715                                    gss_buffer_t outputToken GSSEAP_UNUSED,
716                                    OM_uint32 *smFlags)
717 {
718     GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
719
720     *minor = 0;
721     *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
722
723     return GSS_S_COMPLETE;
724 }
725
726 static struct gss_eap_sm eapGssAcceptorSm[] = {
727     {
728         ITOK_TYPE_ACCEPTOR_NAME_REQ,
729         ITOK_TYPE_ACCEPTOR_NAME_RESP,
730         GSSEAP_STATE_INITIAL,
731         0,
732         eapGssSmAcceptAcceptorName
733     },
734 #ifdef GSSEAP_DEBUG
735     {
736         ITOK_TYPE_VENDOR_INFO,
737         ITOK_TYPE_NONE,
738         GSSEAP_STATE_INITIAL,
739         0,
740         eapGssSmAcceptVendorInfo,
741     },
742 #endif
743 #ifdef GSSEAP_ENABLE_REAUTH
744     {
745         ITOK_TYPE_REAUTH_REQ,
746         ITOK_TYPE_REAUTH_RESP,
747         GSSEAP_STATE_INITIAL,
748         0,
749         eapGssSmAcceptGssReauth,
750     },
751 #endif
752     {
753         ITOK_TYPE_NONE,
754         ITOK_TYPE_EAP_REQ,
755         GSSEAP_STATE_INITIAL,
756         SM_ITOK_FLAG_REQUIRED,
757         eapGssSmAcceptIdentity,
758     },
759     {
760         ITOK_TYPE_EAP_RESP,
761         ITOK_TYPE_EAP_REQ,
762         GSSEAP_STATE_AUTHENTICATE,
763         SM_ITOK_FLAG_REQUIRED,
764         eapGssSmAcceptAuthenticate
765     },
766     {
767         ITOK_TYPE_GSS_CHANNEL_BINDINGS,
768         ITOK_TYPE_NONE,
769         GSSEAP_STATE_INITIATOR_EXTS,
770         SM_ITOK_FLAG_REQUIRED,
771         eapGssSmAcceptGssChannelBindings,
772     },
773     {
774         ITOK_TYPE_NONE,
775         ITOK_TYPE_NONE,
776         GSSEAP_STATE_INITIATOR_EXTS,
777         0,
778         eapGssSmAcceptCompleteInitiatorExts,
779     },
780 #ifdef GSSEAP_ENABLE_REAUTH
781     {
782         ITOK_TYPE_NONE,
783         ITOK_TYPE_REAUTH_CREDS,
784         GSSEAP_STATE_ACCEPTOR_EXTS,
785         0,
786         eapGssSmAcceptReauthCreds,
787     },
788 #endif
789     {
790         ITOK_TYPE_NONE,
791         ITOK_TYPE_NONE,
792         GSSEAP_STATE_ACCEPTOR_EXTS,
793         0,
794         eapGssSmAcceptCompleteAcceptorExts
795     },
796 };
797
798 OM_uint32
799 gss_accept_sec_context(OM_uint32 *minor,
800                        gss_ctx_id_t *context_handle,
801                        gss_cred_id_t cred,
802                        gss_buffer_t input_token,
803                        gss_channel_bindings_t input_chan_bindings,
804                        gss_name_t *src_name,
805                        gss_OID *mech_type,
806                        gss_buffer_t output_token,
807                        OM_uint32 *ret_flags,
808                        OM_uint32 *time_rec,
809                        gss_cred_id_t *delegated_cred_handle)
810 {
811     OM_uint32 major, tmpMinor;
812     gss_ctx_id_t ctx = *context_handle;
813
814     *minor = 0;
815
816     output_token->length = 0;
817     output_token->value = NULL;
818
819     if (src_name != NULL)
820         *src_name = GSS_C_NO_NAME;
821
822     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
823         *minor = GSSEAP_TOK_TRUNC;
824         return GSS_S_DEFECTIVE_TOKEN;
825     }
826
827     if (ctx == GSS_C_NO_CONTEXT) {
828         major = gssEapAllocContext(minor, &ctx);
829         if (GSS_ERROR(major))
830             return major;
831
832         *context_handle = ctx;
833     }
834
835     GSSEAP_MUTEX_LOCK(&ctx->mutex);
836
837     if (cred == GSS_C_NO_CREDENTIAL) {
838         if (ctx->defaultCred == GSS_C_NO_CREDENTIAL) {
839             major = gssEapAcquireCred(minor,
840                                       GSS_C_NO_NAME,
841                                       GSS_C_NO_BUFFER,
842                                       GSS_C_INDEFINITE,
843                                       GSS_C_NO_OID_SET,
844                                       GSS_C_ACCEPT,
845                                       &ctx->defaultCred,
846                                       NULL,
847                                       NULL);
848             if (GSS_ERROR(major))
849                 goto cleanup;
850         }
851
852         cred = ctx->defaultCred;
853     }
854
855     GSSEAP_MUTEX_LOCK(&cred->mutex);
856
857     if (cred->name != GSS_C_NO_NAME) {
858         major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
859         if (GSS_ERROR(major))
860             goto cleanup;
861     }
862
863     major = gssEapSmStep(minor,
864                          cred,
865                          ctx,
866                          GSS_C_NO_NAME,
867                          GSS_C_NO_OID,
868                          0,
869                          GSS_C_INDEFINITE,
870                          input_chan_bindings,
871                          input_token,
872                          output_token,
873                          eapGssAcceptorSm,
874                          sizeof(eapGssAcceptorSm) / sizeof(eapGssAcceptorSm[0]));
875     if (GSS_ERROR(major))
876         goto cleanup;
877
878     if (mech_type != NULL) {
879         OM_uint32 tmpMajor;
880
881         tmpMajor = gssEapCanonicalizeOid(&tmpMinor, ctx->mechanismUsed, 0, mech_type);
882         if (GSS_ERROR(tmpMajor)) {
883             major = tmpMajor;
884             *minor = tmpMinor;
885             goto cleanup;
886         }
887     }
888     if (ret_flags != NULL)
889         *ret_flags = ctx->gssFlags;
890     if (delegated_cred_handle != NULL)
891         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
892
893     if (major == GSS_S_COMPLETE) {
894         if (src_name != NULL && ctx->initiatorName != GSS_C_NO_NAME) {
895             major = gssEapDuplicateName(&tmpMinor, ctx->initiatorName, src_name);
896             if (GSS_ERROR(major))
897                 goto cleanup;
898         }
899         if (time_rec != NULL) {
900             major = gssEapContextTime(&tmpMinor, ctx, time_rec);
901             if (GSS_ERROR(major))
902                 goto cleanup;
903         }
904     }
905
906     assert(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
907
908 cleanup:
909     if (cred != GSS_C_NO_CREDENTIAL)
910         GSSEAP_MUTEX_UNLOCK(&cred->mutex);
911     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
912
913     if (GSS_ERROR(major))
914         gssEapReleaseContext(&tmpMinor, context_handle);
915
916     return major;
917 }
918
919 #ifdef GSSEAP_ENABLE_REAUTH
920 static OM_uint32
921 acceptReadyKrb(OM_uint32 *minor,
922                gss_ctx_id_t ctx,
923                gss_cred_id_t cred,
924                const gss_name_t initiator,
925                const gss_OID mech,
926                OM_uint32 timeRec)
927 {
928     OM_uint32 major;
929
930     major = gssEapGlueToMechName(minor, ctx, initiator, &ctx->initiatorName);
931     if (GSS_ERROR(major))
932         return major;
933
934     major = gssEapReauthComplete(minor, ctx, cred, mech, timeRec);
935     if (GSS_ERROR(major))
936         return major;
937
938     *minor = 0;
939     return GSS_S_COMPLETE;
940 }
941
942 static OM_uint32
943 eapGssSmAcceptGssReauth(OM_uint32 *minor,
944                         gss_cred_id_t cred,
945                         gss_ctx_id_t ctx,
946                         gss_name_t target GSSEAP_UNUSED,
947                         gss_OID mech,
948                         OM_uint32 reqFlags GSSEAP_UNUSED,
949                         OM_uint32 timeReq GSSEAP_UNUSED,
950                         gss_channel_bindings_t chanBindings,
951                         gss_buffer_t inputToken,
952                         gss_buffer_t outputToken,
953                         OM_uint32 *smFlags)
954 {
955     OM_uint32 major, tmpMinor;
956     gss_name_t krbInitiator = GSS_C_NO_NAME;
957     OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE;
958
959     /*
960      * If we're built with fast reauthentication support, it's valid
961      * for an initiator to send a GSS reauthentication token as its
962      * initial context token, causing us to short-circuit the state
963      * machine and process Kerberos GSS messages instead.
964      */
965
966     ctx->flags |= CTX_FLAG_KRB_REAUTH;
967
968     major = gssAcceptSecContext(minor,
969                                 &ctx->kerberosCtx,
970                                 cred->krbCred,
971                                 inputToken,
972                                 chanBindings,
973                                 &krbInitiator,
974                                 &mech,
975                                 outputToken,
976                                 &gssFlags,
977                                 &timeRec,
978                                 NULL);
979     if (major == GSS_S_COMPLETE) {
980         major = acceptReadyKrb(minor, ctx, cred,
981                                krbInitiator, mech, timeRec);
982         if (major == GSS_S_COMPLETE) {
983             GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
984         }
985         ctx->gssFlags = gssFlags;
986     } else if (GSS_ERROR(major) &&
987         (*smFlags & SM_FLAG_INPUT_TOKEN_CRITICAL) == 0) {
988         /* pretend reauthentication attempt never happened */
989         gssDeleteSecContext(&tmpMinor, &ctx->kerberosCtx, GSS_C_NO_BUFFER);
990         ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
991         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
992         major = GSS_S_CONTINUE_NEEDED;
993     }
994
995     gssReleaseName(&tmpMinor, &krbInitiator);
996
997     return major;
998 }
999 #endif /* GSSEAP_ENABLE_REAUTH */