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