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