1/*- 2 * Copyright (c) 2012 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Edward Tomasz Napierala under sponsorship 6 * from the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32#include <sys/types.h> 33#include <assert.h> 34#include <stdbool.h> 35#include <stdio.h> 36#include <stdlib.h> 37#include <string.h> 38#include <netinet/in.h> 39#include <openssl/err.h> 40#include <openssl/md5.h> 41#include <openssl/rand.h> 42 43#include "iscsid.h" 44#include "iscsi_proto.h" 45 46static int 47login_nsg(const struct pdu *response) 48{ 49 struct iscsi_bhs_login_response *bhslr; 50 51 bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; 52 53 return (bhslr->bhslr_flags & 0x03); 54} 55 56static void 57login_set_nsg(struct pdu *request, int nsg) 58{ 59 struct iscsi_bhs_login_request *bhslr; 60 61 assert(nsg == BHSLR_STAGE_SECURITY_NEGOTIATION || 62 nsg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || 63 nsg == BHSLR_STAGE_FULL_FEATURE_PHASE); 64 65 bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; 66 67 bhslr->bhslr_flags &= 0xFC; 68 bhslr->bhslr_flags |= nsg; 69} 70 71static void 72login_set_csg(struct pdu *request, int csg) 73{ 74 struct iscsi_bhs_login_request *bhslr; 75 76 assert(csg == BHSLR_STAGE_SECURITY_NEGOTIATION || 77 csg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || 78 csg == BHSLR_STAGE_FULL_FEATURE_PHASE); 79 80 bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; 81 82 bhslr->bhslr_flags &= 0xF3; 83 bhslr->bhslr_flags |= csg << 2; 84} 85 86static const char * 87login_target_error_str(int class, int detail) 88{ 89 static char msg[128]; 90 91 /* 92 * RFC 3270, 10.13.5. Status-Class and Status-Detail 93 */ 94 switch (class) { 95 case 0x01: 96 switch (detail) { 97 case 0x01: 98 return ("Target moved temporarily"); 99 case 0x02: 100 return ("Target moved permanently"); 101 default: 102 snprintf(msg, sizeof(msg), "unknown redirection; " 103 "Status-Class 0x%x, Status-Detail 0x%x", 104 class, detail); 105 return (msg); 106 } 107 case 0x02: 108 switch (detail) { 109 case 0x00: 110 return ("Initiator error"); 111 case 0x01: 112 return ("Authentication failure"); 113 case 0x02: 114 return ("Authorization failure"); 115 case 0x03: 116 return ("Not found"); 117 case 0x04: 118 return ("Target removed"); 119 case 0x05: 120 return ("Unsupported version"); 121 case 0x06: 122 return ("Too many connections"); 123 case 0x07: 124 return ("Missing parameter"); 125 case 0x08: 126 return ("Can't include in session"); 127 case 0x09: 128 return ("Session type not supported"); 129 case 0x0a: 130 return ("Session does not exist"); 131 case 0x0b: 132 return ("Invalid during login"); 133 default: 134 snprintf(msg, sizeof(msg), "unknown initiator error; " 135 "Status-Class 0x%x, Status-Detail 0x%x", 136 class, detail); 137 return (msg); 138 } 139 case 0x03: 140 switch (detail) { 141 case 0x00: 142 return ("Target error"); 143 case 0x01: 144 return ("Service unavailable"); 145 case 0x02: 146 return ("Out of resources"); 147 default: 148 snprintf(msg, sizeof(msg), "unknown target error; " 149 "Status-Class 0x%x, Status-Detail 0x%x", 150 class, detail); 151 return (msg); 152 } 153 default: 154 snprintf(msg, sizeof(msg), "unknown error; " 155 "Status-Class 0x%x, Status-Detail 0x%x", 156 class, detail); 157 return (msg); 158 } 159} 160 161static struct pdu * 162login_receive(struct connection *conn, bool initial) 163{ 164 struct pdu *response; 165 struct iscsi_bhs_login_response *bhslr; 166 const char *errorstr; 167 168 response = pdu_new(conn); 169 pdu_receive(response); 170 if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_LOGIN_RESPONSE) { 171 log_errx(1, "protocol error: received invalid opcode 0x%x", 172 response->pdu_bhs->bhs_opcode); 173 } 174 bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; 175 /* 176 * XXX: Implement the C flag some day. 177 */ 178 if ((bhslr->bhslr_flags & BHSLR_FLAGS_CONTINUE) != 0) 179 log_errx(1, "received Login PDU with unsupported \"C\" flag"); 180 if (bhslr->bhslr_version_max != 0x00) 181 log_errx(1, "received Login PDU with unsupported " 182 "Version-max 0x%x", bhslr->bhslr_version_max); 183 if (bhslr->bhslr_version_active != 0x00) 184 log_errx(1, "received Login PDU with unsupported " 185 "Version-active 0x%x", bhslr->bhslr_version_active); 186 if (bhslr->bhslr_status_class != 0) { 187 errorstr = login_target_error_str(bhslr->bhslr_status_class, 188 bhslr->bhslr_status_detail); 189 fail(conn, errorstr); 190 log_errx(1, "target returned error: %s", errorstr); 191 } 192#if 0 193 if (response->pdu_data_len == 0) 194 log_errx(1, "received Login PDU with empty data segment"); 195#endif 196 if (initial == false && 197 ntohl(bhslr->bhslr_statsn) != conn->conn_statsn + 1) { 198 /* 199 * It's a warning, not an error, to work around what seems 200 * to be bug in NetBSD iSCSI target. 201 */ 202 log_warnx("received Login PDU with wrong StatSN: " 203 "is %d, should be %d", ntohl(bhslr->bhslr_statsn), 204 conn->conn_statsn + 1); 205 } 206 conn->conn_statsn = ntohl(bhslr->bhslr_statsn); 207 208 return (response); 209} 210 211static struct pdu * 212login_new_request(struct connection *conn) 213{ 214 struct pdu *request; 215 struct iscsi_bhs_login_request *bhslr; 216 217 request = pdu_new(conn); 218 bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; 219 bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGIN_REQUEST | 220 ISCSI_BHS_OPCODE_IMMEDIATE; 221 bhslr->bhslr_flags = BHSLR_FLAGS_TRANSIT; 222 login_set_csg(request, BHSLR_STAGE_SECURITY_NEGOTIATION); 223 login_set_nsg(request, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); 224 memcpy(bhslr->bhslr_isid, &conn->conn_isid, sizeof(bhslr->bhslr_isid)); 225 bhslr->bhslr_initiator_task_tag = 0; 226 bhslr->bhslr_cmdsn = 0; 227 bhslr->bhslr_expstatsn = htonl(conn->conn_statsn + 1); 228 229 return (request); 230} 231 232static int 233login_list_prefers(const char *list, 234 const char *choice1, const char *choice2) 235{ 236 char *tofree, *str, *token; 237 238 tofree = str = checked_strdup(list); 239 240 while ((token = strsep(&str, ",")) != NULL) { 241 if (strcmp(token, choice1) == 0) { 242 free(tofree); 243 return (1); 244 } 245 if (strcmp(token, choice2) == 0) { 246 free(tofree); 247 return (2); 248 } 249 } 250 free(tofree); 251 return (-1); 252} 253 254static int 255login_hex2int(const char hex) 256{ 257 switch (hex) { 258 case '0': 259 return (0x00); 260 case '1': 261 return (0x01); 262 case '2': 263 return (0x02); 264 case '3': 265 return (0x03); 266 case '4': 267 return (0x04); 268 case '5': 269 return (0x05); 270 case '6': 271 return (0x06); 272 case '7': 273 return (0x07); 274 case '8': 275 return (0x08); 276 case '9': 277 return (0x09); 278 case 'a': 279 case 'A': 280 return (0x0a); 281 case 'b': 282 case 'B': 283 return (0x0b); 284 case 'c': 285 case 'C': 286 return (0x0c); 287 case 'd': 288 case 'D': 289 return (0x0d); 290 case 'e': 291 case 'E': 292 return (0x0e); 293 case 'f': 294 case 'F': 295 return (0x0f); 296 default: 297 return (-1); 298 } 299} 300 301/* 302 * XXX: Review this _carefully_. 303 */ 304static int 305login_hex2bin(const char *hex, char **binp, size_t *bin_lenp) 306{ 307 int i, hex_len, nibble; 308 bool lo = true; /* As opposed to 'hi'. */ 309 char *bin; 310 size_t bin_off, bin_len; 311 312 if (strncasecmp(hex, "0x", strlen("0x")) != 0) { 313 log_warnx("malformed variable, should start with \"0x\""); 314 return (-1); 315 } 316 317 hex += strlen("0x"); 318 hex_len = strlen(hex); 319 if (hex_len < 1) { 320 log_warnx("malformed variable; doesn't contain anything " 321 "but \"0x\""); 322 return (-1); 323 } 324 325 bin_len = hex_len / 2 + hex_len % 2; 326 bin = calloc(bin_len, 1); 327 if (bin == NULL) 328 log_err(1, "calloc"); 329 330 bin_off = bin_len - 1; 331 for (i = hex_len - 1; i >= 0; i--) { 332 nibble = login_hex2int(hex[i]); 333 if (nibble < 0) { 334 log_warnx("malformed variable, invalid char \"%c\"", 335 hex[i]); 336 return (-1); 337 } 338 339 assert(bin_off < bin_len); 340 if (lo) { 341 bin[bin_off] = nibble; 342 lo = false; 343 } else { 344 bin[bin_off] |= nibble << 4; 345 bin_off--; 346 lo = true; 347 } 348 } 349 350 *binp = bin; 351 *bin_lenp = bin_len; 352 return (0); 353} 354 355static char * 356login_bin2hex(const char *bin, size_t bin_len) 357{ 358 unsigned char *hex, *tmp, ch; 359 size_t hex_len; 360 size_t i; 361 362 hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ 363 hex = malloc(hex_len); 364 if (hex == NULL) 365 log_err(1, "malloc"); 366 367 tmp = hex; 368 tmp += sprintf(tmp, "0x"); 369 for (i = 0; i < bin_len; i++) { 370 ch = bin[i]; 371 tmp += sprintf(tmp, "%02x", ch); 372 } 373 374 return (hex); 375} 376 377static void 378login_compute_md5(const char id, const char *secret, 379 const void *challenge, size_t challenge_len, void *response, 380 size_t response_len) 381{ 382 MD5_CTX ctx; 383 int rv; 384 385 assert(response_len == MD5_DIGEST_LENGTH); 386 387 MD5_Init(&ctx); 388 MD5_Update(&ctx, &id, sizeof(id)); 389 MD5_Update(&ctx, secret, strlen(secret)); 390 MD5_Update(&ctx, challenge, challenge_len); 391 rv = MD5_Final(response, &ctx); 392 if (rv != 1) 393 log_errx(1, "MD5_Final"); 394} 395 396static void 397login_negotiate_key(struct connection *conn, const char *name, 398 const char *value) 399{ 400 int which, tmp; 401 402 if (strcmp(name, "TargetAlias") == 0) { 403 strlcpy(conn->conn_target_alias, value, 404 sizeof(conn->conn_target_alias)); 405 } else if (strcmp(value, "Irrelevant") == 0) { 406 /* Ignore. */ 407 } else if (strcmp(name, "HeaderDigest") == 0) { 408 which = login_list_prefers(value, "CRC32C", "None"); 409 switch (which) { 410 case 1: 411 log_debugx("target prefers CRC32C " 412 "for header digest; we'll use it"); 413 conn->conn_header_digest = CONN_DIGEST_CRC32C; 414 break; 415 case 2: 416 log_debugx("target prefers not to do " 417 "header digest; we'll comply"); 418 break; 419 default: 420 log_warnx("target sent unrecognized " 421 "HeaderDigest value \"%s\"; will use None", value); 422 break; 423 } 424 } else if (strcmp(name, "DataDigest") == 0) { 425 which = login_list_prefers(value, "CRC32C", "None"); 426 switch (which) { 427 case 1: 428 log_debugx("target prefers CRC32C " 429 "for data digest; we'll use it"); 430 conn->conn_data_digest = CONN_DIGEST_CRC32C; 431 break; 432 case 2: 433 log_debugx("target prefers not to do " 434 "data digest; we'll comply"); 435 break; 436 default: 437 log_warnx("target sent unrecognized " 438 "DataDigest value \"%s\"; will use None", value); 439 break; 440 } 441 } else if (strcmp(name, "MaxConnections") == 0) { 442 /* Ignore. */ 443 } else if (strcmp(name, "InitialR2T") == 0) { 444 if (strcmp(value, "Yes") == 0) 445 conn->conn_initial_r2t = true; 446 else 447 conn->conn_initial_r2t = false; 448 } else if (strcmp(name, "ImmediateData") == 0) { 449 if (strcmp(value, "Yes") == 0) 450 conn->conn_immediate_data = true; 451 else 452 conn->conn_immediate_data = false; 453 } else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) { 454 tmp = strtoul(value, NULL, 10); 455 if (tmp <= 0) 456 log_errx(1, "received invalid " 457 "MaxRecvDataSegmentLength"); 458 conn->conn_max_data_segment_length = tmp; 459 } else if (strcmp(name, "MaxBurstLength") == 0) { 460 if (conn->conn_immediate_data) { 461 tmp = strtoul(value, NULL, 10); 462 if (tmp <= 0) 463 log_errx(1, "received invalid MaxBurstLength"); 464 conn->conn_max_burst_length = tmp; 465 } 466 } else if (strcmp(name, "FirstBurstLength") == 0) { 467 tmp = strtoul(value, NULL, 10); 468 if (tmp <= 0) 469 log_errx(1, "received invalid FirstBurstLength"); 470 conn->conn_first_burst_length = tmp; 471 } else if (strcmp(name, "DefaultTime2Wait") == 0) { 472 /* Ignore */ 473 } else if (strcmp(name, "DefaultTime2Retain") == 0) { 474 /* Ignore */ 475 } else if (strcmp(name, "MaxOutstandingR2T") == 0) { 476 /* Ignore */ 477 } else if (strcmp(name, "DataPDUInOrder") == 0) { 478 /* Ignore */ 479 } else if (strcmp(name, "DataSequenceInOrder") == 0) { 480 /* Ignore */ 481 } else if (strcmp(name, "ErrorRecoveryLevel") == 0) { 482 /* Ignore */ 483 } else if (strcmp(name, "OFMarker") == 0) { 484 /* Ignore */ 485 } else if (strcmp(name, "IFMarker") == 0) { 486 /* Ignore */ 487 } else if (strcmp(name, "TargetPortalGroupTag") == 0) { 488 /* Ignore */ 489 } else { 490 log_debugx("unknown key \"%s\"; ignoring", name); 491 } 492} 493 494static void 495login_negotiate(struct connection *conn) 496{ 497 struct pdu *request, *response; 498 struct keys *request_keys, *response_keys; 499 struct iscsi_bhs_login_response *bhslr; 500 int i; 501 502 log_debugx("beginning parameter negotiation"); 503 request = login_new_request(conn); 504 login_set_csg(request, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); 505 login_set_nsg(request, BHSLR_STAGE_FULL_FEATURE_PHASE); 506 request_keys = keys_new(); 507 508 /* 509 * The following keys are irrelevant for discovery sessions. 510 */ 511 if (conn->conn_conf.isc_discovery == 0) { 512 if (conn->conn_conf.isc_header_digest != 0) 513 keys_add(request_keys, "HeaderDigest", "CRC32C"); 514 else 515 keys_add(request_keys, "HeaderDigest", "None"); 516 if (conn->conn_conf.isc_data_digest != 0) 517 keys_add(request_keys, "DataDigest", "CRC32C"); 518 else 519 keys_add(request_keys, "DataDigest", "None"); 520 521 keys_add(request_keys, "ImmediateData", "Yes"); 522 keys_add_int(request_keys, "MaxBurstLength", 523 ISCSI_MAX_DATA_SEGMENT_LENGTH); 524 keys_add_int(request_keys, "FirstBurstLength", 525 ISCSI_MAX_DATA_SEGMENT_LENGTH); 526 keys_add(request_keys, "InitialR2T", "Yes"); 527 } else { 528 keys_add(request_keys, "HeaderDigest", "None"); 529 keys_add(request_keys, "DataDigest", "None"); 530 } 531 532 keys_add_int(request_keys, "MaxRecvDataSegmentLength", 533 ISCSI_MAX_DATA_SEGMENT_LENGTH); 534 keys_add(request_keys, "DefaultTime2Wait", "0"); 535 keys_add(request_keys, "DefaultTime2Retain", "0"); 536 keys_add(request_keys, "ErrorRecoveryLevel", "0"); 537 keys_add(request_keys, "MaxOutstandingR2T", "1"); 538 keys_save(request_keys, request); 539 keys_delete(request_keys); 540 request_keys = NULL; 541 pdu_send(request); 542 pdu_delete(request); 543 request = NULL; 544 545 response = login_receive(conn, false); 546 response_keys = keys_new(); 547 keys_load(response_keys, response); 548 for (i = 0; i < KEYS_MAX; i++) { 549 if (response_keys->keys_names[i] == NULL) 550 break; 551 552 login_negotiate_key(conn, 553 response_keys->keys_names[i], response_keys->keys_values[i]); 554 } 555 556 bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; 557 if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) == 0) 558 log_warnx("received final login response " 559 "without the \"T\" flag"); 560 else if (login_nsg(response) != BHSLR_STAGE_FULL_FEATURE_PHASE) 561 log_warnx("received final login response with wrong NSG 0x%x", 562 login_nsg(response)); 563 564 log_debugx("parameter negotiation done; " 565 "transitioning to Full Feature phase"); 566 567 keys_delete(response_keys); 568 pdu_delete(response); 569} 570 571static void 572login_send_chap_a(struct connection *conn) 573{ 574 struct pdu *request; 575 struct keys *request_keys; 576 577 request = login_new_request(conn); 578 request_keys = keys_new(); 579 keys_add(request_keys, "CHAP_A", "5"); 580 keys_save(request_keys, request); 581 keys_delete(request_keys); 582 pdu_send(request); 583 pdu_delete(request); 584} 585 586static void 587login_send_chap_r(struct pdu *response) 588{ 589 struct connection *conn; 590 struct pdu *request; 591 struct keys *request_keys, *response_keys; 592 const char *chap_a, *chap_c, *chap_i; 593 char *chap_r, *challenge, response_bin[MD5_DIGEST_LENGTH]; 594 size_t challenge_len; 595 int error, rv; 596 unsigned char id; 597 char *mutual_chap_c, mutual_chap_i[4]; 598 599 /* 600 * As in the rest of the initiator, 'request' means 601 * 'initiator -> target', and 'response' means 'target -> initiator', 602 * 603 * So, here the 'response' from the target is the packet that contains 604 * CHAP challenge; our CHAP response goes into 'request'. 605 */ 606 607 conn = response->pdu_connection; 608 609 response_keys = keys_new(); 610 keys_load(response_keys, response); 611 612 /* 613 * First, compute the response. 614 */ 615 chap_a = keys_find(response_keys, "CHAP_A"); 616 if (chap_a == NULL) 617 log_errx(1, "received CHAP packet without CHAP_A"); 618 chap_c = keys_find(response_keys, "CHAP_C"); 619 if (chap_c == NULL) 620 log_errx(1, "received CHAP packet without CHAP_C"); 621 chap_i = keys_find(response_keys, "CHAP_I"); 622 if (chap_i == NULL) 623 log_errx(1, "received CHAP packet without CHAP_I"); 624 625 if (strcmp(chap_a, "5") != 0) 626 log_errx(1, "received CHAP packet " 627 "with unsupported CHAP_A \"%s\"", chap_a); 628 id = strtoul(chap_i, NULL, 10); 629 error = login_hex2bin(chap_c, &challenge, &challenge_len); 630 if (error != 0) 631 log_errx(1, "received CHAP packet with malformed CHAP_C"); 632 login_compute_md5(id, conn->conn_conf.isc_secret, 633 challenge, challenge_len, response_bin, sizeof(response_bin)); 634 free(challenge); 635 chap_r = login_bin2hex(response_bin, sizeof(response_bin)); 636 637 keys_delete(response_keys); 638 639 request = login_new_request(conn); 640 request_keys = keys_new(); 641 keys_add(request_keys, "CHAP_N", conn->conn_conf.isc_user); 642 keys_add(request_keys, "CHAP_R", chap_r); 643 free(chap_r); 644 645 /* 646 * If we want mutual authentication, we're expected to send 647 * our CHAP_I/CHAP_C now. 648 */ 649 if (conn->conn_conf.isc_mutual_user[0] != '\0') { 650 log_debugx("requesting mutual authentication; " 651 "binary challenge size is %zd bytes", 652 sizeof(conn->conn_mutual_challenge)); 653 654 rv = RAND_bytes(conn->conn_mutual_challenge, 655 sizeof(conn->conn_mutual_challenge)); 656 if (rv != 1) { 657 log_errx(1, "RAND_bytes failed: %s", 658 ERR_error_string(ERR_get_error(), NULL)); 659 } 660 rv = RAND_bytes(&conn->conn_mutual_id, 661 sizeof(conn->conn_mutual_id)); 662 if (rv != 1) { 663 log_errx(1, "RAND_bytes failed: %s", 664 ERR_error_string(ERR_get_error(), NULL)); 665 } 666 mutual_chap_c = login_bin2hex(conn->conn_mutual_challenge, 667 sizeof(conn->conn_mutual_challenge)); 668 snprintf(mutual_chap_i, sizeof(mutual_chap_i), 669 "%d", conn->conn_mutual_id); 670 keys_add(request_keys, "CHAP_I", mutual_chap_i); 671 keys_add(request_keys, "CHAP_C", mutual_chap_c); 672 free(mutual_chap_c); 673 } 674 675 keys_save(request_keys, request); 676 keys_delete(request_keys); 677 pdu_send(request); 678 pdu_delete(request); 679} 680 681static void 682login_verify_mutual(const struct pdu *response) 683{ 684 struct connection *conn; 685 struct keys *response_keys; 686 const char *chap_n, *chap_r; 687 char *response_bin, expected_response_bin[MD5_DIGEST_LENGTH]; 688 size_t response_bin_len; 689 int error; 690 691 conn = response->pdu_connection; 692 693 response_keys = keys_new(); 694 keys_load(response_keys, response); 695 696 chap_n = keys_find(response_keys, "CHAP_N"); 697 if (chap_n == NULL) 698 log_errx(1, "received CHAP Response PDU without CHAP_N"); 699 chap_r = keys_find(response_keys, "CHAP_R"); 700 if (chap_r == NULL) 701 log_errx(1, "received CHAP Response PDU without CHAP_R"); 702 error = login_hex2bin(chap_r, &response_bin, &response_bin_len); 703 if (error != 0) 704 log_errx(1, "received CHAP Response PDU with malformed CHAP_R"); 705 706 if (strcmp(chap_n, conn->conn_conf.isc_mutual_user) != 0) { 707 fail(conn, "Mutual CHAP failed"); 708 log_errx(1, "mutual CHAP authentication failed: wrong user"); 709 } 710 711 login_compute_md5(conn->conn_mutual_id, 712 conn->conn_conf.isc_mutual_secret, conn->conn_mutual_challenge, 713 sizeof(conn->conn_mutual_challenge), expected_response_bin, 714 sizeof(expected_response_bin)); 715 716 if (memcmp(response_bin, expected_response_bin, 717 sizeof(expected_response_bin)) != 0) { 718 fail(conn, "Mutual CHAP failed"); 719 log_errx(1, "mutual CHAP authentication failed: wrong secret"); 720 } 721 722 keys_delete(response_keys); 723 free(response_bin); 724 725 log_debugx("mutual CHAP authentication succeeded"); 726} 727 728static void 729login_chap(struct connection *conn) 730{ 731 struct pdu *response; 732 733 log_debugx("beginning CHAP authentication; sending CHAP_A"); 734 login_send_chap_a(conn); 735 736 log_debugx("waiting for CHAP_A/CHAP_C/CHAP_I"); 737 response = login_receive(conn, false); 738 739 log_debugx("sending CHAP_N/CHAP_R"); 740 login_send_chap_r(response); 741 pdu_delete(response); 742 743 /* 744 * XXX: Make sure this is not susceptible to MITM. 745 */ 746 747 log_debugx("waiting for CHAP result"); 748 response = login_receive(conn, false); 749 if (conn->conn_conf.isc_mutual_user[0] != '\0') 750 login_verify_mutual(response); 751 pdu_delete(response); 752 753 log_debugx("CHAP authentication done"); 754} 755 756static void 757login_create_isid(struct connection *conn) 758{ 759 int rv; 760 761 /* 762 * RFC 3720, 10.12.5: 10b, "Random" ISID. 763 * 764 */ 765 conn->conn_isid[0] = 0x80; 766 767 rv = RAND_bytes(&conn->conn_isid[1], 3); 768 if (rv != 1) { 769 log_errx(1, "RAND_bytes failed: %s", 770 ERR_error_string(ERR_get_error(), NULL)); 771 } 772} 773 774void 775login(struct connection *conn) 776{ 777 struct pdu *request, *response; 778 struct keys *request_keys, *response_keys; 779 struct iscsi_bhs_login_request *bhslr; 780 struct iscsi_bhs_login_response *bhslr2; 781 const char *auth_method; 782 int i; 783 784 login_create_isid(conn); 785 786 log_debugx("beginning Login phase; sending Login PDU"); 787 request = login_new_request(conn); 788 789 bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; 790 bhslr->bhslr_flags |= BHSLR_FLAGS_TRANSIT; 791 792 request_keys = keys_new(); 793 if (conn->conn_conf.isc_mutual_user[0] != '\0') { 794 keys_add(request_keys, "AuthMethod", "CHAP"); 795 } else if (conn->conn_conf.isc_user[0] != '\0') { 796 /* 797 * Give target a chance to skip authentication if it 798 * doesn't feel like it. 799 * 800 * None is first, CHAP second; this is to work around 801 * what seems to be LIO (Linux target) bug: otherwise, 802 * if target is configured with no authentication, 803 * and we are configured to authenticate, the target 804 * will erroneously respond with AuthMethod=CHAP 805 * instead of AuthMethod=None, and will subsequently 806 * fail the connection. This usually happens with 807 * Discovery sessions, which default to no authentication. 808 */ 809 keys_add(request_keys, "AuthMethod", "None,CHAP"); 810 } else { 811 keys_add(request_keys, "AuthMethod", "None"); 812 } 813 keys_add(request_keys, "InitiatorName", 814 conn->conn_conf.isc_initiator); 815 if (conn->conn_conf.isc_initiator_alias[0] != '\0') { 816 keys_add(request_keys, "InitiatorAlias", 817 conn->conn_conf.isc_initiator_alias); 818 } 819 if (conn->conn_conf.isc_discovery == 0) { 820 keys_add(request_keys, "SessionType", "Normal"); 821 keys_add(request_keys, 822 "TargetName", conn->conn_conf.isc_target); 823 } else { 824 keys_add(request_keys, "SessionType", "Discovery"); 825 } 826 keys_save(request_keys, request); 827 keys_delete(request_keys); 828 pdu_send(request); 829 pdu_delete(request); 830 831 response = login_receive(conn, true); 832 833 response_keys = keys_new(); 834 keys_load(response_keys, response); 835 836 for (i = 0; i < KEYS_MAX; i++) { 837 if (response_keys->keys_names[i] == NULL) 838 break; 839 840 /* 841 * Not interested in AuthMethod at this point; we only need 842 * to parse things such as TargetAlias. 843 * 844 * XXX: This is somewhat ugly. We should have a way to apply 845 * all the keys to the session and use that by default 846 * instead of discarding them. 847 */ 848 if (strcmp(response_keys->keys_names[i], "AuthMethod") == 0) 849 continue; 850 851 login_negotiate_key(conn, 852 response_keys->keys_names[i], response_keys->keys_values[i]); 853 } 854 855 bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 856 if ((bhslr2->bhslr_flags & BHSLR_FLAGS_TRANSIT) != 0 && 857 login_nsg(response) == BHSLR_STAGE_OPERATIONAL_NEGOTIATION) { 858 if (conn->conn_conf.isc_mutual_user[0] != '\0') { 859 log_errx(1, "target requested transition " 860 "to operational negotiation, but we require " 861 "mutual CHAP"); 862 } 863 864 log_debugx("target requested transition " 865 "to operational negotiation"); 866 keys_delete(response_keys); 867 pdu_delete(response); 868 login_negotiate(conn); 869 return; 870 } 871 872 auth_method = keys_find(response_keys, "AuthMethod"); 873 if (auth_method == NULL) 874 log_errx(1, "received response without AuthMethod"); 875 if (strcmp(auth_method, "None") == 0) { 876 if (conn->conn_conf.isc_mutual_user[0] != '\0') { 877 log_errx(1, "target does not require authantication, " 878 "but we require mutual CHAP"); 879 } 880 881 log_debugx("target does not require authentication"); 882 keys_delete(response_keys); 883 pdu_delete(response); 884 login_negotiate(conn); 885 return; 886 } 887 888 if (strcmp(auth_method, "CHAP") != 0) { 889 fail(conn, "Unsupported AuthMethod"); 890 log_errx(1, "received response " 891 "with unsupported AuthMethod \"%s\"", auth_method); 892 } 893 894 if (conn->conn_conf.isc_user[0] == '\0' || 895 conn->conn_conf.isc_secret[0] == '\0') { 896 fail(conn, "Authentication required"); 897 log_errx(1, "target requests CHAP authentication, but we don't " 898 "have user and secret"); 899 } 900 901 keys_delete(response_keys); 902 response_keys = NULL; 903 pdu_delete(response); 904 response = NULL; 905 906 login_chap(conn); 907 login_negotiate(conn); 908} 909