1255570Strasz/*- 2255570Strasz * Copyright (c) 2012 The FreeBSD Foundation 3255570Strasz * All rights reserved. 4255570Strasz * 5255570Strasz * This software was developed by Edward Tomasz Napierala under sponsorship 6255570Strasz * from the FreeBSD Foundation. 7255570Strasz * 8255570Strasz * Redistribution and use in source and binary forms, with or without 9255570Strasz * modification, are permitted provided that the following conditions 10255570Strasz * are met: 11255570Strasz * 1. Redistributions of source code must retain the above copyright 12255570Strasz * notice, this list of conditions and the following disclaimer. 13255570Strasz * 2. Redistributions in binary form must reproduce the above copyright 14255570Strasz * notice, this list of conditions and the following disclaimer in the 15255570Strasz * documentation and/or other materials provided with the distribution. 16255570Strasz * 17255570Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18255570Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19255570Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20255570Strasz * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21255570Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25255570Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26255570Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27255570Strasz * SUCH DAMAGE. 28255570Strasz * 29255570Strasz */ 30255570Strasz 31270888Strasz#include <sys/cdefs.h> 32270888Strasz__FBSDID("$FreeBSD$"); 33270888Strasz 34255570Strasz#include <assert.h> 35255570Strasz#include <stdbool.h> 36255570Strasz#include <stdint.h> 37255570Strasz#include <stdio.h> 38255570Strasz#include <stdlib.h> 39255570Strasz#include <string.h> 40255570Strasz#include <unistd.h> 41255570Strasz#include <netinet/in.h> 42255570Strasz#include <openssl/err.h> 43255570Strasz#include <openssl/md5.h> 44255570Strasz#include <openssl/rand.h> 45255570Strasz 46255570Strasz#include "ctld.h" 47255570Strasz#include "iscsi_proto.h" 48255570Strasz 49255570Straszstatic void login_send_error(struct pdu *request, 50255570Strasz char class, char detail); 51255570Strasz 52255570Straszstatic void 53255570Straszlogin_set_nsg(struct pdu *response, int nsg) 54255570Strasz{ 55255570Strasz struct iscsi_bhs_login_response *bhslr; 56255570Strasz 57255570Strasz assert(nsg == BHSLR_STAGE_SECURITY_NEGOTIATION || 58255570Strasz nsg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || 59255570Strasz nsg == BHSLR_STAGE_FULL_FEATURE_PHASE); 60255570Strasz 61255570Strasz bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; 62255570Strasz 63255570Strasz bhslr->bhslr_flags &= 0xFC; 64255570Strasz bhslr->bhslr_flags |= nsg; 65255570Strasz} 66255570Strasz 67255570Straszstatic int 68255570Straszlogin_csg(const struct pdu *request) 69255570Strasz{ 70255570Strasz struct iscsi_bhs_login_request *bhslr; 71255570Strasz 72255570Strasz bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; 73255570Strasz 74255570Strasz return ((bhslr->bhslr_flags & 0x0C) >> 2); 75255570Strasz} 76255570Strasz 77255570Straszstatic void 78255570Straszlogin_set_csg(struct pdu *response, int csg) 79255570Strasz{ 80255570Strasz struct iscsi_bhs_login_response *bhslr; 81255570Strasz 82255570Strasz assert(csg == BHSLR_STAGE_SECURITY_NEGOTIATION || 83255570Strasz csg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || 84255570Strasz csg == BHSLR_STAGE_FULL_FEATURE_PHASE); 85255570Strasz 86255570Strasz bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; 87255570Strasz 88255570Strasz bhslr->bhslr_flags &= 0xF3; 89255570Strasz bhslr->bhslr_flags |= csg << 2; 90255570Strasz} 91255570Strasz 92255570Straszstatic struct pdu * 93255570Straszlogin_receive(struct connection *conn, bool initial) 94255570Strasz{ 95255570Strasz struct pdu *request; 96255570Strasz struct iscsi_bhs_login_request *bhslr; 97255570Strasz 98255570Strasz request = pdu_new(conn); 99255570Strasz pdu_receive(request); 100255570Strasz if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != 101255570Strasz ISCSI_BHS_OPCODE_LOGIN_REQUEST) { 102255570Strasz /* 103255570Strasz * The first PDU in session is special - if we receive any PDU 104255570Strasz * different than login request, we have to drop the connection 105255570Strasz * without sending response ("A target receiving any PDU 106255570Strasz * except a Login request before the Login Phase is started MUST 107255570Strasz * immediately terminate the connection on which the PDU 108255570Strasz * was received.") 109255570Strasz */ 110255570Strasz if (initial == false) 111255570Strasz login_send_error(request, 0x02, 0x0b); 112255570Strasz log_errx(1, "protocol error: received invalid opcode 0x%x", 113255570Strasz request->pdu_bhs->bhs_opcode); 114255570Strasz } 115255570Strasz bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; 116255570Strasz /* 117255570Strasz * XXX: Implement the C flag some day. 118255570Strasz */ 119255570Strasz if ((bhslr->bhslr_flags & BHSLR_FLAGS_CONTINUE) != 0) { 120255570Strasz login_send_error(request, 0x03, 0x00); 121255570Strasz log_errx(1, "received Login PDU with unsupported \"C\" flag"); 122255570Strasz } 123255570Strasz if (bhslr->bhslr_version_max != 0x00) { 124255570Strasz login_send_error(request, 0x02, 0x05); 125255570Strasz log_errx(1, "received Login PDU with unsupported " 126255570Strasz "Version-max 0x%x", bhslr->bhslr_version_max); 127255570Strasz } 128255570Strasz if (bhslr->bhslr_version_min != 0x00) { 129255570Strasz login_send_error(request, 0x02, 0x05); 130255570Strasz log_errx(1, "received Login PDU with unsupported " 131255570Strasz "Version-min 0x%x", bhslr->bhslr_version_min); 132255570Strasz } 133255570Strasz if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) { 134255570Strasz login_send_error(request, 0x02, 0x05); 135255570Strasz log_errx(1, "received Login PDU with decreasing CmdSN: " 136255570Strasz "was %d, is %d", conn->conn_cmdsn, 137255570Strasz ntohl(bhslr->bhslr_cmdsn)); 138255570Strasz } 139255570Strasz if (initial == false && 140255570Strasz ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { 141255570Strasz login_send_error(request, 0x02, 0x05); 142255570Strasz log_errx(1, "received Login PDU with wrong ExpStatSN: " 143255570Strasz "is %d, should be %d", ntohl(bhslr->bhslr_expstatsn), 144255570Strasz conn->conn_statsn); 145255570Strasz } 146255570Strasz conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); 147255570Strasz 148255570Strasz return (request); 149255570Strasz} 150255570Strasz 151255570Straszstatic struct pdu * 152255570Straszlogin_new_response(struct pdu *request) 153255570Strasz{ 154255570Strasz struct pdu *response; 155255570Strasz struct connection *conn; 156255570Strasz struct iscsi_bhs_login_request *bhslr; 157255570Strasz struct iscsi_bhs_login_response *bhslr2; 158255570Strasz 159255570Strasz bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; 160255570Strasz conn = request->pdu_connection; 161255570Strasz 162255570Strasz response = pdu_new_response(request); 163255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 164255570Strasz bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGIN_RESPONSE; 165255570Strasz login_set_csg(response, BHSLR_STAGE_SECURITY_NEGOTIATION); 166255570Strasz memcpy(bhslr2->bhslr_isid, 167255570Strasz bhslr->bhslr_isid, sizeof(bhslr2->bhslr_isid)); 168255570Strasz bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; 169255570Strasz bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); 170255570Strasz bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); 171255570Strasz bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); 172255570Strasz 173255570Strasz return (response); 174255570Strasz} 175255570Strasz 176255570Straszstatic void 177255570Straszlogin_send_error(struct pdu *request, char class, char detail) 178255570Strasz{ 179255570Strasz struct pdu *response; 180255570Strasz struct iscsi_bhs_login_response *bhslr2; 181255570Strasz 182255570Strasz log_debugx("sending Login Response PDU with failure class 0x%x/0x%x; " 183255570Strasz "see next line for reason", class, detail); 184255570Strasz response = login_new_response(request); 185255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 186255570Strasz bhslr2->bhslr_status_class = class; 187255570Strasz bhslr2->bhslr_status_detail = detail; 188255570Strasz 189255570Strasz pdu_send(response); 190255570Strasz pdu_delete(response); 191255570Strasz} 192255570Strasz 193255570Straszstatic int 194255570Straszlogin_list_contains(const char *list, const char *what) 195255570Strasz{ 196255570Strasz char *tofree, *str, *token; 197255570Strasz 198255570Strasz tofree = str = checked_strdup(list); 199255570Strasz 200255570Strasz while ((token = strsep(&str, ",")) != NULL) { 201255570Strasz if (strcmp(token, what) == 0) { 202255570Strasz free(tofree); 203255570Strasz return (1); 204255570Strasz } 205255570Strasz } 206255570Strasz free(tofree); 207255570Strasz return (0); 208255570Strasz} 209255570Strasz 210255570Straszstatic int 211255570Straszlogin_list_prefers(const char *list, 212255570Strasz const char *choice1, const char *choice2) 213255570Strasz{ 214255570Strasz char *tofree, *str, *token; 215255570Strasz 216255570Strasz tofree = str = checked_strdup(list); 217255570Strasz 218255570Strasz while ((token = strsep(&str, ",")) != NULL) { 219255570Strasz if (strcmp(token, choice1) == 0) { 220255570Strasz free(tofree); 221255570Strasz return (1); 222255570Strasz } 223255570Strasz if (strcmp(token, choice2) == 0) { 224255570Strasz free(tofree); 225255570Strasz return (2); 226255570Strasz } 227255570Strasz } 228255570Strasz free(tofree); 229255570Strasz return (-1); 230255570Strasz} 231255570Strasz 232255570Straszstatic int 233255570Straszlogin_hex2int(const char hex) 234255570Strasz{ 235255570Strasz switch (hex) { 236255570Strasz case '0': 237255570Strasz return (0x00); 238255570Strasz case '1': 239255570Strasz return (0x01); 240255570Strasz case '2': 241255570Strasz return (0x02); 242255570Strasz case '3': 243255570Strasz return (0x03); 244255570Strasz case '4': 245255570Strasz return (0x04); 246255570Strasz case '5': 247255570Strasz return (0x05); 248255570Strasz case '6': 249255570Strasz return (0x06); 250255570Strasz case '7': 251255570Strasz return (0x07); 252255570Strasz case '8': 253255570Strasz return (0x08); 254255570Strasz case '9': 255255570Strasz return (0x09); 256255570Strasz case 'a': 257255570Strasz case 'A': 258255570Strasz return (0x0a); 259255570Strasz case 'b': 260255570Strasz case 'B': 261255570Strasz return (0x0b); 262255570Strasz case 'c': 263255570Strasz case 'C': 264255570Strasz return (0x0c); 265255570Strasz case 'd': 266255570Strasz case 'D': 267255570Strasz return (0x0d); 268255570Strasz case 'e': 269255570Strasz case 'E': 270255570Strasz return (0x0e); 271255570Strasz case 'f': 272255570Strasz case 'F': 273255570Strasz return (0x0f); 274255570Strasz default: 275255570Strasz return (-1); 276255570Strasz } 277255570Strasz} 278255570Strasz 279255570Strasz/* 280255570Strasz * XXX: Review this _carefully_. 281255570Strasz */ 282255570Straszstatic int 283255570Straszlogin_hex2bin(const char *hex, char **binp, size_t *bin_lenp) 284255570Strasz{ 285255570Strasz int i, hex_len, nibble; 286255570Strasz bool lo = true; /* As opposed to 'hi'. */ 287255570Strasz char *bin; 288255570Strasz size_t bin_off, bin_len; 289255570Strasz 290255570Strasz if (strncasecmp(hex, "0x", strlen("0x")) != 0) { 291255570Strasz log_warnx("malformed variable, should start with \"0x\""); 292255570Strasz return (-1); 293255570Strasz } 294255570Strasz 295255570Strasz hex += strlen("0x"); 296255570Strasz hex_len = strlen(hex); 297255570Strasz if (hex_len < 1) { 298255570Strasz log_warnx("malformed variable; doesn't contain anything " 299255570Strasz "but \"0x\""); 300255570Strasz return (-1); 301255570Strasz } 302255570Strasz 303255570Strasz bin_len = hex_len / 2 + hex_len % 2; 304255570Strasz bin = calloc(bin_len, 1); 305255570Strasz if (bin == NULL) 306255570Strasz log_err(1, "calloc"); 307255570Strasz 308255570Strasz bin_off = bin_len - 1; 309255570Strasz for (i = hex_len - 1; i >= 0; i--) { 310255570Strasz nibble = login_hex2int(hex[i]); 311255570Strasz if (nibble < 0) { 312255570Strasz log_warnx("malformed variable, invalid char \"%c\"", 313255570Strasz hex[i]); 314255570Strasz return (-1); 315255570Strasz } 316255570Strasz 317255570Strasz assert(bin_off < bin_len); 318255570Strasz if (lo) { 319255570Strasz bin[bin_off] = nibble; 320255570Strasz lo = false; 321255570Strasz } else { 322255570Strasz bin[bin_off] |= nibble << 4; 323255570Strasz bin_off--; 324255570Strasz lo = true; 325255570Strasz } 326255570Strasz } 327255570Strasz 328255570Strasz *binp = bin; 329255570Strasz *bin_lenp = bin_len; 330255570Strasz return (0); 331255570Strasz} 332255570Strasz 333255570Straszstatic char * 334255570Straszlogin_bin2hex(const char *bin, size_t bin_len) 335255570Strasz{ 336255570Strasz unsigned char *hex, *tmp, ch; 337255570Strasz size_t hex_len; 338255570Strasz size_t i; 339255570Strasz 340255570Strasz hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ 341255570Strasz hex = malloc(hex_len); 342255570Strasz if (hex == NULL) 343255570Strasz log_err(1, "malloc"); 344255570Strasz 345255570Strasz tmp = hex; 346255570Strasz tmp += sprintf(tmp, "0x"); 347255570Strasz for (i = 0; i < bin_len; i++) { 348255570Strasz ch = bin[i]; 349255570Strasz tmp += sprintf(tmp, "%02x", ch); 350255570Strasz } 351255570Strasz 352255570Strasz return (hex); 353255570Strasz} 354255570Strasz 355255570Straszstatic void 356255570Straszlogin_compute_md5(const char id, const char *secret, 357255570Strasz const void *challenge, size_t challenge_len, void *response, 358255570Strasz size_t response_len) 359255570Strasz{ 360255570Strasz MD5_CTX ctx; 361255570Strasz int rv; 362255570Strasz 363255570Strasz assert(response_len == MD5_DIGEST_LENGTH); 364255570Strasz 365255570Strasz MD5_Init(&ctx); 366255570Strasz MD5_Update(&ctx, &id, sizeof(id)); 367255570Strasz MD5_Update(&ctx, secret, strlen(secret)); 368255570Strasz MD5_Update(&ctx, challenge, challenge_len); 369255570Strasz rv = MD5_Final(response, &ctx); 370255570Strasz if (rv != 1) 371255570Strasz log_errx(1, "MD5_Final"); 372255570Strasz} 373255570Strasz 374255570Strasz#define LOGIN_CHALLENGE_LEN 1024 375255570Strasz 376255570Straszstatic struct pdu * 377255570Straszlogin_receive_chap_a(struct connection *conn) 378255570Strasz{ 379255570Strasz struct pdu *request; 380255570Strasz struct keys *request_keys; 381255570Strasz const char *chap_a; 382255570Strasz 383255570Strasz request = login_receive(conn, false); 384255570Strasz request_keys = keys_new(); 385255570Strasz keys_load(request_keys, request); 386255570Strasz 387255570Strasz chap_a = keys_find(request_keys, "CHAP_A"); 388255570Strasz if (chap_a == NULL) { 389255570Strasz login_send_error(request, 0x02, 0x07); 390255570Strasz log_errx(1, "received CHAP Login PDU without CHAP_A"); 391255570Strasz } 392255570Strasz if (login_list_contains(chap_a, "5") == 0) { 393255570Strasz login_send_error(request, 0x02, 0x01); 394255570Strasz log_errx(1, "received CHAP Login PDU with unsupported CHAP_A " 395255570Strasz "\"%s\"", chap_a); 396255570Strasz } 397255570Strasz keys_delete(request_keys); 398255570Strasz 399255570Strasz return (request); 400255570Strasz} 401255570Strasz 402255570Straszstatic void 403255570Straszlogin_send_chap_c(struct pdu *request, const unsigned char id, 404255570Strasz const void *challenge, const size_t challenge_len) 405255570Strasz{ 406255570Strasz struct pdu *response; 407255570Strasz struct keys *response_keys; 408255570Strasz char *chap_c, chap_i[4]; 409255570Strasz 410255570Strasz chap_c = login_bin2hex(challenge, challenge_len); 411255570Strasz snprintf(chap_i, sizeof(chap_i), "%d", id); 412255570Strasz 413255570Strasz response = login_new_response(request); 414255570Strasz response_keys = keys_new(); 415255570Strasz keys_add(response_keys, "CHAP_A", "5"); 416255570Strasz keys_add(response_keys, "CHAP_I", chap_i); 417255570Strasz keys_add(response_keys, "CHAP_C", chap_c); 418255570Strasz free(chap_c); 419255570Strasz keys_save(response_keys, response); 420256192Strasz pdu_send(response); 421256192Strasz pdu_delete(response); 422255570Strasz keys_delete(response_keys); 423255570Strasz} 424255570Strasz 425255570Straszstatic struct pdu * 426255570Straszlogin_receive_chap_r(struct connection *conn, 427255570Strasz struct auth_group *ag, const unsigned char id, const void *challenge, 428255570Strasz const size_t challenge_len, const struct auth **cap) 429255570Strasz{ 430255570Strasz struct pdu *request; 431255570Strasz struct keys *request_keys; 432255570Strasz const char *chap_n, *chap_r; 433255570Strasz char *response_bin, expected_response_bin[MD5_DIGEST_LENGTH]; 434255570Strasz size_t response_bin_len; 435255570Strasz const struct auth *auth; 436255570Strasz int error; 437255570Strasz 438255570Strasz request = login_receive(conn, false); 439255570Strasz request_keys = keys_new(); 440255570Strasz keys_load(request_keys, request); 441255570Strasz 442255570Strasz chap_n = keys_find(request_keys, "CHAP_N"); 443255570Strasz if (chap_n == NULL) { 444255570Strasz login_send_error(request, 0x02, 0x07); 445255570Strasz log_errx(1, "received CHAP Login PDU without CHAP_N"); 446255570Strasz } 447255570Strasz chap_r = keys_find(request_keys, "CHAP_R"); 448255570Strasz if (chap_r == NULL) { 449255570Strasz login_send_error(request, 0x02, 0x07); 450255570Strasz log_errx(1, "received CHAP Login PDU without CHAP_R"); 451255570Strasz } 452255570Strasz error = login_hex2bin(chap_r, &response_bin, &response_bin_len); 453255570Strasz if (error != 0) { 454255570Strasz login_send_error(request, 0x02, 0x07); 455255570Strasz log_errx(1, "received CHAP Login PDU with malformed CHAP_R"); 456255570Strasz } 457255570Strasz 458255570Strasz /* 459255570Strasz * Verify the response. 460255570Strasz */ 461255570Strasz assert(ag->ag_type == AG_TYPE_CHAP || 462255570Strasz ag->ag_type == AG_TYPE_CHAP_MUTUAL); 463255570Strasz auth = auth_find(ag, chap_n); 464255570Strasz if (auth == NULL) { 465255570Strasz login_send_error(request, 0x02, 0x01); 466255570Strasz log_errx(1, "received CHAP Login with invalid user \"%s\"", 467255570Strasz chap_n); 468255570Strasz } 469255570Strasz 470255570Strasz assert(auth->a_secret != NULL); 471255570Strasz assert(strlen(auth->a_secret) > 0); 472255570Strasz login_compute_md5(id, auth->a_secret, challenge, 473255570Strasz challenge_len, expected_response_bin, 474255570Strasz sizeof(expected_response_bin)); 475255570Strasz 476255570Strasz if (memcmp(response_bin, expected_response_bin, 477255570Strasz sizeof(expected_response_bin)) != 0) { 478255570Strasz login_send_error(request, 0x02, 0x01); 479255570Strasz log_errx(1, "CHAP authentication failed for user \"%s\"", 480255570Strasz auth->a_user); 481255570Strasz } 482255570Strasz 483255570Strasz keys_delete(request_keys); 484255570Strasz free(response_bin); 485255570Strasz 486255570Strasz *cap = auth; 487255570Strasz return (request); 488255570Strasz} 489255570Strasz 490255570Straszstatic void 491255570Straszlogin_send_chap_success(struct pdu *request, 492255570Strasz const struct auth *auth) 493255570Strasz{ 494255570Strasz struct pdu *response; 495255570Strasz struct keys *request_keys, *response_keys; 496255570Strasz struct iscsi_bhs_login_response *bhslr2; 497255570Strasz const char *chap_i, *chap_c; 498255570Strasz char *chap_r, *challenge, response_bin[MD5_DIGEST_LENGTH]; 499255570Strasz size_t challenge_len; 500255570Strasz unsigned char id; 501255570Strasz int error; 502255570Strasz 503255570Strasz response = login_new_response(request); 504255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 505255570Strasz bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; 506255570Strasz login_set_nsg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); 507255570Strasz 508255570Strasz /* 509255570Strasz * Actually, one more thing: mutual authentication. 510255570Strasz */ 511255570Strasz request_keys = keys_new(); 512255570Strasz keys_load(request_keys, request); 513255570Strasz chap_i = keys_find(request_keys, "CHAP_I"); 514255570Strasz chap_c = keys_find(request_keys, "CHAP_C"); 515255570Strasz if (chap_i != NULL || chap_c != NULL) { 516255570Strasz if (chap_i == NULL) { 517255570Strasz login_send_error(request, 0x02, 0x07); 518255570Strasz log_errx(1, "initiator requested target " 519255570Strasz "authentication, but didn't send CHAP_I"); 520255570Strasz } 521255570Strasz if (chap_c == NULL) { 522255570Strasz login_send_error(request, 0x02, 0x07); 523255570Strasz log_errx(1, "initiator requested target " 524255570Strasz "authentication, but didn't send CHAP_C"); 525255570Strasz } 526255570Strasz if (auth->a_auth_group->ag_type != AG_TYPE_CHAP_MUTUAL) { 527255570Strasz login_send_error(request, 0x02, 0x01); 528255570Strasz log_errx(1, "initiator requests target authentication " 529255570Strasz "for user \"%s\", but mutual user/secret " 530255570Strasz "is not set", auth->a_user); 531255570Strasz } 532255570Strasz 533255570Strasz id = strtoul(chap_i, NULL, 10); 534255570Strasz error = login_hex2bin(chap_c, &challenge, &challenge_len); 535255570Strasz if (error != 0) { 536255570Strasz login_send_error(request, 0x02, 0x07); 537255570Strasz log_errx(1, "received CHAP Login PDU with malformed " 538255570Strasz "CHAP_C"); 539255570Strasz } 540255570Strasz 541255570Strasz log_debugx("performing mutual authentication as user \"%s\"", 542255570Strasz auth->a_mutual_user); 543255570Strasz login_compute_md5(id, auth->a_mutual_secret, challenge, 544255570Strasz challenge_len, response_bin, sizeof(response_bin)); 545255570Strasz 546255570Strasz chap_r = login_bin2hex(response_bin, 547255570Strasz sizeof(response_bin)); 548255570Strasz response_keys = keys_new(); 549255570Strasz keys_add(response_keys, "CHAP_N", auth->a_mutual_user); 550255570Strasz keys_add(response_keys, "CHAP_R", chap_r); 551255570Strasz free(chap_r); 552255570Strasz keys_save(response_keys, response); 553255570Strasz keys_delete(response_keys); 554255570Strasz } else { 555255570Strasz log_debugx("initiator did not request target authentication"); 556255570Strasz } 557255570Strasz 558255570Strasz keys_delete(request_keys); 559255570Strasz pdu_send(response); 560256192Strasz pdu_delete(response); 561255570Strasz} 562255570Strasz 563255570Straszstatic void 564255570Straszlogin_chap(struct connection *conn, struct auth_group *ag) 565255570Strasz{ 566255570Strasz const struct auth *auth; 567255570Strasz struct pdu *request; 568255570Strasz char challenge_bin[LOGIN_CHALLENGE_LEN]; 569255570Strasz unsigned char id; 570255570Strasz int rv; 571255570Strasz 572255570Strasz /* 573255570Strasz * Receive CHAP_A PDU. 574255570Strasz */ 575255570Strasz log_debugx("beginning CHAP authentication; waiting for CHAP_A"); 576255570Strasz request = login_receive_chap_a(conn); 577255570Strasz 578255570Strasz /* 579255570Strasz * Generate the challenge. 580255570Strasz */ 581255570Strasz rv = RAND_bytes(challenge_bin, sizeof(challenge_bin)); 582255570Strasz if (rv != 1) { 583255570Strasz login_send_error(request, 0x03, 0x02); 584255570Strasz log_errx(1, "RAND_bytes failed: %s", 585255570Strasz ERR_error_string(ERR_get_error(), NULL)); 586255570Strasz } 587255570Strasz rv = RAND_bytes(&id, sizeof(id)); 588255570Strasz if (rv != 1) { 589255570Strasz login_send_error(request, 0x03, 0x02); 590255570Strasz log_errx(1, "RAND_bytes failed: %s", 591255570Strasz ERR_error_string(ERR_get_error(), NULL)); 592255570Strasz } 593255570Strasz 594255570Strasz /* 595255570Strasz * Send the challenge. 596255570Strasz */ 597255570Strasz log_debugx("sending CHAP_C, binary challenge size is %zd bytes", 598255570Strasz sizeof(challenge_bin)); 599255570Strasz login_send_chap_c(request, id, challenge_bin, 600255570Strasz sizeof(challenge_bin)); 601255570Strasz pdu_delete(request); 602255570Strasz 603255570Strasz /* 604255570Strasz * Receive CHAP_N/CHAP_R PDU and authenticate. 605255570Strasz */ 606255570Strasz log_debugx("waiting for CHAP_N/CHAP_R"); 607255570Strasz request = login_receive_chap_r(conn, ag, id, challenge_bin, 608255570Strasz sizeof(challenge_bin), &auth); 609255570Strasz 610255570Strasz /* 611255570Strasz * Yay, authentication succeeded! 612255570Strasz */ 613255570Strasz log_debugx("authentication succeeded for user \"%s\"; " 614255570Strasz "transitioning to Negotiation Phase", auth->a_user); 615255570Strasz login_send_chap_success(request, auth); 616255570Strasz pdu_delete(request); 617255570Strasz} 618255570Strasz 619255570Straszstatic void 620255570Straszlogin_negotiate_key(struct pdu *request, const char *name, 621255570Strasz const char *value, bool skipped_security, struct keys *response_keys) 622255570Strasz{ 623255570Strasz int which, tmp; 624255570Strasz struct connection *conn; 625255570Strasz 626255570Strasz conn = request->pdu_connection; 627255570Strasz 628255570Strasz if (strcmp(name, "InitiatorName") == 0) { 629255570Strasz if (!skipped_security) 630255570Strasz log_errx(1, "initiator resent InitiatorName"); 631255570Strasz } else if (strcmp(name, "SessionType") == 0) { 632255570Strasz if (!skipped_security) 633255570Strasz log_errx(1, "initiator resent SessionType"); 634255570Strasz } else if (strcmp(name, "TargetName") == 0) { 635255570Strasz if (!skipped_security) 636255570Strasz log_errx(1, "initiator resent TargetName"); 637255570Strasz } else if (strcmp(name, "InitiatorAlias") == 0) { 638255570Strasz if (conn->conn_initiator_alias != NULL) 639255570Strasz free(conn->conn_initiator_alias); 640255570Strasz conn->conn_initiator_alias = checked_strdup(value); 641255570Strasz } else if (strcmp(value, "Irrelevant") == 0) { 642255570Strasz /* Ignore. */ 643255570Strasz } else if (strcmp(name, "HeaderDigest") == 0) { 644255570Strasz /* 645255570Strasz * We don't handle digests for discovery sessions. 646255570Strasz */ 647255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { 648255570Strasz log_debugx("discovery session; digests disabled"); 649255570Strasz keys_add(response_keys, name, "None"); 650255570Strasz return; 651255570Strasz } 652255570Strasz 653255570Strasz which = login_list_prefers(value, "CRC32C", "None"); 654255570Strasz switch (which) { 655255570Strasz case 1: 656255570Strasz log_debugx("initiator prefers CRC32C " 657255570Strasz "for header digest; we'll use it"); 658255570Strasz conn->conn_header_digest = CONN_DIGEST_CRC32C; 659255570Strasz keys_add(response_keys, name, "CRC32C"); 660255570Strasz break; 661255570Strasz case 2: 662255570Strasz log_debugx("initiator prefers not to do " 663255570Strasz "header digest; we'll comply"); 664255570Strasz keys_add(response_keys, name, "None"); 665255570Strasz break; 666255570Strasz default: 667255570Strasz log_warnx("initiator sent unrecognized " 668255570Strasz "HeaderDigest value \"%s\"; will use None", value); 669255570Strasz keys_add(response_keys, name, "None"); 670255570Strasz break; 671255570Strasz } 672255570Strasz } else if (strcmp(name, "DataDigest") == 0) { 673255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { 674255570Strasz log_debugx("discovery session; digests disabled"); 675255570Strasz keys_add(response_keys, name, "None"); 676255570Strasz return; 677255570Strasz } 678255570Strasz 679255570Strasz which = login_list_prefers(value, "CRC32C", "None"); 680255570Strasz switch (which) { 681255570Strasz case 1: 682255570Strasz log_debugx("initiator prefers CRC32C " 683255570Strasz "for data digest; we'll use it"); 684255570Strasz conn->conn_data_digest = CONN_DIGEST_CRC32C; 685255570Strasz keys_add(response_keys, name, "CRC32C"); 686255570Strasz break; 687255570Strasz case 2: 688255570Strasz log_debugx("initiator prefers not to do " 689255570Strasz "data digest; we'll comply"); 690255570Strasz keys_add(response_keys, name, "None"); 691255570Strasz break; 692255570Strasz default: 693255570Strasz log_warnx("initiator sent unrecognized " 694255570Strasz "DataDigest value \"%s\"; will use None", value); 695255570Strasz keys_add(response_keys, name, "None"); 696255570Strasz break; 697255570Strasz } 698255570Strasz } else if (strcmp(name, "MaxConnections") == 0) { 699255570Strasz keys_add(response_keys, name, "1"); 700255570Strasz } else if (strcmp(name, "InitialR2T") == 0) { 701255570Strasz keys_add(response_keys, name, "Yes"); 702255570Strasz } else if (strcmp(name, "ImmediateData") == 0) { 703255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { 704255570Strasz log_debugx("discovery session; ImmediateData irrelevant"); 705255570Strasz keys_add(response_keys, name, "Irrelevant"); 706255570Strasz } else { 707255570Strasz if (strcmp(value, "Yes") == 0) { 708255570Strasz conn->conn_immediate_data = true; 709255570Strasz keys_add(response_keys, name, "Yes"); 710255570Strasz } else { 711255570Strasz conn->conn_immediate_data = false; 712255570Strasz keys_add(response_keys, name, "No"); 713255570Strasz } 714255570Strasz } 715255570Strasz } else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) { 716255570Strasz tmp = strtoul(value, NULL, 10); 717255570Strasz if (tmp <= 0) { 718255570Strasz login_send_error(request, 0x02, 0x00); 719255570Strasz log_errx(1, "received invalid " 720255570Strasz "MaxRecvDataSegmentLength"); 721255570Strasz } 722255570Strasz if (tmp > MAX_DATA_SEGMENT_LENGTH) { 723271626Strasz log_debugx("capping MaxRecvDataSegmentLength " 724271626Strasz "from %d to %d", tmp, MAX_DATA_SEGMENT_LENGTH); 725255570Strasz tmp = MAX_DATA_SEGMENT_LENGTH; 726255570Strasz } 727255570Strasz conn->conn_max_data_segment_length = tmp; 728255570Strasz keys_add_int(response_keys, name, tmp); 729255570Strasz } else if (strcmp(name, "MaxBurstLength") == 0) { 730255570Strasz tmp = strtoul(value, NULL, 10); 731255570Strasz if (tmp <= 0) { 732255570Strasz login_send_error(request, 0x02, 0x00); 733255570Strasz log_errx(1, "received invalid MaxBurstLength"); 734255570Strasz } 735255570Strasz if (tmp > MAX_BURST_LENGTH) { 736255570Strasz log_debugx("capping MaxBurstLength from %d to %d", 737255570Strasz tmp, MAX_BURST_LENGTH); 738255570Strasz tmp = MAX_BURST_LENGTH; 739255570Strasz } 740255570Strasz conn->conn_max_burst_length = tmp; 741255570Strasz keys_add(response_keys, name, value); 742255570Strasz } else if (strcmp(name, "FirstBurstLength") == 0) { 743255570Strasz tmp = strtoul(value, NULL, 10); 744255570Strasz if (tmp <= 0) { 745255570Strasz login_send_error(request, 0x02, 0x00); 746255570Strasz log_errx(1, "received invalid " 747255570Strasz "FirstBurstLength"); 748255570Strasz } 749255570Strasz if (tmp > MAX_DATA_SEGMENT_LENGTH) { 750255570Strasz log_debugx("capping FirstBurstLength from %d to %d", 751255570Strasz tmp, MAX_DATA_SEGMENT_LENGTH); 752255570Strasz tmp = MAX_DATA_SEGMENT_LENGTH; 753255570Strasz } 754255570Strasz /* 755255570Strasz * We don't pass the value to the kernel; it only enforces 756255570Strasz * hardcoded limit anyway. 757255570Strasz */ 758255570Strasz keys_add_int(response_keys, name, tmp); 759255570Strasz } else if (strcmp(name, "DefaultTime2Wait") == 0) { 760255570Strasz keys_add(response_keys, name, value); 761255570Strasz } else if (strcmp(name, "DefaultTime2Retain") == 0) { 762255570Strasz keys_add(response_keys, name, "0"); 763255570Strasz } else if (strcmp(name, "MaxOutstandingR2T") == 0) { 764255570Strasz keys_add(response_keys, name, "1"); 765255570Strasz } else if (strcmp(name, "DataPDUInOrder") == 0) { 766255570Strasz keys_add(response_keys, name, "Yes"); 767255570Strasz } else if (strcmp(name, "DataSequenceInOrder") == 0) { 768255570Strasz keys_add(response_keys, name, "Yes"); 769255570Strasz } else if (strcmp(name, "ErrorRecoveryLevel") == 0) { 770255570Strasz keys_add(response_keys, name, "0"); 771255570Strasz } else if (strcmp(name, "OFMarker") == 0) { 772255570Strasz keys_add(response_keys, name, "No"); 773255570Strasz } else if (strcmp(name, "IFMarker") == 0) { 774255570Strasz keys_add(response_keys, name, "No"); 775255570Strasz } else { 776255570Strasz log_debugx("unknown key \"%s\"; responding " 777255570Strasz "with NotUnderstood", name); 778255570Strasz keys_add(response_keys, name, "NotUnderstood"); 779255570Strasz } 780255570Strasz} 781255570Strasz 782255570Straszstatic void 783255570Straszlogin_negotiate(struct connection *conn, struct pdu *request) 784255570Strasz{ 785255570Strasz struct pdu *response; 786255570Strasz struct iscsi_bhs_login_response *bhslr2; 787255570Strasz struct keys *request_keys, *response_keys; 788271701Strasz char *portal_group_tag; 789271701Strasz int i, rv; 790255570Strasz bool skipped_security; 791255570Strasz 792255570Strasz if (request == NULL) { 793265515Strasz log_debugx("beginning operational parameter negotiation; " 794255570Strasz "waiting for Login PDU"); 795255570Strasz request = login_receive(conn, false); 796255570Strasz skipped_security = false; 797255570Strasz } else 798255570Strasz skipped_security = true; 799255570Strasz 800255570Strasz request_keys = keys_new(); 801255570Strasz keys_load(request_keys, request); 802255570Strasz 803255570Strasz response = login_new_response(request); 804255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 805255570Strasz bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; 806255570Strasz bhslr2->bhslr_tsih = htons(0xbadd); 807255570Strasz login_set_csg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); 808255570Strasz login_set_nsg(response, BHSLR_STAGE_FULL_FEATURE_PHASE); 809255570Strasz response_keys = keys_new(); 810271701Strasz 811271701Strasz if (skipped_security && 812271701Strasz conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { 813271701Strasz if (conn->conn_target->t_alias != NULL) 814271701Strasz keys_add(response_keys, 815271701Strasz "TargetAlias", conn->conn_target->t_alias); 816271701Strasz rv = asprintf(&portal_group_tag, "%d", 817271701Strasz conn->conn_portal->p_portal_group->pg_tag); 818271701Strasz if (rv <= 0) 819271701Strasz log_err(1, "asprintf"); 820271701Strasz keys_add(response_keys, 821271701Strasz "TargetPortalGroupTag", portal_group_tag); 822271701Strasz free(portal_group_tag); 823271701Strasz } 824271701Strasz 825255570Strasz for (i = 0; i < KEYS_MAX; i++) { 826255570Strasz if (request_keys->keys_names[i] == NULL) 827255570Strasz break; 828255570Strasz 829255570Strasz login_negotiate_key(request, request_keys->keys_names[i], 830255570Strasz request_keys->keys_values[i], skipped_security, 831255570Strasz response_keys); 832255570Strasz } 833255570Strasz 834265515Strasz log_debugx("operational parameter negotiation done; " 835255570Strasz "transitioning to Full Feature Phase"); 836255570Strasz 837255570Strasz keys_save(response_keys, response); 838255570Strasz pdu_send(response); 839255570Strasz pdu_delete(response); 840255570Strasz keys_delete(response_keys); 841255570Strasz pdu_delete(request); 842255570Strasz keys_delete(request_keys); 843255570Strasz} 844255570Strasz 845255570Straszvoid 846255570Straszlogin(struct connection *conn) 847255570Strasz{ 848255570Strasz struct pdu *request, *response; 849255570Strasz struct iscsi_bhs_login_request *bhslr; 850255570Strasz struct iscsi_bhs_login_response *bhslr2; 851255570Strasz struct keys *request_keys, *response_keys; 852255570Strasz struct auth_group *ag; 853255570Strasz const char *initiator_name, *initiator_alias, *session_type, 854255570Strasz *target_name, *auth_method; 855255570Strasz char *portal_group_tag; 856255570Strasz int rv; 857255570Strasz 858255570Strasz /* 859255570Strasz * Handle the initial Login Request - figure out required authentication 860255570Strasz * method and either transition to the next phase, if no authentication 861255570Strasz * is required, or call appropriate authentication code. 862255570Strasz */ 863255570Strasz log_debugx("beginning Login Phase; waiting for Login PDU"); 864255570Strasz request = login_receive(conn, true); 865255570Strasz bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; 866255570Strasz if (bhslr->bhslr_tsih != 0) { 867255570Strasz login_send_error(request, 0x02, 0x0a); 868255570Strasz log_errx(1, "received Login PDU with non-zero TSIH"); 869255570Strasz } 870255570Strasz 871268684Smav memcpy(conn->conn_initiator_isid, bhslr->bhslr_isid, 872268684Smav sizeof(conn->conn_initiator_isid)); 873268684Smav 874255570Strasz /* 875255570Strasz * XXX: Implement the C flag some day. 876255570Strasz */ 877255570Strasz request_keys = keys_new(); 878255570Strasz keys_load(request_keys, request); 879255570Strasz 880255570Strasz assert(conn->conn_initiator_name == NULL); 881255570Strasz initiator_name = keys_find(request_keys, "InitiatorName"); 882255570Strasz if (initiator_name == NULL) { 883255570Strasz login_send_error(request, 0x02, 0x07); 884255570Strasz log_errx(1, "received Login PDU without InitiatorName"); 885255570Strasz } 886255570Strasz if (valid_iscsi_name(initiator_name) == false) { 887255570Strasz login_send_error(request, 0x02, 0x00); 888255570Strasz log_errx(1, "received Login PDU with invalid InitiatorName"); 889255570Strasz } 890255570Strasz conn->conn_initiator_name = checked_strdup(initiator_name); 891255570Strasz log_set_peer_name(conn->conn_initiator_name); 892255570Strasz /* 893255570Strasz * XXX: This doesn't work (does nothing) because of Capsicum. 894255570Strasz */ 895255570Strasz setproctitle("%s (%s)", conn->conn_initiator_addr, conn->conn_initiator_name); 896255570Strasz 897255570Strasz initiator_alias = keys_find(request_keys, "InitiatorAlias"); 898255570Strasz if (initiator_alias != NULL) 899255570Strasz conn->conn_initiator_alias = checked_strdup(initiator_alias); 900255570Strasz 901255570Strasz assert(conn->conn_session_type == CONN_SESSION_TYPE_NONE); 902255570Strasz session_type = keys_find(request_keys, "SessionType"); 903255570Strasz if (session_type != NULL) { 904255570Strasz if (strcmp(session_type, "Normal") == 0) { 905255570Strasz conn->conn_session_type = CONN_SESSION_TYPE_NORMAL; 906255570Strasz } else if (strcmp(session_type, "Discovery") == 0) { 907255570Strasz conn->conn_session_type = CONN_SESSION_TYPE_DISCOVERY; 908255570Strasz } else { 909255570Strasz login_send_error(request, 0x02, 0x00); 910255570Strasz log_errx(1, "received Login PDU with invalid " 911255570Strasz "SessionType \"%s\"", session_type); 912255570Strasz } 913255570Strasz } else 914255570Strasz conn->conn_session_type = CONN_SESSION_TYPE_NORMAL; 915255570Strasz 916255570Strasz assert(conn->conn_target == NULL); 917255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { 918255570Strasz target_name = keys_find(request_keys, "TargetName"); 919255570Strasz if (target_name == NULL) { 920255570Strasz login_send_error(request, 0x02, 0x07); 921255570Strasz log_errx(1, "received Login PDU without TargetName"); 922255570Strasz } 923255570Strasz 924255570Strasz conn->conn_target = 925255570Strasz target_find(conn->conn_portal->p_portal_group->pg_conf, 926255570Strasz target_name); 927255570Strasz if (conn->conn_target == NULL) { 928255570Strasz login_send_error(request, 0x02, 0x03); 929255570Strasz log_errx(1, "requested target \"%s\" not found", 930255570Strasz target_name); 931255570Strasz } 932255570Strasz } 933255570Strasz 934255570Strasz /* 935255570Strasz * At this point we know what kind of authentication we need. 936255570Strasz */ 937255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { 938255570Strasz ag = conn->conn_target->t_auth_group; 939255570Strasz if (ag->ag_name != NULL) { 940255570Strasz log_debugx("initiator requests to connect " 941255570Strasz "to target \"%s\"; auth-group \"%s\"", 942263723Strasz conn->conn_target->t_name, 943255570Strasz conn->conn_target->t_auth_group->ag_name); 944255570Strasz } else { 945255570Strasz log_debugx("initiator requests to connect " 946263723Strasz "to target \"%s\"", conn->conn_target->t_name); 947255570Strasz } 948255570Strasz } else { 949255570Strasz assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY); 950255570Strasz ag = conn->conn_portal->p_portal_group->pg_discovery_auth_group; 951255570Strasz if (ag->ag_name != NULL) { 952255570Strasz log_debugx("initiator requests " 953255570Strasz "discovery session; auth-group \"%s\"", ag->ag_name); 954255570Strasz } else { 955255570Strasz log_debugx("initiator requests discovery session"); 956255570Strasz } 957255570Strasz } 958255570Strasz 959255570Strasz /* 960263720Strasz * Enforce initiator-name and initiator-portal. 961263720Strasz */ 962263720Strasz if (auth_name_defined(ag)) { 963263720Strasz if (auth_name_find(ag, initiator_name) == NULL) { 964263720Strasz login_send_error(request, 0x02, 0x02); 965263720Strasz log_errx(1, "initiator does not match allowed " 966263720Strasz "initiator names"); 967263720Strasz } 968263720Strasz log_debugx("initiator matches allowed initiator names"); 969263720Strasz } else { 970263720Strasz log_debugx("auth-group does not define initiator name " 971263720Strasz "restrictions"); 972263720Strasz } 973263720Strasz 974263720Strasz if (auth_portal_defined(ag)) { 975270137Smav if (auth_portal_find(ag, &conn->conn_initiator_sa) == NULL) { 976263720Strasz login_send_error(request, 0x02, 0x02); 977263720Strasz log_errx(1, "initiator does not match allowed " 978263720Strasz "initiator portals"); 979263720Strasz } 980263720Strasz log_debugx("initiator matches allowed initiator portals"); 981263720Strasz } else { 982263720Strasz log_debugx("auth-group does not define initiator portal " 983263720Strasz "restrictions"); 984263720Strasz } 985263720Strasz 986263720Strasz /* 987255570Strasz * Let's see if the initiator intends to do any kind of authentication 988255570Strasz * at all. 989255570Strasz */ 990255570Strasz if (login_csg(request) == BHSLR_STAGE_OPERATIONAL_NEGOTIATION) { 991255570Strasz if (ag->ag_type != AG_TYPE_NO_AUTHENTICATION) { 992255570Strasz login_send_error(request, 0x02, 0x01); 993255570Strasz log_errx(1, "initiator skipped the authentication, " 994255570Strasz "but authentication is required"); 995255570Strasz } 996255570Strasz 997255570Strasz keys_delete(request_keys); 998255570Strasz 999255570Strasz log_debugx("initiator skipped the authentication, " 1000255570Strasz "and we don't need it; proceeding with negotiation"); 1001255570Strasz login_negotiate(conn, request); 1002255570Strasz return; 1003255570Strasz } 1004255570Strasz 1005255570Strasz if (ag->ag_type == AG_TYPE_NO_AUTHENTICATION) { 1006255570Strasz /* 1007255570Strasz * Initiator might want to to authenticate, 1008255570Strasz * but we don't need it. 1009255570Strasz */ 1010255570Strasz log_debugx("authentication not required; " 1011265515Strasz "transitioning to operational parameter negotiation"); 1012255570Strasz 1013255570Strasz if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) == 0) 1014255570Strasz log_warnx("initiator did not set the \"T\" flag; " 1015255570Strasz "transitioning anyway"); 1016255570Strasz 1017255570Strasz response = login_new_response(request); 1018255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 1019255570Strasz bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; 1020255570Strasz login_set_nsg(response, 1021255570Strasz BHSLR_STAGE_OPERATIONAL_NEGOTIATION); 1022255570Strasz response_keys = keys_new(); 1023255570Strasz /* 1024255570Strasz * Required by Linux initiator. 1025255570Strasz */ 1026255570Strasz auth_method = keys_find(request_keys, "AuthMethod"); 1027255570Strasz if (auth_method != NULL && 1028255570Strasz login_list_contains(auth_method, "None")) 1029255570Strasz keys_add(response_keys, "AuthMethod", "None"); 1030255570Strasz 1031255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { 1032255570Strasz if (conn->conn_target->t_alias != NULL) 1033255570Strasz keys_add(response_keys, 1034255570Strasz "TargetAlias", conn->conn_target->t_alias); 1035255570Strasz rv = asprintf(&portal_group_tag, "%d", 1036255570Strasz conn->conn_portal->p_portal_group->pg_tag); 1037255570Strasz if (rv <= 0) 1038255570Strasz log_err(1, "asprintf"); 1039255570Strasz keys_add(response_keys, 1040255570Strasz "TargetPortalGroupTag", portal_group_tag); 1041255570Strasz free(portal_group_tag); 1042255570Strasz } 1043255570Strasz keys_save(response_keys, response); 1044255570Strasz pdu_send(response); 1045255570Strasz pdu_delete(response); 1046255570Strasz keys_delete(response_keys); 1047255570Strasz pdu_delete(request); 1048255570Strasz keys_delete(request_keys); 1049255570Strasz 1050255570Strasz login_negotiate(conn, NULL); 1051255570Strasz return; 1052255570Strasz } 1053255570Strasz 1054263729Strasz if (ag->ag_type == AG_TYPE_DENY) { 1055263729Strasz login_send_error(request, 0x02, 0x01); 1056263734Strasz log_errx(1, "auth-type is \"deny\""); 1057263729Strasz } 1058263729Strasz 1059259305Strasz if (ag->ag_type == AG_TYPE_UNKNOWN) { 1060259305Strasz /* 1061259305Strasz * This can happen with empty auth-group. 1062259305Strasz */ 1063259305Strasz login_send_error(request, 0x02, 0x01); 1064263734Strasz log_errx(1, "auth-type not set, denying access"); 1065259305Strasz } 1066259305Strasz 1067255570Strasz log_debugx("CHAP authentication required"); 1068255570Strasz 1069255570Strasz auth_method = keys_find(request_keys, "AuthMethod"); 1070255570Strasz if (auth_method == NULL) { 1071255570Strasz login_send_error(request, 0x02, 0x07); 1072255570Strasz log_errx(1, "received Login PDU without AuthMethod"); 1073255570Strasz } 1074255570Strasz /* 1075255570Strasz * XXX: This should be Reject, not just a login failure (5.3.2). 1076255570Strasz */ 1077255570Strasz if (login_list_contains(auth_method, "CHAP") == 0) { 1078255570Strasz login_send_error(request, 0x02, 0x01); 1079255570Strasz log_errx(1, "initiator requests unsupported AuthMethod \"%s\" " 1080255570Strasz "instead of \"CHAP\"", auth_method); 1081255570Strasz } 1082255570Strasz 1083255570Strasz response = login_new_response(request); 1084255570Strasz 1085255570Strasz response_keys = keys_new(); 1086255570Strasz keys_add(response_keys, "AuthMethod", "CHAP"); 1087255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { 1088271701Strasz if (conn->conn_target->t_alias != NULL) 1089271701Strasz keys_add(response_keys, 1090271701Strasz "TargetAlias", conn->conn_target->t_alias); 1091255570Strasz rv = asprintf(&portal_group_tag, "%d", 1092255570Strasz conn->conn_portal->p_portal_group->pg_tag); 1093255570Strasz if (rv <= 0) 1094255570Strasz log_err(1, "asprintf"); 1095255570Strasz keys_add(response_keys, 1096255570Strasz "TargetPortalGroupTag", portal_group_tag); 1097255570Strasz free(portal_group_tag); 1098255570Strasz } 1099255570Strasz keys_save(response_keys, response); 1100255570Strasz 1101255570Strasz pdu_send(response); 1102255570Strasz pdu_delete(response); 1103255570Strasz keys_delete(response_keys); 1104255570Strasz pdu_delete(request); 1105255570Strasz keys_delete(request_keys); 1106255570Strasz 1107255570Strasz login_chap(conn, ag); 1108255570Strasz 1109255570Strasz login_negotiate(conn, NULL); 1110255570Strasz} 1111