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