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