Temporary: set mutual in flags token
[mech_eap.git] / mech_eap / init_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 initiator (client). These functions
35  * wrap around libeap.
36  */
37
38 #include "gssapiP_eap.h"
39 #include "radius/radius.h"
40 #include "util_radius.h"
41 #include "utils/radius_utils.h"
42
43 static OM_uint32
44 policyVariableToFlag(enum eapol_bool_var variable)
45 {
46     OM_uint32 flag = 0;
47
48     switch (variable) {
49     case EAPOL_eapSuccess:
50         flag = CTX_FLAG_EAP_SUCCESS;
51         break;
52     case EAPOL_eapRestart:
53         flag = CTX_FLAG_EAP_RESTART;
54         break;
55     case EAPOL_eapFail:
56         flag = CTX_FLAG_EAP_FAIL;
57         break;
58     case EAPOL_eapResp:
59         flag = CTX_FLAG_EAP_RESP;
60         break;
61     case EAPOL_eapNoResp:
62         flag = CTX_FLAG_EAP_NO_RESP;
63         break;
64     case EAPOL_eapReq:
65         flag = CTX_FLAG_EAP_REQ;
66         break;
67     case EAPOL_portEnabled:
68         flag = CTX_FLAG_EAP_PORT_ENABLED;
69         break;
70     case EAPOL_altAccept:
71         flag = CTX_FLAG_EAP_ALT_ACCEPT;
72         break;
73     case EAPOL_altReject:
74         flag = CTX_FLAG_EAP_ALT_REJECT;
75         break;
76     }
77
78     return flag;
79 }
80
81 static struct eap_peer_config *
82 peerGetConfig(void *ctx)
83 {
84     gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
85
86     return &gssCtx->initiatorCtx.eapPeerConfig;
87 }
88
89 static Boolean
90 peerGetBool(void *data, enum eapol_bool_var variable)
91 {
92     gss_ctx_id_t ctx = data;
93     OM_uint32 flag;
94
95     if (ctx == GSS_C_NO_CONTEXT)
96         return FALSE;
97
98     flag = policyVariableToFlag(variable);
99
100     return ((ctx->flags & flag) != 0);
101 }
102
103 static void
104 peerSetBool(void *data, enum eapol_bool_var variable,
105             Boolean value)
106 {
107     gss_ctx_id_t ctx = data;
108     OM_uint32 flag;
109
110     if (ctx == GSS_C_NO_CONTEXT)
111         return;
112
113     flag = policyVariableToFlag(variable);
114
115     if (value)
116         ctx->flags |= flag;
117     else
118         ctx->flags &= ~(flag);
119 }
120
121 static unsigned int
122 peerGetInt(void *data, enum eapol_int_var variable)
123 {
124     gss_ctx_id_t ctx = data;
125
126     if (ctx == GSS_C_NO_CONTEXT)
127         return FALSE;
128
129     GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));
130
131     switch (variable) {
132     case EAPOL_idleWhile:
133         return ctx->initiatorCtx.idleWhile;
134         break;
135     }
136
137     return 0;
138 }
139
140 static void
141 peerSetInt(void *data, enum eapol_int_var variable,
142            unsigned int value)
143 {
144     gss_ctx_id_t ctx = data;
145
146     if (ctx == GSS_C_NO_CONTEXT)
147         return;
148
149     GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));
150
151     switch (variable) {
152     case EAPOL_idleWhile:
153         ctx->initiatorCtx.idleWhile = value;
154         break;
155     }
156 }
157
158 static struct wpabuf *
159 peerGetEapReqData(void *ctx)
160 {
161     gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
162
163     return &gssCtx->initiatorCtx.reqData;
164 }
165
166 static void
167 peerSetConfigBlob(void *ctx GSSEAP_UNUSED,
168                   struct wpa_config_blob *blob GSSEAP_UNUSED)
169 {
170 }
171
172 static const struct wpa_config_blob *
173 peerGetConfigBlob(void *ctx,
174                   const char *name)
175 {
176     gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
177     size_t index;
178
179     if (strcmp(name, "client-cert") == 0)
180         index = CONFIG_BLOB_CLIENT_CERT;
181     else if (strcmp(name, "private-key") == 0)
182         index = CONFIG_BLOB_PRIVATE_KEY;
183     else
184         return NULL;
185
186     return &gssCtx->initiatorCtx.configBlobs[index];
187 }
188
189 static void
190 peerNotifyPending(void *ctx GSSEAP_UNUSED)
191 {
192 }
193
194 static struct eapol_callbacks gssEapPolicyCallbacks = {
195     peerGetConfig,
196     peerGetBool,
197     peerSetBool,
198     peerGetInt,
199     peerSetInt,
200     peerGetEapReqData,
201     peerSetConfigBlob,
202     peerGetConfigBlob,
203     peerNotifyPending,
204 };
205
206 #ifdef GSSEAP_DEBUG
207 extern int wpa_debug_level;
208 #endif
209
210 #define CHBIND_SERVICE_NAME_FLAG        0x01
211 #define CHBIND_HOST_NAME_FLAG           0x02
212 #define CHBIND_SERVICE_SPECIFIC_FLAG    0x04
213 #define CHBIND_REALM_NAME_FLAG          0x08
214
215 static OM_uint32
216 peerInitEapChannelBinding(OM_uint32 *minor, gss_ctx_id_t ctx)
217 {
218     struct wpabuf *buf = NULL;
219     unsigned int chbindReqFlags = 0;
220     krb5_principal princ = NULL;
221     gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
222     OM_uint32 major = GSS_S_COMPLETE;
223     krb5_context krbContext = NULL;
224
225     /* XXX is this check redundant? */
226     if (ctx->acceptorName == GSS_C_NO_NAME) {
227         major = GSS_S_BAD_NAME;
228         *minor = GSSEAP_NO_ACCEPTOR_NAME;
229         goto cleanup;
230     }
231
232     princ = ctx->acceptorName->krbPrincipal;
233
234     krbPrincComponentToGssBuffer(princ, 0, &nameBuf);
235     if (nameBuf.length > 0) {
236         major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_SERVICE_NAME,
237                                     0, &nameBuf);
238         if (GSS_ERROR(major))
239             goto cleanup;
240
241         chbindReqFlags |= CHBIND_SERVICE_NAME_FLAG;
242     }
243
244     krbPrincComponentToGssBuffer(princ, 1, &nameBuf);
245     if (nameBuf.length > 0) {
246         major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_HOST_NAME,
247                                     0, &nameBuf);
248         if (GSS_ERROR(major))
249             goto cleanup;
250
251         chbindReqFlags |= CHBIND_HOST_NAME_FLAG;
252     }
253
254     GSSEAP_KRB_INIT(&krbContext);
255
256     *minor = krbPrincUnparseServiceSpecifics(krbContext, princ, &nameBuf);
257     if (*minor != 0)
258         goto cleanup;
259
260     if (nameBuf.length > 0) {
261         major = gssEapRadiusAddAttr(minor, &buf,
262                                     PW_GSS_ACCEPTOR_SERVICE_SPECIFICS,
263                                     0, &nameBuf);
264         if (GSS_ERROR(major))
265             goto cleanup;
266
267         chbindReqFlags |= CHBIND_SERVICE_SPECIFIC_FLAG;
268     }
269
270     krbFreeUnparsedName(krbContext, &nameBuf);
271     krbPrincRealmToGssBuffer(princ, &nameBuf);
272
273     if (nameBuf.length > 0) {
274         major = gssEapRadiusAddAttr(minor, &buf,
275                                     PW_GSS_ACCEPTOR_REALM_NAME,
276                                     0, &nameBuf);
277         chbindReqFlags |= CHBIND_REALM_NAME_FLAG;
278     }
279
280     if (chbindReqFlags == 0) {
281         major = GSS_S_BAD_NAME;
282         *minor = GSSEAP_BAD_ACCEPTOR_NAME;
283         goto cleanup;
284     }
285
286     ctx->initiatorCtx.chbindData = buf;
287     ctx->initiatorCtx.chbindReqFlags = chbindReqFlags;
288
289     buf = NULL;
290
291     major = GSS_S_COMPLETE;
292     *minor = 0;
293
294 cleanup:
295     /*namebuf is freed when used and may be left with a unowned pointer*/
296     wpabuf_free(buf);
297
298     return major;
299 }
300
301 static void
302 peerProcessChbindResponse(void *context, int code, int nsid,
303                           u8 *data, size_t len)
304 {
305     radius_parser msg;
306     gss_ctx_id_t ctx = (gss_ctx_id_t )context;
307     void *vsadata;
308     u8 type;
309     u32 vendor_id;
310     u32 chbindRetFlags = 0;
311     size_t vsadata_len;
312
313     if (nsid != CHBIND_NSID_RADIUS)
314         return;
315
316     if (data == NULL)
317         return;
318     msg = radius_parser_start(data, len);
319     if (msg == NULL)
320         return;
321
322     while (radius_parser_parse_tlv(msg, &type, &vendor_id, &vsadata,
323                                    &vsadata_len) == 0) {
324         switch (type) {
325         case PW_GSS_ACCEPTOR_SERVICE_NAME:
326             chbindRetFlags |= CHBIND_SERVICE_NAME_FLAG;
327             break;
328         case PW_GSS_ACCEPTOR_HOST_NAME:
329             chbindRetFlags |= CHBIND_HOST_NAME_FLAG;
330             break;
331         case PW_GSS_ACCEPTOR_SERVICE_SPECIFICS:
332             chbindRetFlags |= CHBIND_SERVICE_SPECIFIC_FLAG;
333             break;
334         case PW_GSS_ACCEPTOR_REALM_NAME:
335             chbindRetFlags |= CHBIND_REALM_NAME_FLAG;
336             break;
337         }
338     }
339
340     radius_parser_finish(msg);
341
342     if (code == CHBIND_CODE_SUCCESS &&
343         ((chbindRetFlags & ctx->initiatorCtx.chbindReqFlags) == ctx->initiatorCtx.chbindReqFlags)) {
344         ctx->flags |= CTX_FLAG_EAP_CHBIND_ACCEPT;
345         ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
346     } /* else log failures? */
347 }
348
349 static OM_uint32
350 peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
351 {
352     OM_uint32 major;
353     krb5_context krbContext;
354     struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
355     struct wpa_config_blob *configBlobs = ctx->initiatorCtx.configBlobs;
356     gss_buffer_desc identity = GSS_C_EMPTY_BUFFER;
357     gss_buffer_desc realm = GSS_C_EMPTY_BUFFER;
358     gss_cred_id_t cred = ctx->cred;
359
360     eapPeerConfig->identity = NULL;
361     eapPeerConfig->identity_len = 0;
362     eapPeerConfig->anonymous_identity = NULL;
363     eapPeerConfig->anonymous_identity_len = 0;
364     eapPeerConfig->password = NULL;
365     eapPeerConfig->password_len = 0;
366
367     GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
368
369     GSSEAP_KRB_INIT(&krbContext);
370
371     eapPeerConfig->fragment_size = 1024;
372 #ifdef GSSEAP_DEBUG
373     wpa_debug_level = 0;
374 #endif
375
376     GSSEAP_ASSERT(cred->name != GSS_C_NO_NAME);
377
378     if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) {
379         *minor = GSSEAP_BAD_INITIATOR_NAME;
380         return GSS_S_BAD_NAME;
381     }
382
383     /* identity */
384     major = gssEapDisplayName(minor, cred->name, &identity, NULL);
385     if (GSS_ERROR(major))
386         return major;
387
388     eapPeerConfig->identity = (unsigned char *)identity.value;
389     eapPeerConfig->identity_len = identity.length;
390
391     krbPrincRealmToGssBuffer(cred->name->krbPrincipal, &realm);
392
393     /* anonymous_identity */
394     eapPeerConfig->anonymous_identity = GSSEAP_MALLOC(realm.length + 2);
395     if (eapPeerConfig->anonymous_identity == NULL) {
396         *minor = ENOMEM;
397         return GSS_S_FAILURE;
398     }
399
400     eapPeerConfig->anonymous_identity[0] = '@';
401     memcpy(eapPeerConfig->anonymous_identity + 1, realm.value, realm.length);
402     eapPeerConfig->anonymous_identity[1 + realm.length] = '\0';
403     eapPeerConfig->anonymous_identity_len = 1 + realm.length;
404
405     /* password */
406     if ((cred->flags & CRED_FLAG_CERTIFICATE) == 0) {
407         eapPeerConfig->password = (unsigned char *)cred->password.value;
408         eapPeerConfig->password_len = cred->password.length;
409     }
410
411     /* certs */
412     eapPeerConfig->ca_cert = (unsigned char *)cred->caCertificate.value;
413     eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
414     eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
415
416     /* eap channel binding */
417     if (ctx->initiatorCtx.chbindData != NULL) {
418         struct eap_peer_chbind_config *chbind_config =
419             (struct eap_peer_chbind_config *)GSSEAP_MALLOC(sizeof(struct eap_peer_chbind_config));
420         if (chbind_config == NULL) {
421             *minor = ENOMEM;
422             return GSS_S_FAILURE;
423         }
424
425         chbind_config->req_data = wpabuf_mhead_u8(ctx->initiatorCtx.chbindData);
426         chbind_config->req_data_len = wpabuf_len(ctx->initiatorCtx.chbindData);
427         chbind_config->nsid = CHBIND_NSID_RADIUS;
428         chbind_config->response_cb = &peerProcessChbindResponse;
429         chbind_config->ctx = ctx;
430         eapPeerConfig->chbind_config = chbind_config;
431         eapPeerConfig->chbind_config_len = 1;
432     } else {
433         eapPeerConfig->chbind_config = NULL;
434         eapPeerConfig->chbind_config_len = 0;
435     }
436     if (cred->flags & CRED_FLAG_CERTIFICATE) {
437         /*
438          * CRED_FLAG_CONFIG_BLOB is an internal flag which will be used in the
439          * future to directly pass certificate and private key data to the
440          * EAP implementation, rather than an indirected string pointer.
441          */
442         if (cred->flags & CRED_FLAG_CONFIG_BLOB) {
443             eapPeerConfig->client_cert = (unsigned char *)"blob://client-cert";
444             configBlobs[CONFIG_BLOB_CLIENT_CERT].data = cred->clientCertificate.value;
445             configBlobs[CONFIG_BLOB_CLIENT_CERT].len  = cred->clientCertificate.length;
446
447             eapPeerConfig->client_cert = (unsigned char *)"blob://private-key";
448             configBlobs[CONFIG_BLOB_PRIVATE_KEY].data = cred->clientCertificate.value;
449             configBlobs[CONFIG_BLOB_PRIVATE_KEY].len  = cred->privateKey.length;
450         } else {
451             eapPeerConfig->client_cert = (unsigned char *)cred->clientCertificate.value;
452             eapPeerConfig->private_key = (unsigned char *)cred->privateKey.value;
453         }
454         eapPeerConfig->private_key_passwd = (unsigned char *)cred->password.value;
455     }
456
457     *minor = 0;
458     return GSS_S_COMPLETE;
459 }
460
461 static OM_uint32
462 peerConfigFree(OM_uint32 *minor,
463                gss_ctx_id_t ctx)
464 {
465     struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
466
467     if (eapPeerConfig->identity != NULL) {
468         GSSEAP_FREE(eapPeerConfig->identity);
469         eapPeerConfig->identity = NULL;
470         eapPeerConfig->identity_len = 0;
471     }
472
473     if (eapPeerConfig->anonymous_identity != NULL) {
474         GSSEAP_FREE(eapPeerConfig->anonymous_identity);
475         eapPeerConfig->anonymous_identity = NULL;
476         eapPeerConfig->anonymous_identity_len = 0;
477     }
478
479     *minor = 0;
480     return GSS_S_COMPLETE;
481 }
482
483 /*
484  * Mark an initiator context as ready for cryptographic operations
485  */
486 static OM_uint32
487 initReady(OM_uint32 *minor, gss_ctx_id_t ctx)
488 {
489     OM_uint32 major;
490     const unsigned char *key;
491     size_t keyLength;
492
493     /* Cache encryption type derived from selected mechanism OID */
494     major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
495     if (GSS_ERROR(major))
496         return major;
497
498     if (!eap_key_available(ctx->initiatorCtx.eap)) {
499         *minor = GSSEAP_KEY_UNAVAILABLE;
500         return GSS_S_UNAVAILABLE;
501     }
502
503     key = eap_get_eapKeyData(ctx->initiatorCtx.eap, &keyLength);
504
505     if (keyLength < EAP_EMSK_LEN) {
506         *minor = GSSEAP_KEY_TOO_SHORT;
507         return GSS_S_UNAVAILABLE;
508     }
509
510     major = gssEapDeriveRfc3961Key(minor,
511                                    &key[EAP_EMSK_LEN / 2],
512                                    EAP_EMSK_LEN / 2,
513                                    ctx->encryptionType,
514                                    &ctx->rfc3961Key);
515        if (GSS_ERROR(major))
516            return major;
517
518     major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
519                                       &ctx->checksumType);
520     if (GSS_ERROR(major))
521         return major;
522
523     major = sequenceInit(minor,
524                          &ctx->seqState,
525                          ctx->recvSeq,
526                          ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
527                          ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
528                          TRUE);
529     if (GSS_ERROR(major))
530         return major;
531
532     *minor = 0;
533     return GSS_S_COMPLETE;
534 }
535
536 static OM_uint32
537 initBegin(OM_uint32 *minor,
538           gss_ctx_id_t ctx,
539           gss_name_t target,
540           gss_OID mech,
541           OM_uint32 reqFlags GSSEAP_UNUSED,
542           OM_uint32 timeReq,
543           gss_channel_bindings_t chanBindings GSSEAP_UNUSED)
544 {
545     OM_uint32 major;
546     gss_cred_id_t cred = ctx->cred;
547
548     GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
549
550     if (cred->expiryTime)
551         ctx->expiryTime = cred->expiryTime;
552     else if (timeReq == 0 || timeReq == GSS_C_INDEFINITE)
553         ctx->expiryTime = 0;
554     else
555         ctx->expiryTime = time(NULL) + timeReq;
556
557     /*
558      * The credential mutex protects its name, however we need to
559      * explicitly lock the acceptor name (unlikely as it may be
560      * that it has attributes set on it).
561      */
562     major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName);
563     if (GSS_ERROR(major))
564         return major;
565
566     if (target != GSS_C_NO_NAME) {
567         GSSEAP_MUTEX_LOCK(&target->mutex);
568
569         major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
570         if (GSS_ERROR(major)) {
571             GSSEAP_MUTEX_UNLOCK(&target->mutex);
572             return major;
573         }
574
575         GSSEAP_MUTEX_UNLOCK(&target->mutex);
576     }
577
578     major = gssEapCanonicalizeOid(minor,
579                                   mech,
580                                   OID_FLAG_NULL_VALID | OID_FLAG_MAP_NULL_TO_DEFAULT_MECH,
581                                   &ctx->mechanismUsed);
582     if (GSS_ERROR(major))
583         return major;
584
585     /* If credentials were provided, check they're usable with this mech */
586     if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
587         *minor = GSSEAP_CRED_MECH_MISMATCH;
588         return GSS_S_BAD_MECH;
589     }
590
591     *minor = 0;
592     return GSS_S_COMPLETE;
593 }
594
595 static OM_uint32
596 eapGssSmInitError(OM_uint32 *minor,
597                   gss_cred_id_t cred GSSEAP_UNUSED,
598                   gss_ctx_id_t ctx GSSEAP_UNUSED,
599                   gss_name_t target GSSEAP_UNUSED,
600                   gss_OID mech GSSEAP_UNUSED,
601                   OM_uint32 reqFlags GSSEAP_UNUSED,
602                   OM_uint32 timeReq GSSEAP_UNUSED,
603                   gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
604                   gss_buffer_t inputToken,
605                   gss_buffer_t outputToken GSSEAP_UNUSED,
606                   OM_uint32 *smFlags GSSEAP_UNUSED)
607 {
608     OM_uint32 major;
609     unsigned char *p;
610
611     if (inputToken->length < 8) {
612         *minor = GSSEAP_TOK_TRUNC;
613         return GSS_S_DEFECTIVE_TOKEN;
614     }
615
616     p = (unsigned char *)inputToken->value;
617
618     major = load_uint32_be(&p[0]);
619     *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]);
620
621     if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) {
622         major = GSS_S_FAILURE;
623         *minor = GSSEAP_BAD_ERROR_TOKEN;
624     }
625
626     GSSEAP_ASSERT(GSS_ERROR(major));
627
628     return major;
629 }
630
631 #ifdef GSSEAP_ENABLE_REAUTH
632 static OM_uint32
633 eapGssSmInitGssReauth(OM_uint32 *minor,
634                       gss_cred_id_t cred,
635                       gss_ctx_id_t ctx,
636                       gss_name_t target,
637                       gss_OID mech GSSEAP_UNUSED,
638                       OM_uint32 reqFlags,
639                       OM_uint32 timeReq,
640                       gss_channel_bindings_t chanBindings,
641                       gss_buffer_t inputToken,
642                       gss_buffer_t outputToken,
643                       OM_uint32 *smFlags GSSEAP_UNUSED)
644 {
645     OM_uint32 major, tmpMinor;
646     gss_name_t mechTarget = GSS_C_NO_NAME;
647     gss_OID actualMech = GSS_C_NO_OID;
648     OM_uint32 gssFlags, timeRec;
649
650     /*
651      * Here we use the passed in credential handle because the resolved
652      * context credential does not currently have the reauth creds.
653      */
654     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
655         if (!gssEapCanReauthP(cred, target, timeReq))
656             return GSS_S_CONTINUE_NEEDED;
657
658         ctx->flags |= CTX_FLAG_KRB_REAUTH;
659     } else if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
660         major = GSS_S_DEFECTIVE_TOKEN;
661         *minor = GSSEAP_WRONG_ITOK;
662         goto cleanup;
663     }
664
665     GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
666
667     major = gssEapMechToGlueName(minor, target, &mechTarget);
668     if (GSS_ERROR(major))
669         goto cleanup;
670
671     major = gssInitSecContext(minor,
672                               cred->reauthCred,
673                               &ctx->reauthCtx,
674                               mechTarget,
675                               (gss_OID)gss_mech_krb5,
676                               reqFlags | GSS_C_MUTUAL_FLAG,
677                               timeReq,
678                               chanBindings,
679                               inputToken,
680                               &actualMech,
681                               outputToken,
682                               &gssFlags,
683                               &timeRec);
684     if (GSS_ERROR(major))
685         goto cleanup;
686
687     ctx->gssFlags = gssFlags;
688
689     if (major == GSS_S_COMPLETE) {
690         GSSEAP_ASSERT(GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE);
691
692         major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec);
693         if (GSS_ERROR(major))
694             goto cleanup;
695         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
696     } else {
697         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE);
698     }
699
700 cleanup:
701     gssReleaseName(&tmpMinor, &mechTarget);
702
703     return major;
704 }
705 #endif /* GSSEAP_ENABLE_REAUTH */
706
707 #ifdef GSSEAP_DEBUG
708 static OM_uint32
709 eapGssSmInitVendorInfo(OM_uint32 *minor,
710                        gss_cred_id_t cred GSSEAP_UNUSED,
711                        gss_ctx_id_t ctx GSSEAP_UNUSED,
712                        gss_name_t target GSSEAP_UNUSED,
713                        gss_OID mech GSSEAP_UNUSED,
714                        OM_uint32 reqFlags GSSEAP_UNUSED,
715                        OM_uint32 timeReq GSSEAP_UNUSED,
716                        gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
717                        gss_buffer_t inputToken GSSEAP_UNUSED,
718                        gss_buffer_t outputToken,
719                        OM_uint32 *smFlags GSSEAP_UNUSED)
720 {
721     OM_uint32 major;
722
723     major = makeStringBuffer(minor, "JANET(UK)", outputToken);
724     if (GSS_ERROR(major))
725         return major;
726
727     return GSS_S_CONTINUE_NEEDED;
728 }
729 #endif
730
731 static OM_uint32
732 eapGssSmInitAcceptorName(OM_uint32 *minor,
733                          gss_cred_id_t cred GSSEAP_UNUSED,
734                          gss_ctx_id_t ctx,
735                          gss_name_t target GSSEAP_UNUSED,
736                          gss_OID mech GSSEAP_UNUSED,
737                          OM_uint32 reqFlags GSSEAP_UNUSED,
738                          OM_uint32 timeReq GSSEAP_UNUSED,
739                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
740                          gss_buffer_t inputToken GSSEAP_UNUSED,
741                          gss_buffer_t outputToken,
742                          OM_uint32 *smFlags GSSEAP_UNUSED)
743 {
744     OM_uint32 major;
745
746     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL &&
747         ctx->acceptorName != GSS_C_NO_NAME) {
748
749         /* Send desired target name to acceptor */
750         major = gssEapDisplayName(minor, ctx->acceptorName,
751                                   outputToken, NULL);
752         if (GSS_ERROR(major))
753             return major;
754     } else if (inputToken != GSS_C_NO_BUFFER) {
755         OM_uint32 tmpMinor;
756         gss_name_t nameHint;
757         int equal;
758
759         /* Accept target name hint from acceptor or verify acceptor */
760         major = gssEapImportName(minor, inputToken,
761                                  GSS_C_NT_USER_NAME,
762                                  ctx->mechanismUsed,
763                                  &nameHint);
764         if (GSS_ERROR(major))
765             return major;
766
767         if (ctx->acceptorName != GSS_C_NO_NAME) {
768             /* verify name hint matched asserted acceptor name  */
769             major = gssEapCompareName(minor,
770                                       nameHint,
771                                       ctx->acceptorName,
772                                       COMPARE_NAME_FLAG_IGNORE_EMPTY_REALMS,
773                                       &equal);
774             if (GSS_ERROR(major)) {
775                 gssEapReleaseName(&tmpMinor, &nameHint);
776                 return major;
777             }
778
779             gssEapReleaseName(&tmpMinor, &nameHint);
780
781             if (!equal) {
782                 *minor = GSSEAP_WRONG_ACCEPTOR_NAME;
783                 return GSS_S_DEFECTIVE_TOKEN;
784             }
785         } else { /* acceptor name is no_name */
786             /* accept acceptor name hint */
787             ctx->acceptorName = nameHint;
788             nameHint = GSS_C_NO_NAME;
789         }
790     }
791
792
793     /*
794      * Currently, other parts of the code assume that the acceptor name
795      * is available, hence this check.
796      */
797     if (ctx->acceptorName == GSS_C_NO_NAME) {
798         *minor = GSSEAP_NO_ACCEPTOR_NAME;
799         return GSS_S_FAILURE;
800     }
801
802     /*
803      * Generate channel binding data
804      */
805     if (ctx->initiatorCtx.chbindData == NULL) {
806         major = peerInitEapChannelBinding(minor, ctx);
807         if (GSS_ERROR(major))
808             return major;
809     }
810
811     return GSS_S_CONTINUE_NEEDED;
812 }
813
814 static OM_uint32
815 eapGssSmInitIdentity(OM_uint32 *minor,
816                      gss_cred_id_t cred GSSEAP_UNUSED,
817                      gss_ctx_id_t ctx,
818                      gss_name_t target GSSEAP_UNUSED,
819                      gss_OID mech GSSEAP_UNUSED,
820                      OM_uint32 reqFlags GSSEAP_UNUSED,
821                      OM_uint32 timeReq GSSEAP_UNUSED,
822                      gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
823                      gss_buffer_t inputToken GSSEAP_UNUSED,
824                      gss_buffer_t outputToken GSSEAP_UNUSED,
825                      OM_uint32 *smFlags)
826 {
827     struct eap_config eapConfig;
828
829 #ifdef GSSEAP_ENABLE_REAUTH
830     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE) {
831         OM_uint32 tmpMinor;
832
833         /* server didn't support reauthentication, sent EAP request */
834         gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
835         ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
836         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
837     } else
838 #endif
839         *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
840
841     GSSEAP_ASSERT((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
842     GSSEAP_ASSERT(inputToken == GSS_C_NO_BUFFER);
843
844     memset(&eapConfig, 0, sizeof(eapConfig));
845
846     ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
847                                              &gssEapPolicyCallbacks,
848                                              ctx,
849                                              &eapConfig);
850     if (ctx->initiatorCtx.eap == NULL) {
851         *minor = GSSEAP_PEER_SM_INIT_FAILURE;
852         return GSS_S_FAILURE;
853     }
854
855     ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
856
857     /* poke EAP state machine */
858     if (eap_peer_sm_step(ctx->initiatorCtx.eap) != 0) {
859         *minor = GSSEAP_PEER_SM_STEP_FAILURE;
860         return GSS_S_FAILURE;
861     }
862
863     GSSEAP_SM_TRANSITION_NEXT(ctx);
864
865     *minor = 0;
866
867     return GSS_S_CONTINUE_NEEDED;
868 }
869
870 static OM_uint32
871 eapGssSmInitAuthenticate(OM_uint32 *minor,
872                          gss_cred_id_t cred GSSEAP_UNUSED,
873                          gss_ctx_id_t ctx,
874                          gss_name_t target GSSEAP_UNUSED,
875                          gss_OID mech GSSEAP_UNUSED,
876                          OM_uint32 reqFlags GSSEAP_UNUSED,
877                          OM_uint32 timeReq GSSEAP_UNUSED,
878                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
879                          gss_buffer_t inputToken GSSEAP_UNUSED,
880                          gss_buffer_t outputToken,
881                          OM_uint32 *smFlags)
882 {
883     OM_uint32 major;
884     OM_uint32 tmpMinor;
885     struct wpabuf *resp = NULL;
886
887     *minor = 0;
888
889     GSSEAP_ASSERT(inputToken != GSS_C_NO_BUFFER);
890
891     major = peerConfigInit(minor, ctx);
892     if (GSS_ERROR(major))
893         goto cleanup;
894
895     GSSEAP_ASSERT(ctx->initiatorCtx.eap != NULL);
896     GSSEAP_ASSERT(ctx->flags & CTX_FLAG_EAP_PORT_ENABLED);
897
898     ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
899
900     wpabuf_set(&ctx->initiatorCtx.reqData,
901                inputToken->value, inputToken->length);
902
903     major = GSS_S_CONTINUE_NEEDED;
904
905     eap_peer_sm_step(ctx->initiatorCtx.eap);
906     if (ctx->flags & CTX_FLAG_EAP_RESP) {
907         ctx->flags &= ~(CTX_FLAG_EAP_RESP);
908
909         resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
910     } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
911         major = initReady(minor, ctx);
912         if (GSS_ERROR(major))
913             goto cleanup;
914
915         ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
916         major = GSS_S_CONTINUE_NEEDED;
917         GSSEAP_SM_TRANSITION_NEXT(ctx);
918     } else if (ctx->flags & CTX_FLAG_EAP_FAIL) {
919         major = GSS_S_DEFECTIVE_CREDENTIAL;
920         *minor = GSSEAP_PEER_AUTH_FAILURE;
921     } else {
922         major = GSS_S_DEFECTIVE_TOKEN;
923         *minor = GSSEAP_PEER_BAD_MESSAGE;
924     }
925
926 cleanup:
927     if (resp != NULL) {
928         OM_uint32 tmpMajor;
929         gss_buffer_desc respBuf;
930
931         GSSEAP_ASSERT(major == GSS_S_CONTINUE_NEEDED);
932
933         respBuf.length = wpabuf_len(resp);
934         respBuf.value = (void *)wpabuf_head(resp);
935
936         tmpMajor = duplicateBuffer(&tmpMinor, &respBuf, outputToken);
937         if (GSS_ERROR(tmpMajor)) {
938             major = tmpMajor;
939             *minor = tmpMinor;
940         }
941
942         *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
943     }
944
945     wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0);
946     peerConfigFree(&tmpMinor, ctx);
947
948     return major;
949 }
950
951 static OM_uint32
952 eapGssSmInitGssFlags(OM_uint32 *minor,
953                      gss_cred_id_t cred GSSEAP_UNUSED,
954                      gss_ctx_id_t ctx,
955                      gss_name_t target GSSEAP_UNUSED,
956                      gss_OID mech GSSEAP_UNUSED,
957                      OM_uint32 reqFlags GSSEAP_UNUSED,
958                      OM_uint32 timeReq GSSEAP_UNUSED,
959                      gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
960                      gss_buffer_t inputToken GSSEAP_UNUSED,
961                      gss_buffer_t outputToken,
962                      OM_uint32 *smFlags GSSEAP_UNUSED)
963 {
964     unsigned char wireFlags[4];
965     gss_buffer_desc flagsBuf;
966
967     /*
968      * As a temporary measure, force mutual authentication until channel binding is
969      * more widely deployed.
970      */
971     ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
972     store_uint32_be(ctx->gssFlags & GSSEAP_WIRE_FLAGS_MASK, wireFlags);
973
974     flagsBuf.length = sizeof(wireFlags);
975     flagsBuf.value = wireFlags;
976
977     return duplicateBuffer(minor, &flagsBuf, outputToken);
978 }
979
980 static OM_uint32
981 eapGssSmInitGssChannelBindings(OM_uint32 *minor,
982                                gss_cred_id_t cred GSSEAP_UNUSED,
983                                gss_ctx_id_t ctx,
984                                gss_name_t target GSSEAP_UNUSED,
985                                gss_OID mech GSSEAP_UNUSED,
986                                OM_uint32 reqFlags GSSEAP_UNUSED,
987                                OM_uint32 timeReq GSSEAP_UNUSED,
988                                gss_channel_bindings_t chanBindings,
989                                gss_buffer_t inputToken GSSEAP_UNUSED,
990                                gss_buffer_t outputToken,
991                                OM_uint32 *smFlags)
992 {
993     OM_uint32 major;
994     krb5_error_code code;
995     krb5_context krbContext;
996     krb5_data data;
997     krb5_checksum cksum;
998     gss_buffer_desc cksumBuffer;
999
1000     if (chanBindings == GSS_C_NO_CHANNEL_BINDINGS ||
1001         chanBindings->application_data.length == 0)
1002         return GSS_S_CONTINUE_NEEDED;
1003
1004     GSSEAP_KRB_INIT(&krbContext);
1005
1006     KRB_DATA_INIT(&data);
1007
1008     gssBufferToKrbData(&chanBindings->application_data, &data);
1009
1010     code = krb5_c_make_checksum(krbContext, ctx->checksumType,
1011                                 &ctx->rfc3961Key,
1012                                 KEY_USAGE_GSSEAP_CHBIND_MIC,
1013                                 &data, &cksum);
1014     if (code != 0) {
1015         *minor = code;
1016         return GSS_S_FAILURE;
1017     }
1018
1019     cksumBuffer.length = KRB_CHECKSUM_LENGTH(&cksum);
1020     cksumBuffer.value  = KRB_CHECKSUM_DATA(&cksum);
1021
1022     major = duplicateBuffer(minor, &cksumBuffer, outputToken);
1023     if (GSS_ERROR(major)) {
1024         krb5_free_checksum_contents(krbContext, &cksum);
1025         return major;
1026     }
1027
1028     *minor = 0;
1029     *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
1030
1031     krb5_free_checksum_contents(krbContext, &cksum);
1032
1033     return GSS_S_CONTINUE_NEEDED;
1034 }
1035
1036 static OM_uint32
1037 eapGssSmInitInitiatorMIC(OM_uint32 *minor,
1038                          gss_cred_id_t cred GSSEAP_UNUSED,
1039                          gss_ctx_id_t ctx,
1040                          gss_name_t target GSSEAP_UNUSED,
1041                          gss_OID mech GSSEAP_UNUSED,
1042                          OM_uint32 reqFlags GSSEAP_UNUSED,
1043                          OM_uint32 timeReq GSSEAP_UNUSED,
1044                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
1045                          gss_buffer_t inputToken GSSEAP_UNUSED,
1046                          gss_buffer_t outputToken,
1047                          OM_uint32 *smFlags)
1048 {
1049     OM_uint32 major;
1050
1051     major = gssEapMakeTokenMIC(minor, ctx, outputToken);
1052     if (GSS_ERROR(major))
1053         return major;
1054
1055     GSSEAP_SM_TRANSITION_NEXT(ctx);
1056
1057     *minor = 0;
1058     *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
1059
1060     return GSS_S_CONTINUE_NEEDED;
1061 }
1062
1063 #ifdef GSSEAP_ENABLE_REAUTH
1064 static OM_uint32
1065 eapGssSmInitReauthCreds(OM_uint32 *minor,
1066                         gss_cred_id_t cred,
1067                         gss_ctx_id_t ctx,
1068                         gss_name_t target GSSEAP_UNUSED,
1069                         gss_OID mech GSSEAP_UNUSED,
1070                         OM_uint32 reqFlags GSSEAP_UNUSED,
1071                         OM_uint32 timeReq GSSEAP_UNUSED,
1072                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
1073                         gss_buffer_t inputToken,
1074                         gss_buffer_t outputToken GSSEAP_UNUSED,
1075                         OM_uint32 *smFlags GSSEAP_UNUSED)
1076 {
1077     OM_uint32 major;
1078
1079     if (ctx->gssFlags & GSS_C_MUTUAL_FLAG) {
1080         major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
1081         if (GSS_ERROR(major))
1082             return major;
1083     }
1084
1085     *minor = 0;
1086     return GSS_S_CONTINUE_NEEDED;
1087 }
1088 #endif /* GSSEAP_ENABLE_REAUTH */
1089
1090 static OM_uint32
1091 eapGssSmInitAcceptorMIC(OM_uint32 *minor,
1092                         gss_cred_id_t cred GSSEAP_UNUSED,
1093                         gss_ctx_id_t ctx,
1094                         gss_name_t target GSSEAP_UNUSED,
1095                         gss_OID mech GSSEAP_UNUSED,
1096                         OM_uint32 reqFlags GSSEAP_UNUSED,
1097                         OM_uint32 timeReq GSSEAP_UNUSED,
1098                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
1099                         gss_buffer_t inputToken,
1100                         gss_buffer_t outputToken GSSEAP_UNUSED,
1101                         OM_uint32 *smFlags GSSEAP_UNUSED)
1102 {
1103     OM_uint32 major;
1104
1105     major = gssEapVerifyTokenMIC(minor, ctx, inputToken);
1106     if (GSS_ERROR(major))
1107         return major;
1108
1109     GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
1110
1111     *minor = 0;
1112
1113     return GSS_S_COMPLETE;
1114 }
1115
1116 static struct gss_eap_sm eapGssInitiatorSm[] = {
1117     {
1118         ITOK_TYPE_CONTEXT_ERR,
1119         ITOK_TYPE_NONE,
1120         GSSEAP_STATE_ALL & ~(GSSEAP_STATE_INITIAL),
1121         0,
1122         eapGssSmInitError
1123     },
1124     {
1125         ITOK_TYPE_ACCEPTOR_NAME_RESP,
1126         ITOK_TYPE_ACCEPTOR_NAME_REQ,
1127         GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE |
1128         GSSEAP_STATE_ACCEPTOR_EXTS,
1129         0,
1130         eapGssSmInitAcceptorName
1131     },
1132 #ifdef GSSEAP_DEBUG
1133     {
1134         ITOK_TYPE_NONE,
1135         ITOK_TYPE_VENDOR_INFO,
1136         GSSEAP_STATE_INITIAL,
1137         0,
1138         eapGssSmInitVendorInfo
1139     },
1140 #endif
1141 #ifdef GSSEAP_ENABLE_REAUTH
1142     {
1143         ITOK_TYPE_REAUTH_RESP,
1144         ITOK_TYPE_REAUTH_REQ,
1145         GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE,
1146         0,
1147         eapGssSmInitGssReauth
1148     },
1149 #endif
1150     {
1151         ITOK_TYPE_NONE,
1152         ITOK_TYPE_NONE,
1153 #ifdef GSSEAP_ENABLE_REAUTH
1154         GSSEAP_STATE_REAUTHENTICATE |
1155 #endif
1156         GSSEAP_STATE_INITIAL,
1157         SM_ITOK_FLAG_REQUIRED,
1158         eapGssSmInitIdentity
1159     },
1160     {
1161         ITOK_TYPE_EAP_REQ,
1162         ITOK_TYPE_EAP_RESP,
1163         GSSEAP_STATE_AUTHENTICATE,
1164         SM_ITOK_FLAG_REQUIRED,
1165         eapGssSmInitAuthenticate
1166     },
1167     {
1168         ITOK_TYPE_NONE,
1169         ITOK_TYPE_GSS_FLAGS,
1170         GSSEAP_STATE_INITIATOR_EXTS,
1171         0,
1172         eapGssSmInitGssFlags
1173     },
1174     {
1175         ITOK_TYPE_NONE,
1176         ITOK_TYPE_GSS_CHANNEL_BINDINGS,
1177         GSSEAP_STATE_INITIATOR_EXTS,
1178         0,
1179         eapGssSmInitGssChannelBindings
1180     },
1181     {
1182         ITOK_TYPE_NONE,
1183         ITOK_TYPE_INITIATOR_MIC,
1184         GSSEAP_STATE_INITIATOR_EXTS,
1185         SM_ITOK_FLAG_REQUIRED,
1186         eapGssSmInitInitiatorMIC
1187     },
1188 #ifdef GSSEAP_ENABLE_REAUTH
1189     {
1190         ITOK_TYPE_REAUTH_CREDS,
1191         ITOK_TYPE_NONE,
1192         GSSEAP_STATE_ACCEPTOR_EXTS,
1193         0,
1194         eapGssSmInitReauthCreds
1195     },
1196 #endif
1197     /* other extensions go here */
1198     {
1199         ITOK_TYPE_ACCEPTOR_MIC,
1200         ITOK_TYPE_NONE,
1201         GSSEAP_STATE_ACCEPTOR_EXTS,
1202         SM_ITOK_FLAG_REQUIRED,
1203         eapGssSmInitAcceptorMIC
1204     }
1205 };
1206
1207 OM_uint32
1208 gssEapInitSecContext(OM_uint32 *minor,
1209                      gss_cred_id_t cred,
1210                      gss_ctx_id_t ctx,
1211                      gss_name_t target_name,
1212                      gss_OID mech_type,
1213                      OM_uint32 req_flags,
1214                      OM_uint32 time_req,
1215                      gss_channel_bindings_t input_chan_bindings,
1216                      gss_buffer_t input_token,
1217                      gss_OID *actual_mech_type,
1218                      gss_buffer_t output_token,
1219                      OM_uint32 *ret_flags,
1220                      OM_uint32 *time_rec)
1221 {
1222     OM_uint32 major, tmpMinor;
1223     int initialContextToken = (ctx->mechanismUsed == GSS_C_NO_OID);
1224
1225     /*
1226      * XXX is acquiring the credential lock here necessary? The password is
1227      * mutable but the contract could specify that this is not updated whilst
1228      * a context is being initialized.
1229      */
1230     if (cred != GSS_C_NO_CREDENTIAL)
1231         GSSEAP_MUTEX_LOCK(&cred->mutex);
1232
1233     if (ctx->cred == GSS_C_NO_CREDENTIAL) {
1234         major = gssEapResolveInitiatorCred(minor, cred, target_name, &ctx->cred);
1235         if (GSS_ERROR(major))
1236             goto cleanup;
1237
1238         GSSEAP_ASSERT(ctx->cred != GSS_C_NO_CREDENTIAL);
1239     }
1240
1241     GSSEAP_MUTEX_LOCK(&ctx->cred->mutex);
1242
1243     GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_RESOLVED);
1244     GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_INITIATE);
1245
1246     if (initialContextToken) {
1247         major = initBegin(minor, ctx, target_name, mech_type,
1248                           req_flags, time_req, input_chan_bindings);
1249         if (GSS_ERROR(major))
1250             goto cleanup;
1251     }
1252
1253     major = gssEapSmStep(minor,
1254                          cred,
1255                          ctx,
1256                          target_name,
1257                          mech_type,
1258                          req_flags,
1259                          time_req,
1260                          input_chan_bindings,
1261                          input_token,
1262                          output_token,
1263                          eapGssInitiatorSm,
1264                          sizeof(eapGssInitiatorSm) / sizeof(eapGssInitiatorSm[0]));
1265     if (GSS_ERROR(major))
1266         goto cleanup;
1267
1268     if (actual_mech_type != NULL) {
1269         OM_uint32 tmpMajor;
1270
1271         tmpMajor = gssEapCanonicalizeOid(&tmpMinor, ctx->mechanismUsed, 0, actual_mech_type);
1272         if (GSS_ERROR(tmpMajor)) {
1273             major = tmpMajor;
1274             *minor = tmpMinor;
1275             goto cleanup;
1276         }
1277     }
1278
1279     if (ret_flags != NULL)
1280         *ret_flags = ctx->gssFlags;
1281
1282     if (time_rec != NULL)
1283         gssEapContextTime(&tmpMinor, ctx, time_rec);
1284
1285     GSSEAP_ASSERT(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
1286
1287 cleanup:
1288     if (cred != GSS_C_NO_CREDENTIAL)
1289         GSSEAP_MUTEX_UNLOCK(&cred->mutex);
1290     if (ctx->cred != GSS_C_NO_CREDENTIAL)
1291         GSSEAP_MUTEX_UNLOCK(&ctx->cred->mutex);
1292
1293     return major;
1294 }
1295
1296 OM_uint32 GSSAPI_CALLCONV
1297 gss_init_sec_context(OM_uint32 *minor,
1298                      gss_cred_id_t cred,
1299                      gss_ctx_id_t *context_handle,
1300                      gss_name_t target_name,
1301                      gss_OID mech_type,
1302                      OM_uint32 req_flags,
1303                      OM_uint32 time_req,
1304                      gss_channel_bindings_t input_chan_bindings,
1305                      gss_buffer_t input_token,
1306                      gss_OID *actual_mech_type,
1307                      gss_buffer_t output_token,
1308                      OM_uint32 *ret_flags,
1309                      OM_uint32 *time_rec)
1310 {
1311     OM_uint32 major, tmpMinor;
1312     gss_ctx_id_t ctx = *context_handle;
1313
1314     *minor = 0;
1315
1316     output_token->length = 0;
1317     output_token->value = NULL;
1318
1319     if (ctx == GSS_C_NO_CONTEXT) {
1320         if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
1321             *minor = GSSEAP_WRONG_SIZE;
1322             return GSS_S_DEFECTIVE_TOKEN;
1323         }
1324
1325         major = gssEapAllocContext(minor, &ctx);
1326         if (GSS_ERROR(major))
1327             return major;
1328
1329         ctx->flags |= CTX_FLAG_INITIATOR;
1330
1331         *context_handle = ctx;
1332     }
1333
1334     GSSEAP_MUTEX_LOCK(&ctx->mutex);
1335
1336     major = gssEapInitSecContext(minor,
1337                                  cred,
1338                                  ctx,
1339                                  target_name,
1340                                  mech_type,
1341                                  req_flags,
1342                                  time_req,
1343                                  input_chan_bindings,
1344                                  input_token,
1345                                  actual_mech_type,
1346                                  output_token,
1347                                  ret_flags,
1348                                  time_rec);
1349
1350     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
1351
1352     if (GSS_ERROR(major))
1353         gssEapReleaseContext(&tmpMinor, context_handle);
1354
1355     return major;
1356 }