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