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