1/*
2 * Copyright (c) 2002-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * eapttls_plugin.c
26 * - EAP-TTLS client using SecureTransport API's
27 */
28
29/*
30 * Modification History
31 *
32 * October 1, 2002	Dieter Siegmund (dieter@apple)
33 * - created (from eaptls_plugin.c)
34 *
35 * September 7, 2004	Dieter Siegmund (dieter@apple)
36 * - use SecTrustEvaluate, and enable user interaction to decide whether to
37 *   proceed or not, instead of just generating an error
38 */
39
40#include <EAP8021X/EAPClientPlugin.h>
41#include <EAP8021X/EAPClientProperties.h>
42#include <SystemConfiguration/SCValidation.h>
43#include <mach/boolean.h>
44#include <unistd.h>
45#include <stdlib.h>
46#include <string.h>
47#include <stdio.h>
48#include <Security/SecureTransport.h>
49#include <Security/SecCertificate.h>
50#include <sys/param.h>
51#include <EAP8021X/EAPTLSUtil.h>
52#include <EAP8021X/EAPUtil.h>
53#include <EAP8021X/EAPSecurity.h>
54#include <EAP8021X/EAPCertificateUtil.h>
55#include <Security/SecureTransportPriv.h>
56#include <EAP8021X/EAP.h>
57#include <EAP8021X/EAPClientModule.h>
58#include <EAP8021X/chap.h>
59#include <EAP8021X/mschap.h>
60#include <EAP8021X/RADIUSAttributes.h>
61#include <EAP8021X/DiameterAVP.h>
62#include "myCFUtil.h"
63#include "printdata.h"
64#include "EAPLog.h"
65
66/*
67 * Declare these here to ensure that the compiler
68 * generates appropriate errors/warnings
69 */
70EAPClientPluginFuncIntrospect eapttls_introspect;
71static EAPClientPluginFuncVersion eapttls_version;
72static EAPClientPluginFuncEAPType eapttls_type;
73static EAPClientPluginFuncEAPName eapttls_name;
74static EAPClientPluginFuncInit eapttls_init;
75static EAPClientPluginFuncFree eapttls_free;
76static EAPClientPluginFuncProcess eapttls_process;
77static EAPClientPluginFuncFreePacket eapttls_free_packet;
78static EAPClientPluginFuncSessionKey eapttls_session_key;
79static EAPClientPluginFuncServerKey eapttls_server_key;
80static EAPClientPluginFuncMasterSessionKeyCopyBytes eapttls_msk_copy_bytes;
81static EAPClientPluginFuncRequireProperties eapttls_require_props;
82static EAPClientPluginFuncPublishProperties eapttls_publish_props;
83static EAPClientPluginFuncCopyPacketDescription eapttls_copy_packet_description;
84
85#define kEAPTTLSClientLabel		"ttls keying material"
86#define kEAPTTLSClientLabelLength	 (sizeof(kEAPTTLSClientLabel) - 1)
87#define kEAPTTLSChallengeLabel		"ttls challenge"
88#define kEAPTTLSChallengeLabelLength	 (sizeof(kEAPTTLSChallengeLabel) - 1)
89
90typedef enum {
91    kInnerAuthTypeNone = 0,
92    kInnerAuthTypePAP,
93    kInnerAuthTypeCHAP,
94    kInnerAuthTypeMSCHAP,
95    kInnerAuthTypeMSCHAPv2,
96    kInnerAuthTypeEAP,
97} InnerAuthType;
98
99static const char * auth_strings[] = {
100    "none",
101    "PAP",
102    "CHAP",
103    "MSCHAP",
104    "MSCHAPv2",
105    "EAP",
106    NULL,
107};
108
109typedef enum {
110    kRequestTypeStart,
111    kRequestTypeAck,
112    kRequestTypeData,
113} RequestType;
114
115typedef enum {
116    kAuthStateIdle,
117    kAuthStateStarted,
118    kAuthStateComplete,
119} AuthState;
120#define TTLS_MSCHAP_RESPONSE_LENGTH	(MSCHAP_NT_RESPONSE_SIZE	\
121					 + MSCHAP_LM_RESPONSE_SIZE	\
122					 + MSCHAP_FLAGS_SIZE		\
123					 + MSCHAP_IDENT_SIZE)
124
125#define TTLS_MSCHAP2_RESPONSE_LENGTH	(MSCHAP2_RESPONSE_LENGTH	\
126					 + MSCHAP_IDENT_SIZE)
127typedef struct {
128    SSLContextRef		ssl_context;
129    memoryBuffer		read_buffer;
130    memoryBuffer		write_buffer;
131    int				last_write_size;
132    int				previous_identifier;
133    memoryIO			mem_io;
134    EAPClientState		plugin_state;
135    bool			cert_is_required;
136    CFArrayRef			certs;
137    int				mtu;
138    OSStatus			last_ssl_error;
139    EAPClientStatus		last_client_status;
140    InnerAuthType		inner_auth_type;
141    bool			handshake_complete;
142    bool			authentication_started;
143    OSStatus			trust_ssl_error;
144    EAPClientStatus		trust_status;
145    bool			trust_proceed;
146    bool			key_data_valid;
147    char			key_data[128];
148    bool			server_auth_completed;
149    CFArrayRef			server_certs;
150    bool			resume_sessions;
151    bool			session_was_resumed;
152
153    /* MSCHAPv2 state: */
154    uint8_t			peer_challenge[MSCHAP2_CHALLENGE_SIZE];
155    uint8_t			nt_response[MSCHAP_NT_RESPONSE_SIZE];
156    uint8_t			auth_challenge_id[MSCHAP2_CHALLENGE_SIZE + 1];
157} EAPTTLSPluginData, * EAPTTLSPluginDataRef;
158
159enum {
160    kEAPTLSAvoidDenialOfServiceSize = 128 * 1024
161};
162
163#define BAD_IDENTIFIER			(-1)
164
165static InnerAuthType
166InnerAuthTypeFromString(char * str)
167{
168    int i;
169
170    for (i = 0; auth_strings[i] != NULL; i++) {
171	if (strcmp(str, auth_strings[i]) == 0) {
172	    return ((InnerAuthType)i);
173	}
174    }
175    return (kInnerAuthTypeNone);
176}
177
178static bool
179eapttls_compute_session_key(EAPTTLSPluginDataRef context)
180{
181    OSStatus		status;
182
183    context->key_data_valid = FALSE;
184    status = EAPTLSComputeKeyData(context->ssl_context,
185				  kEAPTTLSClientLabel,
186				  kEAPTTLSClientLabelLength,
187				  context->key_data,
188				  sizeof(context->key_data));
189    if (status != noErr) {
190	EAPLOG_FL(LOG_NOTICE,
191		  "EAPTLSComputeSessionKey failed, %s",
192		  EAPSSLErrorString(status));
193	return (FALSE);
194    }
195    context->key_data_valid = TRUE;
196    return (TRUE);
197}
198
199static void
200eapttls_free_context(EAPTTLSPluginDataRef context)
201{
202    if (context->ssl_context != NULL) {
203	CFRelease(context->ssl_context);
204	context->ssl_context = NULL;
205    }
206    my_CFRelease(&context->certs);
207    my_CFRelease(&context->server_certs);
208    memoryIOClearBuffers(&context->mem_io);
209    free(context);
210    return;
211}
212
213static OSStatus
214eapttls_start(EAPClientPluginDataRef plugin)
215{
216    EAPTTLSPluginDataRef 	context = (EAPTTLSPluginDataRef)plugin->private;
217    SSLContextRef		ssl_context = NULL;
218    OSStatus			status = noErr;
219
220    if (context->ssl_context != NULL) {
221	CFRelease(context->ssl_context);
222	context->ssl_context = NULL;
223    }
224    my_CFRelease(&context->server_certs);
225    memoryIOClearBuffers(&context->mem_io);
226    ssl_context = EAPTLSMemIOContextCreate(FALSE, &context->mem_io, NULL,
227					   &status);
228    if (ssl_context == NULL) {
229	EAPLOG_FL(LOG_NOTICE, "EAPTLSMemIOContextCreate failed, %s",
230		  EAPSSLErrorString(status));
231	goto failed;
232    }
233    if (context->resume_sessions && plugin->unique_id != NULL) {
234	status = SSLSetPeerID(ssl_context, plugin->unique_id,
235			      plugin->unique_id_length);
236	if (status != noErr) {
237	    EAPLOG_FL(LOG_NOTICE,
238		      "SSLSetPeerID failed, %s", EAPSSLErrorString(status));
239	    goto failed;
240	}
241    }
242    if (context->cert_is_required) {
243	if (context->certs == NULL) {
244	    status = EAPTLSCopyIdentityTrustChain(plugin->sec_identity,
245						  plugin->properties,
246						  &context->certs);
247	    if (status != noErr) {
248		EAPLOG_FL(LOG_NOTICE,
249			  "failed to find client cert/identity, %s (%ld)",
250			  EAPSSLErrorString(status), (long)status);
251		goto failed;
252	    }
253	}
254	status = SSLSetCertificate(ssl_context, context->certs);
255	if (status != noErr) {
256	    EAPLOG_FL(LOG_NOTICE,
257		      "SSLSetCertificate failed, %s",
258		      EAPSSLErrorString(status));
259	    goto failed;
260	}
261    }
262    context->ssl_context = ssl_context;
263    context->plugin_state = kEAPClientStateAuthenticating;
264    context->previous_identifier = BAD_IDENTIFIER;
265    context->last_ssl_error = noErr;
266    context->last_client_status = kEAPClientStatusOK;
267    context->handshake_complete = FALSE;
268    context->authentication_started = FALSE;
269    context->trust_proceed = FALSE;
270    context->server_auth_completed = FALSE;
271    context->key_data_valid = FALSE;
272    context->last_write_size = 0;
273    context->session_was_resumed = FALSE;
274    return (status);
275 failed:
276    if (ssl_context != NULL) {
277	CFRelease(ssl_context);
278    }
279    return (status);
280}
281
282static InnerAuthType
283get_inner_auth_type(CFDictionaryRef properties)
284{
285    InnerAuthType	inner_auth_type = kInnerAuthTypeNone;
286    CFStringRef		inner_auth_cf;
287
288    if (properties != NULL) {
289	inner_auth_cf
290	    = CFDictionaryGetValue(properties,
291				   kEAPClientPropTTLSInnerAuthentication);
292	if (isA_CFString(inner_auth_cf) != NULL) {
293	    char *		inner_auth = NULL;
294
295	    inner_auth = my_CFStringToCString(inner_auth_cf,
296					      kCFStringEncodingASCII);
297	    if (inner_auth != NULL) {
298		inner_auth_type = InnerAuthTypeFromString(inner_auth);
299		free(inner_auth);
300	    }
301	}
302    }
303    return (inner_auth_type);
304}
305
306static EAPClientStatus
307eapttls_init(EAPClientPluginDataRef plugin, CFArrayRef * required_props,
308	     EAPClientDomainSpecificError * error)
309{
310    EAPTTLSPluginDataRef	context = NULL;
311    InnerAuthType		inner_auth_type;
312
313    context = malloc(sizeof(*context));
314    bzero(context, sizeof(*context));
315    context->cert_is_required
316	= my_CFDictionaryGetBooleanValue(plugin->properties,
317					 kEAPClientPropTLSCertificateIsRequired,
318					 FALSE);
319    context->mtu = plugin->mtu;
320    inner_auth_type = get_inner_auth_type(plugin->properties);
321    if (inner_auth_type == kInnerAuthTypeNone) {
322	inner_auth_type = kInnerAuthTypeMSCHAPv2;
323    }
324    context->inner_auth_type = inner_auth_type;
325    context->resume_sessions
326	= my_CFDictionaryGetBooleanValue(plugin->properties,
327					 kEAPClientPropTLSEnableSessionResumption,
328					 TRUE);
329    /* memoryIOInit() initializes the memoryBuffer structures as well */
330    memoryIOInit(&context->mem_io, &context->read_buffer,
331		 &context->write_buffer);
332    //memoryIOSetDebug(&context->mem_io, TRUE);
333    plugin->private = context;
334
335    *error = 0;
336    return (kEAPClientStatusOK);
337}
338
339static void
340eapttls_free(EAPClientPluginDataRef plugin)
341{
342    EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private;
343
344    if (context != NULL) {
345	eapttls_free_context(context);
346	plugin->private = NULL;
347    }
348    return;
349}
350
351static void
352eapttls_free_packet(EAPClientPluginDataRef plugin, EAPPacketRef arg)
353{
354    if (arg != NULL) {
355	free(arg);
356    }
357    return;
358}
359
360static EAPPacketRef
361EAPTTLSPacketCreateAck(int identifier)
362{
363    return (EAPTLSPacketCreate(kEAPCodeResponse, kEAPTypeTTLS,
364			       identifier, 0, NULL, NULL));
365}
366
367static bool
368eapttls_pap(EAPClientPluginDataRef plugin)
369{
370    DiameterAVP *	avp;
371    EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private;
372    void *		data;
373    int			data_length;
374    void *		offset;
375    size_t		length;
376    int			password_length_r;
377    bool		ret = TRUE;
378    OSStatus		status;
379    int			user_length_r;
380
381    /* allocate buffer to hold message */
382    password_length_r = roundup(plugin->password_length, 16);
383    user_length_r = roundup(plugin->username_length, 4);
384    data_length = sizeof(*avp) * 2 + user_length_r + password_length_r;
385    data = malloc(data_length);
386    if (data == NULL) {
387	EAPLOG_FL(LOG_NOTICE, "malloc failed");
388	return (FALSE);
389    }
390    offset = data;
391
392    /* User-Name AVP */
393    avp = (DiameterAVP *)offset;
394    avp->AVP_code = htonl(kRADIUSAttributeTypeUserName);
395    avp->AVP_flags_length
396	= htonl(DiameterAVPMakeFlagsLength(0, sizeof(*avp)
397					   + plugin->username_length));
398    offset = (void *)(avp + 1);
399    bcopy(plugin->username, offset, plugin->username_length);
400    if (user_length_r > plugin->username_length) {
401	bzero(offset + plugin->username_length,
402	      user_length_r - plugin->username_length);
403    }
404    offset += user_length_r;
405
406    /* Password-Name AVP */
407    avp = (DiameterAVP *)offset;
408    avp->AVP_code = htonl(kRADIUSAttributeTypeUserPassword);
409    avp->AVP_flags_length
410	= htonl(DiameterAVPMakeFlagsLength(0,
411					   sizeof(*avp) + password_length_r));
412    offset = (void *)(avp + 1);
413    bcopy(plugin->password, offset, plugin->password_length);
414    if (password_length_r > plugin->password_length) {
415	bzero(offset + plugin->password_length,
416	      password_length_r - plugin->password_length);
417    }
418    offset += password_length_r;
419#if 0
420    printf("\n----------PAP Raw AVP Data START\n");
421    print_data(data, offset - data);
422    printf("----------PAP Raw AVP Data END\n");
423#endif /* 0 */
424    status = SSLWrite(context->ssl_context, data, offset - data, &length);
425    if (status != noErr) {
426	EAPLOG_FL(LOG_NOTICE, "SSLWrite failed, %s",
427		  EAPSSLErrorString(status));
428	ret = FALSE;
429    }
430    free(data);
431    return (ret);
432}
433
434static bool
435eapttls_eap_start(EAPClientPluginDataRef plugin, int identifier)
436{
437    DiameterAVP *	avp;
438    EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private;
439    void *		data;
440    int			data_length;
441    void *		offset;
442    size_t		length;
443    EAPResponsePacket *	resp_p = NULL;
444    bool		ret = TRUE;
445    OSStatus		status;
446
447    /* allocate buffer to hold message */
448    data_length = sizeof(*avp) + plugin->username_length + sizeof(*resp_p);
449    data = malloc(data_length);
450    if (data == NULL) {
451	EAPLOG_FL(LOG_NOTICE, "malloc failed");
452	return (FALSE);
453    }
454    offset = data;
455
456    /* EAP AVP */
457    avp = (DiameterAVP *)offset;
458    avp->AVP_code = htonl(kRADIUSAttributeTypeEAPMessage);
459    avp->AVP_flags_length = htonl(DiameterAVPMakeFlagsLength(0, data_length));
460    offset = (void *)(avp + 1);
461    resp_p = (EAPResponsePacket *)offset;
462    resp_p->code = kEAPCodeResponse;
463    resp_p->identifier = 0; /* identifier */
464    EAPPacketSetLength((EAPPacketRef)resp_p,
465		       sizeof(*resp_p) + plugin->username_length);
466
467    resp_p->type = kEAPTypeIdentity;
468    bcopy(plugin->username, resp_p->type_data, plugin->username_length);
469    offset += sizeof(*resp_p) + plugin->username_length;
470#if 0
471    printf("offset - data %d length %d\n", offset - data, data_length);
472#endif /* 0 */
473    status = SSLWrite(context->ssl_context, data, offset - data, &length);
474    if (status != noErr) {
475	EAPLOG_FL(LOG_NOTICE, "SSLWrite failed, %s",
476		  EAPSSLErrorString(status));
477	ret = FALSE;
478    }
479    free(data);
480    return (ret);
481}
482
483/*
484 * Function: eapttls_chap
485 * Purpose:
486 *   Generate a packet containing the response to an implicit
487 *   CHAP challenge.
488 */
489
490static bool
491eapttls_chap(EAPClientPluginDataRef plugin)
492{
493    DiameterAVP *	avp;
494    EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private;
495    void *		data;
496    int			data_length;
497    int			data_length_r;
498    uint8_t		key_data[17];
499    size_t		length;
500    void *		offset;
501    bool		ret = TRUE;
502    OSStatus		status;
503    int			user_length_r;
504
505    user_length_r = roundup(plugin->username_length, 4);
506    status = EAPTLSComputeKeyData(context->ssl_context,
507				  kEAPTTLSChallengeLabel,
508				  kEAPTTLSChallengeLabelLength,
509				  key_data, sizeof(key_data));
510    if (status != noErr) {
511	EAPLOG_FL(LOG_NOTICE, "EAPTLSComputeKeyData failed, %s",
512		  EAPSSLErrorString(status));
513	return (FALSE);
514    }
515
516    /* allocate buffer to hold message */
517    data_length = sizeof(*avp) * 3
518	+ user_length_r
519	+ 16  /* challenge */
520	+ 1 + 16; /* identifier + response */
521    data_length_r = roundup(data_length, 4);
522    data = malloc(data_length_r);
523    if (data == NULL) {
524	EAPLOG_FL(LOG_NOTICE, "malloc failed");
525	return (FALSE);
526    }
527    offset = data;
528
529    /* User-Name AVP */
530    avp = (DiameterAVP *)offset;
531    avp->AVP_code = htonl(kRADIUSAttributeTypeUserName);
532    avp->AVP_flags_length
533	= htonl(DiameterAVPMakeFlagsLength(0, sizeof(*avp)
534					   + plugin->username_length));
535    offset = (void *)(avp + 1);
536    bcopy(plugin->username, offset, plugin->username_length);
537    if (user_length_r > plugin->username_length) {
538	bzero(offset + plugin->username_length,
539	      user_length_r - plugin->username_length);
540    }
541    offset += user_length_r;
542
543    /* CHAP-Challenge AVP */
544    avp = (DiameterAVP *)offset;
545    avp->AVP_code = htonl(kRADIUSAttributeTypeCHAPChallenge);
546    avp->AVP_flags_length
547	= htonl(DiameterAVPMakeFlagsLength(0, sizeof(*avp) + 16));
548    offset = (void *)(avp + 1);
549    bcopy(key_data, offset, 16);
550    offset += 16;
551
552    /* CHAP-Password AVP */
553    avp = (DiameterAVP *)offset;
554    avp->AVP_code = htonl(kRADIUSAttributeTypeCHAPPassword);
555    avp->AVP_flags_length
556	= htonl(DiameterAVPMakeFlagsLength(0, sizeof(*avp) + 17));
557    offset = (void *)(avp + 1);
558    *((u_char *)offset) = key_data[16]; /* identifier */
559    offset++;
560    chap_md5(key_data[16], plugin->password, plugin->password_length,
561	     key_data, 16, offset);
562    offset += 16;
563    { /* pad out with zeroes */
564	int 	pad_length = data_length_r - data_length;
565	if (pad_length != 0) {
566	    bzero(offset, pad_length);
567	    offset += pad_length;
568	}
569    }
570#if 0
571    printf("\n----------CHAP Raw AVP Data START\n");
572    print_data(data, offset - data);
573    printf("----------CHAP Raw AVP Data END\n");
574#endif /* 0 */
575    status = SSLWrite(context->ssl_context, data, offset - data, &length);
576    if (status != noErr) {
577	EAPLOG_FL(LOG_NOTICE, "SSLWrite failed, %s",
578		  EAPSSLErrorString(status));
579	ret = FALSE;
580    }
581    free(data);
582    return (ret);
583}
584
585/*
586 * Function: eapttls_mschap
587 * Purpose:
588 *   Generate a packet containing the response to an implicit
589 *   MS-CHAP challenge.
590 */
591
592static bool
593eapttls_mschap(EAPClientPluginDataRef plugin)
594{
595    DiameterAVP *	avp;
596    DiameterVendorAVP *	avpv;
597    EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private;
598    void *		data;
599    int			data_length;
600    int			data_length_r;
601    uint8_t		key_data[MSCHAP_NT_CHALLENGE_SIZE + MSCHAP_IDENT_SIZE];
602    size_t		length;
603    void *		offset;
604    bool		ret = TRUE;
605    uint8_t		response[MSCHAP_NT_RESPONSE_SIZE];
606    OSStatus		status;
607    int			user_length_r;
608
609    user_length_r = roundup(plugin->username_length, 4);
610    status = EAPTLSComputeKeyData(context->ssl_context,
611				  kEAPTTLSChallengeLabel,
612				  kEAPTTLSChallengeLabelLength,
613				  key_data, sizeof(key_data));
614    if (status != noErr) {
615	EAPLOG_FL(LOG_NOTICE, "EAPTLSComputeKeyData failed, %s",
616		  EAPSSLErrorString(status));
617	return (FALSE);
618    }
619
620    /* allocate buffer to hold message */
621    data_length = sizeof(*avp) + sizeof(*avpv) * 2
622	+ user_length_r
623	+ MSCHAP_NT_CHALLENGE_SIZE  /* challenge */
624	+ TTLS_MSCHAP_RESPONSE_LENGTH;/* flags + identifer + {NT,LM}Response */
625    data_length_r = roundup(data_length, 4);
626    data = malloc(data_length_r);
627    if (data == NULL) {
628	EAPLOG_FL(LOG_NOTICE, "malloc failed");
629	return (FALSE);
630    }
631    offset = data;
632
633    /* User-Name AVP */
634    avp = (DiameterAVP *)offset;
635    avp->AVP_code = htonl(kRADIUSAttributeTypeUserName);
636    avp->AVP_flags_length
637	= htonl(DiameterAVPMakeFlagsLength(0, sizeof(*avp)
638					   + plugin->username_length));
639    offset = (void *)(avp + 1);
640    bcopy(plugin->username, offset, plugin->username_length);
641    if (user_length_r > plugin->username_length) {
642	bzero(offset + plugin->username_length,
643	      user_length_r - plugin->username_length);
644    }
645    offset += user_length_r;
646
647    /* MS-CHAP-Challenge AVP */
648    avpv = (DiameterVendorAVP *)offset;
649    avpv->AVPV_code = htonl(kMSRADIUSAttributeTypeMSCHAPChallenge);
650    avpv->AVPV_flags_length
651	= htonl(DiameterAVPMakeFlagsLength(kDiameterFlagsVendorSpecific,
652					   sizeof(*avpv)
653					   + MSCHAP_NT_CHALLENGE_SIZE));
654    avpv->AVPV_vendor = htonl(kRADIUSVendorIdentifierMicrosoft);
655    offset = (void *)(avpv + 1);
656    bcopy(key_data, offset, MSCHAP_NT_CHALLENGE_SIZE);
657    offset += MSCHAP_NT_CHALLENGE_SIZE;
658
659    /* MS-CHAP-Response AVP */
660    avpv = (DiameterVendorAVP *)offset;
661    avpv->AVPV_code = htonl(kMSRADIUSAttributeTypeMSCHAPResponse);
662    avpv->AVPV_flags_length
663	= htonl(DiameterAVPMakeFlagsLength(kDiameterFlagsVendorSpecific,
664					   sizeof(*avpv)
665					   + TTLS_MSCHAP_RESPONSE_LENGTH));
666    avpv->AVPV_vendor = htonl(kRADIUSVendorIdentifierMicrosoft);
667    offset = (void *)(avpv + 1);
668    *((u_char *)offset) = key_data[MSCHAP_NT_CHALLENGE_SIZE];	/* ident */
669    offset++;
670    *((u_char *)offset) = 1;		   /* flags: 1 = use NT-Response */
671    offset++;
672    bzero(offset, MSCHAP_LM_RESPONSE_SIZE);/* LM-Response: not used */
673    offset += MSCHAP_LM_RESPONSE_SIZE;
674    MSChap(key_data, plugin->password,
675	   plugin->password_length, response);		/* NT-Response */
676    bcopy(response, offset, MSCHAP_NT_RESPONSE_SIZE);
677    offset += MSCHAP_NT_RESPONSE_SIZE;
678    { /* pad out with zeroes */
679	int 	pad_length = data_length_r - data_length;
680	if (pad_length != 0) {
681	    bzero(offset, pad_length);
682	    offset += pad_length;
683	}
684    }
685#if 0
686    printf("offset - data %d length %d\n", offset - data, data_length);
687    printf("\n----------MSCHAP Raw AVP Data START\n");
688    print_data(data, offset - data);
689    printf("----------MSCHAP Raw AVP Data END\n");
690#endif /* 0 */
691    status = SSLWrite(context->ssl_context, data, offset - data, &length);
692    if (status != noErr) {
693	EAPLOG_FL(LOG_NOTICE, "SSLWrite failed, %s",
694		  EAPSSLErrorString(status));
695	ret = FALSE;
696    }
697    free(data);
698    return (ret);
699}
700
701/*
702 * Function: eapttls_mschapv2
703 * Purpose:
704 *   Generate a packet containing the response to an implicit
705 *   MS-CHAPv2 challenge.
706 */
707
708static bool
709eapttls_mschap2(EAPClientPluginDataRef plugin)
710{
711    DiameterAVP *	avp;
712    DiameterVendorAVP *	avpv;
713    EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private;
714    void *		data;
715    int			data_length;
716    int			data_length_r;
717    size_t		length;
718    void *		offset;
719    bool		ret = TRUE;
720    OSStatus		status;
721    int			user_length_r;
722
723    user_length_r = roundup(plugin->username_length, 4);
724    status = EAPTLSComputeKeyData(context->ssl_context,
725				  kEAPTTLSChallengeLabel,
726				  kEAPTTLSChallengeLabelLength,
727				  context->auth_challenge_id,
728				  MSCHAP2_CHALLENGE_SIZE + 1);
729    if (status != noErr) {
730	EAPLOG_FL(LOG_NOTICE, "EAPTLSComputeKeyData failed, %s",
731		  EAPSSLErrorString(status));
732	return (FALSE);
733    }
734
735    /* allocate buffer to hold message */
736    data_length = sizeof(*avp) + sizeof(*avpv) * 2
737	+ user_length_r
738	+ MSCHAP2_CHALLENGE_SIZE
739	+ TTLS_MSCHAP2_RESPONSE_LENGTH;
740    data_length_r = roundup(data_length, 4);
741    data = malloc(data_length_r);
742    if (data == NULL) {
743	EAPLOG_FL(LOG_NOTICE, "malloc failed");
744	return (FALSE);
745    }
746    offset = data;
747
748    /* User-Name AVP */
749    avp = (DiameterAVP *)offset;
750    avp->AVP_code = htonl(kRADIUSAttributeTypeUserName);
751    avp->AVP_flags_length
752	= htonl(DiameterAVPMakeFlagsLength(0, sizeof(*avp)
753					   + plugin->username_length));
754    offset = (void *)(avp + 1);
755    bcopy(plugin->username, offset, plugin->username_length);
756    if (user_length_r > plugin->username_length) {
757	bzero(offset + plugin->username_length,
758	      user_length_r - plugin->username_length);
759    }
760    offset += user_length_r;
761
762    /* MS-CHAP-Challenge AVP */
763    avpv = (DiameterVendorAVP *)offset;
764    avpv->AVPV_code = htonl(kMSRADIUSAttributeTypeMSCHAPChallenge);
765    avpv->AVPV_flags_length
766	= htonl(DiameterAVPMakeFlagsLength(kDiameterFlagsVendorSpecific,
767					   sizeof(*avpv)
768					   + MSCHAP2_CHALLENGE_SIZE));
769    avpv->AVPV_vendor = htonl(kRADIUSVendorIdentifierMicrosoft);
770    offset = (void *)(avpv + 1);
771    bcopy(context->auth_challenge_id, offset, MSCHAP2_CHALLENGE_SIZE);
772    offset += MSCHAP2_CHALLENGE_SIZE;
773
774    /* MS-CHAP2-Response AVP */
775    avpv = (DiameterVendorAVP *)offset;
776    avpv->AVPV_code = htonl(kMSRADIUSAttributeTypeMSCHAP2Response);
777    avpv->AVPV_flags_length
778	= htonl(DiameterAVPMakeFlagsLength(kDiameterFlagsVendorSpecific,
779					   sizeof(*avpv)
780					   + TTLS_MSCHAP2_RESPONSE_LENGTH));
781    avpv->AVPV_vendor = htonl(kRADIUSVendorIdentifierMicrosoft);
782    offset = (void *)(avpv + 1);
783    *((u_char *)offset) 		/* identifier */
784	= context->auth_challenge_id[MSCHAP2_CHALLENGE_SIZE];
785    offset++;
786    *((u_char *)offset) = 0;		/* flags: must be 0 */
787    offset++;
788    MSChapFillWithRandom(context->peer_challenge,
789			 sizeof(context->peer_challenge));
790    bcopy(context->peer_challenge, offset,
791	  MSCHAP2_CHALLENGE_SIZE);	/* peer challenge */
792    offset += sizeof(context->peer_challenge);
793    bzero(offset, MSCHAP2_RESERVED_SIZE);
794    offset += MSCHAP2_RESERVED_SIZE;
795    MSChap2(context->auth_challenge_id, context->peer_challenge,
796	    plugin->username, plugin->password, plugin->password_length,
797	    context->nt_response);
798    bcopy(context->nt_response, offset, 	/* response */
799	  MSCHAP_NT_RESPONSE_SIZE);
800    offset += MSCHAP_NT_RESPONSE_SIZE;
801    { /* pad out with zeroes */
802	int 	pad_length = data_length_r - data_length;
803	if (pad_length != 0) {
804	    bzero(offset, pad_length);
805	    offset += pad_length;
806	}
807    }
808#if 0
809    printf("offset - data %d length %d\n", offset - data, data_length);
810    printf("\n----------MSCHAP2 Raw AVP Data START\n");
811    print_data(data, offset - data);
812    printf("----------MSCHAP2 Raw AVP Data END\n");
813#endif /* 0 */
814
815    status = SSLWrite(context->ssl_context, data, offset - data, &length);
816    if (status != noErr) {
817	EAPLOG_FL(LOG_NOTICE, "SSLWrite failed, %s",
818		  EAPSSLErrorString(status));
819	ret = FALSE;
820    }
821    free(data);
822    return (ret);
823}
824
825static EAPPacketRef
826eapttls_mschap2_verify(EAPClientPluginDataRef plugin, int identifier)
827{
828    DiameterVendorAVP	avpv;
829    EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private;
830    void *		data = NULL;
831    size_t		data_size = 0;
832    u_int32_t		flags_length;
833    size_t		length;
834    EAPPacketRef	pkt = NULL;
835    OSStatus		status;
836
837    status = SSLRead(context->ssl_context, &avpv, sizeof(avpv),
838		     &data_size);
839    if (status != noErr) {
840	EAPLOG_FL(LOG_NOTICE, "SSLRead failed, %s",
841		  EAPSSLErrorString(status));
842	context->plugin_state = kEAPClientStateFailure;
843	context->last_ssl_error = status;
844	goto done;
845    }
846    if (data_size != sizeof(avpv)) {
847	context->plugin_state = kEAPClientStateFailure;
848	goto done;
849    }
850    flags_length = ntohl(avpv.AVPV_flags_length);
851    if ((DiameterAVPFlagsFromFlagsLength(flags_length)
852	 & kDiameterFlagsVendorSpecific) == 0
853	|| ntohl(avpv.AVPV_code) != kMSRADIUSAttributeTypeMSCHAP2Success
854	|| ntohl(avpv.AVPV_vendor) != kRADIUSVendorIdentifierMicrosoft) {
855	context->plugin_state = kEAPClientStateFailure;
856	goto done;
857    }
858    length = DiameterAVPLengthFromFlagsLength(flags_length);
859    if (length > kEAPTLSAvoidDenialOfServiceSize) {
860	context->plugin_state = kEAPClientStateFailure;
861	goto done;
862    }
863    length -= sizeof(avpv);
864    data = malloc(length);
865    status = SSLRead(context->ssl_context, data, length, &length);
866    if (status != noErr) {
867	context->plugin_state = kEAPClientStateFailure;
868	goto done;
869    }
870    if (length < (MSCHAP2_AUTH_RESPONSE_SIZE + 1)
871	|| (*((uint8_t *)data)
872	    != context->auth_challenge_id[MSCHAP2_CHALLENGE_SIZE])) {
873	context->plugin_state = kEAPClientStateFailure;
874	goto done;
875    }
876    if (MSChap2AuthResponseValid(plugin->password, plugin->password_length,
877				 context->nt_response,
878				 context->peer_challenge,
879				 context->auth_challenge_id,
880				 plugin->username, data + 1) == FALSE) {
881	context->plugin_state = kEAPClientStateFailure;
882	goto done;
883    }
884    pkt = EAPTTLSPacketCreateAck(identifier);
885 done:
886    if (data != NULL) {
887	free(data);
888    }
889    return (pkt);
890}
891
892static EAPPacketRef
893eapttls_do_inner_auth(EAPClientPluginDataRef plugin,
894		      int identifier, EAPClientStatus * client_status)
895{
896    EAPTTLSPluginDataRef	context = (EAPTTLSPluginDataRef)plugin->private;
897    memoryBufferRef		write_buf = &context->write_buffer;
898
899    context->authentication_started = TRUE;
900    switch (context->inner_auth_type) {
901    case kInnerAuthTypePAP:
902	if (eapttls_pap(plugin) == FALSE) {
903	    /* do something */
904	}
905	break;
906    case kInnerAuthTypeCHAP:
907	if (eapttls_chap(plugin) == FALSE) {
908	    /* do something */
909	}
910	break;
911    case kInnerAuthTypeMSCHAP:
912	if (eapttls_mschap(plugin) == FALSE) {
913	    /* do something */
914	}
915	break;
916    case kInnerAuthTypeEAP:
917	if (eapttls_eap_start(plugin, identifier) == FALSE) {
918	    /* do something */
919	}
920	break;
921    default:
922    case kInnerAuthTypeMSCHAPv2:
923	if (eapttls_mschap2(plugin) == FALSE) {
924	    /* do something */
925	}
926	break;
927    }
928    return (EAPTLSPacketCreate(kEAPCodeResponse,
929			       kEAPTypeTTLS,
930			       identifier,
931			       context->mtu,
932			       write_buf,
933			       &context->last_write_size));
934}
935
936static EAPPacketRef
937eapttls_start_inner_auth(EAPClientPluginDataRef plugin,
938			 int identifier, EAPClientStatus * client_status)
939{
940    EAPTTLSPluginDataRef	context = (EAPTTLSPluginDataRef)plugin->private;
941    memoryBufferRef		write_buf = &context->write_buffer;
942
943    if (context->session_was_resumed == FALSE) {
944	return (eapttls_do_inner_auth(plugin, identifier, client_status));
945    }
946    return (EAPTLSPacketCreate(kEAPCodeResponse,
947			       kEAPTypeTTLS,
948			       identifier,
949			       context->mtu,
950			       write_buf,
951			       &context->last_write_size));
952}
953
954static EAPPacketRef
955eapttls_verify_server(EAPClientPluginDataRef plugin,
956		      int identifier, EAPClientStatus * client_status)
957{
958    EAPTTLSPluginDataRef 	context = (EAPTTLSPluginDataRef)plugin->private;
959    EAPPacketRef		pkt = NULL;
960    memoryBufferRef		write_buf = &context->write_buffer;
961
962    context->trust_status
963	= EAPTLSVerifyServerCertificateChain(plugin->properties,
964					     context->server_certs,
965					     &context->trust_ssl_error);
966    if (context->trust_status != kEAPClientStatusOK) {
967	EAPLOG_FL(LOG_NOTICE, "server certificate not trusted status %d %d",
968		  context->trust_status,
969		  (int)context->trust_ssl_error);
970    }
971    switch (context->trust_status) {
972    case kEAPClientStatusOK:
973	context->trust_proceed = TRUE;
974	break;
975    case kEAPClientStatusUserInputRequired:
976	/* ask user whether to proceed or not */
977	*client_status = context->last_client_status
978	    = kEAPClientStatusUserInputRequired;
979	break;
980    default:
981	*client_status = context->last_client_status = context->trust_status;
982	context->last_ssl_error = context->trust_ssl_error;
983	context->plugin_state = kEAPClientStateFailure;
984	SSLClose(context->ssl_context);
985	pkt = EAPTLSPacketCreate(kEAPCodeResponse,
986				 kEAPTypeTTLS,
987				 identifier,
988				 context->mtu,
989				 write_buf,
990				 &context->last_write_size);
991	break;
992    }
993    return (pkt);
994}
995
996static EAPPacketRef
997eapttls_tunnel(EAPClientPluginDataRef plugin,
998	       int identifier, EAPClientStatus * client_status)
999{
1000    EAPTTLSPluginDataRef 	context = (EAPTTLSPluginDataRef)plugin->private;
1001    EAPPacketRef		pkt = NULL;
1002    memoryBufferRef		read_buf = &context->read_buffer;
1003
1004    if (context->authentication_started == FALSE) {
1005	if (identifier == context->previous_identifier) {
1006	    /* we've already seen this packet, discard buffer contents */
1007	    memoryBufferClear(read_buf);
1008	}
1009	if (plugin->password == NULL) {
1010	    *client_status = kEAPClientStatusUserInputRequired;
1011	    return (NULL);
1012	}
1013	return (eapttls_start_inner_auth(plugin, identifier, client_status));
1014    }
1015    switch (context->inner_auth_type) {
1016    default:
1017	/* we didn't expect data, Ack it anyways */
1018	pkt = EAPTTLSPacketCreateAck(identifier);
1019	break;
1020    case kInnerAuthTypeMSCHAPv2:
1021	if (identifier == context->previous_identifier) {
1022	    /* we've already verified the MSCHAP2 response, just Ack it */
1023	    pkt = EAPTTLSPacketCreateAck(identifier);
1024	}
1025	else {
1026	    pkt = eapttls_mschap2_verify(plugin, identifier);
1027	}
1028	break;
1029    }
1030    return (pkt);
1031}
1032
1033static void
1034eapttls_set_session_was_resumed(EAPTTLSPluginDataRef context)
1035{
1036    char		buf[MAX_SESSION_ID_LENGTH];
1037    size_t		buf_len = sizeof(buf);
1038    Boolean		resumed = FALSE;
1039    OSStatus		status;
1040
1041    status = SSLGetResumableSessionInfo(context->ssl_context,
1042					&resumed, buf, &buf_len);
1043    if (status == noErr) {
1044	context->session_was_resumed = resumed;
1045    }
1046    return;
1047}
1048
1049static EAPPacketRef
1050eapttls_handshake(EAPClientPluginDataRef plugin,
1051		  int identifier, EAPClientStatus * client_status)
1052
1053{
1054    EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private;
1055    EAPPacketRef	eaptls_out = NULL;
1056    OSStatus		status = noErr;
1057    memoryBufferRef	write_buf = &context->write_buffer;
1058
1059    if (context->server_auth_completed && context->trust_proceed == FALSE) {
1060	eaptls_out = eapttls_verify_server(plugin, identifier, client_status);
1061	if (context->trust_proceed == FALSE) {
1062	    goto done;
1063	}
1064    }
1065    status = SSLHandshake(context->ssl_context);
1066    if (status == errSSLServerAuthCompleted) {
1067	if (context->server_auth_completed) {
1068	    /* this should not happen */
1069	    EAPLOG_FL(LOG_NOTICE, "AuthCompleted again?");
1070	    goto done;
1071	}
1072	context->server_auth_completed = TRUE;
1073	my_CFRelease(&context->server_certs);
1074	(void)EAPSSLCopyPeerCertificates(context->ssl_context,
1075					 &context->server_certs);
1076	eaptls_out = eapttls_verify_server(plugin, identifier, client_status);
1077	if (context->trust_proceed == FALSE) {
1078	    goto done;
1079	}
1080	/* handshake again to get past the AuthCompleted status */
1081	status = SSLHandshake(context->ssl_context);
1082    }
1083    switch (status) {
1084    case noErr:
1085	/* handshake complete, tunnel established */
1086	if (context->trust_proceed == FALSE) {
1087	    my_CFRelease(&context->server_certs);
1088	    (void)EAPSSLCopyPeerCertificates(context->ssl_context,
1089					     &context->server_certs);
1090	    eaptls_out = eapttls_verify_server(plugin, identifier,
1091					       client_status);
1092	    if (context->trust_proceed == FALSE) {
1093		/* this should not happen */
1094		EAPLOG_FL(LOG_NOTICE, "trust_proceed is FALSE?");
1095		break;
1096	    }
1097	}
1098	context->handshake_complete = TRUE;
1099	eapttls_compute_session_key(context);
1100	eapttls_set_session_was_resumed(context);
1101	if (plugin->password == NULL) {
1102	    *client_status = context->last_client_status
1103		= kEAPClientStatusUserInputRequired;
1104	    break;
1105	}
1106	eaptls_out = eapttls_start_inner_auth(plugin,
1107					      identifier, client_status);
1108	break;
1109    default:
1110	EAPLOG_FL(LOG_NOTICE, "SSLHandshake failed, %s",
1111		  EAPSSLErrorString(status));
1112	context->last_ssl_error = status;
1113	my_CFRelease(&context->server_certs);
1114	(void) EAPSSLCopyPeerCertificates(context->ssl_context,
1115					  &context->server_certs);
1116	/* close_up_shop */
1117	context->plugin_state = kEAPClientStateFailure;
1118	SSLClose(context->ssl_context);
1119	/* FALL THROUGH */
1120    case errSSLWouldBlock:
1121	if (write_buf->data == NULL) {
1122	    if (status == errSSLFatalAlert) {
1123		/* send an ACK if we received a fatal alert message */
1124		eaptls_out
1125		    = EAPTTLSPacketCreateAck(identifier);
1126	    }
1127	}
1128	else {
1129	    eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse,
1130					    kEAPTypeTTLS,
1131					    identifier,
1132					    context->mtu,
1133					    write_buf,
1134					    &context->last_write_size);
1135	}
1136	break;
1137    }
1138
1139 done:
1140    return (eaptls_out);
1141}
1142
1143static EAPPacketRef
1144eapttls_request(EAPClientPluginDataRef plugin,
1145		const EAPPacketRef in_pkt,
1146		EAPClientStatus * client_status)
1147{
1148    EAPTTLSPluginDataRef	context = (EAPTTLSPluginDataRef)plugin->private;
1149    EAPTLSPacket * 		eaptls_in = (EAPTLSPacket *)in_pkt;
1150    EAPTLSLengthIncludedPacket *eaptls_in_l;
1151    EAPPacketRef		eaptls_out = NULL;
1152    int				in_data_length;
1153    void *			in_data_ptr = NULL;
1154    u_int16_t			in_length = EAPPacketGetLength(in_pkt);
1155    memoryBufferRef		write_buf = &context->write_buffer;
1156    memoryBufferRef		read_buf = &context->read_buffer;
1157    SSLSessionState		ssl_state = kSSLIdle;
1158    OSStatus			status = noErr;
1159    u_int32_t			tls_message_length = 0;
1160    RequestType			type;
1161
1162    /* ALIGN: void * cast OK, we don't expect proper alignment */
1163    eaptls_in_l = (EAPTLSLengthIncludedPacket *)(void *)in_pkt;
1164    if (in_length < sizeof(*eaptls_in)) {
1165	EAPLOG_FL(LOG_NOTICE, "length %d < %ld",
1166		  in_length, sizeof(*eaptls_in));
1167	goto done;
1168    }
1169    if (context->ssl_context != NULL) {
1170	status = SSLGetSessionState(context->ssl_context, &ssl_state);
1171	if (status != noErr) {
1172	    EAPLOG_FL(LOG_NOTICE, "SSLGetSessionState failed, %s",
1173		      EAPSSLErrorString(status));
1174	    context->plugin_state = kEAPClientStateFailure;
1175	    context->last_ssl_error = status;
1176	    goto done;
1177	}
1178    }
1179    in_data_ptr = eaptls_in->tls_data;
1180    tls_message_length = in_data_length = in_length - sizeof(EAPTLSPacket);
1181
1182    type = kRequestTypeData;
1183    if ((eaptls_in->flags & kEAPTLSPacketFlagsStart) != 0) {
1184	type = kRequestTypeStart;
1185	/* only reset our state if this is not a re-transmitted Start packet */
1186	if (ssl_state != kSSLHandshake
1187	    || write_buf->data == NULL
1188	    || in_pkt->identifier != context->previous_identifier) {
1189	    ssl_state = kSSLIdle;
1190	}
1191    }
1192    else if (in_length == sizeof(*eaptls_in)) {
1193	type = kRequestTypeAck;
1194    }
1195    else if ((eaptls_in->flags & kEAPTLSPacketFlagsLengthIncluded) != 0) {
1196	if (in_length < sizeof(EAPTLSLengthIncludedPacket)) {
1197	    EAPLOG_FL(LOG_NOTICE,
1198		      "packet too short %d < %ld",
1199		      in_length, sizeof(EAPTLSLengthIncludedPacket));
1200	    goto done;
1201	}
1202	in_data_ptr = eaptls_in_l->tls_data;
1203	in_data_length = in_length - sizeof(EAPTLSLengthIncludedPacket);
1204	tls_message_length
1205	    = EAPTLSLengthIncludedPacketGetMessageLength(eaptls_in_l);
1206	if (tls_message_length > kEAPTLSAvoidDenialOfServiceSize) {
1207	    EAPLOG_FL(LOG_NOTICE,
1208		      "received message too large, %d > %d",
1209		      tls_message_length, kEAPTLSAvoidDenialOfServiceSize);
1210	    context->plugin_state = kEAPClientStateFailure;
1211	    goto done;
1212	}
1213	if (tls_message_length == 0) {
1214	    type = kRequestTypeAck;
1215	}
1216    }
1217
1218    switch (ssl_state) {
1219    case kSSLClosed:
1220    case kSSLAborted:
1221	break;
1222
1223    case kSSLIdle:
1224	if (type != kRequestTypeStart) {
1225	    /* ignore it: XXX should this be an error? */
1226	    EAPLOG_FL(LOG_NOTICE,
1227		      "ignoring non TTLS start frame");
1228	    goto done;
1229	}
1230	status = eapttls_start(plugin);
1231	if (status != noErr) {
1232	    context->last_ssl_error = status;
1233	    context->plugin_state = kEAPClientStateFailure;
1234	    goto done;
1235	}
1236	status = SSLHandshake(context->ssl_context);
1237	if (status != errSSLWouldBlock) {
1238	    EAPLOG_FL(LOG_NOTICE, "SSLHandshake failed, %s",
1239		      EAPSSLErrorString(status));
1240	    context->last_ssl_error = status;
1241	    context->plugin_state = kEAPClientStateFailure;
1242	    goto done;
1243	}
1244	eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse,
1245					kEAPTypeTTLS,
1246					eaptls_in->identifier,
1247					context->mtu,
1248					write_buf,
1249					&context->last_write_size);
1250	break;
1251    case kSSLHandshake:
1252    case kSSLConnected:
1253	if (write_buf->data != NULL) {
1254	    /* we have data to write */
1255	    if (in_pkt->identifier == context->previous_identifier) {
1256		/* resend the existing fragment */
1257		eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse,
1258						kEAPTypeTTLS,
1259						in_pkt->identifier,
1260						context->mtu,
1261						write_buf,
1262						&context->last_write_size);
1263		break;
1264	    }
1265	    if ((write_buf->offset + context->last_write_size)
1266		< write_buf->length) {
1267		/* advance the offset, and send the next fragment */
1268		write_buf->offset += context->last_write_size;
1269		eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse,
1270						kEAPTypeTTLS,
1271						in_pkt->identifier,
1272						context->mtu,
1273						write_buf,
1274						&context->last_write_size);
1275		break;
1276	    }
1277	    /* we're done, release the write buffer */
1278	    memoryBufferClear(write_buf);
1279	    context->last_write_size = 0;
1280	}
1281	if (type != kRequestTypeData) {
1282	    EAPTTLSPluginDataRef context;
1283
1284	    context = (EAPTTLSPluginDataRef)plugin->private;
1285	    if (ssl_state != kSSLConnected
1286		|| context->session_was_resumed == FALSE
1287		|| context->trust_proceed == FALSE
1288		|| context->authentication_started == TRUE) {
1289		EAPLOG_FL(LOG_NOTICE, "unexpected %s frame",
1290			  type == kRequestTypeAck ? "Ack" : "Start");
1291		goto done;
1292	    }
1293	    /* server is forcing us to re-auth even though we resumed */
1294	    EAPLOG(LOG_NOTICE, "server forcing re-auth after resume");
1295	    eaptls_out
1296		= eapttls_do_inner_auth(plugin, eaptls_in->identifier,
1297					client_status);
1298	    break;
1299	}
1300	if (in_pkt->identifier == context->previous_identifier) {
1301	    if ((eaptls_in->flags & kEAPTLSPacketFlagsMoreFragments) != 0) {
1302		/* just ack it, we've already seen the fragment */
1303		eaptls_out = EAPTTLSPacketCreateAck(eaptls_in->identifier);
1304		break;
1305	    }
1306	}
1307	else {
1308	    if (read_buf->data == NULL) {
1309		memoryBufferAllocate(read_buf, tls_message_length);
1310	    }
1311	    if (memoryBufferAddData(read_buf, in_data_ptr, in_data_length)
1312		== FALSE) {
1313		EAPLOG_FL(LOG_NOTICE,
1314			  "fragment too large %d",
1315			  in_data_length);
1316		goto done;
1317	    }
1318	    if (memoryBufferIsComplete(read_buf) == FALSE) {
1319		if ((eaptls_in->flags & kEAPTLSPacketFlagsMoreFragments) == 0) {
1320		    EAPLOG_FL(LOG_NOTICE,
1321			      "expecting more data but "
1322			      "more fragments bit is not set, ignoring");
1323		    goto done;
1324		}
1325		/* we haven't received the entire TLS message */
1326		eaptls_out = EAPTTLSPacketCreateAck(eaptls_in->identifier);
1327		break;
1328	    }
1329	}
1330	/* we've got the whole TLS message, process it */
1331	if (context->handshake_complete) {
1332	    /* subsequent request */
1333	    eaptls_out = eapttls_tunnel(plugin,
1334					eaptls_in->identifier,
1335					client_status);
1336	}
1337	else {
1338	    eaptls_out = eapttls_handshake(plugin,
1339					   eaptls_in->identifier,
1340					   client_status);
1341	}
1342	break;
1343    default:
1344	break;
1345    }
1346
1347    context->previous_identifier = in_pkt->identifier;
1348 done:
1349    return (eaptls_out);
1350}
1351
1352static EAPClientState
1353eapttls_process(EAPClientPluginDataRef plugin,
1354		const EAPPacketRef in_pkt,
1355		EAPPacketRef * out_pkt_p,
1356		EAPClientStatus * client_status,
1357		EAPClientDomainSpecificError * error)
1358{
1359    EAPTTLSPluginDataRef	context = (EAPTTLSPluginDataRef)plugin->private;
1360
1361    *client_status = kEAPClientStatusOK;
1362    *error = 0;
1363
1364    *out_pkt_p = NULL;
1365    switch (in_pkt->code) {
1366    case kEAPCodeRequest:
1367	*out_pkt_p = eapttls_request(plugin, in_pkt, client_status);
1368	break;
1369    case kEAPCodeSuccess:
1370	if (context->trust_proceed) {
1371	    context->plugin_state = kEAPClientStateSuccess;
1372	}
1373	else if (context->handshake_complete) {
1374	    *out_pkt_p
1375		= eapttls_verify_server(plugin, in_pkt->identifier,
1376					client_status);
1377	    if (context->trust_proceed) {
1378		context->plugin_state = kEAPClientStateSuccess;
1379	    }
1380	}
1381	break;
1382    case kEAPCodeFailure:
1383	context->plugin_state = kEAPClientStateFailure;
1384	break;
1385    case kEAPCodeResponse:
1386    default:
1387	break;
1388    }
1389    if (context->plugin_state == kEAPClientStateFailure) {
1390	if (context->last_ssl_error == noErr) {
1391	    switch (context->last_client_status) {
1392	    case kEAPClientStatusOK:
1393	    case kEAPClientStatusUserInputRequired:
1394		*client_status = kEAPClientStatusFailed;
1395		break;
1396	    default:
1397		*client_status = context->last_client_status;
1398		break;
1399	    }
1400	}
1401	else {
1402	    *error = context->last_ssl_error;
1403	    *client_status = kEAPClientStatusSecurityError;
1404	}
1405    }
1406    return (context->plugin_state);
1407}
1408
1409static const char *
1410eapttls_failure_string(EAPClientPluginDataRef plugin)
1411{
1412    return (NULL);
1413}
1414
1415static void *
1416eapttls_session_key(EAPClientPluginDataRef plugin, int * key_length)
1417{
1418    EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private;
1419
1420    *key_length = 0;
1421    if (context->key_data_valid == FALSE) {
1422	return (NULL);
1423    }
1424
1425    /* return the first 32 bytes of key data */
1426    *key_length = 32;
1427    return (context->key_data);
1428}
1429
1430static void *
1431eapttls_server_key(EAPClientPluginDataRef plugin, int * key_length)
1432{
1433    EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private;
1434
1435    *key_length = 0;
1436    if (context->key_data_valid == FALSE) {
1437	return (NULL);
1438    }
1439
1440    /* return the second 32 bytes of key data */
1441    *key_length = 32;
1442    return (context->key_data + 32);
1443}
1444
1445static int
1446eapttls_msk_copy_bytes(EAPClientPluginDataRef plugin,
1447		      void * msk, int msk_size)
1448{
1449    EAPTTLSPluginDataRef 	context = (EAPTTLSPluginDataRef)plugin->private;
1450    int				ret_msk_size;
1451
1452    if (msk_size < kEAPMasterSessionKeyMinimumSize
1453	|| context->key_data_valid == FALSE) {
1454	ret_msk_size = 0;
1455    }
1456    else {
1457	ret_msk_size = kEAPMasterSessionKeyMinimumSize;
1458	bcopy(context->key_data, msk, ret_msk_size);
1459    }
1460    return (ret_msk_size);
1461}
1462
1463static CFArrayRef
1464eapttls_require_props(EAPClientPluginDataRef plugin)
1465{
1466    CFArrayRef 			array = NULL;
1467    EAPTTLSPluginDataRef	context = (EAPTTLSPluginDataRef)plugin->private;
1468
1469    if (context->last_client_status != kEAPClientStatusUserInputRequired) {
1470	goto done;
1471    }
1472    if (context->trust_proceed == FALSE) {
1473	CFStringRef	str = kEAPClientPropTLSUserTrustProceedCertificateChain;
1474	array = CFArrayCreate(NULL, (const void **)&str,
1475			      1, &kCFTypeArrayCallBacks);
1476    }
1477    else if (plugin->password == NULL) {
1478	CFStringRef	str = kEAPClientPropUserPassword;
1479	array = CFArrayCreate(NULL, (const void **)&str,
1480			      1, &kCFTypeArrayCallBacks);
1481    }
1482 done:
1483    return (array);
1484}
1485
1486static CFDictionaryRef
1487eapttls_publish_props(EAPClientPluginDataRef plugin)
1488{
1489    CFArrayRef			cert_list;
1490    SSLCipherSuite		cipher = SSL_NULL_WITH_NULL_NULL;
1491    EAPTTLSPluginDataRef	context = (EAPTTLSPluginDataRef)plugin->private;
1492    CFMutableDictionaryRef	dict;
1493
1494    if (context->server_certs == NULL) {
1495	return (NULL);
1496    }
1497    cert_list = EAPSecCertificateArrayCreateCFDataArray(context->server_certs);
1498    if (cert_list == NULL) {
1499	return (NULL);
1500    }
1501    dict = CFDictionaryCreateMutable(NULL, 0,
1502				     &kCFTypeDictionaryKeyCallBacks,
1503				     &kCFTypeDictionaryValueCallBacks);
1504    CFDictionarySetValue(dict, kEAPClientPropTLSServerCertificateChain,
1505			 cert_list);
1506    CFDictionarySetValue(dict, kEAPClientPropTLSSessionWasResumed,
1507			 context->session_was_resumed
1508			 ? kCFBooleanTrue
1509			 : kCFBooleanFalse);
1510    my_CFRelease(&cert_list);
1511    (void)SSLGetNegotiatedCipher(context->ssl_context, &cipher);
1512    if (cipher != SSL_NULL_WITH_NULL_NULL) {
1513	CFNumberRef	c;
1514	int		tmp = cipher;
1515
1516	c = CFNumberCreate(NULL, kCFNumberIntType, &tmp);
1517	CFDictionarySetValue(dict, kEAPClientPropTLSNegotiatedCipher, c);
1518	CFRelease(c);
1519    }
1520    if (context->last_client_status == kEAPClientStatusUserInputRequired
1521	&& context->trust_proceed == FALSE) {
1522	CFNumberRef	num;
1523	num = CFNumberCreate(NULL, kCFNumberSInt32Type,
1524			     &context->trust_status);
1525	CFDictionarySetValue(dict, kEAPClientPropTLSTrustClientStatus, num);
1526	CFRelease(num);
1527    }
1528    return (dict);
1529}
1530
1531static CFStringRef
1532eapttls_copy_packet_description(const EAPPacketRef pkt, bool * packet_is_valid)
1533{
1534    EAPTLSPacketRef 	eaptls_pkt = (EAPTLSPacketRef)pkt;
1535
1536    return (EAPTLSPacketCopyDescription(eaptls_pkt, packet_is_valid));
1537}
1538
1539static EAPType
1540eapttls_type()
1541{
1542    return (kEAPTypeTTLS);
1543
1544}
1545
1546static const char *
1547eapttls_name()
1548{
1549    return (EAPTypeStr(kEAPTypeTTLS));
1550}
1551
1552static EAPClientPluginVersion
1553eapttls_version()
1554{
1555    return (kEAPClientPluginVersion);
1556}
1557
1558static struct func_table_ent {
1559    const char *		name;
1560    void *			func;
1561} func_table[] = {
1562#if 0
1563    { kEAPClientPluginFuncNameIntrospect, eapttls_introspect },
1564#endif /* 0 */
1565    { kEAPClientPluginFuncNameVersion, eapttls_version },
1566    { kEAPClientPluginFuncNameEAPType, eapttls_type },
1567    { kEAPClientPluginFuncNameEAPName, eapttls_name },
1568    { kEAPClientPluginFuncNameInit, eapttls_init },
1569    { kEAPClientPluginFuncNameFree, eapttls_free },
1570    { kEAPClientPluginFuncNameProcess, eapttls_process },
1571    { kEAPClientPluginFuncNameFreePacket, eapttls_free_packet },
1572    { kEAPClientPluginFuncNameFailureString, eapttls_failure_string },
1573    { kEAPClientPluginFuncNameSessionKey, eapttls_session_key },
1574    { kEAPClientPluginFuncNameServerKey, eapttls_server_key },
1575    { kEAPClientPluginFuncNameMasterSessionKeyCopyBytes,
1576      eapttls_msk_copy_bytes },
1577    { kEAPClientPluginFuncNameRequireProperties, eapttls_require_props },
1578    { kEAPClientPluginFuncNamePublishProperties, eapttls_publish_props },
1579    { kEAPClientPluginFuncNameCopyPacketDescription,
1580      eapttls_copy_packet_description },
1581    { NULL, NULL},
1582};
1583
1584
1585EAPClientPluginFuncRef
1586eapttls_introspect(EAPClientPluginFuncName name)
1587{
1588    struct func_table_ent * scan;
1589
1590
1591    for (scan = func_table; scan->name != NULL; scan++) {
1592	if (strcmp(name, scan->name) == 0) {
1593	    return (scan->func);
1594	}
1595    }
1596    return (NULL);
1597}
1598