1/*
2 * Copyright (c) 2004 Apple Computer, 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#include <sys/types.h>
25#include <sys/uio.h>
26#include <unistd.h>
27#include <syslog.h>
28#include <sys/fcntl.h>
29#include <CoreFoundation/CoreFoundation.h>
30#include <CoreFoundation/CFBundle.h>
31#include <mach/mach.h>
32#include <EAP8021X/EAP.h>
33#include <EAP8021X/EAPClientModule.h>
34#include <EAP8021X/EAPClientProperties.h>
35#if !TARGET_OS_EMBEDDED	// This file is not built for Embedded
36#include <Security/SecKeychain.h>
37#include <Security/SecKeychainSearch.h>
38#include <Security/SecKeychainItem.h>
39#include <Security/SecIdentity.h>
40#endif /* TARGET_OS_EMBEDDED */
41#include <SystemConfiguration/SCNetworkConnection.h>
42
43#include "eaptls.h"
44#include "eaptls_ui.h"
45
46/*---------------------------------------------------------------------------
47** Internal routines
48**---------------------------------------------------------------------------
49*/
50
51static CFBundleRef 	bundle = 0;		/* our bundle ref */
52static int			initialized_UI = 0;	/* is UI ready */
53static char			eaptls_unique[17];
54static eaptls_ui_ctx ui_ctx;
55
56static void (*log_debug) __P((char *, ...)) = 0;
57static void (*log_error) __P((char *, ...)) = 0;
58
59static EAPClientModuleRef  eapRef = NULL;
60static EAPClientPluginData eapData;
61static CFMutableDictionaryRef eapProperties = NULL;
62static CFDictionaryRef eapOptions = NULL;
63static struct EAP_Packet	*eapSavePacket = NULL;
64static int eap_in_ui = 0;
65
66
67extern EAPClientPluginFuncRef
68eaptls_introspect(EAPClientPluginFuncName name);
69
70
71extern CFDictionaryRef		userOptions;	/* user options from pppd */
72
73
74/* ------------------------------------------------------------------------------------
75get the EAP dictionary from the options
76------------------------------------------------------------------------------------ */
77static void get_options ()
78{
79	if (eapOptions)
80		return;
81
82	if (userOptions) {
83		eapOptions = CFDictionaryGetValue(userOptions, CFSTR("EAP"));
84		if (eapOptions)
85			CFRetain(eapOptions);
86	}
87
88	// no option, use empty dictionary
89	if (!eapOptions)
90		eapOptions = CFDictionaryCreate(0, 0, 0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
91
92}
93
94/* ------------------------------------------------------------------------------------
95------------------------------------------------------------------------------------ */
96static int load_plugin ()
97{
98    EAPClientModuleStatus status;
99
100	if (eapRef)
101		return EAP_NO_ERROR;
102
103	status = EAPClientModuleAddBuiltinModule(eaptls_introspect);
104	if (status != kEAPClientModuleStatusOK) {
105		syslog(LOG_INFO, "EAP-TLS: EAPClientAddBuiltinModule(eaptls) failed %d\n", status);
106		return EAP_ERROR_GENERIC;
107	}
108
109	eapRef = EAPClientModuleLookup(kEAPTypeTLS);
110	if (eapRef == NULL) {
111		syslog(LOG_INFO, "EAP-TLS: EAPClientModuleLookup(eaptls) failed\n");
112		return EAP_ERROR_GENERIC;
113	}
114
115	return EAP_NO_ERROR;
116}
117
118/* ------------------------------------------------------------------------------------
119------------------------------------------------------------------------------------ */
120int Identity (char *identity, int maxlen)
121{
122    CFStringRef			identRef = NULL;
123	int					error;
124	int					ret = EAP_ERROR_GENERIC;
125
126	error = load_plugin();
127	if (error)
128		return error;
129
130	get_options();
131	if (eapOptions == NULL)
132		return ret;
133
134	identRef = EAPClientModulePluginUserName(eapRef, eapOptions);
135    if (identRef) {
136		if (CFStringGetCString(identRef, identity, maxlen, kCFStringEncodingUTF8))
137			ret = EAP_NO_ERROR;
138		CFRelease(identRef);
139	}
140
141	return ret;
142}
143
144/* ------------------------------------------------------------------------------------
145Init routine called by the EAP engine when it needs the module.
146Identity of the peer is known at this point.
147mode is 0 for client, 1 for server.
148cookie is the EAP engine context, to pass to subsequent calls to EAP.
149context is EAP module context, that will be passed to subsequent calls to the module
150------------------------------------------------------------------------------------ */
151int Init (struct EAP_Input *eap_in, void **context)
152{
153	int error;
154    EAPClientModuleStatus status;
155	int ret = EAP_ERROR_GENERIC;
156    int fd;
157
158	error = load_plugin();
159	if (error)
160		return error;
161
162	bundle = (CFBundleRef)eap_in->data;
163	CFRetain(bundle);
164
165	log_debug = eap_in->log_debug;
166    log_error = eap_in->log_error;
167
168	get_options();
169
170	bzero(&eapData, sizeof(eapData));
171
172    /* remaining fields are read-only: */
173	*((bool *)&eapData.log_enabled) = 1;
174	*((uint32_t *)&eapData.log_level) = LOG_ERR;
175	*((uint32_t *)&eapData.mtu) = eap_in->mtu;
176	*((uint32_t *)&eapData.generation) = 0;/* changed when user updates */
177
178	fd = open("/dev/random", O_RDONLY);
179    if (fd < 0) {
180		if (log_error)
181			(log_error)("EAP-TLS: Cannot open /dev/random\n");
182		goto failed;
183	}
184
185	read(fd, eaptls_unique, sizeof(eaptls_unique) - 1);
186	eaptls_unique[sizeof(eaptls_unique)-1] = 0;
187	close(fd);
188
189    eapData.unique_id = eaptls_unique;  /* used for TLS session resumption */
190	*((uint32_t *)&eapData.unique_id_length) = strlen(eapData.unique_id);
191
192    eapData.username = (u_char*)eap_in->identity;
193	*((uint32_t *)&eapData.username_length) = strlen((char*)eapData.username);
194
195    eapData.password = 0; 	/* may be NULL */
196	*((uint32_t *)&eapData.password_length) = 0;
197
198	if (eapOptions)
199		eapProperties = CFDictionaryCreateMutableCopy(0, 0, eapOptions);
200	else
201		eapProperties = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
202	if (eapProperties == NULL) {
203		if (log_error)
204			(log_error)("EAP-TLS: Cannot allocate memory\n");
205		goto failed;
206	}
207
208	*((CFDictionaryRef *)&eapData.properties) = eapProperties;
209
210	//CFDictionarySetValue(prop_dict, kEAPClientPropTLSVerifyServerCertificate, kCFBooleanFalse);
211
212	status = EAPClientModulePluginInit(eapRef, &eapData, NULL, &error);
213	if (status != kEAPClientStatusOK) {
214		if (log_error)
215			(log_error)("EAP-TLS: EAPClientPluginInit(eaptls) failed, error %d\n", status);
216		goto failed;
217	}
218
219	eapSavePacket = NULL;
220	eap_in_ui = 0;
221    if (eaptls_ui_load(bundle, log_debug, log_error) == 0)
222        initialized_UI = 1;
223
224    return EAP_NO_ERROR;
225
226failed:
227
228    return ret;
229}
230
231/* ------------------------------------------------------------------------------------
232------------------------------------------------------------------------------------ */
233int Dispose(void *context)
234{
235
236	EAPClientModulePluginFree(eapRef, &eapData);
237	eapRef = 0;
238
239	if (bundle) {
240		CFRelease(bundle);
241		bundle = 0;
242	}
243
244	if (eapOptions) {
245		CFRelease(eapOptions);
246		eapOptions = 0;
247	}
248
249	if (eapProperties) {
250		CFRelease(eapProperties);
251		eapProperties = 0;
252	}
253
254	if (eapSavePacket) {
255		free(eapSavePacket);
256		eapSavePacket = 0;
257	}
258
259	if (initialized_UI) {
260		eaptls_ui_dispose();
261	}
262
263    return EAP_NO_ERROR;
264}
265
266/* ------------------------------------------------------------------------------------
267------------------------------------------------------------------------------------ */
268int Process(void *context, struct EAP_Input *eap_in, struct EAP_Output *eap_out)
269{
270    struct EAP_Packet *pkt_in = NULL;
271    struct EAP_Packet *pkt_out = NULL;
272	EAPClientStatus status;
273	EAPClientState	state;
274	EAPClientDomainSpecificError error;
275	eaptls_ui_ctx *ui_ctx_in;
276	int do_process = 0;
277	CFDictionaryRef	publish_prop;
278
279	// by default, ignore the message
280	eap_out->action = EAP_ACTION_NONE;
281	eap_out->data = 0;
282	eap_out->data_len = 0;
283
284	switch (eap_in->notification) {
285
286		case EAP_NOTIFICATION_DATA_FROM_UI:
287
288			eap_in_ui = 0;
289
290			ui_ctx_in = (eaptls_ui_ctx *)eap_in->data;
291			switch (ui_ctx_in->response) {
292				case RESPONSE_OK:
293
294					// add the required property to the eap config dictionary
295					publish_prop = EAPClientModulePluginPublishProperties(eapRef, &eapData);
296					if (publish_prop) {
297						 CFArrayRef     chain;
298						 chain = CFDictionaryGetValue(publish_prop, kEAPClientPropTLSServerCertificateChain);
299						 if (chain) {
300							CFDictionarySetValue(eapProperties, kEAPClientPropTLSUserTrustProceedCertificateChain, chain);
301						 }
302						 CFRelease(publish_prop);
303					}
304					pkt_in = eapSavePacket;
305					do_process = 1;
306					break;
307				case RESPONSE_CANCEL:
308					eap_out->action = EAP_ACTION_CANCEL;
309					break;
310				case RESPONSE_ERROR:
311					eap_out->action = EAP_ACTION_ACCESS_DENIED;
312					break;
313			}
314			break;
315
316		case EAP_NOTIFICATION_PACKET:
317
318			pkt_in = (struct EAP_Packet *)eap_in->data;
319			do_process = 1;
320			break;
321	}
322
323	if (do_process) {
324
325		state = EAPClientModulePluginProcess(eapRef, &eapData, (EAPPacketRef)pkt_in, (EAPPacketRef*)&pkt_out, &status, &error);
326		switch(state) {
327			case kEAPClientStateAuthenticating:
328				switch (status) {
329
330					case kEAPClientStatusOK:
331						eap_out->data = pkt_out;
332						eap_out->data_len = ntohs(pkt_out->len);
333						eap_out->action = EAP_ACTION_SEND;
334						break;
335
336					case kEAPClientStatusUserInputRequired:
337
338						/* save last packet receive, will be process after user input obtained */
339						if (eapSavePacket)
340							free(eapSavePacket);
341						eapSavePacket = malloc(pkt_in->len);
342						if (!eapSavePacket) {
343							if (log_error)
344								(log_error)("EAP-TLS: no memory to save packet\n");
345							eap_out->action = EAP_ACTION_ACCESS_DENIED;
346						}
347						bcopy(pkt_in, eapSavePacket, pkt_in->len);
348
349						// waiting for UI notification, ignore the request
350						if (eap_in_ui)
351							break;
352
353						eap_in_ui = 1;
354						eap_out->action = EAP_ACTION_INVOKE_UI;
355						bzero(&ui_ctx, sizeof(ui_ctx));
356						ui_ctx.request = REQUEST_TRUST_EVAL;
357						eap_out->data = &ui_ctx;
358						eap_out->data_len = sizeof(ui_ctx);
359						break;
360
361					default:
362						eap_out->action = EAP_ACTION_ACCESS_DENIED;
363				}
364				break;
365
366			case kEAPClientStateSuccess:
367				eap_out->action = EAP_ACTION_ACCESS_GRANTED;
368				break;
369
370			default:
371			case kEAPClientStateFailure:
372				eap_out->action = EAP_ACTION_ACCESS_DENIED;
373				break;
374		}
375	}
376
377	if (eapSavePacket && !eap_in_ui) {
378		free(eapSavePacket);
379		eapSavePacket = 0;
380	}
381
382    return 0;
383}
384
385/* ------------------------------------------------------------------------------------
386------------------------------------------------------------------------------------ */
387int Free(void *context, struct EAP_Output *eap_out)
388{
389
390	EAPClientModulePluginFreePacket(eapRef, &eapData, eap_out->data);
391    return EAP_NO_ERROR;
392}
393
394/* ------------------------------------------------------------------------------------
395------------------------------------------------------------------------------------ */
396int GetAttribute(void *context, struct EAP_Attribute *eap_attr)
397{
398	void *data = NULL;
399	int len = 0;
400
401	eap_attr->data = 0;
402
403    switch (eap_attr->type) {
404
405        case EAP_ATTRIBUTE_MPPE_SEND_KEY:
406            data = EAPClientModulePluginSessionKey(eapRef, &eapData, &len);
407            break;
408        case EAP_ATTRIBUTE_MPPE_RECV_KEY:
409            data = EAPClientModulePluginServerKey(eapRef, &eapData, &len);
410            break;
411    }
412
413	if (data == NULL)
414		return -1;
415
416	eap_attr->data = data;
417	eap_attr->data_len = len;
418    return 0;
419}
420
421/* ------------------------------------------------------------------------------------
422------------------------------------------------------------------------------------ */
423int InteractiveUI(void *data_in, int data_in_len,
424                    void **data_out, int *data_out_len)
425{
426
427    eaptls_ui_ctx	*ctx = (eaptls_ui_ctx *)data_in;
428	CFDictionaryRef	publish_prop;
429
430    ctx->response = RESPONSE_OK;
431
432    if (!initialized_UI)
433        return -1;
434
435    switch (ctx->request)
436    {
437        case REQUEST_TRUST_EVAL:
438
439			publish_prop = EAPClientModulePluginPublishProperties(eapRef, &eapData);
440			if (publish_prop == NULL) {
441				ctx->response = RESPONSE_ERROR;
442				break;
443			}
444
445            eaptls_ui_trusteval(publish_prop, data_in, data_in_len, data_out, data_out_len);
446
447			CFRelease(publish_prop);
448
449            break;
450        default:
451            break;
452    }
453    return EAP_NO_ERROR;
454}
455