1/* 2 * Copyright (c) 2002-2014 Apple 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/* 25 * eapttls_plugin.c 26 * - EAP-TTLS client using SecureTransport API's 27 */ 28 29/* 30 * Modification History 31 * 32 * October 1, 2002 Dieter Siegmund (dieter@apple) 33 * - created (from eaptls_plugin.c) 34 * 35 * September 7, 2004 Dieter Siegmund (dieter@apple) 36 * - use SecTrustEvaluate, and enable user interaction to decide whether to 37 * proceed or not, instead of just generating an error 38 */ 39 40#include <EAP8021X/EAPClientPlugin.h> 41#include <EAP8021X/EAPClientProperties.h> 42#include <SystemConfiguration/SCValidation.h> 43#include <mach/boolean.h> 44#include <unistd.h> 45#include <stdlib.h> 46#include <string.h> 47#include <stdio.h> 48#include <Security/SecureTransport.h> 49#include <Security/SecCertificate.h> 50#include <sys/param.h> 51#include <EAP8021X/EAPTLSUtil.h> 52#include <EAP8021X/EAPUtil.h> 53#include <EAP8021X/EAPSecurity.h> 54#include <EAP8021X/EAPCertificateUtil.h> 55#include <Security/SecureTransportPriv.h> 56#include <EAP8021X/EAP.h> 57#include <EAP8021X/EAPClientModule.h> 58#include <EAP8021X/chap.h> 59#include <EAP8021X/mschap.h> 60#include <EAP8021X/RADIUSAttributes.h> 61#include <EAP8021X/DiameterAVP.h> 62#include "myCFUtil.h" 63#include "printdata.h" 64#include "EAPLog.h" 65 66/* 67 * Declare these here to ensure that the compiler 68 * generates appropriate errors/warnings 69 */ 70EAPClientPluginFuncIntrospect eapttls_introspect; 71static EAPClientPluginFuncVersion eapttls_version; 72static EAPClientPluginFuncEAPType eapttls_type; 73static EAPClientPluginFuncEAPName eapttls_name; 74static EAPClientPluginFuncInit eapttls_init; 75static EAPClientPluginFuncFree eapttls_free; 76static EAPClientPluginFuncProcess eapttls_process; 77static EAPClientPluginFuncFreePacket eapttls_free_packet; 78static EAPClientPluginFuncSessionKey eapttls_session_key; 79static EAPClientPluginFuncServerKey eapttls_server_key; 80static EAPClientPluginFuncMasterSessionKeyCopyBytes eapttls_msk_copy_bytes; 81static EAPClientPluginFuncRequireProperties eapttls_require_props; 82static EAPClientPluginFuncPublishProperties eapttls_publish_props; 83static EAPClientPluginFuncCopyPacketDescription eapttls_copy_packet_description; 84 85#define kEAPTTLSClientLabel "ttls keying material" 86#define kEAPTTLSClientLabelLength (sizeof(kEAPTTLSClientLabel) - 1) 87#define kEAPTTLSChallengeLabel "ttls challenge" 88#define kEAPTTLSChallengeLabelLength (sizeof(kEAPTTLSChallengeLabel) - 1) 89 90typedef enum { 91 kInnerAuthTypeNone = 0, 92 kInnerAuthTypePAP, 93 kInnerAuthTypeCHAP, 94 kInnerAuthTypeMSCHAP, 95 kInnerAuthTypeMSCHAPv2, 96 kInnerAuthTypeEAP, 97} InnerAuthType; 98 99static const char * auth_strings[] = { 100 "none", 101 "PAP", 102 "CHAP", 103 "MSCHAP", 104 "MSCHAPv2", 105 "EAP", 106 NULL, 107}; 108 109typedef enum { 110 kRequestTypeStart, 111 kRequestTypeAck, 112 kRequestTypeData, 113} RequestType; 114 115typedef enum { 116 kAuthStateIdle, 117 kAuthStateStarted, 118 kAuthStateComplete, 119} AuthState; 120#define TTLS_MSCHAP_RESPONSE_LENGTH (MSCHAP_NT_RESPONSE_SIZE \ 121 + MSCHAP_LM_RESPONSE_SIZE \ 122 + MSCHAP_FLAGS_SIZE \ 123 + MSCHAP_IDENT_SIZE) 124 125#define TTLS_MSCHAP2_RESPONSE_LENGTH (MSCHAP2_RESPONSE_LENGTH \ 126 + MSCHAP_IDENT_SIZE) 127typedef struct { 128 SSLContextRef ssl_context; 129 memoryBuffer read_buffer; 130 memoryBuffer write_buffer; 131 int last_write_size; 132 int previous_identifier; 133 memoryIO mem_io; 134 EAPClientState plugin_state; 135 bool cert_is_required; 136 CFArrayRef certs; 137 int mtu; 138 OSStatus last_ssl_error; 139 EAPClientStatus last_client_status; 140 InnerAuthType inner_auth_type; 141 bool handshake_complete; 142 bool authentication_started; 143 OSStatus trust_ssl_error; 144 EAPClientStatus trust_status; 145 bool trust_proceed; 146 bool key_data_valid; 147 char key_data[128]; 148 bool server_auth_completed; 149 CFArrayRef server_certs; 150 bool resume_sessions; 151 bool session_was_resumed; 152 153 /* MSCHAPv2 state: */ 154 uint8_t peer_challenge[MSCHAP2_CHALLENGE_SIZE]; 155 uint8_t nt_response[MSCHAP_NT_RESPONSE_SIZE]; 156 uint8_t auth_challenge_id[MSCHAP2_CHALLENGE_SIZE + 1]; 157} EAPTTLSPluginData, * EAPTTLSPluginDataRef; 158 159enum { 160 kEAPTLSAvoidDenialOfServiceSize = 128 * 1024 161}; 162 163#define BAD_IDENTIFIER (-1) 164 165static InnerAuthType 166InnerAuthTypeFromString(char * str) 167{ 168 int i; 169 170 for (i = 0; auth_strings[i] != NULL; i++) { 171 if (strcmp(str, auth_strings[i]) == 0) { 172 return ((InnerAuthType)i); 173 } 174 } 175 return (kInnerAuthTypeNone); 176} 177 178static bool 179eapttls_compute_session_key(EAPTTLSPluginDataRef context) 180{ 181 OSStatus status; 182 183 context->key_data_valid = FALSE; 184 status = EAPTLSComputeKeyData(context->ssl_context, 185 kEAPTTLSClientLabel, 186 kEAPTTLSClientLabelLength, 187 context->key_data, 188 sizeof(context->key_data)); 189 if (status != noErr) { 190 EAPLOG_FL(LOG_NOTICE, 191 "EAPTLSComputeSessionKey failed, %s", 192 EAPSSLErrorString(status)); 193 return (FALSE); 194 } 195 context->key_data_valid = TRUE; 196 return (TRUE); 197} 198 199static void 200eapttls_free_context(EAPTTLSPluginDataRef context) 201{ 202 if (context->ssl_context != NULL) { 203 CFRelease(context->ssl_context); 204 context->ssl_context = NULL; 205 } 206 my_CFRelease(&context->certs); 207 my_CFRelease(&context->server_certs); 208 memoryIOClearBuffers(&context->mem_io); 209 free(context); 210 return; 211} 212 213static OSStatus 214eapttls_start(EAPClientPluginDataRef plugin) 215{ 216 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 217 SSLContextRef ssl_context = NULL; 218 OSStatus status = noErr; 219 220 if (context->ssl_context != NULL) { 221 CFRelease(context->ssl_context); 222 context->ssl_context = NULL; 223 } 224 my_CFRelease(&context->server_certs); 225 memoryIOClearBuffers(&context->mem_io); 226 ssl_context = EAPTLSMemIOContextCreate(FALSE, &context->mem_io, NULL, 227 &status); 228 if (ssl_context == NULL) { 229 EAPLOG_FL(LOG_NOTICE, "EAPTLSMemIOContextCreate failed, %s", 230 EAPSSLErrorString(status)); 231 goto failed; 232 } 233 if (context->resume_sessions && plugin->unique_id != NULL) { 234 status = SSLSetPeerID(ssl_context, plugin->unique_id, 235 plugin->unique_id_length); 236 if (status != noErr) { 237 EAPLOG_FL(LOG_NOTICE, 238 "SSLSetPeerID failed, %s", EAPSSLErrorString(status)); 239 goto failed; 240 } 241 } 242 if (context->cert_is_required) { 243 if (context->certs == NULL) { 244 status = EAPTLSCopyIdentityTrustChain(plugin->sec_identity, 245 plugin->properties, 246 &context->certs); 247 if (status != noErr) { 248 EAPLOG_FL(LOG_NOTICE, 249 "failed to find client cert/identity, %s (%ld)", 250 EAPSSLErrorString(status), (long)status); 251 goto failed; 252 } 253 } 254 status = SSLSetCertificate(ssl_context, context->certs); 255 if (status != noErr) { 256 EAPLOG_FL(LOG_NOTICE, 257 "SSLSetCertificate failed, %s", 258 EAPSSLErrorString(status)); 259 goto failed; 260 } 261 } 262 context->ssl_context = ssl_context; 263 context->plugin_state = kEAPClientStateAuthenticating; 264 context->previous_identifier = BAD_IDENTIFIER; 265 context->last_ssl_error = noErr; 266 context->last_client_status = kEAPClientStatusOK; 267 context->handshake_complete = FALSE; 268 context->authentication_started = FALSE; 269 context->trust_proceed = FALSE; 270 context->server_auth_completed = FALSE; 271 context->key_data_valid = FALSE; 272 context->last_write_size = 0; 273 context->session_was_resumed = FALSE; 274 return (status); 275 failed: 276 if (ssl_context != NULL) { 277 CFRelease(ssl_context); 278 } 279 return (status); 280} 281 282static InnerAuthType 283get_inner_auth_type(CFDictionaryRef properties) 284{ 285 InnerAuthType inner_auth_type = kInnerAuthTypeNone; 286 CFStringRef inner_auth_cf; 287 288 if (properties != NULL) { 289 inner_auth_cf 290 = CFDictionaryGetValue(properties, 291 kEAPClientPropTTLSInnerAuthentication); 292 if (isA_CFString(inner_auth_cf) != NULL) { 293 char * inner_auth = NULL; 294 295 inner_auth = my_CFStringToCString(inner_auth_cf, 296 kCFStringEncodingASCII); 297 if (inner_auth != NULL) { 298 inner_auth_type = InnerAuthTypeFromString(inner_auth); 299 free(inner_auth); 300 } 301 } 302 } 303 return (inner_auth_type); 304} 305 306static EAPClientStatus 307eapttls_init(EAPClientPluginDataRef plugin, CFArrayRef * required_props, 308 EAPClientDomainSpecificError * error) 309{ 310 EAPTTLSPluginDataRef context = NULL; 311 InnerAuthType inner_auth_type; 312 313 context = malloc(sizeof(*context)); 314 bzero(context, sizeof(*context)); 315 context->cert_is_required 316 = my_CFDictionaryGetBooleanValue(plugin->properties, 317 kEAPClientPropTLSCertificateIsRequired, 318 FALSE); 319 context->mtu = plugin->mtu; 320 inner_auth_type = get_inner_auth_type(plugin->properties); 321 if (inner_auth_type == kInnerAuthTypeNone) { 322 inner_auth_type = kInnerAuthTypeMSCHAPv2; 323 } 324 context->inner_auth_type = inner_auth_type; 325 context->resume_sessions 326 = my_CFDictionaryGetBooleanValue(plugin->properties, 327 kEAPClientPropTLSEnableSessionResumption, 328 TRUE); 329 /* memoryIOInit() initializes the memoryBuffer structures as well */ 330 memoryIOInit(&context->mem_io, &context->read_buffer, 331 &context->write_buffer); 332 //memoryIOSetDebug(&context->mem_io, TRUE); 333 plugin->private = context; 334 335 *error = 0; 336 return (kEAPClientStatusOK); 337} 338 339static void 340eapttls_free(EAPClientPluginDataRef plugin) 341{ 342 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 343 344 if (context != NULL) { 345 eapttls_free_context(context); 346 plugin->private = NULL; 347 } 348 return; 349} 350 351static void 352eapttls_free_packet(EAPClientPluginDataRef plugin, EAPPacketRef arg) 353{ 354 if (arg != NULL) { 355 free(arg); 356 } 357 return; 358} 359 360static EAPPacketRef 361EAPTTLSPacketCreateAck(int identifier) 362{ 363 return (EAPTLSPacketCreate(kEAPCodeResponse, kEAPTypeTTLS, 364 identifier, 0, NULL, NULL)); 365} 366 367static bool 368eapttls_pap(EAPClientPluginDataRef plugin) 369{ 370 DiameterAVP * avp; 371 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 372 void * data; 373 int data_length; 374 void * offset; 375 size_t length; 376 int password_length_r; 377 bool ret = TRUE; 378 OSStatus status; 379 int user_length_r; 380 381 /* allocate buffer to hold message */ 382 password_length_r = roundup(plugin->password_length, 16); 383 user_length_r = roundup(plugin->username_length, 4); 384 data_length = sizeof(*avp) * 2 + user_length_r + password_length_r; 385 data = malloc(data_length); 386 if (data == NULL) { 387 EAPLOG_FL(LOG_NOTICE, "malloc failed"); 388 return (FALSE); 389 } 390 offset = data; 391 392 /* User-Name AVP */ 393 avp = (DiameterAVP *)offset; 394 avp->AVP_code = htonl(kRADIUSAttributeTypeUserName); 395 avp->AVP_flags_length 396 = htonl(DiameterAVPMakeFlagsLength(0, sizeof(*avp) 397 + plugin->username_length)); 398 offset = (void *)(avp + 1); 399 bcopy(plugin->username, offset, plugin->username_length); 400 if (user_length_r > plugin->username_length) { 401 bzero(offset + plugin->username_length, 402 user_length_r - plugin->username_length); 403 } 404 offset += user_length_r; 405 406 /* Password-Name AVP */ 407 avp = (DiameterAVP *)offset; 408 avp->AVP_code = htonl(kRADIUSAttributeTypeUserPassword); 409 avp->AVP_flags_length 410 = htonl(DiameterAVPMakeFlagsLength(0, 411 sizeof(*avp) + password_length_r)); 412 offset = (void *)(avp + 1); 413 bcopy(plugin->password, offset, plugin->password_length); 414 if (password_length_r > plugin->password_length) { 415 bzero(offset + plugin->password_length, 416 password_length_r - plugin->password_length); 417 } 418 offset += password_length_r; 419#if 0 420 printf("\n----------PAP Raw AVP Data START\n"); 421 print_data(data, offset - data); 422 printf("----------PAP Raw AVP Data END\n"); 423#endif /* 0 */ 424 status = SSLWrite(context->ssl_context, data, offset - data, &length); 425 if (status != noErr) { 426 EAPLOG_FL(LOG_NOTICE, "SSLWrite failed, %s", 427 EAPSSLErrorString(status)); 428 ret = FALSE; 429 } 430 free(data); 431 return (ret); 432} 433 434static bool 435eapttls_eap_start(EAPClientPluginDataRef plugin, int identifier) 436{ 437 DiameterAVP * avp; 438 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 439 void * data; 440 int data_length; 441 void * offset; 442 size_t length; 443 EAPResponsePacket * resp_p = NULL; 444 bool ret = TRUE; 445 OSStatus status; 446 447 /* allocate buffer to hold message */ 448 data_length = sizeof(*avp) + plugin->username_length + sizeof(*resp_p); 449 data = malloc(data_length); 450 if (data == NULL) { 451 EAPLOG_FL(LOG_NOTICE, "malloc failed"); 452 return (FALSE); 453 } 454 offset = data; 455 456 /* EAP AVP */ 457 avp = (DiameterAVP *)offset; 458 avp->AVP_code = htonl(kRADIUSAttributeTypeEAPMessage); 459 avp->AVP_flags_length = htonl(DiameterAVPMakeFlagsLength(0, data_length)); 460 offset = (void *)(avp + 1); 461 resp_p = (EAPResponsePacket *)offset; 462 resp_p->code = kEAPCodeResponse; 463 resp_p->identifier = 0; /* identifier */ 464 EAPPacketSetLength((EAPPacketRef)resp_p, 465 sizeof(*resp_p) + plugin->username_length); 466 467 resp_p->type = kEAPTypeIdentity; 468 bcopy(plugin->username, resp_p->type_data, plugin->username_length); 469 offset += sizeof(*resp_p) + plugin->username_length; 470#if 0 471 printf("offset - data %d length %d\n", offset - data, data_length); 472#endif /* 0 */ 473 status = SSLWrite(context->ssl_context, data, offset - data, &length); 474 if (status != noErr) { 475 EAPLOG_FL(LOG_NOTICE, "SSLWrite failed, %s", 476 EAPSSLErrorString(status)); 477 ret = FALSE; 478 } 479 free(data); 480 return (ret); 481} 482 483/* 484 * Function: eapttls_chap 485 * Purpose: 486 * Generate a packet containing the response to an implicit 487 * CHAP challenge. 488 */ 489 490static bool 491eapttls_chap(EAPClientPluginDataRef plugin) 492{ 493 DiameterAVP * avp; 494 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 495 void * data; 496 int data_length; 497 int data_length_r; 498 uint8_t key_data[17]; 499 size_t length; 500 void * offset; 501 bool ret = TRUE; 502 OSStatus status; 503 int user_length_r; 504 505 user_length_r = roundup(plugin->username_length, 4); 506 status = EAPTLSComputeKeyData(context->ssl_context, 507 kEAPTTLSChallengeLabel, 508 kEAPTTLSChallengeLabelLength, 509 key_data, sizeof(key_data)); 510 if (status != noErr) { 511 EAPLOG_FL(LOG_NOTICE, "EAPTLSComputeKeyData failed, %s", 512 EAPSSLErrorString(status)); 513 return (FALSE); 514 } 515 516 /* allocate buffer to hold message */ 517 data_length = sizeof(*avp) * 3 518 + user_length_r 519 + 16 /* challenge */ 520 + 1 + 16; /* identifier + response */ 521 data_length_r = roundup(data_length, 4); 522 data = malloc(data_length_r); 523 if (data == NULL) { 524 EAPLOG_FL(LOG_NOTICE, "malloc failed"); 525 return (FALSE); 526 } 527 offset = data; 528 529 /* User-Name AVP */ 530 avp = (DiameterAVP *)offset; 531 avp->AVP_code = htonl(kRADIUSAttributeTypeUserName); 532 avp->AVP_flags_length 533 = htonl(DiameterAVPMakeFlagsLength(0, sizeof(*avp) 534 + plugin->username_length)); 535 offset = (void *)(avp + 1); 536 bcopy(plugin->username, offset, plugin->username_length); 537 if (user_length_r > plugin->username_length) { 538 bzero(offset + plugin->username_length, 539 user_length_r - plugin->username_length); 540 } 541 offset += user_length_r; 542 543 /* CHAP-Challenge AVP */ 544 avp = (DiameterAVP *)offset; 545 avp->AVP_code = htonl(kRADIUSAttributeTypeCHAPChallenge); 546 avp->AVP_flags_length 547 = htonl(DiameterAVPMakeFlagsLength(0, sizeof(*avp) + 16)); 548 offset = (void *)(avp + 1); 549 bcopy(key_data, offset, 16); 550 offset += 16; 551 552 /* CHAP-Password AVP */ 553 avp = (DiameterAVP *)offset; 554 avp->AVP_code = htonl(kRADIUSAttributeTypeCHAPPassword); 555 avp->AVP_flags_length 556 = htonl(DiameterAVPMakeFlagsLength(0, sizeof(*avp) + 17)); 557 offset = (void *)(avp + 1); 558 *((u_char *)offset) = key_data[16]; /* identifier */ 559 offset++; 560 chap_md5(key_data[16], plugin->password, plugin->password_length, 561 key_data, 16, offset); 562 offset += 16; 563 { /* pad out with zeroes */ 564 int pad_length = data_length_r - data_length; 565 if (pad_length != 0) { 566 bzero(offset, pad_length); 567 offset += pad_length; 568 } 569 } 570#if 0 571 printf("\n----------CHAP Raw AVP Data START\n"); 572 print_data(data, offset - data); 573 printf("----------CHAP Raw AVP Data END\n"); 574#endif /* 0 */ 575 status = SSLWrite(context->ssl_context, data, offset - data, &length); 576 if (status != noErr) { 577 EAPLOG_FL(LOG_NOTICE, "SSLWrite failed, %s", 578 EAPSSLErrorString(status)); 579 ret = FALSE; 580 } 581 free(data); 582 return (ret); 583} 584 585/* 586 * Function: eapttls_mschap 587 * Purpose: 588 * Generate a packet containing the response to an implicit 589 * MS-CHAP challenge. 590 */ 591 592static bool 593eapttls_mschap(EAPClientPluginDataRef plugin) 594{ 595 DiameterAVP * avp; 596 DiameterVendorAVP * avpv; 597 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 598 void * data; 599 int data_length; 600 int data_length_r; 601 uint8_t key_data[MSCHAP_NT_CHALLENGE_SIZE + MSCHAP_IDENT_SIZE]; 602 size_t length; 603 void * offset; 604 bool ret = TRUE; 605 uint8_t response[MSCHAP_NT_RESPONSE_SIZE]; 606 OSStatus status; 607 int user_length_r; 608 609 user_length_r = roundup(plugin->username_length, 4); 610 status = EAPTLSComputeKeyData(context->ssl_context, 611 kEAPTTLSChallengeLabel, 612 kEAPTTLSChallengeLabelLength, 613 key_data, sizeof(key_data)); 614 if (status != noErr) { 615 EAPLOG_FL(LOG_NOTICE, "EAPTLSComputeKeyData failed, %s", 616 EAPSSLErrorString(status)); 617 return (FALSE); 618 } 619 620 /* allocate buffer to hold message */ 621 data_length = sizeof(*avp) + sizeof(*avpv) * 2 622 + user_length_r 623 + MSCHAP_NT_CHALLENGE_SIZE /* challenge */ 624 + TTLS_MSCHAP_RESPONSE_LENGTH;/* flags + identifer + {NT,LM}Response */ 625 data_length_r = roundup(data_length, 4); 626 data = malloc(data_length_r); 627 if (data == NULL) { 628 EAPLOG_FL(LOG_NOTICE, "malloc failed"); 629 return (FALSE); 630 } 631 offset = data; 632 633 /* User-Name AVP */ 634 avp = (DiameterAVP *)offset; 635 avp->AVP_code = htonl(kRADIUSAttributeTypeUserName); 636 avp->AVP_flags_length 637 = htonl(DiameterAVPMakeFlagsLength(0, sizeof(*avp) 638 + plugin->username_length)); 639 offset = (void *)(avp + 1); 640 bcopy(plugin->username, offset, plugin->username_length); 641 if (user_length_r > plugin->username_length) { 642 bzero(offset + plugin->username_length, 643 user_length_r - plugin->username_length); 644 } 645 offset += user_length_r; 646 647 /* MS-CHAP-Challenge AVP */ 648 avpv = (DiameterVendorAVP *)offset; 649 avpv->AVPV_code = htonl(kMSRADIUSAttributeTypeMSCHAPChallenge); 650 avpv->AVPV_flags_length 651 = htonl(DiameterAVPMakeFlagsLength(kDiameterFlagsVendorSpecific, 652 sizeof(*avpv) 653 + MSCHAP_NT_CHALLENGE_SIZE)); 654 avpv->AVPV_vendor = htonl(kRADIUSVendorIdentifierMicrosoft); 655 offset = (void *)(avpv + 1); 656 bcopy(key_data, offset, MSCHAP_NT_CHALLENGE_SIZE); 657 offset += MSCHAP_NT_CHALLENGE_SIZE; 658 659 /* MS-CHAP-Response AVP */ 660 avpv = (DiameterVendorAVP *)offset; 661 avpv->AVPV_code = htonl(kMSRADIUSAttributeTypeMSCHAPResponse); 662 avpv->AVPV_flags_length 663 = htonl(DiameterAVPMakeFlagsLength(kDiameterFlagsVendorSpecific, 664 sizeof(*avpv) 665 + TTLS_MSCHAP_RESPONSE_LENGTH)); 666 avpv->AVPV_vendor = htonl(kRADIUSVendorIdentifierMicrosoft); 667 offset = (void *)(avpv + 1); 668 *((u_char *)offset) = key_data[MSCHAP_NT_CHALLENGE_SIZE]; /* ident */ 669 offset++; 670 *((u_char *)offset) = 1; /* flags: 1 = use NT-Response */ 671 offset++; 672 bzero(offset, MSCHAP_LM_RESPONSE_SIZE);/* LM-Response: not used */ 673 offset += MSCHAP_LM_RESPONSE_SIZE; 674 MSChap(key_data, plugin->password, 675 plugin->password_length, response); /* NT-Response */ 676 bcopy(response, offset, MSCHAP_NT_RESPONSE_SIZE); 677 offset += MSCHAP_NT_RESPONSE_SIZE; 678 { /* pad out with zeroes */ 679 int pad_length = data_length_r - data_length; 680 if (pad_length != 0) { 681 bzero(offset, pad_length); 682 offset += pad_length; 683 } 684 } 685#if 0 686 printf("offset - data %d length %d\n", offset - data, data_length); 687 printf("\n----------MSCHAP Raw AVP Data START\n"); 688 print_data(data, offset - data); 689 printf("----------MSCHAP Raw AVP Data END\n"); 690#endif /* 0 */ 691 status = SSLWrite(context->ssl_context, data, offset - data, &length); 692 if (status != noErr) { 693 EAPLOG_FL(LOG_NOTICE, "SSLWrite failed, %s", 694 EAPSSLErrorString(status)); 695 ret = FALSE; 696 } 697 free(data); 698 return (ret); 699} 700 701/* 702 * Function: eapttls_mschapv2 703 * Purpose: 704 * Generate a packet containing the response to an implicit 705 * MS-CHAPv2 challenge. 706 */ 707 708static bool 709eapttls_mschap2(EAPClientPluginDataRef plugin) 710{ 711 DiameterAVP * avp; 712 DiameterVendorAVP * avpv; 713 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 714 void * data; 715 int data_length; 716 int data_length_r; 717 size_t length; 718 void * offset; 719 bool ret = TRUE; 720 OSStatus status; 721 int user_length_r; 722 723 user_length_r = roundup(plugin->username_length, 4); 724 status = EAPTLSComputeKeyData(context->ssl_context, 725 kEAPTTLSChallengeLabel, 726 kEAPTTLSChallengeLabelLength, 727 context->auth_challenge_id, 728 MSCHAP2_CHALLENGE_SIZE + 1); 729 if (status != noErr) { 730 EAPLOG_FL(LOG_NOTICE, "EAPTLSComputeKeyData failed, %s", 731 EAPSSLErrorString(status)); 732 return (FALSE); 733 } 734 735 /* allocate buffer to hold message */ 736 data_length = sizeof(*avp) + sizeof(*avpv) * 2 737 + user_length_r 738 + MSCHAP2_CHALLENGE_SIZE 739 + TTLS_MSCHAP2_RESPONSE_LENGTH; 740 data_length_r = roundup(data_length, 4); 741 data = malloc(data_length_r); 742 if (data == NULL) { 743 EAPLOG_FL(LOG_NOTICE, "malloc failed"); 744 return (FALSE); 745 } 746 offset = data; 747 748 /* User-Name AVP */ 749 avp = (DiameterAVP *)offset; 750 avp->AVP_code = htonl(kRADIUSAttributeTypeUserName); 751 avp->AVP_flags_length 752 = htonl(DiameterAVPMakeFlagsLength(0, sizeof(*avp) 753 + plugin->username_length)); 754 offset = (void *)(avp + 1); 755 bcopy(plugin->username, offset, plugin->username_length); 756 if (user_length_r > plugin->username_length) { 757 bzero(offset + plugin->username_length, 758 user_length_r - plugin->username_length); 759 } 760 offset += user_length_r; 761 762 /* MS-CHAP-Challenge AVP */ 763 avpv = (DiameterVendorAVP *)offset; 764 avpv->AVPV_code = htonl(kMSRADIUSAttributeTypeMSCHAPChallenge); 765 avpv->AVPV_flags_length 766 = htonl(DiameterAVPMakeFlagsLength(kDiameterFlagsVendorSpecific, 767 sizeof(*avpv) 768 + MSCHAP2_CHALLENGE_SIZE)); 769 avpv->AVPV_vendor = htonl(kRADIUSVendorIdentifierMicrosoft); 770 offset = (void *)(avpv + 1); 771 bcopy(context->auth_challenge_id, offset, MSCHAP2_CHALLENGE_SIZE); 772 offset += MSCHAP2_CHALLENGE_SIZE; 773 774 /* MS-CHAP2-Response AVP */ 775 avpv = (DiameterVendorAVP *)offset; 776 avpv->AVPV_code = htonl(kMSRADIUSAttributeTypeMSCHAP2Response); 777 avpv->AVPV_flags_length 778 = htonl(DiameterAVPMakeFlagsLength(kDiameterFlagsVendorSpecific, 779 sizeof(*avpv) 780 + TTLS_MSCHAP2_RESPONSE_LENGTH)); 781 avpv->AVPV_vendor = htonl(kRADIUSVendorIdentifierMicrosoft); 782 offset = (void *)(avpv + 1); 783 *((u_char *)offset) /* identifier */ 784 = context->auth_challenge_id[MSCHAP2_CHALLENGE_SIZE]; 785 offset++; 786 *((u_char *)offset) = 0; /* flags: must be 0 */ 787 offset++; 788 MSChapFillWithRandom(context->peer_challenge, 789 sizeof(context->peer_challenge)); 790 bcopy(context->peer_challenge, offset, 791 MSCHAP2_CHALLENGE_SIZE); /* peer challenge */ 792 offset += sizeof(context->peer_challenge); 793 bzero(offset, MSCHAP2_RESERVED_SIZE); 794 offset += MSCHAP2_RESERVED_SIZE; 795 MSChap2(context->auth_challenge_id, context->peer_challenge, 796 plugin->username, plugin->password, plugin->password_length, 797 context->nt_response); 798 bcopy(context->nt_response, offset, /* response */ 799 MSCHAP_NT_RESPONSE_SIZE); 800 offset += MSCHAP_NT_RESPONSE_SIZE; 801 { /* pad out with zeroes */ 802 int pad_length = data_length_r - data_length; 803 if (pad_length != 0) { 804 bzero(offset, pad_length); 805 offset += pad_length; 806 } 807 } 808#if 0 809 printf("offset - data %d length %d\n", offset - data, data_length); 810 printf("\n----------MSCHAP2 Raw AVP Data START\n"); 811 print_data(data, offset - data); 812 printf("----------MSCHAP2 Raw AVP Data END\n"); 813#endif /* 0 */ 814 815 status = SSLWrite(context->ssl_context, data, offset - data, &length); 816 if (status != noErr) { 817 EAPLOG_FL(LOG_NOTICE, "SSLWrite failed, %s", 818 EAPSSLErrorString(status)); 819 ret = FALSE; 820 } 821 free(data); 822 return (ret); 823} 824 825static EAPPacketRef 826eapttls_mschap2_verify(EAPClientPluginDataRef plugin, int identifier) 827{ 828 DiameterVendorAVP avpv; 829 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 830 void * data = NULL; 831 size_t data_size = 0; 832 u_int32_t flags_length; 833 size_t length; 834 EAPPacketRef pkt = NULL; 835 OSStatus status; 836 837 status = SSLRead(context->ssl_context, &avpv, sizeof(avpv), 838 &data_size); 839 if (status != noErr) { 840 EAPLOG_FL(LOG_NOTICE, "SSLRead failed, %s", 841 EAPSSLErrorString(status)); 842 context->plugin_state = kEAPClientStateFailure; 843 context->last_ssl_error = status; 844 goto done; 845 } 846 if (data_size != sizeof(avpv)) { 847 context->plugin_state = kEAPClientStateFailure; 848 goto done; 849 } 850 flags_length = ntohl(avpv.AVPV_flags_length); 851 if ((DiameterAVPFlagsFromFlagsLength(flags_length) 852 & kDiameterFlagsVendorSpecific) == 0 853 || ntohl(avpv.AVPV_code) != kMSRADIUSAttributeTypeMSCHAP2Success 854 || ntohl(avpv.AVPV_vendor) != kRADIUSVendorIdentifierMicrosoft) { 855 context->plugin_state = kEAPClientStateFailure; 856 goto done; 857 } 858 length = DiameterAVPLengthFromFlagsLength(flags_length); 859 if (length > kEAPTLSAvoidDenialOfServiceSize) { 860 context->plugin_state = kEAPClientStateFailure; 861 goto done; 862 } 863 length -= sizeof(avpv); 864 data = malloc(length); 865 status = SSLRead(context->ssl_context, data, length, &length); 866 if (status != noErr) { 867 context->plugin_state = kEAPClientStateFailure; 868 goto done; 869 } 870 if (length < (MSCHAP2_AUTH_RESPONSE_SIZE + 1) 871 || (*((uint8_t *)data) 872 != context->auth_challenge_id[MSCHAP2_CHALLENGE_SIZE])) { 873 context->plugin_state = kEAPClientStateFailure; 874 goto done; 875 } 876 if (MSChap2AuthResponseValid(plugin->password, plugin->password_length, 877 context->nt_response, 878 context->peer_challenge, 879 context->auth_challenge_id, 880 plugin->username, data + 1) == FALSE) { 881 context->plugin_state = kEAPClientStateFailure; 882 goto done; 883 } 884 pkt = EAPTTLSPacketCreateAck(identifier); 885 done: 886 if (data != NULL) { 887 free(data); 888 } 889 return (pkt); 890} 891 892static EAPPacketRef 893eapttls_do_inner_auth(EAPClientPluginDataRef plugin, 894 int identifier, EAPClientStatus * client_status) 895{ 896 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 897 memoryBufferRef write_buf = &context->write_buffer; 898 899 context->authentication_started = TRUE; 900 switch (context->inner_auth_type) { 901 case kInnerAuthTypePAP: 902 if (eapttls_pap(plugin) == FALSE) { 903 /* do something */ 904 } 905 break; 906 case kInnerAuthTypeCHAP: 907 if (eapttls_chap(plugin) == FALSE) { 908 /* do something */ 909 } 910 break; 911 case kInnerAuthTypeMSCHAP: 912 if (eapttls_mschap(plugin) == FALSE) { 913 /* do something */ 914 } 915 break; 916 case kInnerAuthTypeEAP: 917 if (eapttls_eap_start(plugin, identifier) == FALSE) { 918 /* do something */ 919 } 920 break; 921 default: 922 case kInnerAuthTypeMSCHAPv2: 923 if (eapttls_mschap2(plugin) == FALSE) { 924 /* do something */ 925 } 926 break; 927 } 928 return (EAPTLSPacketCreate(kEAPCodeResponse, 929 kEAPTypeTTLS, 930 identifier, 931 context->mtu, 932 write_buf, 933 &context->last_write_size)); 934} 935 936static EAPPacketRef 937eapttls_start_inner_auth(EAPClientPluginDataRef plugin, 938 int identifier, EAPClientStatus * client_status) 939{ 940 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 941 memoryBufferRef write_buf = &context->write_buffer; 942 943 if (context->session_was_resumed == FALSE) { 944 return (eapttls_do_inner_auth(plugin, identifier, client_status)); 945 } 946 return (EAPTLSPacketCreate(kEAPCodeResponse, 947 kEAPTypeTTLS, 948 identifier, 949 context->mtu, 950 write_buf, 951 &context->last_write_size)); 952} 953 954static EAPPacketRef 955eapttls_verify_server(EAPClientPluginDataRef plugin, 956 int identifier, EAPClientStatus * client_status) 957{ 958 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 959 EAPPacketRef pkt = NULL; 960 memoryBufferRef write_buf = &context->write_buffer; 961 962 context->trust_status 963 = EAPTLSVerifyServerCertificateChain(plugin->properties, 964 context->server_certs, 965 &context->trust_ssl_error); 966 if (context->trust_status != kEAPClientStatusOK) { 967 EAPLOG_FL(LOG_NOTICE, "server certificate not trusted status %d %d", 968 context->trust_status, 969 (int)context->trust_ssl_error); 970 } 971 switch (context->trust_status) { 972 case kEAPClientStatusOK: 973 context->trust_proceed = TRUE; 974 break; 975 case kEAPClientStatusUserInputRequired: 976 /* ask user whether to proceed or not */ 977 *client_status = context->last_client_status 978 = kEAPClientStatusUserInputRequired; 979 break; 980 default: 981 *client_status = context->last_client_status = context->trust_status; 982 context->last_ssl_error = context->trust_ssl_error; 983 context->plugin_state = kEAPClientStateFailure; 984 SSLClose(context->ssl_context); 985 pkt = EAPTLSPacketCreate(kEAPCodeResponse, 986 kEAPTypeTTLS, 987 identifier, 988 context->mtu, 989 write_buf, 990 &context->last_write_size); 991 break; 992 } 993 return (pkt); 994} 995 996static EAPPacketRef 997eapttls_tunnel(EAPClientPluginDataRef plugin, 998 int identifier, EAPClientStatus * client_status) 999{ 1000 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 1001 EAPPacketRef pkt = NULL; 1002 memoryBufferRef read_buf = &context->read_buffer; 1003 1004 if (context->authentication_started == FALSE) { 1005 if (identifier == context->previous_identifier) { 1006 /* we've already seen this packet, discard buffer contents */ 1007 memoryBufferClear(read_buf); 1008 } 1009 if (plugin->password == NULL) { 1010 *client_status = kEAPClientStatusUserInputRequired; 1011 return (NULL); 1012 } 1013 return (eapttls_start_inner_auth(plugin, identifier, client_status)); 1014 } 1015 switch (context->inner_auth_type) { 1016 default: 1017 /* we didn't expect data, Ack it anyways */ 1018 pkt = EAPTTLSPacketCreateAck(identifier); 1019 break; 1020 case kInnerAuthTypeMSCHAPv2: 1021 if (identifier == context->previous_identifier) { 1022 /* we've already verified the MSCHAP2 response, just Ack it */ 1023 pkt = EAPTTLSPacketCreateAck(identifier); 1024 } 1025 else { 1026 pkt = eapttls_mschap2_verify(plugin, identifier); 1027 } 1028 break; 1029 } 1030 return (pkt); 1031} 1032 1033static void 1034eapttls_set_session_was_resumed(EAPTTLSPluginDataRef context) 1035{ 1036 char buf[MAX_SESSION_ID_LENGTH]; 1037 size_t buf_len = sizeof(buf); 1038 Boolean resumed = FALSE; 1039 OSStatus status; 1040 1041 status = SSLGetResumableSessionInfo(context->ssl_context, 1042 &resumed, buf, &buf_len); 1043 if (status == noErr) { 1044 context->session_was_resumed = resumed; 1045 } 1046 return; 1047} 1048 1049static EAPPacketRef 1050eapttls_handshake(EAPClientPluginDataRef plugin, 1051 int identifier, EAPClientStatus * client_status) 1052 1053{ 1054 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 1055 EAPPacketRef eaptls_out = NULL; 1056 OSStatus status = noErr; 1057 memoryBufferRef write_buf = &context->write_buffer; 1058 1059 if (context->server_auth_completed && context->trust_proceed == FALSE) { 1060 eaptls_out = eapttls_verify_server(plugin, identifier, client_status); 1061 if (context->trust_proceed == FALSE) { 1062 goto done; 1063 } 1064 } 1065 status = SSLHandshake(context->ssl_context); 1066 if (status == errSSLServerAuthCompleted) { 1067 if (context->server_auth_completed) { 1068 /* this should not happen */ 1069 EAPLOG_FL(LOG_NOTICE, "AuthCompleted again?"); 1070 goto done; 1071 } 1072 context->server_auth_completed = TRUE; 1073 my_CFRelease(&context->server_certs); 1074 (void)EAPSSLCopyPeerCertificates(context->ssl_context, 1075 &context->server_certs); 1076 eaptls_out = eapttls_verify_server(plugin, identifier, client_status); 1077 if (context->trust_proceed == FALSE) { 1078 goto done; 1079 } 1080 /* handshake again to get past the AuthCompleted status */ 1081 status = SSLHandshake(context->ssl_context); 1082 } 1083 switch (status) { 1084 case noErr: 1085 /* handshake complete, tunnel established */ 1086 if (context->trust_proceed == FALSE) { 1087 my_CFRelease(&context->server_certs); 1088 (void)EAPSSLCopyPeerCertificates(context->ssl_context, 1089 &context->server_certs); 1090 eaptls_out = eapttls_verify_server(plugin, identifier, 1091 client_status); 1092 if (context->trust_proceed == FALSE) { 1093 /* this should not happen */ 1094 EAPLOG_FL(LOG_NOTICE, "trust_proceed is FALSE?"); 1095 break; 1096 } 1097 } 1098 context->handshake_complete = TRUE; 1099 eapttls_compute_session_key(context); 1100 eapttls_set_session_was_resumed(context); 1101 if (plugin->password == NULL) { 1102 *client_status = context->last_client_status 1103 = kEAPClientStatusUserInputRequired; 1104 break; 1105 } 1106 eaptls_out = eapttls_start_inner_auth(plugin, 1107 identifier, client_status); 1108 break; 1109 default: 1110 EAPLOG_FL(LOG_NOTICE, "SSLHandshake failed, %s", 1111 EAPSSLErrorString(status)); 1112 context->last_ssl_error = status; 1113 my_CFRelease(&context->server_certs); 1114 (void) EAPSSLCopyPeerCertificates(context->ssl_context, 1115 &context->server_certs); 1116 /* close_up_shop */ 1117 context->plugin_state = kEAPClientStateFailure; 1118 SSLClose(context->ssl_context); 1119 /* FALL THROUGH */ 1120 case errSSLWouldBlock: 1121 if (write_buf->data == NULL) { 1122 if (status == errSSLFatalAlert) { 1123 /* send an ACK if we received a fatal alert message */ 1124 eaptls_out 1125 = EAPTTLSPacketCreateAck(identifier); 1126 } 1127 } 1128 else { 1129 eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse, 1130 kEAPTypeTTLS, 1131 identifier, 1132 context->mtu, 1133 write_buf, 1134 &context->last_write_size); 1135 } 1136 break; 1137 } 1138 1139 done: 1140 return (eaptls_out); 1141} 1142 1143static EAPPacketRef 1144eapttls_request(EAPClientPluginDataRef plugin, 1145 const EAPPacketRef in_pkt, 1146 EAPClientStatus * client_status) 1147{ 1148 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 1149 EAPTLSPacket * eaptls_in = (EAPTLSPacket *)in_pkt; 1150 EAPTLSLengthIncludedPacket *eaptls_in_l; 1151 EAPPacketRef eaptls_out = NULL; 1152 int in_data_length; 1153 void * in_data_ptr = NULL; 1154 u_int16_t in_length = EAPPacketGetLength(in_pkt); 1155 memoryBufferRef write_buf = &context->write_buffer; 1156 memoryBufferRef read_buf = &context->read_buffer; 1157 SSLSessionState ssl_state = kSSLIdle; 1158 OSStatus status = noErr; 1159 u_int32_t tls_message_length = 0; 1160 RequestType type; 1161 1162 /* ALIGN: void * cast OK, we don't expect proper alignment */ 1163 eaptls_in_l = (EAPTLSLengthIncludedPacket *)(void *)in_pkt; 1164 if (in_length < sizeof(*eaptls_in)) { 1165 EAPLOG_FL(LOG_NOTICE, "length %d < %ld", 1166 in_length, sizeof(*eaptls_in)); 1167 goto done; 1168 } 1169 if (context->ssl_context != NULL) { 1170 status = SSLGetSessionState(context->ssl_context, &ssl_state); 1171 if (status != noErr) { 1172 EAPLOG_FL(LOG_NOTICE, "SSLGetSessionState failed, %s", 1173 EAPSSLErrorString(status)); 1174 context->plugin_state = kEAPClientStateFailure; 1175 context->last_ssl_error = status; 1176 goto done; 1177 } 1178 } 1179 in_data_ptr = eaptls_in->tls_data; 1180 tls_message_length = in_data_length = in_length - sizeof(EAPTLSPacket); 1181 1182 type = kRequestTypeData; 1183 if ((eaptls_in->flags & kEAPTLSPacketFlagsStart) != 0) { 1184 type = kRequestTypeStart; 1185 /* only reset our state if this is not a re-transmitted Start packet */ 1186 if (ssl_state != kSSLHandshake 1187 || write_buf->data == NULL 1188 || in_pkt->identifier != context->previous_identifier) { 1189 ssl_state = kSSLIdle; 1190 } 1191 } 1192 else if (in_length == sizeof(*eaptls_in)) { 1193 type = kRequestTypeAck; 1194 } 1195 else if ((eaptls_in->flags & kEAPTLSPacketFlagsLengthIncluded) != 0) { 1196 if (in_length < sizeof(EAPTLSLengthIncludedPacket)) { 1197 EAPLOG_FL(LOG_NOTICE, 1198 "packet too short %d < %ld", 1199 in_length, sizeof(EAPTLSLengthIncludedPacket)); 1200 goto done; 1201 } 1202 in_data_ptr = eaptls_in_l->tls_data; 1203 in_data_length = in_length - sizeof(EAPTLSLengthIncludedPacket); 1204 tls_message_length 1205 = EAPTLSLengthIncludedPacketGetMessageLength(eaptls_in_l); 1206 if (tls_message_length > kEAPTLSAvoidDenialOfServiceSize) { 1207 EAPLOG_FL(LOG_NOTICE, 1208 "received message too large, %d > %d", 1209 tls_message_length, kEAPTLSAvoidDenialOfServiceSize); 1210 context->plugin_state = kEAPClientStateFailure; 1211 goto done; 1212 } 1213 if (tls_message_length == 0) { 1214 type = kRequestTypeAck; 1215 } 1216 } 1217 1218 switch (ssl_state) { 1219 case kSSLClosed: 1220 case kSSLAborted: 1221 break; 1222 1223 case kSSLIdle: 1224 if (type != kRequestTypeStart) { 1225 /* ignore it: XXX should this be an error? */ 1226 EAPLOG_FL(LOG_NOTICE, 1227 "ignoring non TTLS start frame"); 1228 goto done; 1229 } 1230 status = eapttls_start(plugin); 1231 if (status != noErr) { 1232 context->last_ssl_error = status; 1233 context->plugin_state = kEAPClientStateFailure; 1234 goto done; 1235 } 1236 status = SSLHandshake(context->ssl_context); 1237 if (status != errSSLWouldBlock) { 1238 EAPLOG_FL(LOG_NOTICE, "SSLHandshake failed, %s", 1239 EAPSSLErrorString(status)); 1240 context->last_ssl_error = status; 1241 context->plugin_state = kEAPClientStateFailure; 1242 goto done; 1243 } 1244 eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse, 1245 kEAPTypeTTLS, 1246 eaptls_in->identifier, 1247 context->mtu, 1248 write_buf, 1249 &context->last_write_size); 1250 break; 1251 case kSSLHandshake: 1252 case kSSLConnected: 1253 if (write_buf->data != NULL) { 1254 /* we have data to write */ 1255 if (in_pkt->identifier == context->previous_identifier) { 1256 /* resend the existing fragment */ 1257 eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse, 1258 kEAPTypeTTLS, 1259 in_pkt->identifier, 1260 context->mtu, 1261 write_buf, 1262 &context->last_write_size); 1263 break; 1264 } 1265 if ((write_buf->offset + context->last_write_size) 1266 < write_buf->length) { 1267 /* advance the offset, and send the next fragment */ 1268 write_buf->offset += context->last_write_size; 1269 eaptls_out = EAPTLSPacketCreate(kEAPCodeResponse, 1270 kEAPTypeTTLS, 1271 in_pkt->identifier, 1272 context->mtu, 1273 write_buf, 1274 &context->last_write_size); 1275 break; 1276 } 1277 /* we're done, release the write buffer */ 1278 memoryBufferClear(write_buf); 1279 context->last_write_size = 0; 1280 } 1281 if (type != kRequestTypeData) { 1282 EAPTTLSPluginDataRef context; 1283 1284 context = (EAPTTLSPluginDataRef)plugin->private; 1285 if (ssl_state != kSSLConnected 1286 || context->session_was_resumed == FALSE 1287 || context->trust_proceed == FALSE 1288 || context->authentication_started == TRUE) { 1289 EAPLOG_FL(LOG_NOTICE, "unexpected %s frame", 1290 type == kRequestTypeAck ? "Ack" : "Start"); 1291 goto done; 1292 } 1293 /* server is forcing us to re-auth even though we resumed */ 1294 EAPLOG(LOG_NOTICE, "server forcing re-auth after resume"); 1295 eaptls_out 1296 = eapttls_do_inner_auth(plugin, eaptls_in->identifier, 1297 client_status); 1298 break; 1299 } 1300 if (in_pkt->identifier == context->previous_identifier) { 1301 if ((eaptls_in->flags & kEAPTLSPacketFlagsMoreFragments) != 0) { 1302 /* just ack it, we've already seen the fragment */ 1303 eaptls_out = EAPTTLSPacketCreateAck(eaptls_in->identifier); 1304 break; 1305 } 1306 } 1307 else { 1308 if (read_buf->data == NULL) { 1309 memoryBufferAllocate(read_buf, tls_message_length); 1310 } 1311 if (memoryBufferAddData(read_buf, in_data_ptr, in_data_length) 1312 == FALSE) { 1313 EAPLOG_FL(LOG_NOTICE, 1314 "fragment too large %d", 1315 in_data_length); 1316 goto done; 1317 } 1318 if (memoryBufferIsComplete(read_buf) == FALSE) { 1319 if ((eaptls_in->flags & kEAPTLSPacketFlagsMoreFragments) == 0) { 1320 EAPLOG_FL(LOG_NOTICE, 1321 "expecting more data but " 1322 "more fragments bit is not set, ignoring"); 1323 goto done; 1324 } 1325 /* we haven't received the entire TLS message */ 1326 eaptls_out = EAPTTLSPacketCreateAck(eaptls_in->identifier); 1327 break; 1328 } 1329 } 1330 /* we've got the whole TLS message, process it */ 1331 if (context->handshake_complete) { 1332 /* subsequent request */ 1333 eaptls_out = eapttls_tunnel(plugin, 1334 eaptls_in->identifier, 1335 client_status); 1336 } 1337 else { 1338 eaptls_out = eapttls_handshake(plugin, 1339 eaptls_in->identifier, 1340 client_status); 1341 } 1342 break; 1343 default: 1344 break; 1345 } 1346 1347 context->previous_identifier = in_pkt->identifier; 1348 done: 1349 return (eaptls_out); 1350} 1351 1352static EAPClientState 1353eapttls_process(EAPClientPluginDataRef plugin, 1354 const EAPPacketRef in_pkt, 1355 EAPPacketRef * out_pkt_p, 1356 EAPClientStatus * client_status, 1357 EAPClientDomainSpecificError * error) 1358{ 1359 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 1360 1361 *client_status = kEAPClientStatusOK; 1362 *error = 0; 1363 1364 *out_pkt_p = NULL; 1365 switch (in_pkt->code) { 1366 case kEAPCodeRequest: 1367 *out_pkt_p = eapttls_request(plugin, in_pkt, client_status); 1368 break; 1369 case kEAPCodeSuccess: 1370 if (context->trust_proceed) { 1371 context->plugin_state = kEAPClientStateSuccess; 1372 } 1373 else if (context->handshake_complete) { 1374 *out_pkt_p 1375 = eapttls_verify_server(plugin, in_pkt->identifier, 1376 client_status); 1377 if (context->trust_proceed) { 1378 context->plugin_state = kEAPClientStateSuccess; 1379 } 1380 } 1381 break; 1382 case kEAPCodeFailure: 1383 context->plugin_state = kEAPClientStateFailure; 1384 break; 1385 case kEAPCodeResponse: 1386 default: 1387 break; 1388 } 1389 if (context->plugin_state == kEAPClientStateFailure) { 1390 if (context->last_ssl_error == noErr) { 1391 switch (context->last_client_status) { 1392 case kEAPClientStatusOK: 1393 case kEAPClientStatusUserInputRequired: 1394 *client_status = kEAPClientStatusFailed; 1395 break; 1396 default: 1397 *client_status = context->last_client_status; 1398 break; 1399 } 1400 } 1401 else { 1402 *error = context->last_ssl_error; 1403 *client_status = kEAPClientStatusSecurityError; 1404 } 1405 } 1406 return (context->plugin_state); 1407} 1408 1409static const char * 1410eapttls_failure_string(EAPClientPluginDataRef plugin) 1411{ 1412 return (NULL); 1413} 1414 1415static void * 1416eapttls_session_key(EAPClientPluginDataRef plugin, int * key_length) 1417{ 1418 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 1419 1420 *key_length = 0; 1421 if (context->key_data_valid == FALSE) { 1422 return (NULL); 1423 } 1424 1425 /* return the first 32 bytes of key data */ 1426 *key_length = 32; 1427 return (context->key_data); 1428} 1429 1430static void * 1431eapttls_server_key(EAPClientPluginDataRef plugin, int * key_length) 1432{ 1433 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 1434 1435 *key_length = 0; 1436 if (context->key_data_valid == FALSE) { 1437 return (NULL); 1438 } 1439 1440 /* return the second 32 bytes of key data */ 1441 *key_length = 32; 1442 return (context->key_data + 32); 1443} 1444 1445static int 1446eapttls_msk_copy_bytes(EAPClientPluginDataRef plugin, 1447 void * msk, int msk_size) 1448{ 1449 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 1450 int ret_msk_size; 1451 1452 if (msk_size < kEAPMasterSessionKeyMinimumSize 1453 || context->key_data_valid == FALSE) { 1454 ret_msk_size = 0; 1455 } 1456 else { 1457 ret_msk_size = kEAPMasterSessionKeyMinimumSize; 1458 bcopy(context->key_data, msk, ret_msk_size); 1459 } 1460 return (ret_msk_size); 1461} 1462 1463static CFArrayRef 1464eapttls_require_props(EAPClientPluginDataRef plugin) 1465{ 1466 CFArrayRef array = NULL; 1467 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 1468 1469 if (context->last_client_status != kEAPClientStatusUserInputRequired) { 1470 goto done; 1471 } 1472 if (context->trust_proceed == FALSE) { 1473 CFStringRef str = kEAPClientPropTLSUserTrustProceedCertificateChain; 1474 array = CFArrayCreate(NULL, (const void **)&str, 1475 1, &kCFTypeArrayCallBacks); 1476 } 1477 else if (plugin->password == NULL) { 1478 CFStringRef str = kEAPClientPropUserPassword; 1479 array = CFArrayCreate(NULL, (const void **)&str, 1480 1, &kCFTypeArrayCallBacks); 1481 } 1482 done: 1483 return (array); 1484} 1485 1486static CFDictionaryRef 1487eapttls_publish_props(EAPClientPluginDataRef plugin) 1488{ 1489 CFArrayRef cert_list; 1490 SSLCipherSuite cipher = SSL_NULL_WITH_NULL_NULL; 1491 EAPTTLSPluginDataRef context = (EAPTTLSPluginDataRef)plugin->private; 1492 CFMutableDictionaryRef dict; 1493 1494 if (context->server_certs == NULL) { 1495 return (NULL); 1496 } 1497 cert_list = EAPSecCertificateArrayCreateCFDataArray(context->server_certs); 1498 if (cert_list == NULL) { 1499 return (NULL); 1500 } 1501 dict = CFDictionaryCreateMutable(NULL, 0, 1502 &kCFTypeDictionaryKeyCallBacks, 1503 &kCFTypeDictionaryValueCallBacks); 1504 CFDictionarySetValue(dict, kEAPClientPropTLSServerCertificateChain, 1505 cert_list); 1506 CFDictionarySetValue(dict, kEAPClientPropTLSSessionWasResumed, 1507 context->session_was_resumed 1508 ? kCFBooleanTrue 1509 : kCFBooleanFalse); 1510 my_CFRelease(&cert_list); 1511 (void)SSLGetNegotiatedCipher(context->ssl_context, &cipher); 1512 if (cipher != SSL_NULL_WITH_NULL_NULL) { 1513 CFNumberRef c; 1514 int tmp = cipher; 1515 1516 c = CFNumberCreate(NULL, kCFNumberIntType, &tmp); 1517 CFDictionarySetValue(dict, kEAPClientPropTLSNegotiatedCipher, c); 1518 CFRelease(c); 1519 } 1520 if (context->last_client_status == kEAPClientStatusUserInputRequired 1521 && context->trust_proceed == FALSE) { 1522 CFNumberRef num; 1523 num = CFNumberCreate(NULL, kCFNumberSInt32Type, 1524 &context->trust_status); 1525 CFDictionarySetValue(dict, kEAPClientPropTLSTrustClientStatus, num); 1526 CFRelease(num); 1527 } 1528 return (dict); 1529} 1530 1531static CFStringRef 1532eapttls_copy_packet_description(const EAPPacketRef pkt, bool * packet_is_valid) 1533{ 1534 EAPTLSPacketRef eaptls_pkt = (EAPTLSPacketRef)pkt; 1535 1536 return (EAPTLSPacketCopyDescription(eaptls_pkt, packet_is_valid)); 1537} 1538 1539static EAPType 1540eapttls_type() 1541{ 1542 return (kEAPTypeTTLS); 1543 1544} 1545 1546static const char * 1547eapttls_name() 1548{ 1549 return (EAPTypeStr(kEAPTypeTTLS)); 1550} 1551 1552static EAPClientPluginVersion 1553eapttls_version() 1554{ 1555 return (kEAPClientPluginVersion); 1556} 1557 1558static struct func_table_ent { 1559 const char * name; 1560 void * func; 1561} func_table[] = { 1562#if 0 1563 { kEAPClientPluginFuncNameIntrospect, eapttls_introspect }, 1564#endif /* 0 */ 1565 { kEAPClientPluginFuncNameVersion, eapttls_version }, 1566 { kEAPClientPluginFuncNameEAPType, eapttls_type }, 1567 { kEAPClientPluginFuncNameEAPName, eapttls_name }, 1568 { kEAPClientPluginFuncNameInit, eapttls_init }, 1569 { kEAPClientPluginFuncNameFree, eapttls_free }, 1570 { kEAPClientPluginFuncNameProcess, eapttls_process }, 1571 { kEAPClientPluginFuncNameFreePacket, eapttls_free_packet }, 1572 { kEAPClientPluginFuncNameFailureString, eapttls_failure_string }, 1573 { kEAPClientPluginFuncNameSessionKey, eapttls_session_key }, 1574 { kEAPClientPluginFuncNameServerKey, eapttls_server_key }, 1575 { kEAPClientPluginFuncNameMasterSessionKeyCopyBytes, 1576 eapttls_msk_copy_bytes }, 1577 { kEAPClientPluginFuncNameRequireProperties, eapttls_require_props }, 1578 { kEAPClientPluginFuncNamePublishProperties, eapttls_publish_props }, 1579 { kEAPClientPluginFuncNameCopyPacketDescription, 1580 eapttls_copy_packet_description }, 1581 { NULL, NULL}, 1582}; 1583 1584 1585EAPClientPluginFuncRef 1586eapttls_introspect(EAPClientPluginFuncName name) 1587{ 1588 struct func_table_ent * scan; 1589 1590 1591 for (scan = func_table; scan->name != NULL; scan++) { 1592 if (strcmp(name, scan->name) == 0) { 1593 return (scan->func); 1594 } 1595 } 1596 return (NULL); 1597} 1598