1/* 2 * Copyright (c) 2003-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 * eapmschapv2_plugin.c 26 * - EAP-MSCHAPv2 plug-in 27 */ 28 29/* 30 * Modification History 31 * 32 * May 21, 2003 Dieter Siegmund (dieter@apple) 33 * - created 34 */ 35 36#include <EAP8021X/EAPClientPlugin.h> 37#include <EAP8021X/EAPClientProperties.h> 38#include <SystemConfiguration/SCValidation.h> 39#include <mach/boolean.h> 40#include <unistd.h> 41#include <stdlib.h> 42#include <string.h> 43#include <stdio.h> 44#include "EAPLog.h" 45#include <EAP8021X/EAP.h> 46#include <EAP8021X/EAPUtil.h> 47#include <EAP8021X/EAPClientModule.h> 48#include <EAP8021X/mschap.h> 49#include "myCFUtil.h" 50#include "nbo.h" 51#include "printdata.h" 52 53/* 54 * Declare these here to ensure that the compiler 55 * generates appropriate errors/warnings 56 */ 57EAPClientPluginFuncIntrospect eapmschapv2_introspect; 58static EAPClientPluginFuncVersion eapmschapv2_version; 59static EAPClientPluginFuncEAPType eapmschapv2_type; 60static EAPClientPluginFuncEAPName eapmschapv2_name; 61static EAPClientPluginFuncInit eapmschapv2_init; 62static EAPClientPluginFuncFree eapmschapv2_free; 63static EAPClientPluginFuncProcess eapmschapv2_process; 64static EAPClientPluginFuncFreePacket eapmschapv2_free_packet; 65static EAPClientPluginFuncRequireProperties eapmschapv2_require_props; 66static EAPClientPluginFuncPublishProperties eapmschapv2_publish_props; 67static EAPClientPluginFuncSessionKey eapmschapv2_session_key; 68static EAPClientPluginFuncMasterSessionKeyCopyBytes eapmschapv2_msk_copy_bytes; 69static EAPClientPluginFuncServerKey eapmschapv2_server_key; 70static EAPClientPluginFuncCopyPacketDescription eapmschapv2_copy_packet_description; 71 72typedef struct { 73 NTPasswordBlock encrypted_password; 74 uint8_t encrypted_hash[NT_PASSWORD_HASH_SIZE]; 75 uint8_t peer_challenge[MSCHAP2_CHALLENGE_SIZE]; 76 uint8_t reserved[MSCHAP2_RESERVED_SIZE]; 77 uint8_t nt_response[MSCHAP_NT_RESPONSE_SIZE]; 78 uint8_t flags[2]; 79} MSCHAPv2ChangePasswordResponse, * MSCHAPv2ChangePasswordResponseRef; 80 81enum { 82 kMSCHAPv2ChangePasswordVersion = 3 83}; 84 85#define MSCHAP2_CHANGE_PASSWORD_RESPONSE_LENGTH sizeof(MSCHAPv2ChangePasswordResponse) 86 87enum { 88 kMSCHAPv2OpCodeChallenge = 1, 89 kMSCHAPv2OpCodeResponse = 2, 90 kMSCHAPv2OpCodeSuccess = 3, 91 kMSCHAPv2OpCodeFailure = 4, 92 kMSCHAPv2OpCodeChangePassword = 7 93}; 94typedef uint8_t MSCHAPv2OpCode; 95 96static const char * 97MSCHAPv2OpCodeStr(MSCHAPv2OpCode op_code) 98{ 99 switch (op_code) { 100 case kMSCHAPv2OpCodeChallenge: 101 return ("Challenge"); 102 case kMSCHAPv2OpCodeResponse: 103 return ("Response"); 104 case kMSCHAPv2OpCodeSuccess: 105 return ("Success"); 106 case kMSCHAPv2OpCodeFailure: 107 return ("Failure"); 108 case kMSCHAPv2OpCodeChangePassword: 109 return ("ChangePassword"); 110 default: 111 break; 112 } 113 return ("<unknown>"); 114} 115 116/* 117 * EAP_MSCHAP2_MS_LENGTH_DIFFERENCE 118 * 119 * pkt.ms_length is always (pkt.length - 5) because ms_length is the number of 120 * bytes starting with the op_code field (offsetof(pkt.op_code) == 5). 121 */ 122#define EAP_MSCHAP2_MS_LENGTH_DIFFERENCE (sizeof(EAPRequestPacket)) 123 124typedef struct EAPMSCHAPv2Packet_s { 125 uint8_t code; 126 uint8_t identifier; 127 uint8_t length[2]; /* of entire request/response */ 128 uint8_t type; 129 uint8_t op_code; 130 uint8_t mschapv2_id; 131 uint8_t ms_length[2]; /* must be pkt.length - 5 */ 132 uint8_t data[0]; 133} EAPMSCHAPv2Packet, *EAPMSCHAPv2PacketRef; 134 135typedef struct EAPMSCHAPv2ChallengePacket_s { 136 uint8_t code; 137 uint8_t identifier; 138 uint8_t length[2]; /* of entire request/response */ 139 uint8_t type; 140 uint8_t op_code; 141 uint8_t mschapv2_id; 142 uint8_t ms_length[2]; /* pkt.length - 5 */ 143 uint8_t value_size; /* MSCHAP2_CHALLENGE_SIZE */ 144 uint8_t challenge[MSCHAP2_CHALLENGE_SIZE]; 145 uint8_t name[0]; 146} EAPMSCHAPv2ChallengePacket, *EAPMSCHAPv2ChallengePacketRef; 147 148typedef struct EAPMSCHAPv2ResponsePacket_s { 149 uint8_t code; 150 uint8_t identifier; 151 uint8_t length[2]; /* of entire request/response */ 152 uint8_t type; 153 uint8_t op_code; 154 uint8_t mschapv2_id; 155 uint8_t ms_length[2]; /* pkt.length - 5 */ 156 uint8_t value_size; /* MSCHAP2_RESPONSE_LENGTH */ 157 uint8_t response[MSCHAP2_RESPONSE_LENGTH]; 158 uint8_t name[0]; 159} EAPMSCHAPv2ResponsePacket, *EAPMSCHAPv2ResponsePacketRef; 160 161typedef struct EAPMSCHAPv2SuccessRequestPacket_s { 162 uint8_t code; 163 uint8_t identifier; 164 uint8_t length[2]; /* of entire request/response */ 165 uint8_t type; 166 uint8_t op_code; 167 uint8_t mschapv2_id; 168 uint8_t ms_length[2]; /* pkt.length - 5 */ 169 uint8_t auth_response[MSCHAP2_AUTH_RESPONSE_SIZE]; 170 uint8_t message[0]; 171} EAPMSCHAPv2SuccessRequestPacket, *EAPMSCHAPv2SuccessRequestPacketRef; 172 173typedef struct EAPMSCHAPv2SuccessResponsePacket_s { 174 uint8_t code; 175 uint8_t identifier; 176 uint8_t length[2]; /* of entire request/response */ 177 uint8_t type; 178 uint8_t op_code; 179} EAPMSCHAPv2SuccessResponsePacket, *EAPMSCHAPv2SuccessResponsePacketRef; 180 181typedef struct EAPMSCHAPv2FailureRequestPacket_s { 182 uint8_t code; 183 uint8_t identifier; 184 uint8_t length[2]; /* of entire request/response */ 185 uint8_t type; 186 uint8_t op_code; 187 uint8_t mschapv2_id; 188 uint8_t ms_length[2]; /* pkt.length - 5 */ 189 uint8_t message[0]; 190} EAPMSCHAPv2FailureRequestPacket, *EAPMSCHAPv2FailureRequestPacketRef; 191 192typedef struct EAPMSCHAPv2FailureResponsePacket_s { 193 uint8_t code; 194 uint8_t identifier; 195 uint8_t length[2]; /* of entire request/response */ 196 uint8_t type; 197 uint8_t op_code; 198} EAPMSCHAPv2FailureResponsePacket, *EAPMSCHAPv2FailureResponsePacketRef; 199 200typedef struct EAPMSCHAPv2ChangePasswordPacket_s { 201 uint8_t code; 202 uint8_t identifier; 203 uint8_t length[2]; /* of entire request/response */ 204 uint8_t type; 205 uint8_t op_code; 206 uint8_t mschapv2_id; 207 uint8_t ms_length[2]; /* pkt.length - 5 */ 208 uint8_t data[MSCHAP2_CHANGE_PASSWORD_RESPONSE_LENGTH]; 209} EAPMSCHAPv2ChangePasswordPacket, *EAPMSCHAPv2ChangePasswordPacketRef; 210 211static __inline__ void 212EAPMSCHAPv2PacketSetMSLength(EAPMSCHAPv2PacketRef pkt, uint16_t length) 213{ 214 net_uint16_set(pkt->ms_length, length); 215 return; 216} 217 218static __inline__ uint16_t 219EAPMSCHAPv2PacketGetMSLength(const EAPMSCHAPv2PacketRef pkt) 220{ 221 return (net_uint16_get(pkt->ms_length)); 222} 223 224typedef enum { 225 kMSCHAPv2ClientStateNone = 0, 226 kMSCHAPv2ClientStateResponseSent, 227 kMSCHAPv2ClientStateSuccessResponseSent, 228 kMSCHAPv2ClientStateChangePasswordSent, 229 kMSCHAPv2ClientStateSuccess, 230 kMSCHAPv2ClientStateFailure, 231} MSCHAPv2ClientState; 232 233typedef struct { 234 uint8_t master_send_key[NT_SESSION_KEY_SIZE]; 235 uint8_t master_receive_key[NT_SESSION_KEY_SIZE]; 236} eapmschap2_session_key; 237 238typedef struct { 239 MSCHAPv2ClientState state; 240 EAPClientState plugin_state; 241 bool need_password; 242 bool need_new_password; 243 uint32_t last_generation; 244 uint8_t peer_challenge[MSCHAP2_CHALLENGE_SIZE]; 245 uint8_t nt_response[MSCHAP_NT_RESPONSE_SIZE]; 246 uint8_t auth_challenge[MSCHAP2_CHALLENGE_SIZE]; 247 eapmschap2_session_key session_key; 248 bool session_key_valid; 249 uint8_t pkt_buffer[1024]; 250} EAPMSCHAPv2PluginData, * EAPMSCHAPv2PluginDataRef; 251 252static void 253eapmschapv2_free_context(EAPMSCHAPv2PluginData * context) 254{ 255 free(context); 256 return; 257} 258 259static void 260EAPMSCHAPv2PluginDataInit(EAPMSCHAPv2PluginDataRef context) 261{ 262 context->state = kMSCHAPv2ClientStateNone; 263 context->plugin_state = kEAPClientStateAuthenticating; 264 context->need_password = FALSE; 265 context->need_new_password = FALSE; 266 context->session_key_valid = FALSE; 267 return; 268} 269 270static EAPClientStatus 271eapmschapv2_init(EAPClientPluginDataRef plugin, CFArrayRef * require_props, 272 EAPClientDomainSpecificError * error) 273{ 274 EAPMSCHAPv2PluginDataRef context = NULL; 275 EAPClientStatus result = kEAPClientStatusOK; 276 277 *error = 0; 278 *require_props = NULL; 279 context = malloc(sizeof(*context)); 280 plugin->private = context; 281 EAPMSCHAPv2PluginDataInit(context); 282 context->last_generation = plugin->generation; 283 return (result); 284} 285 286static void 287eapmschapv2_free(EAPClientPluginDataRef plugin) 288{ 289 EAPMSCHAPv2PluginDataRef context; 290 291 context = (EAPMSCHAPv2PluginDataRef)plugin->private; 292 if (context != NULL) { 293 eapmschapv2_free_context(context); 294 plugin->private = NULL; 295 } 296 return; 297} 298 299static void 300eapmschapv2_free_packet(EAPClientPluginDataRef plugin, EAPPacketRef arg) 301{ 302 EAPMSCHAPv2PluginDataRef context; 303 304 context = (EAPMSCHAPv2PluginDataRef)plugin->private; 305 if ((void*)arg != context->pkt_buffer) { 306 free(arg); 307 } 308 return; 309} 310 311static EAPMSCHAPv2ResponsePacketRef 312EAPMSCHAPv2ResponsePacketCreate(EAPClientPluginDataRef plugin, 313 int identifier, int mschapv2_id, 314 EAPClientStatus * client_status) 315{ 316 CFDataRef client_challenge; 317 EAPMSCHAPv2PluginDataRef context; 318 EAPMSCHAPv2ResponsePacketRef out_pkt_p; 319 MSCHAP2ResponseRef resp_p; 320 int out_length; 321 322 /* check for out-of-band client challenge */ 323 client_challenge 324 = CFDictionaryGetValue(plugin->properties, 325 kEAPClientPropEAPMSCHAPv2ClientChallenge); 326 context = (EAPMSCHAPv2PluginDataRef)plugin->private; 327 out_length = sizeof(*out_pkt_p) + plugin->username_length; 328 out_pkt_p = (EAPMSCHAPv2ResponsePacketRef) 329 EAPPacketCreate(context->pkt_buffer, sizeof(context->pkt_buffer), 330 kEAPCodeResponse, identifier, 331 kEAPTypeMSCHAPv2, NULL, 332 out_length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE, 333 NULL); 334 335 if (client_challenge != NULL) { 336 if (CFDataGetLength(client_challenge) 337 != sizeof(context->peer_challenge)) { 338 EAPLOG(LOG_NOTICE, 339 "EAPMSCHAPv2ResponsePacketCreate: internal error %ld != %ld", 340 CFDataGetLength(client_challenge), 341 sizeof(context->peer_challenge)); 342 if (client_status != NULL) { 343 *client_status = kEAPClientStatusInternalError; 344 } 345 context->plugin_state = kEAPClientStateFailure; 346 return (NULL); 347 } 348 memcpy(context->peer_challenge, CFDataGetBytePtr(client_challenge), 349 sizeof(context->peer_challenge)); 350 } 351 else { 352 MSChapFillWithRandom(context->peer_challenge, 353 sizeof(context->peer_challenge)); 354 } 355 MSChap2(context->auth_challenge, context->peer_challenge, 356 plugin->username, plugin->password, plugin->password_length, 357 context->nt_response); 358 359 /* fill in the data */ 360 out_pkt_p->op_code = kMSCHAPv2OpCodeResponse; 361 out_pkt_p->mschapv2_id = mschapv2_id; 362 EAPMSCHAPv2PacketSetMSLength((EAPMSCHAPv2PacketRef)out_pkt_p, 363 out_length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE); 364 out_pkt_p->value_size = sizeof(out_pkt_p->response); 365 resp_p = (MSCHAP2ResponseRef)out_pkt_p->response; 366 if (client_challenge == NULL) { 367 memcpy(resp_p->peer_challenge, context->peer_challenge, 368 sizeof(context->peer_challenge)); 369 } 370 else { 371 memset(resp_p->peer_challenge, 0, sizeof(resp_p->peer_challenge)); 372 } 373 memset(resp_p->reserved, 0, sizeof(resp_p->reserved)); 374 memcpy(resp_p->nt_response, context->nt_response, 375 sizeof(context->nt_response)); 376 resp_p->flags[0] = 0; 377 memcpy(out_pkt_p->name, plugin->username, plugin->username_length); 378 return (out_pkt_p); 379} 380 381static EAPMSCHAPv2PacketRef 382eapmschapv2_challenge(EAPClientPluginDataRef plugin, 383 EAPMSCHAPv2PacketRef in_pkt_p, 384 uint16_t in_length, 385 EAPClientStatus * client_status, 386 EAPClientDomainSpecificError * error) 387{ 388 EAPMSCHAPv2PluginDataRef context; 389 EAPMSCHAPv2ChallengePacketRef challenge_p; 390 EAPMSCHAPv2ResponsePacketRef out_pkt_p; 391 CFDataRef server_challenge; 392 393 if (in_length < sizeof(*challenge_p)) { 394 EAPLOG(LOG_NOTICE, "eapmschapv2_challenge: length %d < %ld", 395 in_length, sizeof(*challenge_p)); 396 goto done; 397 } 398 challenge_p = (EAPMSCHAPv2ChallengePacketRef)in_pkt_p; 399 context = (EAPMSCHAPv2PluginDataRef)plugin->private; 400 401 /* if we don't have a password, ask for one */ 402 EAPMSCHAPv2PluginDataInit(context); 403 if (plugin->password == NULL) { 404 context->need_password = TRUE; 405 *client_status = kEAPClientStatusUserInputRequired; 406 goto done; 407 } 408 409 /* check for out-of-band server challenge */ 410 server_challenge 411 = CFDictionaryGetValue(plugin->properties, 412 kEAPClientPropEAPMSCHAPv2ServerChallenge); 413 if (server_challenge != NULL) { 414 if (CFDataGetLength(server_challenge) 415 != sizeof(context->auth_challenge)) { 416 EAPLOG(LOG_NOTICE, 417 "eapmschapv2_challenge: internal error %ld != %ld", 418 CFDataGetLength(server_challenge), 419 sizeof(context->auth_challenge)); 420 *client_status = kEAPClientStatusInternalError; 421 context->plugin_state = kEAPClientStateFailure; 422 goto done; 423 } 424 memcpy(context->auth_challenge, CFDataGetBytePtr(server_challenge), 425 sizeof(context->auth_challenge)); 426 } 427 else { 428 /* remember the auth challenge for later */ 429 memcpy(context->auth_challenge, challenge_p->challenge, 430 sizeof(context->auth_challenge)); 431 } 432 out_pkt_p = EAPMSCHAPv2ResponsePacketCreate(plugin, in_pkt_p->identifier, 433 challenge_p->mschapv2_id, 434 client_status); 435 if (out_pkt_p == NULL) { 436 goto done; 437 } 438 context->state = kMSCHAPv2ClientStateResponseSent; 439 return ((EAPMSCHAPv2PacketRef)out_pkt_p); 440 441 done: 442 return (NULL); 443} 444 445static void 446eapmschapv2_compute_session_key(EAPMSCHAPv2PluginDataRef context, 447 const char * password, 448 int password_length) 449{ 450 uint8_t master_key[NT_MASTER_KEY_SIZE]; 451 452 MSChap2_MPPEGetMasterKey((const uint8_t *)password, password_length, 453 context->nt_response, 454 master_key); 455 /* MasterSendKey */ 456 MSChap2_MPPEGetAsymetricStartKey(master_key, 457 context->session_key.master_send_key, 458 NT_SESSION_KEY_SIZE, 459 TRUE, TRUE); 460 /* MasterReceiveKey */ 461 MSChap2_MPPEGetAsymetricStartKey(master_key, 462 context->session_key.master_receive_key, 463 NT_SESSION_KEY_SIZE, 464 FALSE, TRUE); 465 context->session_key_valid = TRUE; 466 return; 467} 468 469static EAPMSCHAPv2PacketRef 470eapmschapv2_success_request(EAPClientPluginDataRef plugin, 471 EAPMSCHAPv2PacketRef in_pkt_p, 472 uint16_t in_length, 473 EAPClientStatus * client_status, 474 EAPClientDomainSpecificError * error) 475{ 476 EAPMSCHAPv2PluginDataRef context; 477 char * new_password = NULL; 478 CFStringRef new_password_cf; 479 EAPMSCHAPv2SuccessResponsePacketRef out_pkt_p = NULL; 480 const char * password; 481 int password_length; 482 EAPMSCHAPv2SuccessRequestPacketRef r_p; 483 Boolean valid; 484 485 if (in_length < sizeof(*r_p)) { 486 EAPLOG_FL(LOG_NOTICE, "length %d < %ld", in_length, sizeof(*r_p)); 487 goto done; 488 } 489 context = (EAPMSCHAPv2PluginDataRef)plugin->private; 490 switch (context->state) { 491 case kMSCHAPv2ClientStateChangePasswordSent: 492 new_password_cf 493 = CFDictionaryGetValue(plugin->properties, 494 kEAPClientPropNewPassword); 495 if (new_password_cf == NULL) { 496 EAPLOG_FL(LOG_NOTICE, "NewPassword is missing"); 497 goto done; 498 } 499 new_password = my_CFStringToCString(new_password_cf, 500 kCFStringEncodingUTF8); 501 password = new_password; 502 password_length = (int)strlen(new_password); 503 break; 504 case kMSCHAPv2ClientStateResponseSent: 505 case kMSCHAPv2ClientStateSuccess: 506 password = (const char *)plugin->password; 507 password_length = plugin->password_length; 508 break; 509 case kMSCHAPv2ClientStateFailure: 510 default: 511 goto done; 512 } 513 /* process success request */ 514 r_p = (EAPMSCHAPv2SuccessRequestPacketRef)in_pkt_p; 515 valid = MSChap2AuthResponseValid((const uint8_t *)password, 516 password_length, 517 context->nt_response, 518 context->peer_challenge, 519 context->auth_challenge, 520 (const uint8_t *)plugin->username, 521 r_p->auth_response); 522 if (valid == FALSE) { 523 EAPLOG(LOG_NOTICE, 524 "eapmschapv2_success_request: invalid server auth response"); 525 context->plugin_state = kEAPClientStateFailure; 526 context->state = kMSCHAPv2ClientStateFailure; 527 *client_status = kEAPClientStatusFailed; 528 goto done; 529 } 530 switch (context->state) { 531 case kMSCHAPv2ClientStateResponseSent: 532 EAPLOG(LOG_DEBUG, 533 "eapmschapv2_success_request: successfully authenticated"); 534 break; 535 case kMSCHAPv2ClientStateChangePasswordSent: 536 EAPLOG(LOG_NOTICE, 537 "eapmschapv2_success_request: change password succeeded"); 538 break; 539 default: 540 break; 541 } 542 eapmschapv2_compute_session_key(context, password, password_length); 543 context->state = kMSCHAPv2ClientStateSuccess; 544 out_pkt_p = (EAPMSCHAPv2SuccessResponsePacketRef) 545 EAPPacketCreate(context->pkt_buffer, sizeof(context->pkt_buffer), 546 kEAPCodeResponse, in_pkt_p->identifier, 547 kEAPTypeMSCHAPv2, NULL, 548 sizeof(*out_pkt_p) - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE, 549 NULL); 550 out_pkt_p->op_code = kMSCHAPv2OpCodeSuccess; 551 done: 552 if (new_password != NULL) { 553 free(new_password); 554 } 555 return ((EAPMSCHAPv2PacketRef)out_pkt_p); 556} 557 558 559enum { 560 kMSCHAP2FailureMessageFlagError = 0x1, 561 kMSCHAP2FailureMessageFlagRetry = 0x2, 562 kMSCHAP2FailureMessageFlagChallenge = 0x4, 563 kMSCHAP2FailureMessageFlagVersion = 0x8, 564 kMSCHAP2FailureMessageFlagMessage = 0x10, 565}; 566 567static bool 568mschap2_message_int32_attr(const char * message, uint16_t message_length, 569 const char * attr, int attr_len, 570 int32_t * int_value) 571{ 572 bool present = FALSE; 573 char * val; 574 575 val = strnstr(message, attr, message_length); 576 if (val != NULL && message_length > attr_len) { 577 val += attr_len; 578 579 *int_value = (int)strtol(val, NULL, 10); 580 present = TRUE; 581 } 582 return (present); 583} 584 585static bool 586mschap2_message_challenge_attr(const char * message, uint16_t message_length, 587 const char * attr, int attr_len, 588 uint8_t challenge[MSCHAP2_CHALLENGE_SIZE]) 589{ 590 int i; 591 bool present = FALSE; 592 char * val; 593 594 val = strnstr(message, attr, message_length); 595 if (val != NULL 596 && message_length > (attr_len + MSCHAP2_CHALLENGE_SIZE * 2)) { 597 char str[3]; 598 599 str[2] = '\0'; /* nul-terminate */ 600 val += attr_len; 601 for (i = 0; i < MSCHAP2_CHALLENGE_SIZE; i++) { 602 str[0] = val[0]; 603 str[1] = val[1]; 604 challenge[i] = strtoul(str, NULL, 16); 605 val += 2; 606 } 607 present = TRUE; 608 } 609 return (present); 610} 611 612static EAPMSCHAPv2ChangePasswordPacketRef 613EAPMSCHAPv2ChangePasswordPacketCreate(EAPClientPluginDataRef plugin, 614 int identifier, int mschapv2_id, 615 const char * new_password, 616 int new_password_length, 617 EAPClientStatus * client_status) 618{ 619 MSCHAPv2ChangePasswordResponseRef change_p; 620 EAPMSCHAPv2PluginDataRef context; 621 EAPMSCHAPv2ChangePasswordPacketRef out_pkt_p; 622 int out_length; 623 624 context = (EAPMSCHAPv2PluginDataRef)plugin->private; 625 out_length = sizeof(*out_pkt_p); 626 out_pkt_p = (EAPMSCHAPv2ChangePasswordPacketRef) 627 EAPPacketCreate(context->pkt_buffer, sizeof(context->pkt_buffer), 628 kEAPCodeResponse, identifier, 629 kEAPTypeMSCHAPv2, NULL, 630 out_length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE, 631 NULL); 632 MSChapFillWithRandom(context->peer_challenge, 633 sizeof(context->peer_challenge)); 634 /* compute nt_response using challenge from error packet and new password */ 635 MSChap2(context->auth_challenge, context->peer_challenge, 636 plugin->username, 637 (const uint8_t *)new_password, new_password_length, 638 context->nt_response); 639 640 /* fill in the packet */ 641 out_pkt_p->op_code = kMSCHAPv2OpCodeChangePassword; 642 out_pkt_p->mschapv2_id = mschapv2_id; 643 EAPMSCHAPv2PacketSetMSLength((EAPMSCHAPv2PacketRef)out_pkt_p, 644 out_length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE); 645 change_p = (MSCHAPv2ChangePasswordResponseRef)out_pkt_p->data; 646 memcpy(change_p->peer_challenge, context->peer_challenge, 647 sizeof(context->peer_challenge)); 648 memset(change_p->reserved, 0, sizeof(change_p->reserved)); 649 memcpy(change_p->nt_response, context->nt_response, 650 sizeof(context->nt_response)); 651 NTPasswordBlockEncryptNewPasswordWithOldHash((const uint8_t *)new_password, 652 new_password_length, 653 plugin->password, 654 plugin->password_length, 655 &change_p->encrypted_password); 656 NTPasswordHashEncryptOldWithNew((const uint8_t *)new_password, 657 new_password_length, 658 plugin->password, 659 plugin->password_length, 660 change_p->encrypted_hash); 661 change_p->flags[0] = 0; 662 change_p->flags[1] = 0; 663 return (out_pkt_p); 664} 665 666static uint8_t 667MSCHAPv2ParseFailureMessage(const char * message, 668 uint16_t message_length, 669 int32_t * error, int32_t * retry, 670 uint8_t challenge[MSCHAP2_CHALLENGE_SIZE], 671 int32_t * version, char * * ret_message) 672{ 673 uint32_t flags = 0; 674 char * val; 675 676 /* 677 * Parse the message string format: 678 * E=eeeeeeeee R=r C=cccccccccccccccccccccccccccccccc V=vvvvvvvvvv M=mm... 679 * We don't assume that they are in any particular order, since that's 680 * the most flexible. 681 */ 682 if (mschap2_message_int32_attr(message, message_length, "E=", 2, error)) { 683 flags |= kMSCHAP2FailureMessageFlagError; 684 } 685 if (mschap2_message_int32_attr(message, message_length, "R=", 2, retry)) { 686 flags |= kMSCHAP2FailureMessageFlagRetry; 687 } 688 if (mschap2_message_int32_attr(message, message_length, "V=", 2, version)) { 689 flags |= kMSCHAP2FailureMessageFlagVersion; 690 } 691 if (mschap2_message_challenge_attr(message, message_length, 692 "C=", 2, challenge)) { 693 flags |= kMSCHAP2FailureMessageFlagChallenge; 694 } 695 val = strnstr(message, "M=", message_length); 696 if (val != NULL) { 697 *ret_message = val + 2; 698 flags |= kMSCHAP2FailureMessageFlagMessage; 699 } 700 return (flags); 701} 702 703static EAPMSCHAPv2PacketRef 704eapmschapv2_failure_request(EAPClientPluginDataRef plugin, 705 EAPMSCHAPv2PacketRef in_pkt_p, 706 uint16_t in_length, 707 EAPClientStatus * client_status, 708 EAPClientDomainSpecificError * error) 709{ 710 EAPMSCHAPv2PluginDataRef context; 711 bool handled = FALSE; 712 char * message = NULL; 713 int message_length; 714 uint32_t flags = 0; 715 int32_t r_error; 716 int32_t r_retry = 0; 717 int32_t r_version = 0; 718 char * r_message; 719 EAPMSCHAPv2FailureRequestPacketRef r_p; 720 EAPMSCHAPv2PacketRef out_pkt_p = NULL; 721 722 if (in_length < sizeof(*r_p)) { 723 EAPLOG(LOG_NOTICE, "eapmschapv2_failure_request: length %d < %ld", 724 in_length, sizeof(*r_p)); 725 goto done; 726 } 727 context = (EAPMSCHAPv2PluginDataRef)plugin->private; 728 switch (context->state) { 729 case kMSCHAPv2ClientStateResponseSent: 730 case kMSCHAPv2ClientStateChangePasswordSent: 731 break; 732 case kMSCHAPv2ClientStateFailure: 733 break; 734 case kMSCHAPv2ClientStateSuccess: 735 default: 736 goto done; 737 } 738 r_p = (EAPMSCHAPv2FailureRequestPacketRef)in_pkt_p; 739 do { /* something to break out of */ 740 /* allocate a message buffer that's guaranteed to be nul-terminated */ 741 message_length = in_length - sizeof(*r_p); 742 if (message_length == 0) { 743 break; 744 } 745 message = malloc(message_length + 1); 746 memcpy(message, r_p->message, message_length); 747 message[message_length] = '\0'; 748 749 /* parse it */ 750 flags = MSCHAPv2ParseFailureMessage(message, message_length, 751 &r_error, &r_retry, 752 context->auth_challenge, 753 &r_version, &r_message); 754 755 if ((flags & kMSCHAP2FailureMessageFlagError) == 0) { 756 break; 757 } 758 EAPLOG(LOG_NOTICE, "MSCHAPv2 Error = %d, Retry = %d, Version = %d", 759 r_error, r_retry, r_version); 760 761 if ((flags & kMSCHAP2FailureMessageFlagChallenge) == 0) { 762 EAPLOG(LOG_NOTICE, 763 "MSCHAPv2 Failure Request does not contain challenge"); 764 break; 765 } 766 if (r_error != kMSCHAP2_ERROR_PASSWD_EXPIRED) { 767 if (r_retry == 0) { 768 /* can't retry, acknowledge the error */ 769 break; 770 } 771 if (context->last_generation == plugin->generation 772 || plugin->password == NULL) { 773 context->need_password = TRUE; 774 *client_status = kEAPClientStatusUserInputRequired; 775 } 776 else { 777 out_pkt_p = (EAPMSCHAPv2PacketRef) 778 EAPMSCHAPv2ResponsePacketCreate(plugin, 779 r_p->identifier, 780 r_p->mschapv2_id + 1, 781 NULL); 782 if (out_pkt_p == NULL) { 783 break; 784 } 785 context->state = kMSCHAPv2ClientStateResponseSent; 786 } 787 } 788 else { 789 char * new_password = NULL; 790 791 if (r_version != kMSCHAPv2ChangePasswordVersion) { 792 break; 793 } 794 if (plugin->password == NULL) { 795 break; 796 } 797 if (context->last_generation != plugin->generation 798 && plugin->properties != NULL) { 799 CFStringRef new_password_cf; 800 801 new_password_cf 802 = CFDictionaryGetValue(plugin->properties, 803 kEAPClientPropNewPassword); 804 if (new_password_cf != NULL) { 805 new_password = my_CFStringToCString(new_password_cf, 806 kCFStringEncodingUTF8); 807 } 808 } 809 if (new_password != NULL) { 810 out_pkt_p = (EAPMSCHAPv2PacketRef) 811 EAPMSCHAPv2ChangePasswordPacketCreate(plugin, 812 r_p->identifier, 813 r_p->mschapv2_id + 1, 814 new_password, 815 (int) 816 strlen(new_password), 817 NULL); 818 free(new_password); 819 if (out_pkt_p == NULL) { 820 break; 821 } 822 context->state = kMSCHAPv2ClientStateChangePasswordSent; 823 } 824 else { 825 context->need_new_password = TRUE; 826 *client_status = kEAPClientStatusUserInputRequired; 827 } 828 } 829 handled = TRUE; 830 } while (0); /* something to break out of */ 831 832 if (handled == FALSE) { 833 context->state = kMSCHAPv2ClientStateFailure; 834 context->plugin_state = kEAPClientStateFailure; 835 836 *client_status = kEAPClientStatusFailed; 837 out_pkt_p = (EAPMSCHAPv2PacketRef) 838 EAPPacketCreate(context->pkt_buffer, sizeof(context->pkt_buffer), 839 kEAPCodeResponse, in_pkt_p->identifier, 840 kEAPTypeMSCHAPv2, NULL, 841 sizeof(*out_pkt_p) - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE, 842 NULL); 843 out_pkt_p->op_code = kMSCHAPv2OpCodeFailure; 844 out_pkt_p->mschapv2_id = r_p->mschapv2_id; 845 EAPMSCHAPv2PacketSetMSLength((EAPMSCHAPv2PacketRef)out_pkt_p, 846 (sizeof(*out_pkt_p) 847 - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)); 848 } 849 if (message != NULL) { 850 free(message); 851 } 852 return (out_pkt_p); 853 854 done: 855 if (out_pkt_p != NULL) { 856 free(out_pkt_p); 857 } 858 if (message != NULL) { 859 free(message); 860 } 861 return (NULL); 862} 863 864static EAPPacketRef 865eapmschapv2_request(EAPClientPluginDataRef plugin, 866 const EAPPacketRef in_pkt, 867 EAPClientStatus * client_status, 868 EAPClientDomainSpecificError * error) 869{ 870 EAPMSCHAPv2PacketRef mschap_in_p; 871 EAPMSCHAPv2PacketRef mschap_out_p = NULL; 872 uint16_t in_length = EAPPacketGetLength(in_pkt); 873 874 mschap_in_p = (EAPMSCHAPv2PacketRef)in_pkt; 875 if (in_length < sizeof(*mschap_in_p)) { 876 EAPLOG(LOG_NOTICE, "eapmschapv2_request: length %d < %ld", 877 in_length, sizeof(*mschap_in_p)); 878 goto done; 879 } 880 switch (mschap_in_p->op_code) { 881 case kMSCHAPv2OpCodeChallenge: 882 mschap_out_p = eapmschapv2_challenge(plugin, mschap_in_p, in_length, 883 client_status, error); 884 break; 885 case kMSCHAPv2OpCodeResponse: 886 /* ignore */ 887 break; 888 case kMSCHAPv2OpCodeSuccess: 889 mschap_out_p = eapmschapv2_success_request(plugin, mschap_in_p, 890 in_length, client_status, 891 error); 892 break; 893 case kMSCHAPv2OpCodeFailure: 894 mschap_out_p = eapmschapv2_failure_request(plugin, mschap_in_p, 895 in_length, client_status, 896 error); 897 break; 898 default: 899 break; 900 } 901 return ((EAPPacketRef)mschap_out_p); 902 903 done: 904 if (mschap_out_p != NULL) { 905 free(mschap_out_p); 906 } 907 return (NULL); 908} 909 910static EAPClientState 911eapmschapv2_process(EAPClientPluginDataRef plugin, 912 const EAPPacketRef in_pkt, 913 EAPPacketRef * out_pkt_p, 914 EAPClientStatus * client_status, 915 EAPClientDomainSpecificError * error) 916{ 917 EAPMSCHAPv2PluginDataRef context; 918 919 context = (EAPMSCHAPv2PluginDataRef)plugin->private; 920 921 *client_status = kEAPClientStatusOK; 922 *error = 0; 923 *out_pkt_p = NULL; 924 switch (in_pkt->code) { 925 case kEAPCodeRequest: 926 *out_pkt_p = eapmschapv2_request(plugin, in_pkt, client_status, 927 error); 928 break; 929 case kEAPCodeSuccess: 930 if (context->state == kMSCHAPv2ClientStateSuccess) { 931 context->plugin_state = kEAPClientStateSuccess; 932 } 933 break; 934 case kEAPCodeFailure: 935 if (context->state == kMSCHAPv2ClientStateFailure) { 936 context->plugin_state = kEAPClientStateFailure; 937 } 938 break; 939 case kEAPCodeResponse: 940 default: 941 /* ignore */ 942 break; 943 } 944 context->last_generation = plugin->generation; 945 return (context->plugin_state); 946} 947 948static CFDictionaryRef 949eapmschapv2_publish_props(EAPClientPluginDataRef plugin) 950{ 951 return (NULL); 952} 953 954static CFArrayRef 955eapmschapv2_require_props(EAPClientPluginDataRef plugin) 956{ 957 CFMutableArrayRef array = NULL; 958 EAPMSCHAPv2PluginDataRef context; 959 960 context = (EAPMSCHAPv2PluginDataRef)plugin->private; 961 if (context->need_password || context->need_new_password) { 962 array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 963 if (context->need_password) { 964 CFArrayAppendValue(array, kEAPClientPropUserPassword); 965 } 966 if (context->need_new_password) { 967 CFArrayAppendValue(array, kEAPClientPropNewPassword); 968 } 969 } 970 return (array); 971} 972 973static bool 974eapmschapv2_challenge_append(CFMutableStringRef str, const EAPPacketRef pkt, 975 int length) 976{ 977 EAPMSCHAPv2ChallengePacketRef challenge_p; 978 int name_length; 979 bool packet_is_valid = FALSE; 980 981 if (length < sizeof(*challenge_p)) { 982 STRING_APPEND(str, "Error: length %d < %d\n", length, 983 (int)sizeof(*challenge_p)); 984 goto done; 985 } 986 challenge_p = (EAPMSCHAPv2ChallengePacketRef)pkt; 987 STRING_APPEND(str, "MS-CHAPv2-ID %d MS-Length %d Value-Size %d\n", 988 challenge_p->mschapv2_id, 989 EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt), 990 challenge_p->value_size); 991 992 if (challenge_p->value_size != sizeof(challenge_p->challenge)) { 993 STRING_APPEND(str, "Error: Value-Size should be %d\n", 994 (int)sizeof(challenge_p->challenge)); 995 } 996 if (EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt) 997 != (length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)) { 998 STRING_APPEND(str, "Error: MS-Length should be %d\n", 999 (int)(length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)); 1000 goto done; 1001 } 1002 STRING_APPEND(str, "Challenge: "); 1003 print_bytes_cfstr(str, challenge_p->challenge, 1004 sizeof(challenge_p->challenge)); 1005 STRING_APPEND(str, "\n"); 1006 name_length = length - sizeof(*challenge_p); 1007 if (name_length > 0) { 1008 STRING_APPEND(str, "Name: %.*s\n", name_length, challenge_p->name); 1009 } 1010 packet_is_valid = TRUE; 1011 1012 done: 1013 return (packet_is_valid); 1014} 1015 1016static bool 1017eapmschapv2_response_append(CFMutableStringRef str, const EAPPacketRef pkt, 1018 int length) 1019{ 1020 int name_length; 1021 bool packet_is_valid = FALSE; 1022 EAPMSCHAPv2ResponsePacketRef r_p; 1023 MSCHAP2ResponseRef resp_p; 1024 1025 if (length < sizeof(*r_p)) { 1026 STRING_APPEND(str, "Error: length %d < %d\n", length, 1027 (int)sizeof(*r_p)); 1028 goto done; 1029 } 1030 r_p = (EAPMSCHAPv2ResponsePacketRef)pkt; 1031 STRING_APPEND(str, "MS-CHAPv2-ID %d MS-Length %d Value-Size %d\n", 1032 r_p->mschapv2_id, 1033 EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt), 1034 r_p->value_size); 1035 1036 if (r_p->value_size != sizeof(r_p->response)) { 1037 STRING_APPEND(str, "Error: Value-Size should be %d\n", 1038 (int)sizeof(r_p->response)); 1039 } 1040 if (EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt) 1041 != (length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)) { 1042 STRING_APPEND(str, "Error: MS-Length should be %d\n", 1043 (int)(length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)); 1044 goto done; 1045 } 1046 resp_p = (MSCHAP2ResponseRef)r_p->response; 1047 STRING_APPEND(str, "Response:\n"); 1048 STRING_APPEND(str, 1049 "Peer Challenge: "); 1050 print_bytes_cfstr(str, resp_p->peer_challenge, 1051 sizeof(resp_p->peer_challenge)); 1052 STRING_APPEND(str, "\n" 1053 "Reserved: "); 1054 print_bytes_cfstr(str, resp_p->reserved, sizeof(resp_p->reserved)); 1055 STRING_APPEND(str, "\n" 1056 "NT Response: "); 1057 print_bytes_cfstr(str, resp_p->nt_response, sizeof(resp_p->nt_response)); 1058 STRING_APPEND(str, "\n" 1059 "Flags: "); 1060 print_bytes_cfstr(str, resp_p->flags, sizeof(resp_p->flags)); 1061 name_length = length - sizeof(*r_p); 1062 if (name_length > 0) { 1063 STRING_APPEND(str, "\n" 1064 "Name: %.*s\n", name_length, r_p->name); 1065 } 1066 packet_is_valid = TRUE; 1067 1068 done: 1069 return (packet_is_valid); 1070} 1071 1072static bool 1073eapmschapv2_success_request_append(CFMutableStringRef str, 1074 const EAPPacketRef pkt, int length) 1075{ 1076 int message_length; 1077 EAPMSCHAPv2SuccessRequestPacketRef r_p; 1078 bool packet_is_valid = FALSE; 1079 1080 if (length < sizeof(*r_p)) { 1081 STRING_APPEND(str, 1082 "Error: length %d < %d\n", length, (int)sizeof(*r_p)); 1083 goto done; 1084 } 1085 r_p = (EAPMSCHAPv2SuccessRequestPacketRef)pkt; 1086 STRING_APPEND(str, "MS-CHAPv2-ID %d MS-Length %d\n", 1087 r_p->mschapv2_id, 1088 EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt)); 1089 if (EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt) 1090 != (length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)) { 1091 STRING_APPEND(str, "Error: MS-Length should be %d\n", 1092 (int)(length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)); 1093 goto done; 1094 } 1095 STRING_APPEND(str, 1096 "Auth Response: %.*s\n", (int)sizeof(r_p->auth_response), 1097 r_p->auth_response); 1098 message_length = length - sizeof(*r_p); 1099 if (message_length > 1) { 1100 /* skip the space when we print it out */ 1101 STRING_APPEND(str, 1102 "Message: %.*s\n", message_length - 1, 1103 r_p->message + 1); 1104 } 1105 packet_is_valid = TRUE; 1106 1107 done: 1108 return (packet_is_valid); 1109} 1110 1111static bool 1112eapmschapv2_failure_request_append(CFMutableStringRef str, 1113 const EAPPacketRef pkt, int length) 1114{ 1115 int message_length; 1116 bool packet_is_valid = FALSE; 1117 EAPMSCHAPv2FailureRequestPacketRef r_p; 1118 1119 if (length < sizeof(*r_p)) { 1120 STRING_APPEND(str, 1121 "Error: length %d < %d\n", length, (int)sizeof(*r_p)); 1122 goto done; 1123 } 1124 r_p = (EAPMSCHAPv2FailureRequestPacketRef)pkt; 1125 STRING_APPEND(str, "MS-CHAPv2-ID %d MS-Length %d\n", 1126 r_p->mschapv2_id, 1127 EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt)); 1128 if (EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt) 1129 != (length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)) { 1130 STRING_APPEND(str, "Error: MS-Length should be %d\n", 1131 (int)(length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)); 1132 goto done; 1133 } 1134 message_length = length - sizeof(*r_p); 1135 if (message_length > 0) { 1136 STRING_APPEND(str, 1137 "Message: %.*s\n", message_length, r_p->message); 1138 } 1139 packet_is_valid = TRUE; 1140 1141 done: 1142 return (packet_is_valid); 1143} 1144 1145static bool 1146eapmschapv2_change_password_append(CFMutableStringRef str, 1147 const EAPPacketRef pkt, int length) 1148{ 1149 MSCHAPv2ChangePasswordResponseRef change_p; 1150 bool packet_is_valid = FALSE; 1151 EAPMSCHAPv2ChangePasswordPacketRef r_p; 1152 1153 if (length < sizeof(*r_p)) { 1154 STRING_APPEND(str, 1155 "Error: length %d < %d\n", length, (int)sizeof(*r_p)); 1156 goto done; 1157 } 1158 r_p = (EAPMSCHAPv2ChangePasswordPacketRef)pkt; 1159 STRING_APPEND(str, "MS-CHAPv2-ID %d MS-Length %d\n", 1160 r_p->mschapv2_id, 1161 EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt)); 1162 if (EAPMSCHAPv2PacketGetMSLength((EAPMSCHAPv2PacketRef)pkt) 1163 != (length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)) { 1164 STRING_APPEND(str, "Error: MS-Length should be %d\n", 1165 (int)(length - EAP_MSCHAP2_MS_LENGTH_DIFFERENCE)); 1166 goto done; 1167 } 1168 change_p = (MSCHAPv2ChangePasswordResponseRef)(r_p->data); 1169 STRING_APPEND(str, "Encrypted Password:\n"); 1170 print_data_cfstr(str, (void *)&change_p->encrypted_password, 1171 sizeof(change_p->encrypted_password)); 1172 STRING_APPEND(str, "Encrypted Hash: "); 1173 print_bytes_cfstr(str, change_p->encrypted_hash, 1174 sizeof(change_p->encrypted_hash)); 1175 STRING_APPEND(str, "\n"); 1176 STRING_APPEND(str, "Peer Challenge: "); 1177 print_bytes_cfstr(str, change_p->peer_challenge, 1178 sizeof(change_p->peer_challenge)); 1179 STRING_APPEND(str, "\n"); 1180 STRING_APPEND(str, "Reserved: "); 1181 print_bytes_cfstr(str, change_p->reserved, 1182 sizeof(change_p->reserved)); 1183 STRING_APPEND(str, "\n"); 1184 STRING_APPEND(str, "NT Response: "); 1185 print_bytes_cfstr(str, change_p->nt_response, 1186 sizeof(change_p->nt_response)); 1187 STRING_APPEND(str, "\n"); 1188 STRING_APPEND(str, "NT Flags: "); 1189 print_bytes_cfstr(str, change_p->flags, 1190 sizeof(change_p->flags)); 1191 STRING_APPEND(str, "\n"); 1192 1193 packet_is_valid = TRUE; 1194 1195 done: 1196 return (packet_is_valid); 1197} 1198 1199static CFStringRef 1200eapmschapv2_copy_packet_description(const EAPPacketRef pkt, 1201 bool * packet_is_valid) 1202{ 1203 int data_length; 1204 EAPMSCHAPv2PacketRef ms_pkt_p = (EAPMSCHAPv2PacketRef)pkt; 1205 u_int16_t length = EAPPacketGetLength(pkt); 1206 CFMutableStringRef str = NULL; 1207 bool valid; 1208 1209 valid = FALSE; 1210 switch (pkt->code) { 1211 case kEAPCodeRequest: 1212 break; 1213 case kEAPCodeResponse: 1214 break; 1215 default: 1216 /* don't handle it */ 1217 goto done; 1218 } 1219 str = CFStringCreateMutable(NULL, 0); 1220 if (length < sizeof(EAPMSCHAPv2SuccessResponsePacket)) { 1221 STRING_APPEND(str, "invalid packet: length %d < min length %ld\n", 1222 length, sizeof(*ms_pkt_p)); 1223 goto done; 1224 } 1225 data_length = length - sizeof(EAPMSCHAPv2SuccessResponsePacket); 1226 STRING_APPEND(str, "EAP-MSCHAPv2 %s: Identifier %d Length %d OpCode %s ", 1227 EAPCodeStr(pkt->code), pkt->identifier, length, 1228 MSCHAPv2OpCodeStr(ms_pkt_p->op_code)); 1229 /* Request */ 1230 switch (ms_pkt_p->op_code) { 1231 case kMSCHAPv2OpCodeChallenge: 1232 valid = eapmschapv2_challenge_append(str, pkt, length); 1233 if (pkt->code == kEAPCodeResponse) { 1234 valid = FALSE; 1235 STRING_APPEND(str, "EAP Response contains " 1236 "MSCHAPv2 Challenge (invalid)\n"); 1237 } 1238 break; 1239 case kMSCHAPv2OpCodeResponse: 1240 valid = eapmschapv2_response_append(str, pkt, length); 1241 if (pkt->code == kEAPCodeRequest) { 1242 valid = FALSE; 1243 STRING_APPEND(str, "EAP Request contains " 1244 "MSCHAPv2 Response (invalid)\n"); 1245 } 1246 break; 1247 case kMSCHAPv2OpCodeSuccess: 1248 if (pkt->code == kEAPCodeRequest) { 1249 valid = eapmschapv2_success_request_append(str, pkt, length); 1250 } 1251 break; 1252 case kMSCHAPv2OpCodeFailure: 1253 if (pkt->code == kEAPCodeRequest) { 1254 valid = eapmschapv2_failure_request_append(str, pkt, length); 1255 } 1256 break; 1257 case kMSCHAPv2OpCodeChangePassword: 1258 valid = eapmschapv2_change_password_append(str, pkt, length); 1259 break; 1260 default: 1261 STRING_APPEND(str, "Unknown code %d\n", ms_pkt_p->op_code); 1262 if (data_length > 0) { 1263 print_data_cfstr(str, &ms_pkt_p->mschapv2_id, 1264 data_length); 1265 } 1266 break; 1267 } 1268 1269 done: 1270 *packet_is_valid = valid; 1271 return (str); 1272} 1273 1274static EAPType 1275eapmschapv2_type() 1276{ 1277 return (kEAPTypeMSCHAPv2); 1278 1279} 1280 1281static const char * 1282eapmschapv2_name() 1283{ 1284 return ("MSCHAPv2"); 1285 1286} 1287 1288static EAPClientPluginVersion 1289eapmschapv2_version() 1290{ 1291 return (kEAPClientPluginVersion); 1292} 1293 1294static void * 1295eapmschapv2_session_key(EAPClientPluginDataRef plugin, int * key_length) 1296{ 1297 EAPMSCHAPv2PluginDataRef context; 1298 1299 context = (EAPMSCHAPv2PluginDataRef)plugin->private; 1300 *key_length = 0; 1301 if (context->session_key_valid == FALSE) { 1302 return (NULL); 1303 } 1304 *key_length = sizeof(context->session_key); 1305 return (&context->session_key); 1306} 1307 1308static void * 1309eapmschapv2_server_key(EAPClientPluginDataRef plugin, int * key_length) 1310{ 1311 EAPMSCHAPv2PluginDataRef context; 1312 1313 context = (EAPMSCHAPv2PluginDataRef)plugin->private; 1314 *key_length = 0; 1315 if (context->session_key_valid == FALSE) { 1316 return (NULL); 1317 } 1318 *key_length = sizeof(context->session_key); 1319 return (&context->session_key); 1320} 1321 1322static int 1323eapmschapv2_msk_copy_bytes(EAPClientPluginDataRef plugin, 1324 void * msk, int msk_size) 1325{ 1326 EAPMSCHAPv2PluginDataRef context; 1327 int ret_msk_size; 1328 1329 context = (EAPMSCHAPv2PluginDataRef)plugin->private; 1330 if (msk_size < kEAPMasterSessionKeyMinimumSize 1331 || context->session_key_valid == FALSE) { 1332 ret_msk_size = 0; 1333 } 1334 else { 1335 void * offset; 1336 1337 offset = msk; 1338 1339 /* MasterReceiveKey */ 1340 bcopy(context->session_key.master_receive_key, offset, 1341 sizeof(context->session_key.master_receive_key)); 1342 offset += sizeof(context->session_key.master_receive_key); 1343 1344 /* MasterSendKey */ 1345 bcopy(context->session_key.master_send_key, offset, 1346 sizeof(context->session_key.master_send_key)); 1347 offset += sizeof(context->session_key.master_send_key); 1348 1349 /* Zero-padding */ 1350 bzero(offset, 1351 kEAPMasterSessionKeyMinimumSize - sizeof(context->session_key)); 1352 1353 ret_msk_size = kEAPMasterSessionKeyMinimumSize; 1354 } 1355 return (ret_msk_size); 1356} 1357 1358static struct func_table_ent { 1359 const char * name; 1360 void * func; 1361} func_table[] = { 1362#if 0 1363 { kEAPClientPluginFuncNameIntrospect, eapmschapv2_introspect }, 1364#endif /* 0 */ 1365 { kEAPClientPluginFuncNameVersion, eapmschapv2_version }, 1366 { kEAPClientPluginFuncNameEAPType, eapmschapv2_type }, 1367 { kEAPClientPluginFuncNameEAPName, eapmschapv2_name }, 1368 { kEAPClientPluginFuncNameInit, eapmschapv2_init }, 1369 { kEAPClientPluginFuncNameFree, eapmschapv2_free }, 1370 { kEAPClientPluginFuncNameProcess, eapmschapv2_process }, 1371 { kEAPClientPluginFuncNameFreePacket, eapmschapv2_free_packet }, 1372 { kEAPClientPluginFuncNameSessionKey, eapmschapv2_session_key }, 1373 { kEAPClientPluginFuncNameServerKey, eapmschapv2_server_key }, 1374 { kEAPClientPluginFuncNameMasterSessionKeyCopyBytes, 1375 eapmschapv2_msk_copy_bytes }, 1376 { kEAPClientPluginFuncNameRequireProperties, eapmschapv2_require_props }, 1377 { kEAPClientPluginFuncNamePublishProperties, eapmschapv2_publish_props }, 1378 { kEAPClientPluginFuncNameCopyPacketDescription, 1379 eapmschapv2_copy_packet_description }, 1380 { NULL, NULL}, 1381}; 1382 1383 1384EAPClientPluginFuncRef 1385eapmschapv2_introspect(EAPClientPluginFuncName name) 1386{ 1387 struct func_table_ent * scan; 1388 1389 1390 for (scan = func_table; scan->name != NULL; scan++) { 1391 if (strcmp(name, scan->name) == 0) { 1392 return (scan->func); 1393 } 1394 } 1395 return (NULL); 1396} 1397