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