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