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