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