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