login.c revision 270137
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 * $FreeBSD: stable/10/usr.sbin/ctld/login.c 270137 2014-08-18 16:06:04Z mav $ 30255570Strasz */ 31255570Strasz 32255570Strasz#include <assert.h> 33255570Strasz#include <stdbool.h> 34255570Strasz#include <stdint.h> 35255570Strasz#include <stdio.h> 36255570Strasz#include <stdlib.h> 37255570Strasz#include <string.h> 38255570Strasz#include <unistd.h> 39255570Strasz#include <netinet/in.h> 40255570Strasz#include <openssl/err.h> 41255570Strasz#include <openssl/md5.h> 42255570Strasz#include <openssl/rand.h> 43255570Strasz 44255570Strasz#include "ctld.h" 45255570Strasz#include "iscsi_proto.h" 46255570Strasz 47255570Straszstatic void login_send_error(struct pdu *request, 48255570Strasz char class, char detail); 49255570Strasz 50255570Straszstatic void 51255570Straszlogin_set_nsg(struct pdu *response, int nsg) 52255570Strasz{ 53255570Strasz struct iscsi_bhs_login_response *bhslr; 54255570Strasz 55255570Strasz assert(nsg == BHSLR_STAGE_SECURITY_NEGOTIATION || 56255570Strasz nsg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || 57255570Strasz nsg == BHSLR_STAGE_FULL_FEATURE_PHASE); 58255570Strasz 59255570Strasz bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; 60255570Strasz 61255570Strasz bhslr->bhslr_flags &= 0xFC; 62255570Strasz bhslr->bhslr_flags |= nsg; 63255570Strasz} 64255570Strasz 65255570Straszstatic int 66255570Straszlogin_csg(const struct pdu *request) 67255570Strasz{ 68255570Strasz struct iscsi_bhs_login_request *bhslr; 69255570Strasz 70255570Strasz bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; 71255570Strasz 72255570Strasz return ((bhslr->bhslr_flags & 0x0C) >> 2); 73255570Strasz} 74255570Strasz 75255570Straszstatic void 76255570Straszlogin_set_csg(struct pdu *response, int csg) 77255570Strasz{ 78255570Strasz struct iscsi_bhs_login_response *bhslr; 79255570Strasz 80255570Strasz assert(csg == BHSLR_STAGE_SECURITY_NEGOTIATION || 81255570Strasz csg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || 82255570Strasz csg == BHSLR_STAGE_FULL_FEATURE_PHASE); 83255570Strasz 84255570Strasz bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; 85255570Strasz 86255570Strasz bhslr->bhslr_flags &= 0xF3; 87255570Strasz bhslr->bhslr_flags |= csg << 2; 88255570Strasz} 89255570Strasz 90255570Straszstatic struct pdu * 91255570Straszlogin_receive(struct connection *conn, bool initial) 92255570Strasz{ 93255570Strasz struct pdu *request; 94255570Strasz struct iscsi_bhs_login_request *bhslr; 95255570Strasz 96255570Strasz request = pdu_new(conn); 97255570Strasz pdu_receive(request); 98255570Strasz if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != 99255570Strasz ISCSI_BHS_OPCODE_LOGIN_REQUEST) { 100255570Strasz /* 101255570Strasz * The first PDU in session is special - if we receive any PDU 102255570Strasz * different than login request, we have to drop the connection 103255570Strasz * without sending response ("A target receiving any PDU 104255570Strasz * except a Login request before the Login Phase is started MUST 105255570Strasz * immediately terminate the connection on which the PDU 106255570Strasz * was received.") 107255570Strasz */ 108255570Strasz if (initial == false) 109255570Strasz login_send_error(request, 0x02, 0x0b); 110255570Strasz log_errx(1, "protocol error: received invalid opcode 0x%x", 111255570Strasz request->pdu_bhs->bhs_opcode); 112255570Strasz } 113255570Strasz bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; 114255570Strasz /* 115255570Strasz * XXX: Implement the C flag some day. 116255570Strasz */ 117255570Strasz if ((bhslr->bhslr_flags & BHSLR_FLAGS_CONTINUE) != 0) { 118255570Strasz login_send_error(request, 0x03, 0x00); 119255570Strasz log_errx(1, "received Login PDU with unsupported \"C\" flag"); 120255570Strasz } 121255570Strasz if (bhslr->bhslr_version_max != 0x00) { 122255570Strasz login_send_error(request, 0x02, 0x05); 123255570Strasz log_errx(1, "received Login PDU with unsupported " 124255570Strasz "Version-max 0x%x", bhslr->bhslr_version_max); 125255570Strasz } 126255570Strasz if (bhslr->bhslr_version_min != 0x00) { 127255570Strasz login_send_error(request, 0x02, 0x05); 128255570Strasz log_errx(1, "received Login PDU with unsupported " 129255570Strasz "Version-min 0x%x", bhslr->bhslr_version_min); 130255570Strasz } 131255570Strasz if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) { 132255570Strasz login_send_error(request, 0x02, 0x05); 133255570Strasz log_errx(1, "received Login PDU with decreasing CmdSN: " 134255570Strasz "was %d, is %d", conn->conn_cmdsn, 135255570Strasz ntohl(bhslr->bhslr_cmdsn)); 136255570Strasz } 137255570Strasz if (initial == false && 138255570Strasz ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { 139255570Strasz login_send_error(request, 0x02, 0x05); 140255570Strasz log_errx(1, "received Login PDU with wrong ExpStatSN: " 141255570Strasz "is %d, should be %d", ntohl(bhslr->bhslr_expstatsn), 142255570Strasz conn->conn_statsn); 143255570Strasz } 144255570Strasz conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); 145255570Strasz 146255570Strasz return (request); 147255570Strasz} 148255570Strasz 149255570Straszstatic struct pdu * 150255570Straszlogin_new_response(struct pdu *request) 151255570Strasz{ 152255570Strasz struct pdu *response; 153255570Strasz struct connection *conn; 154255570Strasz struct iscsi_bhs_login_request *bhslr; 155255570Strasz struct iscsi_bhs_login_response *bhslr2; 156255570Strasz 157255570Strasz bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; 158255570Strasz conn = request->pdu_connection; 159255570Strasz 160255570Strasz response = pdu_new_response(request); 161255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 162255570Strasz bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGIN_RESPONSE; 163255570Strasz login_set_csg(response, BHSLR_STAGE_SECURITY_NEGOTIATION); 164255570Strasz memcpy(bhslr2->bhslr_isid, 165255570Strasz bhslr->bhslr_isid, sizeof(bhslr2->bhslr_isid)); 166255570Strasz bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; 167255570Strasz bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); 168255570Strasz bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); 169255570Strasz bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); 170255570Strasz 171255570Strasz return (response); 172255570Strasz} 173255570Strasz 174255570Straszstatic void 175255570Straszlogin_send_error(struct pdu *request, char class, char detail) 176255570Strasz{ 177255570Strasz struct pdu *response; 178255570Strasz struct iscsi_bhs_login_response *bhslr2; 179255570Strasz 180255570Strasz log_debugx("sending Login Response PDU with failure class 0x%x/0x%x; " 181255570Strasz "see next line for reason", class, detail); 182255570Strasz response = login_new_response(request); 183255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 184255570Strasz bhslr2->bhslr_status_class = class; 185255570Strasz bhslr2->bhslr_status_detail = detail; 186255570Strasz 187255570Strasz pdu_send(response); 188255570Strasz pdu_delete(response); 189255570Strasz} 190255570Strasz 191255570Straszstatic int 192255570Straszlogin_list_contains(const char *list, const char *what) 193255570Strasz{ 194255570Strasz char *tofree, *str, *token; 195255570Strasz 196255570Strasz tofree = str = checked_strdup(list); 197255570Strasz 198255570Strasz while ((token = strsep(&str, ",")) != NULL) { 199255570Strasz if (strcmp(token, what) == 0) { 200255570Strasz free(tofree); 201255570Strasz return (1); 202255570Strasz } 203255570Strasz } 204255570Strasz free(tofree); 205255570Strasz return (0); 206255570Strasz} 207255570Strasz 208255570Straszstatic int 209255570Straszlogin_list_prefers(const char *list, 210255570Strasz const char *choice1, const char *choice2) 211255570Strasz{ 212255570Strasz char *tofree, *str, *token; 213255570Strasz 214255570Strasz tofree = str = checked_strdup(list); 215255570Strasz 216255570Strasz while ((token = strsep(&str, ",")) != NULL) { 217255570Strasz if (strcmp(token, choice1) == 0) { 218255570Strasz free(tofree); 219255570Strasz return (1); 220255570Strasz } 221255570Strasz if (strcmp(token, choice2) == 0) { 222255570Strasz free(tofree); 223255570Strasz return (2); 224255570Strasz } 225255570Strasz } 226255570Strasz free(tofree); 227255570Strasz return (-1); 228255570Strasz} 229255570Strasz 230255570Straszstatic int 231255570Straszlogin_hex2int(const char hex) 232255570Strasz{ 233255570Strasz switch (hex) { 234255570Strasz case '0': 235255570Strasz return (0x00); 236255570Strasz case '1': 237255570Strasz return (0x01); 238255570Strasz case '2': 239255570Strasz return (0x02); 240255570Strasz case '3': 241255570Strasz return (0x03); 242255570Strasz case '4': 243255570Strasz return (0x04); 244255570Strasz case '5': 245255570Strasz return (0x05); 246255570Strasz case '6': 247255570Strasz return (0x06); 248255570Strasz case '7': 249255570Strasz return (0x07); 250255570Strasz case '8': 251255570Strasz return (0x08); 252255570Strasz case '9': 253255570Strasz return (0x09); 254255570Strasz case 'a': 255255570Strasz case 'A': 256255570Strasz return (0x0a); 257255570Strasz case 'b': 258255570Strasz case 'B': 259255570Strasz return (0x0b); 260255570Strasz case 'c': 261255570Strasz case 'C': 262255570Strasz return (0x0c); 263255570Strasz case 'd': 264255570Strasz case 'D': 265255570Strasz return (0x0d); 266255570Strasz case 'e': 267255570Strasz case 'E': 268255570Strasz return (0x0e); 269255570Strasz case 'f': 270255570Strasz case 'F': 271255570Strasz return (0x0f); 272255570Strasz default: 273255570Strasz return (-1); 274255570Strasz } 275255570Strasz} 276255570Strasz 277255570Strasz/* 278255570Strasz * XXX: Review this _carefully_. 279255570Strasz */ 280255570Straszstatic int 281255570Straszlogin_hex2bin(const char *hex, char **binp, size_t *bin_lenp) 282255570Strasz{ 283255570Strasz int i, hex_len, nibble; 284255570Strasz bool lo = true; /* As opposed to 'hi'. */ 285255570Strasz char *bin; 286255570Strasz size_t bin_off, bin_len; 287255570Strasz 288255570Strasz if (strncasecmp(hex, "0x", strlen("0x")) != 0) { 289255570Strasz log_warnx("malformed variable, should start with \"0x\""); 290255570Strasz return (-1); 291255570Strasz } 292255570Strasz 293255570Strasz hex += strlen("0x"); 294255570Strasz hex_len = strlen(hex); 295255570Strasz if (hex_len < 1) { 296255570Strasz log_warnx("malformed variable; doesn't contain anything " 297255570Strasz "but \"0x\""); 298255570Strasz return (-1); 299255570Strasz } 300255570Strasz 301255570Strasz bin_len = hex_len / 2 + hex_len % 2; 302255570Strasz bin = calloc(bin_len, 1); 303255570Strasz if (bin == NULL) 304255570Strasz log_err(1, "calloc"); 305255570Strasz 306255570Strasz bin_off = bin_len - 1; 307255570Strasz for (i = hex_len - 1; i >= 0; i--) { 308255570Strasz nibble = login_hex2int(hex[i]); 309255570Strasz if (nibble < 0) { 310255570Strasz log_warnx("malformed variable, invalid char \"%c\"", 311255570Strasz hex[i]); 312255570Strasz return (-1); 313255570Strasz } 314255570Strasz 315255570Strasz assert(bin_off < bin_len); 316255570Strasz if (lo) { 317255570Strasz bin[bin_off] = nibble; 318255570Strasz lo = false; 319255570Strasz } else { 320255570Strasz bin[bin_off] |= nibble << 4; 321255570Strasz bin_off--; 322255570Strasz lo = true; 323255570Strasz } 324255570Strasz } 325255570Strasz 326255570Strasz *binp = bin; 327255570Strasz *bin_lenp = bin_len; 328255570Strasz return (0); 329255570Strasz} 330255570Strasz 331255570Straszstatic char * 332255570Straszlogin_bin2hex(const char *bin, size_t bin_len) 333255570Strasz{ 334255570Strasz unsigned char *hex, *tmp, ch; 335255570Strasz size_t hex_len; 336255570Strasz size_t i; 337255570Strasz 338255570Strasz hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ 339255570Strasz hex = malloc(hex_len); 340255570Strasz if (hex == NULL) 341255570Strasz log_err(1, "malloc"); 342255570Strasz 343255570Strasz tmp = hex; 344255570Strasz tmp += sprintf(tmp, "0x"); 345255570Strasz for (i = 0; i < bin_len; i++) { 346255570Strasz ch = bin[i]; 347255570Strasz tmp += sprintf(tmp, "%02x", ch); 348255570Strasz } 349255570Strasz 350255570Strasz return (hex); 351255570Strasz} 352255570Strasz 353255570Straszstatic void 354255570Straszlogin_compute_md5(const char id, const char *secret, 355255570Strasz const void *challenge, size_t challenge_len, void *response, 356255570Strasz size_t response_len) 357255570Strasz{ 358255570Strasz MD5_CTX ctx; 359255570Strasz int rv; 360255570Strasz 361255570Strasz assert(response_len == MD5_DIGEST_LENGTH); 362255570Strasz 363255570Strasz MD5_Init(&ctx); 364255570Strasz MD5_Update(&ctx, &id, sizeof(id)); 365255570Strasz MD5_Update(&ctx, secret, strlen(secret)); 366255570Strasz MD5_Update(&ctx, challenge, challenge_len); 367255570Strasz rv = MD5_Final(response, &ctx); 368255570Strasz if (rv != 1) 369255570Strasz log_errx(1, "MD5_Final"); 370255570Strasz} 371255570Strasz 372255570Strasz#define LOGIN_CHALLENGE_LEN 1024 373255570Strasz 374255570Straszstatic struct pdu * 375255570Straszlogin_receive_chap_a(struct connection *conn) 376255570Strasz{ 377255570Strasz struct pdu *request; 378255570Strasz struct keys *request_keys; 379255570Strasz const char *chap_a; 380255570Strasz 381255570Strasz request = login_receive(conn, false); 382255570Strasz request_keys = keys_new(); 383255570Strasz keys_load(request_keys, request); 384255570Strasz 385255570Strasz chap_a = keys_find(request_keys, "CHAP_A"); 386255570Strasz if (chap_a == NULL) { 387255570Strasz login_send_error(request, 0x02, 0x07); 388255570Strasz log_errx(1, "received CHAP Login PDU without CHAP_A"); 389255570Strasz } 390255570Strasz if (login_list_contains(chap_a, "5") == 0) { 391255570Strasz login_send_error(request, 0x02, 0x01); 392255570Strasz log_errx(1, "received CHAP Login PDU with unsupported CHAP_A " 393255570Strasz "\"%s\"", chap_a); 394255570Strasz } 395255570Strasz keys_delete(request_keys); 396255570Strasz 397255570Strasz return (request); 398255570Strasz} 399255570Strasz 400255570Straszstatic void 401255570Straszlogin_send_chap_c(struct pdu *request, const unsigned char id, 402255570Strasz const void *challenge, const size_t challenge_len) 403255570Strasz{ 404255570Strasz struct pdu *response; 405255570Strasz struct keys *response_keys; 406255570Strasz char *chap_c, chap_i[4]; 407255570Strasz 408255570Strasz chap_c = login_bin2hex(challenge, challenge_len); 409255570Strasz snprintf(chap_i, sizeof(chap_i), "%d", id); 410255570Strasz 411255570Strasz response = login_new_response(request); 412255570Strasz response_keys = keys_new(); 413255570Strasz keys_add(response_keys, "CHAP_A", "5"); 414255570Strasz keys_add(response_keys, "CHAP_I", chap_i); 415255570Strasz keys_add(response_keys, "CHAP_C", chap_c); 416255570Strasz free(chap_c); 417255570Strasz keys_save(response_keys, response); 418256192Strasz pdu_send(response); 419256192Strasz pdu_delete(response); 420255570Strasz keys_delete(response_keys); 421255570Strasz} 422255570Strasz 423255570Straszstatic struct pdu * 424255570Straszlogin_receive_chap_r(struct connection *conn, 425255570Strasz struct auth_group *ag, const unsigned char id, const void *challenge, 426255570Strasz const size_t challenge_len, const struct auth **cap) 427255570Strasz{ 428255570Strasz struct pdu *request; 429255570Strasz struct keys *request_keys; 430255570Strasz const char *chap_n, *chap_r; 431255570Strasz char *response_bin, expected_response_bin[MD5_DIGEST_LENGTH]; 432255570Strasz size_t response_bin_len; 433255570Strasz const struct auth *auth; 434255570Strasz int error; 435255570Strasz 436255570Strasz request = login_receive(conn, false); 437255570Strasz request_keys = keys_new(); 438255570Strasz keys_load(request_keys, request); 439255570Strasz 440255570Strasz chap_n = keys_find(request_keys, "CHAP_N"); 441255570Strasz if (chap_n == NULL) { 442255570Strasz login_send_error(request, 0x02, 0x07); 443255570Strasz log_errx(1, "received CHAP Login PDU without CHAP_N"); 444255570Strasz } 445255570Strasz chap_r = keys_find(request_keys, "CHAP_R"); 446255570Strasz if (chap_r == NULL) { 447255570Strasz login_send_error(request, 0x02, 0x07); 448255570Strasz log_errx(1, "received CHAP Login PDU without CHAP_R"); 449255570Strasz } 450255570Strasz error = login_hex2bin(chap_r, &response_bin, &response_bin_len); 451255570Strasz if (error != 0) { 452255570Strasz login_send_error(request, 0x02, 0x07); 453255570Strasz log_errx(1, "received CHAP Login PDU with malformed CHAP_R"); 454255570Strasz } 455255570Strasz 456255570Strasz /* 457255570Strasz * Verify the response. 458255570Strasz */ 459255570Strasz assert(ag->ag_type == AG_TYPE_CHAP || 460255570Strasz ag->ag_type == AG_TYPE_CHAP_MUTUAL); 461255570Strasz auth = auth_find(ag, chap_n); 462255570Strasz if (auth == NULL) { 463255570Strasz login_send_error(request, 0x02, 0x01); 464255570Strasz log_errx(1, "received CHAP Login with invalid user \"%s\"", 465255570Strasz chap_n); 466255570Strasz } 467255570Strasz 468255570Strasz assert(auth->a_secret != NULL); 469255570Strasz assert(strlen(auth->a_secret) > 0); 470255570Strasz login_compute_md5(id, auth->a_secret, challenge, 471255570Strasz challenge_len, expected_response_bin, 472255570Strasz sizeof(expected_response_bin)); 473255570Strasz 474255570Strasz if (memcmp(response_bin, expected_response_bin, 475255570Strasz sizeof(expected_response_bin)) != 0) { 476255570Strasz login_send_error(request, 0x02, 0x01); 477255570Strasz log_errx(1, "CHAP authentication failed for user \"%s\"", 478255570Strasz auth->a_user); 479255570Strasz } 480255570Strasz 481255570Strasz keys_delete(request_keys); 482255570Strasz free(response_bin); 483255570Strasz 484255570Strasz *cap = auth; 485255570Strasz return (request); 486255570Strasz} 487255570Strasz 488255570Straszstatic void 489255570Straszlogin_send_chap_success(struct pdu *request, 490255570Strasz const struct auth *auth) 491255570Strasz{ 492255570Strasz struct pdu *response; 493255570Strasz struct keys *request_keys, *response_keys; 494255570Strasz struct iscsi_bhs_login_response *bhslr2; 495255570Strasz const char *chap_i, *chap_c; 496255570Strasz char *chap_r, *challenge, response_bin[MD5_DIGEST_LENGTH]; 497255570Strasz size_t challenge_len; 498255570Strasz unsigned char id; 499255570Strasz int error; 500255570Strasz 501255570Strasz response = login_new_response(request); 502255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 503255570Strasz bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; 504255570Strasz login_set_nsg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); 505255570Strasz 506255570Strasz /* 507255570Strasz * Actually, one more thing: mutual authentication. 508255570Strasz */ 509255570Strasz request_keys = keys_new(); 510255570Strasz keys_load(request_keys, request); 511255570Strasz chap_i = keys_find(request_keys, "CHAP_I"); 512255570Strasz chap_c = keys_find(request_keys, "CHAP_C"); 513255570Strasz if (chap_i != NULL || chap_c != NULL) { 514255570Strasz if (chap_i == NULL) { 515255570Strasz login_send_error(request, 0x02, 0x07); 516255570Strasz log_errx(1, "initiator requested target " 517255570Strasz "authentication, but didn't send CHAP_I"); 518255570Strasz } 519255570Strasz if (chap_c == NULL) { 520255570Strasz login_send_error(request, 0x02, 0x07); 521255570Strasz log_errx(1, "initiator requested target " 522255570Strasz "authentication, but didn't send CHAP_C"); 523255570Strasz } 524255570Strasz if (auth->a_auth_group->ag_type != AG_TYPE_CHAP_MUTUAL) { 525255570Strasz login_send_error(request, 0x02, 0x01); 526255570Strasz log_errx(1, "initiator requests target authentication " 527255570Strasz "for user \"%s\", but mutual user/secret " 528255570Strasz "is not set", auth->a_user); 529255570Strasz } 530255570Strasz 531255570Strasz id = strtoul(chap_i, NULL, 10); 532255570Strasz error = login_hex2bin(chap_c, &challenge, &challenge_len); 533255570Strasz if (error != 0) { 534255570Strasz login_send_error(request, 0x02, 0x07); 535255570Strasz log_errx(1, "received CHAP Login PDU with malformed " 536255570Strasz "CHAP_C"); 537255570Strasz } 538255570Strasz 539255570Strasz log_debugx("performing mutual authentication as user \"%s\"", 540255570Strasz auth->a_mutual_user); 541255570Strasz login_compute_md5(id, auth->a_mutual_secret, challenge, 542255570Strasz challenge_len, response_bin, sizeof(response_bin)); 543255570Strasz 544255570Strasz chap_r = login_bin2hex(response_bin, 545255570Strasz sizeof(response_bin)); 546255570Strasz response_keys = keys_new(); 547255570Strasz keys_add(response_keys, "CHAP_N", auth->a_mutual_user); 548255570Strasz keys_add(response_keys, "CHAP_R", chap_r); 549255570Strasz free(chap_r); 550255570Strasz keys_save(response_keys, response); 551255570Strasz keys_delete(response_keys); 552255570Strasz } else { 553255570Strasz log_debugx("initiator did not request target authentication"); 554255570Strasz } 555255570Strasz 556255570Strasz keys_delete(request_keys); 557255570Strasz pdu_send(response); 558256192Strasz pdu_delete(response); 559255570Strasz} 560255570Strasz 561255570Straszstatic void 562255570Straszlogin_chap(struct connection *conn, struct auth_group *ag) 563255570Strasz{ 564255570Strasz const struct auth *auth; 565255570Strasz struct pdu *request; 566255570Strasz char challenge_bin[LOGIN_CHALLENGE_LEN]; 567255570Strasz unsigned char id; 568255570Strasz int rv; 569255570Strasz 570255570Strasz /* 571255570Strasz * Receive CHAP_A PDU. 572255570Strasz */ 573255570Strasz log_debugx("beginning CHAP authentication; waiting for CHAP_A"); 574255570Strasz request = login_receive_chap_a(conn); 575255570Strasz 576255570Strasz /* 577255570Strasz * Generate the challenge. 578255570Strasz */ 579255570Strasz rv = RAND_bytes(challenge_bin, sizeof(challenge_bin)); 580255570Strasz if (rv != 1) { 581255570Strasz login_send_error(request, 0x03, 0x02); 582255570Strasz log_errx(1, "RAND_bytes failed: %s", 583255570Strasz ERR_error_string(ERR_get_error(), NULL)); 584255570Strasz } 585255570Strasz rv = RAND_bytes(&id, sizeof(id)); 586255570Strasz if (rv != 1) { 587255570Strasz login_send_error(request, 0x03, 0x02); 588255570Strasz log_errx(1, "RAND_bytes failed: %s", 589255570Strasz ERR_error_string(ERR_get_error(), NULL)); 590255570Strasz } 591255570Strasz 592255570Strasz /* 593255570Strasz * Send the challenge. 594255570Strasz */ 595255570Strasz log_debugx("sending CHAP_C, binary challenge size is %zd bytes", 596255570Strasz sizeof(challenge_bin)); 597255570Strasz login_send_chap_c(request, id, challenge_bin, 598255570Strasz sizeof(challenge_bin)); 599255570Strasz pdu_delete(request); 600255570Strasz 601255570Strasz /* 602255570Strasz * Receive CHAP_N/CHAP_R PDU and authenticate. 603255570Strasz */ 604255570Strasz log_debugx("waiting for CHAP_N/CHAP_R"); 605255570Strasz request = login_receive_chap_r(conn, ag, id, challenge_bin, 606255570Strasz sizeof(challenge_bin), &auth); 607255570Strasz 608255570Strasz /* 609255570Strasz * Yay, authentication succeeded! 610255570Strasz */ 611255570Strasz log_debugx("authentication succeeded for user \"%s\"; " 612255570Strasz "transitioning to Negotiation Phase", auth->a_user); 613255570Strasz login_send_chap_success(request, auth); 614255570Strasz pdu_delete(request); 615255570Strasz} 616255570Strasz 617255570Straszstatic void 618255570Straszlogin_negotiate_key(struct pdu *request, const char *name, 619255570Strasz const char *value, bool skipped_security, struct keys *response_keys) 620255570Strasz{ 621255570Strasz int which, tmp; 622255570Strasz struct connection *conn; 623255570Strasz 624255570Strasz conn = request->pdu_connection; 625255570Strasz 626255570Strasz if (strcmp(name, "InitiatorName") == 0) { 627255570Strasz if (!skipped_security) 628255570Strasz log_errx(1, "initiator resent InitiatorName"); 629255570Strasz } else if (strcmp(name, "SessionType") == 0) { 630255570Strasz if (!skipped_security) 631255570Strasz log_errx(1, "initiator resent SessionType"); 632255570Strasz } else if (strcmp(name, "TargetName") == 0) { 633255570Strasz if (!skipped_security) 634255570Strasz log_errx(1, "initiator resent TargetName"); 635255570Strasz } else if (strcmp(name, "InitiatorAlias") == 0) { 636255570Strasz if (conn->conn_initiator_alias != NULL) 637255570Strasz free(conn->conn_initiator_alias); 638255570Strasz conn->conn_initiator_alias = checked_strdup(value); 639255570Strasz } else if (strcmp(value, "Irrelevant") == 0) { 640255570Strasz /* Ignore. */ 641255570Strasz } else if (strcmp(name, "HeaderDigest") == 0) { 642255570Strasz /* 643255570Strasz * We don't handle digests for discovery sessions. 644255570Strasz */ 645255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { 646255570Strasz log_debugx("discovery session; digests disabled"); 647255570Strasz keys_add(response_keys, name, "None"); 648255570Strasz return; 649255570Strasz } 650255570Strasz 651255570Strasz which = login_list_prefers(value, "CRC32C", "None"); 652255570Strasz switch (which) { 653255570Strasz case 1: 654255570Strasz log_debugx("initiator prefers CRC32C " 655255570Strasz "for header digest; we'll use it"); 656255570Strasz conn->conn_header_digest = CONN_DIGEST_CRC32C; 657255570Strasz keys_add(response_keys, name, "CRC32C"); 658255570Strasz break; 659255570Strasz case 2: 660255570Strasz log_debugx("initiator prefers not to do " 661255570Strasz "header digest; we'll comply"); 662255570Strasz keys_add(response_keys, name, "None"); 663255570Strasz break; 664255570Strasz default: 665255570Strasz log_warnx("initiator sent unrecognized " 666255570Strasz "HeaderDigest value \"%s\"; will use None", value); 667255570Strasz keys_add(response_keys, name, "None"); 668255570Strasz break; 669255570Strasz } 670255570Strasz } else if (strcmp(name, "DataDigest") == 0) { 671255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { 672255570Strasz log_debugx("discovery session; digests disabled"); 673255570Strasz keys_add(response_keys, name, "None"); 674255570Strasz return; 675255570Strasz } 676255570Strasz 677255570Strasz which = login_list_prefers(value, "CRC32C", "None"); 678255570Strasz switch (which) { 679255570Strasz case 1: 680255570Strasz log_debugx("initiator prefers CRC32C " 681255570Strasz "for data digest; we'll use it"); 682255570Strasz conn->conn_data_digest = CONN_DIGEST_CRC32C; 683255570Strasz keys_add(response_keys, name, "CRC32C"); 684255570Strasz break; 685255570Strasz case 2: 686255570Strasz log_debugx("initiator prefers not to do " 687255570Strasz "data digest; we'll comply"); 688255570Strasz keys_add(response_keys, name, "None"); 689255570Strasz break; 690255570Strasz default: 691255570Strasz log_warnx("initiator sent unrecognized " 692255570Strasz "DataDigest value \"%s\"; will use None", value); 693255570Strasz keys_add(response_keys, name, "None"); 694255570Strasz break; 695255570Strasz } 696255570Strasz } else if (strcmp(name, "MaxConnections") == 0) { 697255570Strasz keys_add(response_keys, name, "1"); 698255570Strasz } else if (strcmp(name, "InitialR2T") == 0) { 699255570Strasz keys_add(response_keys, name, "Yes"); 700255570Strasz } else if (strcmp(name, "ImmediateData") == 0) { 701255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { 702255570Strasz log_debugx("discovery session; ImmediateData irrelevant"); 703255570Strasz keys_add(response_keys, name, "Irrelevant"); 704255570Strasz } else { 705255570Strasz if (strcmp(value, "Yes") == 0) { 706255570Strasz conn->conn_immediate_data = true; 707255570Strasz keys_add(response_keys, name, "Yes"); 708255570Strasz } else { 709255570Strasz conn->conn_immediate_data = false; 710255570Strasz keys_add(response_keys, name, "No"); 711255570Strasz } 712255570Strasz } 713255570Strasz } else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) { 714255570Strasz tmp = strtoul(value, NULL, 10); 715255570Strasz if (tmp <= 0) { 716255570Strasz login_send_error(request, 0x02, 0x00); 717255570Strasz log_errx(1, "received invalid " 718255570Strasz "MaxRecvDataSegmentLength"); 719255570Strasz } 720255570Strasz if (tmp > MAX_DATA_SEGMENT_LENGTH) { 721255570Strasz log_debugx("capping MaxDataSegmentLength from %d to %d", 722255570Strasz tmp, MAX_DATA_SEGMENT_LENGTH); 723255570Strasz tmp = MAX_DATA_SEGMENT_LENGTH; 724255570Strasz } 725255570Strasz conn->conn_max_data_segment_length = tmp; 726255570Strasz keys_add_int(response_keys, name, tmp); 727255570Strasz } else if (strcmp(name, "MaxBurstLength") == 0) { 728255570Strasz tmp = strtoul(value, NULL, 10); 729255570Strasz if (tmp <= 0) { 730255570Strasz login_send_error(request, 0x02, 0x00); 731255570Strasz log_errx(1, "received invalid MaxBurstLength"); 732255570Strasz } 733255570Strasz if (tmp > MAX_BURST_LENGTH) { 734255570Strasz log_debugx("capping MaxBurstLength from %d to %d", 735255570Strasz tmp, MAX_BURST_LENGTH); 736255570Strasz tmp = MAX_BURST_LENGTH; 737255570Strasz } 738255570Strasz conn->conn_max_burst_length = tmp; 739255570Strasz keys_add(response_keys, name, value); 740255570Strasz } else if (strcmp(name, "FirstBurstLength") == 0) { 741255570Strasz tmp = strtoul(value, NULL, 10); 742255570Strasz if (tmp <= 0) { 743255570Strasz login_send_error(request, 0x02, 0x00); 744255570Strasz log_errx(1, "received invalid " 745255570Strasz "FirstBurstLength"); 746255570Strasz } 747255570Strasz if (tmp > MAX_DATA_SEGMENT_LENGTH) { 748255570Strasz log_debugx("capping FirstBurstLength from %d to %d", 749255570Strasz tmp, MAX_DATA_SEGMENT_LENGTH); 750255570Strasz tmp = MAX_DATA_SEGMENT_LENGTH; 751255570Strasz } 752255570Strasz /* 753255570Strasz * We don't pass the value to the kernel; it only enforces 754255570Strasz * hardcoded limit anyway. 755255570Strasz */ 756255570Strasz keys_add_int(response_keys, name, tmp); 757255570Strasz } else if (strcmp(name, "DefaultTime2Wait") == 0) { 758255570Strasz keys_add(response_keys, name, value); 759255570Strasz } else if (strcmp(name, "DefaultTime2Retain") == 0) { 760255570Strasz keys_add(response_keys, name, "0"); 761255570Strasz } else if (strcmp(name, "MaxOutstandingR2T") == 0) { 762255570Strasz keys_add(response_keys, name, "1"); 763255570Strasz } else if (strcmp(name, "DataPDUInOrder") == 0) { 764255570Strasz keys_add(response_keys, name, "Yes"); 765255570Strasz } else if (strcmp(name, "DataSequenceInOrder") == 0) { 766255570Strasz keys_add(response_keys, name, "Yes"); 767255570Strasz } else if (strcmp(name, "ErrorRecoveryLevel") == 0) { 768255570Strasz keys_add(response_keys, name, "0"); 769255570Strasz } else if (strcmp(name, "OFMarker") == 0) { 770255570Strasz keys_add(response_keys, name, "No"); 771255570Strasz } else if (strcmp(name, "IFMarker") == 0) { 772255570Strasz keys_add(response_keys, name, "No"); 773255570Strasz } else { 774255570Strasz log_debugx("unknown key \"%s\"; responding " 775255570Strasz "with NotUnderstood", name); 776255570Strasz keys_add(response_keys, name, "NotUnderstood"); 777255570Strasz } 778255570Strasz} 779255570Strasz 780255570Straszstatic void 781255570Straszlogin_negotiate(struct connection *conn, struct pdu *request) 782255570Strasz{ 783255570Strasz struct pdu *response; 784255570Strasz struct iscsi_bhs_login_response *bhslr2; 785255570Strasz struct keys *request_keys, *response_keys; 786255570Strasz int i; 787255570Strasz bool skipped_security; 788255570Strasz 789255570Strasz if (request == NULL) { 790265515Strasz log_debugx("beginning operational parameter negotiation; " 791255570Strasz "waiting for Login PDU"); 792255570Strasz request = login_receive(conn, false); 793255570Strasz skipped_security = false; 794255570Strasz } else 795255570Strasz skipped_security = true; 796255570Strasz 797255570Strasz request_keys = keys_new(); 798255570Strasz keys_load(request_keys, request); 799255570Strasz 800255570Strasz response = login_new_response(request); 801255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 802255570Strasz bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; 803255570Strasz bhslr2->bhslr_tsih = htons(0xbadd); 804255570Strasz login_set_csg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); 805255570Strasz login_set_nsg(response, BHSLR_STAGE_FULL_FEATURE_PHASE); 806255570Strasz response_keys = keys_new(); 807255570Strasz for (i = 0; i < KEYS_MAX; i++) { 808255570Strasz if (request_keys->keys_names[i] == NULL) 809255570Strasz break; 810255570Strasz 811255570Strasz login_negotiate_key(request, request_keys->keys_names[i], 812255570Strasz request_keys->keys_values[i], skipped_security, 813255570Strasz response_keys); 814255570Strasz } 815255570Strasz 816265515Strasz log_debugx("operational parameter negotiation done; " 817255570Strasz "transitioning to Full Feature Phase"); 818255570Strasz 819255570Strasz keys_save(response_keys, response); 820255570Strasz pdu_send(response); 821255570Strasz pdu_delete(response); 822255570Strasz keys_delete(response_keys); 823255570Strasz pdu_delete(request); 824255570Strasz keys_delete(request_keys); 825255570Strasz} 826255570Strasz 827255570Straszvoid 828255570Straszlogin(struct connection *conn) 829255570Strasz{ 830255570Strasz struct pdu *request, *response; 831255570Strasz struct iscsi_bhs_login_request *bhslr; 832255570Strasz struct iscsi_bhs_login_response *bhslr2; 833255570Strasz struct keys *request_keys, *response_keys; 834255570Strasz struct auth_group *ag; 835255570Strasz const char *initiator_name, *initiator_alias, *session_type, 836255570Strasz *target_name, *auth_method; 837255570Strasz char *portal_group_tag; 838255570Strasz int rv; 839255570Strasz 840255570Strasz /* 841255570Strasz * Handle the initial Login Request - figure out required authentication 842255570Strasz * method and either transition to the next phase, if no authentication 843255570Strasz * is required, or call appropriate authentication code. 844255570Strasz */ 845255570Strasz log_debugx("beginning Login Phase; waiting for Login PDU"); 846255570Strasz request = login_receive(conn, true); 847255570Strasz bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; 848255570Strasz if (bhslr->bhslr_tsih != 0) { 849255570Strasz login_send_error(request, 0x02, 0x0a); 850255570Strasz log_errx(1, "received Login PDU with non-zero TSIH"); 851255570Strasz } 852255570Strasz 853268684Smav memcpy(conn->conn_initiator_isid, bhslr->bhslr_isid, 854268684Smav sizeof(conn->conn_initiator_isid)); 855268684Smav 856255570Strasz /* 857255570Strasz * XXX: Implement the C flag some day. 858255570Strasz */ 859255570Strasz request_keys = keys_new(); 860255570Strasz keys_load(request_keys, request); 861255570Strasz 862255570Strasz assert(conn->conn_initiator_name == NULL); 863255570Strasz initiator_name = keys_find(request_keys, "InitiatorName"); 864255570Strasz if (initiator_name == NULL) { 865255570Strasz login_send_error(request, 0x02, 0x07); 866255570Strasz log_errx(1, "received Login PDU without InitiatorName"); 867255570Strasz } 868255570Strasz if (valid_iscsi_name(initiator_name) == false) { 869255570Strasz login_send_error(request, 0x02, 0x00); 870255570Strasz log_errx(1, "received Login PDU with invalid InitiatorName"); 871255570Strasz } 872255570Strasz conn->conn_initiator_name = checked_strdup(initiator_name); 873255570Strasz log_set_peer_name(conn->conn_initiator_name); 874255570Strasz /* 875255570Strasz * XXX: This doesn't work (does nothing) because of Capsicum. 876255570Strasz */ 877255570Strasz setproctitle("%s (%s)", conn->conn_initiator_addr, conn->conn_initiator_name); 878255570Strasz 879255570Strasz initiator_alias = keys_find(request_keys, "InitiatorAlias"); 880255570Strasz if (initiator_alias != NULL) 881255570Strasz conn->conn_initiator_alias = checked_strdup(initiator_alias); 882255570Strasz 883255570Strasz assert(conn->conn_session_type == CONN_SESSION_TYPE_NONE); 884255570Strasz session_type = keys_find(request_keys, "SessionType"); 885255570Strasz if (session_type != NULL) { 886255570Strasz if (strcmp(session_type, "Normal") == 0) { 887255570Strasz conn->conn_session_type = CONN_SESSION_TYPE_NORMAL; 888255570Strasz } else if (strcmp(session_type, "Discovery") == 0) { 889255570Strasz conn->conn_session_type = CONN_SESSION_TYPE_DISCOVERY; 890255570Strasz } else { 891255570Strasz login_send_error(request, 0x02, 0x00); 892255570Strasz log_errx(1, "received Login PDU with invalid " 893255570Strasz "SessionType \"%s\"", session_type); 894255570Strasz } 895255570Strasz } else 896255570Strasz conn->conn_session_type = CONN_SESSION_TYPE_NORMAL; 897255570Strasz 898255570Strasz assert(conn->conn_target == NULL); 899255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { 900255570Strasz target_name = keys_find(request_keys, "TargetName"); 901255570Strasz if (target_name == NULL) { 902255570Strasz login_send_error(request, 0x02, 0x07); 903255570Strasz log_errx(1, "received Login PDU without TargetName"); 904255570Strasz } 905255570Strasz 906255570Strasz conn->conn_target = 907255570Strasz target_find(conn->conn_portal->p_portal_group->pg_conf, 908255570Strasz target_name); 909255570Strasz if (conn->conn_target == NULL) { 910255570Strasz login_send_error(request, 0x02, 0x03); 911255570Strasz log_errx(1, "requested target \"%s\" not found", 912255570Strasz target_name); 913255570Strasz } 914255570Strasz } 915255570Strasz 916255570Strasz /* 917255570Strasz * At this point we know what kind of authentication we need. 918255570Strasz */ 919255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { 920255570Strasz ag = conn->conn_target->t_auth_group; 921255570Strasz if (ag->ag_name != NULL) { 922255570Strasz log_debugx("initiator requests to connect " 923255570Strasz "to target \"%s\"; auth-group \"%s\"", 924263723Strasz conn->conn_target->t_name, 925255570Strasz conn->conn_target->t_auth_group->ag_name); 926255570Strasz } else { 927255570Strasz log_debugx("initiator requests to connect " 928263723Strasz "to target \"%s\"", conn->conn_target->t_name); 929255570Strasz } 930255570Strasz } else { 931255570Strasz assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY); 932255570Strasz ag = conn->conn_portal->p_portal_group->pg_discovery_auth_group; 933255570Strasz if (ag->ag_name != NULL) { 934255570Strasz log_debugx("initiator requests " 935255570Strasz "discovery session; auth-group \"%s\"", ag->ag_name); 936255570Strasz } else { 937255570Strasz log_debugx("initiator requests discovery session"); 938255570Strasz } 939255570Strasz } 940255570Strasz 941255570Strasz /* 942263720Strasz * Enforce initiator-name and initiator-portal. 943263720Strasz */ 944263720Strasz if (auth_name_defined(ag)) { 945263720Strasz if (auth_name_find(ag, initiator_name) == NULL) { 946263720Strasz login_send_error(request, 0x02, 0x02); 947263720Strasz log_errx(1, "initiator does not match allowed " 948263720Strasz "initiator names"); 949263720Strasz } 950263720Strasz log_debugx("initiator matches allowed initiator names"); 951263720Strasz } else { 952263720Strasz log_debugx("auth-group does not define initiator name " 953263720Strasz "restrictions"); 954263720Strasz } 955263720Strasz 956263720Strasz if (auth_portal_defined(ag)) { 957270137Smav if (auth_portal_find(ag, &conn->conn_initiator_sa) == NULL) { 958263720Strasz login_send_error(request, 0x02, 0x02); 959263720Strasz log_errx(1, "initiator does not match allowed " 960263720Strasz "initiator portals"); 961263720Strasz } 962263720Strasz log_debugx("initiator matches allowed initiator portals"); 963263720Strasz } else { 964263720Strasz log_debugx("auth-group does not define initiator portal " 965263720Strasz "restrictions"); 966263720Strasz } 967263720Strasz 968263720Strasz /* 969255570Strasz * Let's see if the initiator intends to do any kind of authentication 970255570Strasz * at all. 971255570Strasz */ 972255570Strasz if (login_csg(request) == BHSLR_STAGE_OPERATIONAL_NEGOTIATION) { 973255570Strasz if (ag->ag_type != AG_TYPE_NO_AUTHENTICATION) { 974255570Strasz login_send_error(request, 0x02, 0x01); 975255570Strasz log_errx(1, "initiator skipped the authentication, " 976255570Strasz "but authentication is required"); 977255570Strasz } 978255570Strasz 979255570Strasz keys_delete(request_keys); 980255570Strasz 981255570Strasz log_debugx("initiator skipped the authentication, " 982255570Strasz "and we don't need it; proceeding with negotiation"); 983255570Strasz login_negotiate(conn, request); 984255570Strasz return; 985255570Strasz } 986255570Strasz 987255570Strasz if (ag->ag_type == AG_TYPE_NO_AUTHENTICATION) { 988255570Strasz /* 989255570Strasz * Initiator might want to to authenticate, 990255570Strasz * but we don't need it. 991255570Strasz */ 992255570Strasz log_debugx("authentication not required; " 993265515Strasz "transitioning to operational parameter negotiation"); 994255570Strasz 995255570Strasz if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) == 0) 996255570Strasz log_warnx("initiator did not set the \"T\" flag; " 997255570Strasz "transitioning anyway"); 998255570Strasz 999255570Strasz response = login_new_response(request); 1000255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 1001255570Strasz bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; 1002255570Strasz login_set_nsg(response, 1003255570Strasz BHSLR_STAGE_OPERATIONAL_NEGOTIATION); 1004255570Strasz response_keys = keys_new(); 1005255570Strasz /* 1006255570Strasz * Required by Linux initiator. 1007255570Strasz */ 1008255570Strasz auth_method = keys_find(request_keys, "AuthMethod"); 1009255570Strasz if (auth_method != NULL && 1010255570Strasz login_list_contains(auth_method, "None")) 1011255570Strasz keys_add(response_keys, "AuthMethod", "None"); 1012255570Strasz 1013255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { 1014255570Strasz if (conn->conn_target->t_alias != NULL) 1015255570Strasz keys_add(response_keys, 1016255570Strasz "TargetAlias", conn->conn_target->t_alias); 1017255570Strasz rv = asprintf(&portal_group_tag, "%d", 1018255570Strasz conn->conn_portal->p_portal_group->pg_tag); 1019255570Strasz if (rv <= 0) 1020255570Strasz log_err(1, "asprintf"); 1021255570Strasz keys_add(response_keys, 1022255570Strasz "TargetPortalGroupTag", portal_group_tag); 1023255570Strasz free(portal_group_tag); 1024255570Strasz } 1025255570Strasz keys_save(response_keys, response); 1026255570Strasz pdu_send(response); 1027255570Strasz pdu_delete(response); 1028255570Strasz keys_delete(response_keys); 1029255570Strasz pdu_delete(request); 1030255570Strasz keys_delete(request_keys); 1031255570Strasz 1032255570Strasz login_negotiate(conn, NULL); 1033255570Strasz return; 1034255570Strasz } 1035255570Strasz 1036263729Strasz if (ag->ag_type == AG_TYPE_DENY) { 1037263729Strasz login_send_error(request, 0x02, 0x01); 1038263734Strasz log_errx(1, "auth-type is \"deny\""); 1039263729Strasz } 1040263729Strasz 1041259305Strasz if (ag->ag_type == AG_TYPE_UNKNOWN) { 1042259305Strasz /* 1043259305Strasz * This can happen with empty auth-group. 1044259305Strasz */ 1045259305Strasz login_send_error(request, 0x02, 0x01); 1046263734Strasz log_errx(1, "auth-type not set, denying access"); 1047259305Strasz } 1048259305Strasz 1049255570Strasz log_debugx("CHAP authentication required"); 1050255570Strasz 1051255570Strasz auth_method = keys_find(request_keys, "AuthMethod"); 1052255570Strasz if (auth_method == NULL) { 1053255570Strasz login_send_error(request, 0x02, 0x07); 1054255570Strasz log_errx(1, "received Login PDU without AuthMethod"); 1055255570Strasz } 1056255570Strasz /* 1057255570Strasz * XXX: This should be Reject, not just a login failure (5.3.2). 1058255570Strasz */ 1059255570Strasz if (login_list_contains(auth_method, "CHAP") == 0) { 1060255570Strasz login_send_error(request, 0x02, 0x01); 1061255570Strasz log_errx(1, "initiator requests unsupported AuthMethod \"%s\" " 1062255570Strasz "instead of \"CHAP\"", auth_method); 1063255570Strasz } 1064255570Strasz 1065255570Strasz response = login_new_response(request); 1066255570Strasz 1067255570Strasz response_keys = keys_new(); 1068255570Strasz keys_add(response_keys, "AuthMethod", "CHAP"); 1069255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { 1070255570Strasz rv = asprintf(&portal_group_tag, "%d", 1071255570Strasz conn->conn_portal->p_portal_group->pg_tag); 1072255570Strasz if (rv <= 0) 1073255570Strasz log_err(1, "asprintf"); 1074255570Strasz keys_add(response_keys, 1075255570Strasz "TargetPortalGroupTag", portal_group_tag); 1076255570Strasz free(portal_group_tag); 1077255570Strasz if (conn->conn_target->t_alias != NULL) 1078255570Strasz keys_add(response_keys, 1079255570Strasz "TargetAlias", conn->conn_target->t_alias); 1080255570Strasz } 1081255570Strasz keys_save(response_keys, response); 1082255570Strasz 1083255570Strasz pdu_send(response); 1084255570Strasz pdu_delete(response); 1085255570Strasz keys_delete(response_keys); 1086255570Strasz pdu_delete(request); 1087255570Strasz keys_delete(request_keys); 1088255570Strasz 1089255570Strasz login_chap(conn, ag); 1090255570Strasz 1091255570Strasz login_negotiate(conn, NULL); 1092255570Strasz} 1093