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