1/*
2 * Copyright (c) 2012 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 <stdio.h>
25#include <sys/types.h>
26#include <sys/uio.h>
27#include <unistd.h>
28#include <syslog.h>
29#include <sys/fcntl.h>
30#include <CoreFoundation/CoreFoundation.h>
31#include <CoreFoundation/CFBundle.h>
32#include <mach/mach.h>
33#include <EAP8021X/EAP.h>
34#include <EAP8021X/EAPClientModule.h>
35#include <EAP8021X/EAPClientProperties.h>
36#if !TARGET_OS_EMBEDDED	// This file is not built for Embedded
37#include <Security/SecKeychain.h>
38#include <Security/SecKeychainSearch.h>
39#include <Security/SecKeychainItem.h>
40#include <Security/SecIdentity.h>
41#endif /* TARGET_OS_EMBEDDED */
42#include <SystemConfiguration/SCNetworkConnection.h>
43#include "plog.h"
44#include "eap.h"
45#include "eap_sim.h"
46
47/*---------------------------------------------------------------------------
48 ** Internal routines
49 **---------------------------------------------------------------------------
50 */
51
52static CFBundleRef 	bundle = 0;		/* our bundle ref */
53static char			eapsim_unique[17];
54
55static EAPClientModuleRef  eapRef = NULL;
56static EAPClientPluginData eapData;
57static CFMutableDictionaryRef eapProperties = NULL;
58static CFDictionaryRef eapOptions = NULL;
59static struct EAP_Packet	*eapSavePacket = NULL;
60
61extern EAPClientPluginFuncRef eapsim_introspect(EAPClientPluginFuncName name);
62
63/* ------------------------------------------------------------------------------------
64 get the EAP dictionary from the options
65 ------------------------------------------------------------------------------------ */
66static void
67EAPSIMGetOptions (void)
68{
69	if (eapOptions)
70		return;
71
72	// no option, use empty dictionary
73	if (!eapOptions)
74		eapOptions = CFDictionaryCreate(0, 0, 0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
75}
76
77/* ------------------------------------------------------------------------------------
78 ------------------------------------------------------------------------------------ */
79static int
80EAPSIMLoad (void)
81{
82    EAPClientModuleStatus status;
83
84	if (eapRef)
85		return EAP_NO_ERROR;
86
87	status = EAPClientModuleAddBuiltinModule(eapsim_introspect);
88	if (status != kEAPClientModuleStatusOK) {
89		plog(ASL_LEVEL_INFO, "EAP-SIM: EAPClientAddBuiltinModule(eapsim) failed %d\n", status);
90		return EAP_ERROR_GENERIC;
91	}
92
93	eapRef = EAPClientModuleLookup(kEAPTypeEAPSIM);
94	if (eapRef == NULL) {
95		plog(ASL_LEVEL_INFO, "EAP-SIM: EAPClientModuleLookup(eapsim) failed\n");
96		return EAP_ERROR_GENERIC;
97	}
98
99	return EAP_NO_ERROR;
100}
101
102/* ------------------------------------------------------------------------------------
103 ------------------------------------------------------------------------------------ */
104int EAPSIMIdentity (char *identity, int maxlen)
105{
106    CFStringRef			identRef = NULL;
107	int					error;
108	int					ret = EAP_ERROR_GENERIC;
109
110	error = EAPSIMLoad();
111	if (error)
112		return error;
113
114	EAPSIMGetOptions();
115	if (eapOptions == NULL)
116		return ret;
117
118	identRef = EAPClientModulePluginUserName(eapRef, eapOptions);
119    if (identRef) {
120		if (CFStringGetCString(identRef, identity, maxlen, kCFStringEncodingUTF8))
121			ret = EAP_NO_ERROR;
122		CFRelease(identRef);
123	}
124
125	return ret;
126}
127
128/* ------------------------------------------------------------------------------------
129 Init routine called by the EAP engine when it needs the module.
130 Identity of the peer is known at this point.
131 mode is 0 for client, 1 for server.
132 cookie is the EAP engine context, to pass to subsequent calls to EAP.
133 context is EAP module context, that will be passed to subsequent calls to the module
134 ------------------------------------------------------------------------------------ */
135int
136EAPSIMInit (EAP_Input_t *eap_in, void **context, CFDictionaryRef eapOptions)
137{
138	int error;
139    EAPClientModuleStatus status;
140	int ret = EAP_ERROR_GENERIC;
141
142	error = EAPSIMLoad();
143	if (error)
144		return error;
145
146	bundle = (CFBundleRef)eap_in->data;
147    if (bundle)
148		CFRetain(bundle);
149
150	EAPSIMGetOptions();
151
152	bzero(&eapData, sizeof(eapData));
153
154    /* remaining fields are read-only: */
155	*((bool *)&eapData.log_enabled) = 1;
156	*((uint32_t *)&eapData.log_level) = LOG_NOTICE;
157	*((uint32_t *)&eapData.mtu) = eap_in->mtu;
158	*((uint32_t *)&eapData.generation) = 0;/* changed when user updates */
159
160	arc4random_buf(eapsim_unique, sizeof(eapsim_unique) - 1);
161	eapsim_unique[sizeof(eapsim_unique)-1] = 0;
162
163    eapData.unique_id = eapsim_unique;  /* used for TLS session resumption??? */
164	*((uint32_t *)&eapData.unique_id_length) = strlen(eapData.unique_id);
165
166	if (eapOptions) {
167        CFTypeRef value = CFDictionaryGetValue(eapOptions, kEAPPropertiesTypeEAPSIM);
168        if (value && CFGetTypeID(value) == CFDictionaryGetTypeID()) {
169            eapProperties = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, (CFDictionaryRef)value);
170        } else {
171            eapProperties = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, eapOptions);
172        }
173	} else
174		eapProperties = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
175	if (eapProperties == NULL) {
176		plog(ASL_LEVEL_ERR, "EAP-SIM: Cannot allocate memory\n");
177		goto failed;
178	}
179
180	*((CFDictionaryRef *)&eapData.properties) = (CFDictionaryRef)eapProperties;
181
182	status = EAPClientModulePluginInit(eapRef, &eapData, NULL, &error);
183	if (status != kEAPClientStatusOK) {
184		plog(ASL_LEVEL_ERR, "EAP-SIM: EAPClientPluginInit(eapsim) failed, error %d\n", status);
185		goto failed;
186	}
187
188	eapSavePacket = NULL;
189
190    return EAP_NO_ERROR;
191
192failed:
193
194    return ret;
195}
196
197/* ------------------------------------------------------------------------------------
198 ------------------------------------------------------------------------------------ */
199int EAPSIMDispose (void *context)
200{
201
202	EAPClientModulePluginFree(eapRef, &eapData);
203	eapRef = 0;
204
205	if (bundle) {
206		CFRelease(bundle);
207		bundle = 0;
208	}
209
210	if (eapOptions) {
211		CFRelease(eapOptions);
212		eapOptions = 0;
213	}
214
215	if (eapProperties) {
216		CFRelease(eapProperties);
217		eapProperties = 0;
218	}
219
220	if (eapSavePacket) {
221		free(eapSavePacket);
222		eapSavePacket = 0;
223	}
224
225    return EAP_NO_ERROR;
226}
227
228/* ------------------------------------------------------------------------------------
229 ------------------------------------------------------------------------------------ */
230int
231EAPSIMProcess (void *context, EAP_Input_t *eap_in, EAP_Output_t *eap_out)
232{
233    struct EAP_Packet *pkt_in = NULL;
234    struct EAP_Packet *pkt_out = NULL;
235	EAPClientStatus status;
236	EAPClientState	state;
237	EAPClientDomainSpecificError error;
238	int do_process = 0;
239
240	// by default, ignore the message
241	eap_out->action = EAP_ACTION_NONE;
242	eap_out->data = 0;
243	eap_out->data_len = 0;
244
245	switch (eap_in->notification) {
246
247		case EAP_NOTIFICATION_DATA_FROM_UI:
248			plog(ASL_LEVEL_ERR, "unexpected EAP UI event");
249			break;
250
251		case EAP_NOTIFICATION_PACKET:
252
253			pkt_in = (struct EAP_Packet *)eap_in->data;
254			do_process = 1;
255			break;
256	}
257
258	if (do_process) {
259
260		state = EAPClientModulePluginProcess(eapRef, &eapData, (EAPPacketRef)pkt_in, (EAPPacketRef*)&pkt_out, &status, &error);
261		switch(state) {
262			case kEAPClientStateAuthenticating:
263				switch (status) {
264
265					case kEAPClientStatusOK:
266						eap_out->data = pkt_out;
267						eap_out->data_len = ntohs(pkt_out->len);
268						eap_out->action = EAP_ACTION_SEND;
269						break;
270
271					case kEAPClientStatusUserInputRequired:
272						plog(ASL_LEVEL_ERR, "unsupported EAP UI input");
273					default:
274						eap_out->action = EAP_ACTION_ACCESS_DENIED;
275				}
276				break;
277
278			case kEAPClientStateSuccess:
279				eap_out->action = EAP_ACTION_ACCESS_GRANTED;
280				break;
281
282			default:
283			case kEAPClientStateFailure:
284				eap_out->action = EAP_ACTION_ACCESS_DENIED;
285				break;
286		}
287	}
288
289	if (eapSavePacket) {
290		free(eapSavePacket);
291		eapSavePacket = 0;
292	}
293
294    return 0;
295}
296
297/* ------------------------------------------------------------------------------------
298 ------------------------------------------------------------------------------------ */
299int
300EAPSIMFree (void *context, EAP_Output_t *eap_out)
301{
302
303	EAPClientModulePluginFreePacket(eapRef, &eapData, eap_out->data);
304    return EAP_NO_ERROR;
305}
306
307/* ------------------------------------------------------------------------------------
308 ------------------------------------------------------------------------------------ */
309int
310EAPSIMGetAttribute (void *context, EAP_Attribute_t *eap_attr)
311{
312	void *data = NULL;
313	int len = 0;
314
315	eap_attr->data = 0;
316
317    switch (eap_attr->type) {
318
319        case EAP_ATTRIBUTE_MPPE_SEND_KEY:
320            data = EAPClientModulePluginSessionKey(eapRef, &eapData, &len);
321            break;
322        case EAP_ATTRIBUTE_MPPE_RECV_KEY:
323            data = EAPClientModulePluginServerKey(eapRef, &eapData, &len);
324            break;
325    }
326
327	if (data == NULL)
328		return -1;
329
330	eap_attr->data = data;
331    if (len == 32)
332        eap_attr->data_len = 64;
333    else
334        eap_attr->data_len = len;
335    return 0;
336}
337