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