1/*
2 * Copyright (c) 2002-2013 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 * eaptls_plugin.c
26 * - EAP-TLS client using SecureTransport API's
27 */
28
29/*
30 * Modification History
31 *
32 * August 26, 2002	Dieter Siegmund (dieter@apple)
33 * - created
34 *
35 * September 8, 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 <unistd.h>
41#include <stdlib.h>
42#include <string.h>
43#include <stdio.h>
44#include <SystemConfiguration/SCValidation.h>
45#include <CoreFoundation/CFArray.h>
46#include <CoreFoundation/CFDictionary.h>
47#include <EAP8021X/EAPClientPlugin.h>
48#include <EAP8021X/EAPClientProperties.h>
49#include <EAP8021X/EAPTLSUtil.h>
50#include <EAP8021X/EAPCertificateUtil.h>
51#include <EAP8021X/EAPUtil.h>
52#include <Security/SecureTransport.h>
53#include <Security/SecCertificate.h>
54#include <Security/SecIdentity.h>
55#include <Security/SecureTransportPriv.h>
56#include "myCFUtil.h"
57#include "printdata.h"
58#include "EAPLog.h"
59
60/*
61 * Declare these here to ensure that the compiler
62 * generates appropriate errors/warnings
63 */
64EAPClientPluginFuncIntrospect eaptls_introspect;
65static EAPClientPluginFuncVersion eaptls_version;
66static EAPClientPluginFuncEAPType eaptls_type;
67static EAPClientPluginFuncEAPName eaptls_name;
68static EAPClientPluginFuncInit eaptls_init;
69static EAPClientPluginFuncFree eaptls_free;
70static EAPClientPluginFuncProcess eaptls_process;
71static EAPClientPluginFuncFreePacket eaptls_free_packet;
72static EAPClientPluginFuncSessionKey eaptls_session_key;
73static EAPClientPluginFuncServerKey eaptls_server_key;
74static EAPClientPluginFuncRequireProperties eaptls_require_props;
75static EAPClientPluginFuncPublishProperties eaptls_publish_props;
76static EAPClientPluginFuncUserName eaptls_user_name_copy;
77static EAPClientPluginFuncCopyPacketDescription eaptls_copy_packet_description;
78
79typedef enum {
80    kRequestTypeStart,
81    kRequestTypeAck,
82    kRequestTypeData,
83} RequestType;
84
85typedef struct {
86    SSLContextRef		ssl_context;
87    memoryBuffer		read_buffer;
88    memoryBuffer		write_buffer;
89    int				last_write_size;
90    int				previous_identifier;
91    memoryIO			mem_io;
92    EAPClientState		plugin_state;
93    bool			cert_is_required;
94    CFArrayRef			certs;
95    int				mtu;
96    OSStatus			last_ssl_error;
97    EAPClientStatus		last_client_status;
98    OSStatus			trust_ssl_error;
99    EAPClientStatus		trust_status;
100    bool			trust_proceed;
101    bool			key_data_valid;
102    char			key_data[128];
103    bool			resume_sessions;
104    bool			server_auth_completed;
105    CFArrayRef			server_certs;
106    bool			session_was_resumed;
107} EAPTLSPluginData, * EAPTLSPluginDataRef;
108
109enum {
110    kAvoidDenialOfServiceSize = 128 * 1024
111};
112
113#define BAD_IDENTIFIER			(-1)
114
115#define kEAPTLSClientLabel		"client EAP encryption"
116#define kEAPTLSClientLabelLength	(sizeof(kEAPTLSClientLabel) - 1)
117
118static bool
119eaptls_compute_session_key(EAPTLSPluginDataRef context)
120{
121    OSStatus		status;
122
123    context->key_data_valid = FALSE;
124    status = EAPTLSComputeKeyData(context->ssl_context,
125				  kEAPTLSClientLabel,
126				  kEAPTLSClientLabelLength,
127				  context->key_data,
128				  sizeof(context->key_data));
129    if (status != noErr) {
130	EAPLOG_FL(LOG_NOTICE, "EAPTLSComputeSessionKey failed, %s",
131		  EAPSSLErrorString(status));
132	return (FALSE);
133    }
134    context->key_data_valid = TRUE;
135    return (TRUE);
136}
137
138static void
139eaptls_free_context(EAPTLSPluginDataRef context)
140{
141    if (context->ssl_context != NULL) {
142	CFRelease(context->ssl_context);
143	context->ssl_context = NULL;
144    }
145    my_CFRelease(&context->certs);
146    my_CFRelease(&context->server_certs);
147    memoryIOClearBuffers(&context->mem_io);
148    free(context);
149    return;
150}
151
152static OSStatus
153eaptls_start(EAPClientPluginDataRef plugin)
154{
155    EAPTLSPluginDataRef	context = (EAPTLSPluginDataRef)plugin->private;
156    SSLContextRef	ssl_context = NULL;
157    OSStatus		status = noErr;
158
159    if (context->ssl_context != NULL) {
160	CFRelease(context->ssl_context);
161	context->ssl_context = NULL;
162    }
163    my_CFRelease(&context->server_certs);
164    memoryIOClearBuffers(&context->mem_io);
165    ssl_context = EAPTLSMemIOContextCreate(FALSE, &context->mem_io, NULL,
166					   &status);
167    if (ssl_context == NULL) {
168	EAPLOG_FL(LOG_NOTICE, "EAPTLSMemIOContextCreate failed, %s",
169		  EAPSSLErrorString(status));
170	goto failed;
171    }
172    if (context->resume_sessions && plugin->unique_id != NULL) {
173	status = SSLSetPeerID(ssl_context, plugin->unique_id,
174			      plugin->unique_id_length);
175	if (status != noErr) {
176	    EAPLOG_FL(LOG_NOTICE,
177		      "SSLSetPeerID failed, %s", EAPSSLErrorString(status));
178	    goto failed;
179	}
180    }
181    if (context->cert_is_required) {
182	if (context->certs == NULL) {
183	    status = EAPTLSCopyIdentityTrustChain(plugin->sec_identity,
184						  plugin->properties,
185						  &context->certs);
186	    if (status != noErr) {
187		EAPLOG_FL(LOG_NOTICE,
188			  "failed to find client cert/identity, %s (%ld)",
189			  EAPSSLErrorString(status), (long)status);
190		goto failed;
191	    }
192	}
193	status = SSLSetCertificate(ssl_context, context->certs);
194	if (status != noErr) {
195	    EAPLOG_FL(LOG_NOTICE,
196		      "SSLSetCertificate failed, %s",
197		      EAPSSLErrorString(status));
198	    goto failed;
199	}
200    }
201    context->ssl_context = ssl_context;
202    context->plugin_state = kEAPClientStateAuthenticating;
203    context->previous_identifier = BAD_IDENTIFIER;
204    context->last_ssl_error = noErr;
205    context->last_client_status = kEAPClientStatusOK;
206    context->trust_proceed = FALSE;
207    context->server_auth_completed = FALSE;
208    context->key_data_valid = FALSE;
209    context->last_write_size = 0;
210    context->session_was_resumed = FALSE;
211    return (status);
212 failed:
213    if (ssl_context != NULL) {
214	CFRelease(ssl_context);
215    }
216    return (status);
217
218}
219
220static EAPClientStatus
221eaptls_init(EAPClientPluginDataRef plugin, CFArrayRef * required_props,
222	    EAPClientDomainSpecificError * error)
223{
224    EAPTLSPluginDataRef	context = NULL;
225
226    context = malloc(sizeof(*context));
227    bzero(context, sizeof(*context));
228    context->cert_is_required
229	= my_CFDictionaryGetBooleanValue(plugin->properties,
230					 kEAPClientPropTLSCertificateIsRequired,
231					 TRUE);
232    context->mtu = plugin->mtu;
233    context->resume_sessions
234	= my_CFDictionaryGetBooleanValue(plugin->properties,
235					 kEAPClientPropTLSEnableSessionResumption,
236					 TRUE);
237    /* memoryIOInit() initializes the memoryBuffer structures as well */
238    memoryIOInit(&context->mem_io, &context->read_buffer,
239		 &context->write_buffer);
240    //memoryIOSetDebug(&context->mem_io, TRUE);
241
242    plugin->private = context;
243    *error = 0;
244    return (kEAPClientStatusOK);
245}
246
247static void
248eaptls_free(EAPClientPluginDataRef plugin)
249{
250    EAPTLSPluginDataRef	context = (EAPTLSPluginDataRef)plugin->private;
251
252    if (context != NULL) {
253	eaptls_free_context(context);
254	plugin->private = NULL;
255    }
256    return;
257}
258
259static void
260eaptls_free_packet(EAPClientPluginDataRef plugin, EAPPacketRef arg)
261{
262    if (arg != NULL) {
263	free(arg);
264    }
265    return;
266}
267
268static EAPPacketRef
269EAPTLSPacketCreateAck(u_char identifier)
270{
271    return (EAPTLSPacketCreate(kEAPCodeResponse, kEAPTypeTLS,
272			       identifier, 0, NULL, NULL));
273}
274
275static EAPPacketRef
276eaptls_verify_server(EAPClientPluginDataRef plugin,
277		     int identifier, EAPClientStatus * client_status)
278{
279    EAPTLSPluginDataRef 	context = (EAPTLSPluginDataRef)plugin->private;
280    EAPPacketRef		pkt = NULL;
281    memoryBufferRef		write_buf = &context->write_buffer;
282
283    context->trust_status
284	= EAPTLSVerifyServerCertificateChain(plugin->properties,
285					     context->server_certs,
286					     &context->trust_ssl_error);
287    if (context->trust_status != kEAPClientStatusOK) {
288	EAPLOG_FL(LOG_NOTICE, "server certificate not trusted status %d %d",
289		  context->trust_status,
290		  (int)context->trust_ssl_error);
291    }
292    switch (context->trust_status) {
293    case kEAPClientStatusOK:
294	context->trust_proceed = TRUE;
295	break;
296    case kEAPClientStatusUserInputRequired:
297	/* ask user whether to proceed or not */
298	*client_status = context->last_client_status
299	    = kEAPClientStatusUserInputRequired;
300	break;
301    default:
302	*client_status = context->trust_status;
303	context->last_ssl_error = context->trust_ssl_error;
304	context->plugin_state = kEAPClientStateFailure;
305	SSLClose(context->ssl_context);
306	pkt = EAPTLSPacketCreate(kEAPCodeResponse,
307				 kEAPTypeTLS,
308				 identifier,
309				 context->mtu,
310				 write_buf,
311				 &context->last_write_size);
312	break;
313    }
314    return (pkt);
315}
316
317static void
318eaptls_set_session_was_resumed(EAPTLSPluginDataRef context)
319{
320    char		buf[MAX_SESSION_ID_LENGTH];
321    size_t		buf_len = sizeof(buf);
322    Boolean		resumed = FALSE;
323    OSStatus		status;
324
325    status = SSLGetResumableSessionInfo(context->ssl_context,
326					&resumed, buf, &buf_len);
327    if (status == noErr) {
328	context->session_was_resumed = resumed;
329    }
330    return;
331}
332
333static EAPPacketRef
334eaptls_handshake(EAPClientPluginDataRef plugin,
335		 int identifier, EAPClientStatus * client_status)
336
337{
338    EAPTLSPluginDataRef context = (EAPTLSPluginDataRef)plugin->private;
339    EAPPacketRef	eaptls_out = NULL;
340    OSStatus		status = noErr;
341    memoryBufferRef	write_buf = &context->write_buffer;
342
343    if (context->server_auth_completed && context->trust_proceed == FALSE) {
344	eaptls_out
345	    = eaptls_verify_server(plugin, identifier, client_status);
346	if (context->trust_proceed == FALSE) {
347	    goto done;
348	}
349    }
350
351    status = SSLHandshake(context->ssl_context);
352    if (status == errSSLServerAuthCompleted) {
353	if (context->server_auth_completed) {
354	    /* this should not happen */
355	    EAPLOG_FL(LOG_NOTICE, "AuthCompleted again?");
356	    goto done;
357	}
358	context->server_auth_completed = TRUE;
359	my_CFRelease(&context->server_certs);
360	(void)EAPSSLCopyPeerCertificates(context->ssl_context,
361					 &context->server_certs);
362	eaptls_out = eaptls_verify_server(plugin, identifier, client_status);
363	if (context->trust_proceed == FALSE) {
364	    goto done;
365	}
366	/* handshake again to get past the AuthCompleted status */
367	status = SSLHandshake(context->ssl_context);
368    }
369    switch (status) {
370    case noErr:
371	/* handshake complete */
372	if (context->trust_proceed == FALSE) {
373	    my_CFRelease(&context->server_certs);
374	    (void)EAPSSLCopyPeerCertificates(context->ssl_context,
375					     &context->server_certs);
376	    eaptls_out = eaptls_verify_server(plugin, identifier,
377					      client_status);
378	    if (context->trust_proceed == FALSE) {
379		/* this should not happen */
380		EAPLOG_FL(LOG_NOTICE, "trust_proceed is FALSE?");
381		break;
382	    }
383	}
384	eaptls_compute_session_key(context);
385	eaptls_set_session_was_resumed(context);
386	eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse,
387					kEAPTypeTLS,
388					identifier,
389					context->mtu,
390					write_buf,
391					&context->last_write_size);
392	break;
393    default:
394	EAPLOG_FL(LOG_NOTICE, "SSLHandshake failed, %s",
395		  EAPSSLErrorString(status));
396	context->last_ssl_error = status;
397	my_CFRelease(&context->server_certs);
398	(void)EAPSSLCopyPeerCertificates(context->ssl_context,
399					 &context->server_certs);
400	/* close_up_shop */
401	context->plugin_state = kEAPClientStateFailure;
402	SSLClose(context->ssl_context);
403	/* FALL THROUGH */
404    case errSSLWouldBlock:
405	if (write_buf->data == NULL) {
406	    if (status == errSSLFatalAlert) {
407		/* send an ACK if we received a fatal alert message */
408		eaptls_out
409		    = EAPTLSPacketCreateAck(identifier);
410	    }
411	}
412	else {
413	    eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse,
414					    kEAPTypeTLS,
415					    identifier,
416					    context->mtu,
417					    write_buf,
418					    &context->last_write_size);
419	}
420	break;
421    }
422
423 done:
424    return (eaptls_out);
425}
426
427static EAPPacketRef
428eaptls_request(EAPClientPluginDataRef plugin,
429	       const EAPPacketRef in_pkt,
430	       EAPClientStatus * client_status)
431{
432    EAPTLSPluginDataRef	context = (EAPTLSPluginDataRef)plugin->private;
433    EAPTLSPacketRef 	eaptls_in = (EAPTLSPacketRef)in_pkt;
434    EAPTLSLengthIncludedPacketRef eaptls_in_l;
435    EAPPacketRef	eaptls_out = NULL;
436    int			in_data_length;
437    void *		in_data_ptr = NULL;
438    u_int16_t		in_length = EAPPacketGetLength(in_pkt);
439    memoryBufferRef	write_buf = &context->write_buffer;
440    memoryBufferRef	read_buf = &context->read_buffer;
441    SSLSessionState	ssl_state = kSSLIdle;
442    OSStatus		status = noErr;
443    u_int32_t		tls_message_length = 0;
444    RequestType		type;
445
446    /* ALIGN: void * cast OK, we don't expect proper alignment */
447    eaptls_in_l = (EAPTLSLengthIncludedPacketRef)(void *)in_pkt;
448    if (in_length < sizeof(*eaptls_in)) {
449	EAPLOG(LOG_NOTICE, "eaptls_request: length %d < %ld",
450	       in_length, sizeof(*eaptls_in));
451	goto done;
452    }
453    if (context->ssl_context != NULL) {
454	status = SSLGetSessionState(context->ssl_context, &ssl_state);
455	if (status != noErr) {
456	    EAPLOG_FL(LOG_NOTICE, "SSLGetSessionState failed, %s",
457		      EAPSSLErrorString(status));
458	    context->plugin_state = kEAPClientStateFailure;
459	    context->last_ssl_error = status;
460	    goto done;
461	}
462    }
463    in_data_ptr = eaptls_in->tls_data;
464    tls_message_length = in_data_length = in_length - sizeof(EAPTLSPacket);
465
466    type = kRequestTypeData;
467    if ((eaptls_in->flags & kEAPTLSPacketFlagsStart) != 0) {
468	type = kRequestTypeStart;
469	/* only reset our state if this is not a re-transmitted Start packet */
470	if (ssl_state != kSSLHandshake
471	    || write_buf->data == NULL
472	    || in_pkt->identifier != context->previous_identifier) {
473	    ssl_state = kSSLIdle;
474	}
475    }
476    else if (in_length == sizeof(*eaptls_in)) {
477	type = kRequestTypeAck;
478    }
479    else if ((eaptls_in->flags & kEAPTLSPacketFlagsLengthIncluded) != 0) {
480	if (in_length < sizeof(EAPTLSLengthIncludedPacket)) {
481	    EAPLOG_FL(LOG_NOTICE,
482		      "packet too short %d < %ld",
483		      in_length, sizeof(EAPTLSLengthIncludedPacket));
484	    goto done;
485	}
486	in_data_ptr = eaptls_in_l->tls_data;
487	in_data_length = in_length - sizeof(EAPTLSLengthIncludedPacket);
488	tls_message_length = EAPTLSLengthIncludedPacketGetMessageLength(eaptls_in_l);
489	if (tls_message_length > kAvoidDenialOfServiceSize) {
490	    EAPLOG_FL(LOG_NOTICE,
491		      "received message too large, %d > %d",
492		      tls_message_length, kAvoidDenialOfServiceSize);
493	    context->plugin_state = kEAPClientStateFailure;
494	    goto done;
495	}
496	if (tls_message_length == 0) {
497	    type = kRequestTypeAck;
498	}
499    }
500
501    switch (ssl_state) {
502    case kSSLClosed:
503    case kSSLAborted:
504	break;
505
506    case kSSLIdle:
507	if (type != kRequestTypeStart) {
508	    /* ignore it: XXX should this be an error? */
509	    EAPLOG_FL(LOG_NOTICE,
510		      "ignoring non EAP-TLS start frame");
511	    goto done;
512	}
513	status = eaptls_start(plugin);
514	if (status != noErr) {
515	    context->last_ssl_error = status;
516	    context->plugin_state = kEAPClientStateFailure;
517	    goto done;
518	}
519	status = SSLHandshake(context->ssl_context);
520	if (status != errSSLWouldBlock) {
521	    EAPLOG_FL(LOG_NOTICE,
522		      "SSLHandshake failed, %s (%d)",
523		      EAPSSLErrorString(status), (int)status);
524	    context->last_ssl_error = status;
525	    context->plugin_state = kEAPClientStateFailure;
526	    goto done;
527	}
528	eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse,
529					kEAPTypeTLS,
530					eaptls_in->identifier,
531					context->mtu,
532					write_buf,
533					&context->last_write_size);
534	break;
535    case kSSLHandshake:
536    case kSSLConnected:
537	if (write_buf->data != NULL) {
538	    /* we have data to write */
539	    if (in_pkt->identifier == context->previous_identifier) {
540		/* resend the existing fragment */
541		eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse,
542						kEAPTypeTLS,
543						in_pkt->identifier,
544						context->mtu,
545						write_buf,
546						&context->last_write_size);
547		break;
548	    }
549	    if ((write_buf->offset + context->last_write_size)
550		< write_buf->length) {
551		/* advance the offset, and send the next fragment */
552		write_buf->offset += context->last_write_size;
553		eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse,
554						kEAPTypeTLS,
555						in_pkt->identifier,
556						context->mtu,
557						write_buf,
558						&context->last_write_size);
559		break;
560	    }
561	    /* we're done, release the write buffer */
562	    memoryBufferClear(write_buf);
563	    context->last_write_size = 0;
564	}
565	if (type != kRequestTypeData) {
566	    EAPLOG_FL(LOG_NOTICE, "unexpected %s frame",
567		      type == kRequestTypeAck ? "Ack" : "Start");
568	    goto done;
569	}
570	if (in_pkt->identifier == context->previous_identifier) {
571	    if ((eaptls_in->flags & kEAPTLSPacketFlagsMoreFragments) != 0) {
572		/* just ack it, we've already seen the fragment */
573		eaptls_out = EAPTLSPacketCreateAck(eaptls_in->identifier);
574		break;
575	    }
576	}
577	else {
578	    if (read_buf->data == NULL) {
579		memoryBufferAllocate(read_buf, tls_message_length);
580	    }
581	    if (memoryBufferAddData(read_buf, in_data_ptr, in_data_length)
582		== FALSE) {
583		EAPLOG_FL(LOG_NOTICE, "fragment too large %d", in_data_length);
584		goto done;
585	    }
586	    if (memoryBufferIsComplete(read_buf) == FALSE) {
587		if ((eaptls_in->flags & kEAPTLSPacketFlagsMoreFragments) == 0) {
588		    EAPLOG_FL(LOG_NOTICE,
589			      "expecting more data but "
590			      "more fragments bit is not set, ignoring");
591		    goto done;
592		}
593		/* we haven't received the entire TLS message */
594		eaptls_out = EAPTLSPacketCreateAck(eaptls_in->identifier);
595		break;
596	    }
597	}
598	/* we've got the whole TLS message, process it */
599	eaptls_out = eaptls_handshake(plugin, eaptls_in->identifier,
600				      client_status);
601	break;
602    default:
603	break;
604    }
605    context->previous_identifier = in_pkt->identifier;
606 done:
607    return (eaptls_out);
608}
609
610static EAPClientState
611eaptls_process(EAPClientPluginDataRef plugin,
612	       const EAPPacketRef in_pkt,
613	       EAPPacketRef * out_pkt_p,
614	       EAPClientStatus * client_status,
615	       EAPClientDomainSpecificError * error)
616{
617    EAPTLSPluginDataRef	context = (EAPTLSPluginDataRef)plugin->private;
618
619    *client_status = kEAPClientStatusOK;
620    *error = 0;
621    *out_pkt_p = NULL;
622    switch (in_pkt->code) {
623    case kEAPCodeRequest:
624	*out_pkt_p = eaptls_request(plugin, in_pkt, client_status);
625	break;
626    case kEAPCodeSuccess:
627	if (context->trust_proceed) {
628	    context->plugin_state = kEAPClientStateSuccess;
629	}
630	break;
631    case kEAPCodeFailure:
632	context->plugin_state = kEAPClientStateFailure;
633	break;
634    case kEAPCodeResponse:
635    default:
636	break;
637    }
638    if (context->plugin_state == kEAPClientStateFailure) {
639	if (context->last_ssl_error == noErr) {
640	    if (context->server_certs != NULL) {
641		*client_status = kEAPClientStatusFailed;
642	    }
643	    else {
644		*client_status = kEAPClientStatusProtocolError;
645	    }
646	}
647	else {
648	    *error = context->last_ssl_error;
649	    *client_status = kEAPClientStatusSecurityError;
650	}
651    }
652    return (context->plugin_state);
653}
654
655static const char *
656eaptls_failure_string(EAPClientPluginDataRef plugin)
657{
658    return (NULL);
659}
660
661static void *
662eaptls_session_key(EAPClientPluginDataRef plugin, int * key_length)
663{
664    EAPTLSPluginDataRef	context = (EAPTLSPluginDataRef)plugin->private;
665
666    *key_length = 0;
667    if (context->key_data_valid == FALSE) {
668	return (NULL);
669    }
670
671    /* return the first 32 bytes of key data */
672    *key_length = 32;
673    return (context->key_data);
674}
675
676static void *
677eaptls_server_key(EAPClientPluginDataRef plugin, int * key_length)
678{
679    EAPTLSPluginDataRef	context = (EAPTLSPluginDataRef)plugin->private;
680
681    *key_length = 0;
682    if (context->key_data_valid == FALSE) {
683	return (NULL);
684    }
685
686    /* return the second 32 bytes of key data */
687    *key_length = 32;
688    return (context->key_data + 32);
689}
690
691static CFArrayRef
692eaptls_require_props(EAPClientPluginDataRef plugin)
693{
694    CFArrayRef 			array = NULL;
695    EAPTLSPluginDataRef		context = (EAPTLSPluginDataRef)plugin->private;
696
697    if (context->last_client_status != kEAPClientStatusUserInputRequired) {
698	goto done;
699    }
700    if (context->trust_proceed == FALSE) {
701	CFStringRef	str = kEAPClientPropTLSUserTrustProceedCertificateChain;
702	array = CFArrayCreate(NULL, (const void **)&str,
703			      1, &kCFTypeArrayCallBacks);
704    }
705 done:
706    return (array);
707}
708
709static CFDictionaryRef
710eaptls_publish_props(EAPClientPluginDataRef plugin)
711{
712    CFArrayRef			cert_list;
713    SSLCipherSuite		cipher = SSL_NULL_WITH_NULL_NULL;
714    EAPTLSPluginDataRef		context = (EAPTLSPluginDataRef)plugin->private;
715    CFMutableDictionaryRef	dict;
716
717    if (context->server_certs == NULL) {
718	return (NULL);
719    }
720    cert_list = EAPSecCertificateArrayCreateCFDataArray(context->server_certs);
721    if (cert_list == NULL) {
722	return (NULL);
723    }
724    dict = CFDictionaryCreateMutable(NULL, 0,
725				     &kCFTypeDictionaryKeyCallBacks,
726				     &kCFTypeDictionaryValueCallBacks);
727    CFDictionarySetValue(dict, kEAPClientPropTLSServerCertificateChain,
728			 cert_list);
729    my_CFRelease(&cert_list);
730    CFDictionarySetValue(dict, kEAPClientPropTLSSessionWasResumed,
731			 context->session_was_resumed
732			 ? kCFBooleanTrue
733			 : kCFBooleanFalse);
734    (void)SSLGetNegotiatedCipher(context->ssl_context, &cipher);
735    if (cipher != SSL_NULL_WITH_NULL_NULL) {
736	CFNumberRef	c;
737	int		tmp = cipher;
738
739	c = CFNumberCreate(NULL, kCFNumberIntType, &tmp);
740	CFDictionarySetValue(dict, kEAPClientPropTLSNegotiatedCipher, c);
741	CFRelease(c);
742    }
743    if (context->last_client_status == kEAPClientStatusUserInputRequired
744	&& context->trust_proceed == FALSE) {
745	CFNumberRef	num;
746	num = CFNumberCreate(NULL, kCFNumberSInt32Type,
747			     &context->trust_status);
748	CFDictionarySetValue(dict, kEAPClientPropTLSTrustClientStatus, num);
749	CFRelease(num);
750    }
751    return (dict);
752}
753
754static CFStringRef
755eaptls_copy_packet_description(const EAPPacketRef pkt, bool * packet_is_valid)
756{
757    EAPTLSPacketRef 	eaptls_pkt = (EAPTLSPacketRef)pkt;
758
759    return (EAPTLSPacketCopyDescription(eaptls_pkt, packet_is_valid));
760}
761
762static EAPType
763eaptls_type()
764{
765    return (kEAPTypeTLS);
766}
767
768static const char *
769eaptls_name()
770{
771    return (EAPTypeStr(kEAPTypeTLS));
772}
773
774static EAPClientPluginVersion
775eaptls_version()
776{
777    return (kEAPClientPluginVersion);
778}
779
780static CFStringRef
781eaptls_user_name_copy(CFDictionaryRef properties)
782{
783    SecCertificateRef		cert = NULL;
784    EAPSecIdentityHandleRef	id_handle = NULL;
785    SecIdentityRef		identity = NULL;
786    OSStatus			status;
787    CFStringRef			user_name = NULL;
788
789    if (properties != NULL) {
790	id_handle = CFDictionaryGetValue(properties,
791					 kEAPClientPropTLSIdentityHandle);
792    }
793    status = EAPSecIdentityHandleCreateSecIdentity(id_handle, &identity);
794    if (status != noErr) {
795	goto done;
796    }
797    status = SecIdentityCopyCertificate(identity, &cert);
798    if (status != noErr) {
799	goto done;
800    }
801    user_name = EAPSecCertificateCopyUserNameString(cert);
802 done:
803    my_CFRelease(&cert);
804    my_CFRelease(&identity);
805    return (user_name);
806}
807
808static struct func_table_ent {
809    const char *		name;
810    void *			func;
811} func_table[] = {
812#if 0
813    { kEAPClientPluginFuncNameIntrospect, eaptls_introspect },
814#endif /* 0 */
815    { kEAPClientPluginFuncNameVersion, eaptls_version },
816    { kEAPClientPluginFuncNameEAPType, eaptls_type },
817    { kEAPClientPluginFuncNameEAPName, eaptls_name },
818    { kEAPClientPluginFuncNameInit, eaptls_init },
819    { kEAPClientPluginFuncNameFree, eaptls_free },
820    { kEAPClientPluginFuncNameProcess, eaptls_process },
821    { kEAPClientPluginFuncNameFreePacket, eaptls_free_packet },
822    { kEAPClientPluginFuncNameFailureString, eaptls_failure_string },
823    { kEAPClientPluginFuncNameSessionKey, eaptls_session_key },
824    { kEAPClientPluginFuncNameServerKey, eaptls_server_key },
825    { kEAPClientPluginFuncNameRequireProperties, eaptls_require_props },
826    { kEAPClientPluginFuncNamePublishProperties, eaptls_publish_props },
827    { kEAPClientPluginFuncNameUserName, eaptls_user_name_copy },
828    { kEAPClientPluginFuncNameCopyPacketDescription,
829      eaptls_copy_packet_description },
830    { NULL, NULL},
831};
832
833
834EAPClientPluginFuncRef
835eaptls_introspect(EAPClientPluginFuncName name)
836{
837    struct func_table_ent * scan;
838
839
840    for (scan = func_table; scan->name != NULL; scan++) {
841	if (strcmp(name, scan->name) == 0) {
842	    return (scan->func);
843	}
844    }
845    return (NULL);
846}
847