]>
Commit | Line | Data |
---|---|---|
65c25746 A |
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 | ||
52 | static CFBundleRef bundle = 0; /* our bundle ref */ | |
53 | static char eapaka_unique[17]; | |
54 | ||
55 | static EAPClientModuleRef eapRef = NULL; | |
56 | static EAPClientPluginData eapData; | |
57 | static CFMutableDictionaryRef eapProperties = NULL; | |
58 | static CFDictionaryRef eapOptions = NULL; | |
59 | static struct EAP_Packet *eapSavePacket = NULL; | |
60 | ||
61 | extern EAPClientPluginFuncRef | |
62 | eapaka_introspect(EAPClientPluginFuncName name); | |
63 | ||
64 | /* ------------------------------------------------------------------------------------ | |
65 | get the EAP dictionary from the options | |
66 | ------------------------------------------------------------------------------------ */ | |
67 | static void | |
68 | EAPAKAGetOptions (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 | ------------------------------------------------------------------------------------ */ | |
80 | static int | |
81 | EAPAKALoad (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 | ------------------------------------------------------------------------------------ */ | |
105 | int 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 | ------------------------------------------------------------------------------------ */ | |
136 | int | |
137 | EAPAKAInit (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 | ||
196 | failed: | |
197 | ||
198 | return ret; | |
199 | } | |
200 | ||
201 | /* ------------------------------------------------------------------------------------ | |
202 | ------------------------------------------------------------------------------------ */ | |
203 | int 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 | ------------------------------------------------------------------------------------ */ | |
234 | int | |
235 | EAPAKAProcess (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 | ------------------------------------------------------------------------------------ */ | |
303 | int | |
304 | EAPAKAFree (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 | ------------------------------------------------------------------------------------ */ | |
313 | int | |
314 | EAPAKAGetAttribute (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 | } |