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$ 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 (request->pdu_data_len == 0) { 132255570Strasz login_send_error(request, 0x02, 0x00); 133255570Strasz log_errx(1, "received Login PDU with empty data segment"); 134255570Strasz } 135255570Strasz if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) { 136255570Strasz login_send_error(request, 0x02, 0x05); 137255570Strasz log_errx(1, "received Login PDU with decreasing CmdSN: " 138255570Strasz "was %d, is %d", conn->conn_cmdsn, 139255570Strasz ntohl(bhslr->bhslr_cmdsn)); 140255570Strasz } 141255570Strasz if (initial == false && 142255570Strasz ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { 143255570Strasz login_send_error(request, 0x02, 0x05); 144255570Strasz log_errx(1, "received Login PDU with wrong ExpStatSN: " 145255570Strasz "is %d, should be %d", ntohl(bhslr->bhslr_expstatsn), 146255570Strasz conn->conn_statsn); 147255570Strasz } 148255570Strasz conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); 149255570Strasz 150255570Strasz return (request); 151255570Strasz} 152255570Strasz 153255570Straszstatic struct pdu * 154255570Straszlogin_new_response(struct pdu *request) 155255570Strasz{ 156255570Strasz struct pdu *response; 157255570Strasz struct connection *conn; 158255570Strasz struct iscsi_bhs_login_request *bhslr; 159255570Strasz struct iscsi_bhs_login_response *bhslr2; 160255570Strasz 161255570Strasz bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; 162255570Strasz conn = request->pdu_connection; 163255570Strasz 164255570Strasz response = pdu_new_response(request); 165255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 166255570Strasz bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGIN_RESPONSE; 167255570Strasz login_set_csg(response, BHSLR_STAGE_SECURITY_NEGOTIATION); 168255570Strasz memcpy(bhslr2->bhslr_isid, 169255570Strasz bhslr->bhslr_isid, sizeof(bhslr2->bhslr_isid)); 170255570Strasz bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; 171255570Strasz bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); 172255570Strasz bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); 173255570Strasz bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); 174255570Strasz 175255570Strasz return (response); 176255570Strasz} 177255570Strasz 178255570Straszstatic void 179255570Straszlogin_send_error(struct pdu *request, char class, char detail) 180255570Strasz{ 181255570Strasz struct pdu *response; 182255570Strasz struct iscsi_bhs_login_response *bhslr2; 183255570Strasz 184255570Strasz log_debugx("sending Login Response PDU with failure class 0x%x/0x%x; " 185255570Strasz "see next line for reason", class, detail); 186255570Strasz response = login_new_response(request); 187255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 188255570Strasz bhslr2->bhslr_status_class = class; 189255570Strasz bhslr2->bhslr_status_detail = detail; 190255570Strasz 191255570Strasz pdu_send(response); 192255570Strasz pdu_delete(response); 193255570Strasz} 194255570Strasz 195255570Straszstatic int 196255570Straszlogin_list_contains(const char *list, const char *what) 197255570Strasz{ 198255570Strasz char *tofree, *str, *token; 199255570Strasz 200255570Strasz tofree = str = checked_strdup(list); 201255570Strasz 202255570Strasz while ((token = strsep(&str, ",")) != NULL) { 203255570Strasz if (strcmp(token, what) == 0) { 204255570Strasz free(tofree); 205255570Strasz return (1); 206255570Strasz } 207255570Strasz } 208255570Strasz free(tofree); 209255570Strasz return (0); 210255570Strasz} 211255570Strasz 212255570Straszstatic int 213255570Straszlogin_list_prefers(const char *list, 214255570Strasz const char *choice1, const char *choice2) 215255570Strasz{ 216255570Strasz char *tofree, *str, *token; 217255570Strasz 218255570Strasz tofree = str = checked_strdup(list); 219255570Strasz 220255570Strasz while ((token = strsep(&str, ",")) != NULL) { 221255570Strasz if (strcmp(token, choice1) == 0) { 222255570Strasz free(tofree); 223255570Strasz return (1); 224255570Strasz } 225255570Strasz if (strcmp(token, choice2) == 0) { 226255570Strasz free(tofree); 227255570Strasz return (2); 228255570Strasz } 229255570Strasz } 230255570Strasz free(tofree); 231255570Strasz return (-1); 232255570Strasz} 233255570Strasz 234255570Straszstatic int 235255570Straszlogin_hex2int(const char hex) 236255570Strasz{ 237255570Strasz switch (hex) { 238255570Strasz case '0': 239255570Strasz return (0x00); 240255570Strasz case '1': 241255570Strasz return (0x01); 242255570Strasz case '2': 243255570Strasz return (0x02); 244255570Strasz case '3': 245255570Strasz return (0x03); 246255570Strasz case '4': 247255570Strasz return (0x04); 248255570Strasz case '5': 249255570Strasz return (0x05); 250255570Strasz case '6': 251255570Strasz return (0x06); 252255570Strasz case '7': 253255570Strasz return (0x07); 254255570Strasz case '8': 255255570Strasz return (0x08); 256255570Strasz case '9': 257255570Strasz return (0x09); 258255570Strasz case 'a': 259255570Strasz case 'A': 260255570Strasz return (0x0a); 261255570Strasz case 'b': 262255570Strasz case 'B': 263255570Strasz return (0x0b); 264255570Strasz case 'c': 265255570Strasz case 'C': 266255570Strasz return (0x0c); 267255570Strasz case 'd': 268255570Strasz case 'D': 269255570Strasz return (0x0d); 270255570Strasz case 'e': 271255570Strasz case 'E': 272255570Strasz return (0x0e); 273255570Strasz case 'f': 274255570Strasz case 'F': 275255570Strasz return (0x0f); 276255570Strasz default: 277255570Strasz return (-1); 278255570Strasz } 279255570Strasz} 280255570Strasz 281255570Strasz/* 282255570Strasz * XXX: Review this _carefully_. 283255570Strasz */ 284255570Straszstatic int 285255570Straszlogin_hex2bin(const char *hex, char **binp, size_t *bin_lenp) 286255570Strasz{ 287255570Strasz int i, hex_len, nibble; 288255570Strasz bool lo = true; /* As opposed to 'hi'. */ 289255570Strasz char *bin; 290255570Strasz size_t bin_off, bin_len; 291255570Strasz 292255570Strasz if (strncasecmp(hex, "0x", strlen("0x")) != 0) { 293255570Strasz log_warnx("malformed variable, should start with \"0x\""); 294255570Strasz return (-1); 295255570Strasz } 296255570Strasz 297255570Strasz hex += strlen("0x"); 298255570Strasz hex_len = strlen(hex); 299255570Strasz if (hex_len < 1) { 300255570Strasz log_warnx("malformed variable; doesn't contain anything " 301255570Strasz "but \"0x\""); 302255570Strasz return (-1); 303255570Strasz } 304255570Strasz 305255570Strasz bin_len = hex_len / 2 + hex_len % 2; 306255570Strasz bin = calloc(bin_len, 1); 307255570Strasz if (bin == NULL) 308255570Strasz log_err(1, "calloc"); 309255570Strasz 310255570Strasz bin_off = bin_len - 1; 311255570Strasz for (i = hex_len - 1; i >= 0; i--) { 312255570Strasz nibble = login_hex2int(hex[i]); 313255570Strasz if (nibble < 0) { 314255570Strasz log_warnx("malformed variable, invalid char \"%c\"", 315255570Strasz hex[i]); 316255570Strasz return (-1); 317255570Strasz } 318255570Strasz 319255570Strasz assert(bin_off < bin_len); 320255570Strasz if (lo) { 321255570Strasz bin[bin_off] = nibble; 322255570Strasz lo = false; 323255570Strasz } else { 324255570Strasz bin[bin_off] |= nibble << 4; 325255570Strasz bin_off--; 326255570Strasz lo = true; 327255570Strasz } 328255570Strasz } 329255570Strasz 330255570Strasz *binp = bin; 331255570Strasz *bin_lenp = bin_len; 332255570Strasz return (0); 333255570Strasz} 334255570Strasz 335255570Straszstatic char * 336255570Straszlogin_bin2hex(const char *bin, size_t bin_len) 337255570Strasz{ 338255570Strasz unsigned char *hex, *tmp, ch; 339255570Strasz size_t hex_len; 340255570Strasz size_t i; 341255570Strasz 342255570Strasz hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ 343255570Strasz hex = malloc(hex_len); 344255570Strasz if (hex == NULL) 345255570Strasz log_err(1, "malloc"); 346255570Strasz 347255570Strasz tmp = hex; 348255570Strasz tmp += sprintf(tmp, "0x"); 349255570Strasz for (i = 0; i < bin_len; i++) { 350255570Strasz ch = bin[i]; 351255570Strasz tmp += sprintf(tmp, "%02x", ch); 352255570Strasz } 353255570Strasz 354255570Strasz return (hex); 355255570Strasz} 356255570Strasz 357255570Straszstatic void 358255570Straszlogin_compute_md5(const char id, const char *secret, 359255570Strasz const void *challenge, size_t challenge_len, void *response, 360255570Strasz size_t response_len) 361255570Strasz{ 362255570Strasz MD5_CTX ctx; 363255570Strasz int rv; 364255570Strasz 365255570Strasz assert(response_len == MD5_DIGEST_LENGTH); 366255570Strasz 367255570Strasz MD5_Init(&ctx); 368255570Strasz MD5_Update(&ctx, &id, sizeof(id)); 369255570Strasz MD5_Update(&ctx, secret, strlen(secret)); 370255570Strasz MD5_Update(&ctx, challenge, challenge_len); 371255570Strasz rv = MD5_Final(response, &ctx); 372255570Strasz if (rv != 1) 373255570Strasz log_errx(1, "MD5_Final"); 374255570Strasz} 375255570Strasz 376255570Strasz#define LOGIN_CHALLENGE_LEN 1024 377255570Strasz 378255570Straszstatic struct pdu * 379255570Straszlogin_receive_chap_a(struct connection *conn) 380255570Strasz{ 381255570Strasz struct pdu *request; 382255570Strasz struct keys *request_keys; 383255570Strasz const char *chap_a; 384255570Strasz 385255570Strasz request = login_receive(conn, false); 386255570Strasz request_keys = keys_new(); 387255570Strasz keys_load(request_keys, request); 388255570Strasz 389255570Strasz chap_a = keys_find(request_keys, "CHAP_A"); 390255570Strasz if (chap_a == NULL) { 391255570Strasz login_send_error(request, 0x02, 0x07); 392255570Strasz log_errx(1, "received CHAP Login PDU without CHAP_A"); 393255570Strasz } 394255570Strasz if (login_list_contains(chap_a, "5") == 0) { 395255570Strasz login_send_error(request, 0x02, 0x01); 396255570Strasz log_errx(1, "received CHAP Login PDU with unsupported CHAP_A " 397255570Strasz "\"%s\"", chap_a); 398255570Strasz } 399255570Strasz keys_delete(request_keys); 400255570Strasz 401255570Strasz return (request); 402255570Strasz} 403255570Strasz 404255570Straszstatic void 405255570Straszlogin_send_chap_c(struct pdu *request, const unsigned char id, 406255570Strasz const void *challenge, const size_t challenge_len) 407255570Strasz{ 408255570Strasz struct pdu *response; 409255570Strasz struct keys *response_keys; 410255570Strasz char *chap_c, chap_i[4]; 411255570Strasz 412255570Strasz chap_c = login_bin2hex(challenge, challenge_len); 413255570Strasz snprintf(chap_i, sizeof(chap_i), "%d", id); 414255570Strasz 415255570Strasz response = login_new_response(request); 416255570Strasz response_keys = keys_new(); 417255570Strasz keys_add(response_keys, "CHAP_A", "5"); 418255570Strasz keys_add(response_keys, "CHAP_I", chap_i); 419255570Strasz keys_add(response_keys, "CHAP_C", chap_c); 420255570Strasz free(chap_c); 421255570Strasz keys_save(response_keys, response); 422256192Strasz pdu_send(response); 423256192Strasz pdu_delete(response); 424255570Strasz keys_delete(response_keys); 425255570Strasz} 426255570Strasz 427255570Straszstatic struct pdu * 428255570Straszlogin_receive_chap_r(struct connection *conn, 429255570Strasz struct auth_group *ag, const unsigned char id, const void *challenge, 430255570Strasz const size_t challenge_len, const struct auth **cap) 431255570Strasz{ 432255570Strasz struct pdu *request; 433255570Strasz struct keys *request_keys; 434255570Strasz const char *chap_n, *chap_r; 435255570Strasz char *response_bin, expected_response_bin[MD5_DIGEST_LENGTH]; 436255570Strasz size_t response_bin_len; 437255570Strasz const struct auth *auth; 438255570Strasz int error; 439255570Strasz 440255570Strasz request = login_receive(conn, false); 441255570Strasz request_keys = keys_new(); 442255570Strasz keys_load(request_keys, request); 443255570Strasz 444255570Strasz chap_n = keys_find(request_keys, "CHAP_N"); 445255570Strasz if (chap_n == NULL) { 446255570Strasz login_send_error(request, 0x02, 0x07); 447255570Strasz log_errx(1, "received CHAP Login PDU without CHAP_N"); 448255570Strasz } 449255570Strasz chap_r = keys_find(request_keys, "CHAP_R"); 450255570Strasz if (chap_r == NULL) { 451255570Strasz login_send_error(request, 0x02, 0x07); 452255570Strasz log_errx(1, "received CHAP Login PDU without CHAP_R"); 453255570Strasz } 454255570Strasz error = login_hex2bin(chap_r, &response_bin, &response_bin_len); 455255570Strasz if (error != 0) { 456255570Strasz login_send_error(request, 0x02, 0x07); 457255570Strasz log_errx(1, "received CHAP Login PDU with malformed CHAP_R"); 458255570Strasz } 459255570Strasz 460255570Strasz /* 461255570Strasz * Verify the response. 462255570Strasz */ 463255570Strasz assert(ag->ag_type == AG_TYPE_CHAP || 464255570Strasz ag->ag_type == AG_TYPE_CHAP_MUTUAL); 465255570Strasz auth = auth_find(ag, chap_n); 466255570Strasz if (auth == NULL) { 467255570Strasz login_send_error(request, 0x02, 0x01); 468255570Strasz log_errx(1, "received CHAP Login with invalid user \"%s\"", 469255570Strasz chap_n); 470255570Strasz } 471255570Strasz 472255570Strasz assert(auth->a_secret != NULL); 473255570Strasz assert(strlen(auth->a_secret) > 0); 474255570Strasz login_compute_md5(id, auth->a_secret, challenge, 475255570Strasz challenge_len, expected_response_bin, 476255570Strasz sizeof(expected_response_bin)); 477255570Strasz 478255570Strasz if (memcmp(response_bin, expected_response_bin, 479255570Strasz sizeof(expected_response_bin)) != 0) { 480255570Strasz login_send_error(request, 0x02, 0x01); 481255570Strasz log_errx(1, "CHAP authentication failed for user \"%s\"", 482255570Strasz auth->a_user); 483255570Strasz } 484255570Strasz 485255570Strasz keys_delete(request_keys); 486255570Strasz free(response_bin); 487255570Strasz 488255570Strasz *cap = auth; 489255570Strasz return (request); 490255570Strasz} 491255570Strasz 492255570Straszstatic void 493255570Straszlogin_send_chap_success(struct pdu *request, 494255570Strasz const struct auth *auth) 495255570Strasz{ 496255570Strasz struct pdu *response; 497255570Strasz struct keys *request_keys, *response_keys; 498255570Strasz struct iscsi_bhs_login_response *bhslr2; 499255570Strasz const char *chap_i, *chap_c; 500255570Strasz char *chap_r, *challenge, response_bin[MD5_DIGEST_LENGTH]; 501255570Strasz size_t challenge_len; 502255570Strasz unsigned char id; 503255570Strasz int error; 504255570Strasz 505255570Strasz response = login_new_response(request); 506255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 507255570Strasz bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; 508255570Strasz login_set_nsg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); 509255570Strasz 510255570Strasz /* 511255570Strasz * Actually, one more thing: mutual authentication. 512255570Strasz */ 513255570Strasz request_keys = keys_new(); 514255570Strasz keys_load(request_keys, request); 515255570Strasz chap_i = keys_find(request_keys, "CHAP_I"); 516255570Strasz chap_c = keys_find(request_keys, "CHAP_C"); 517255570Strasz if (chap_i != NULL || chap_c != NULL) { 518255570Strasz if (chap_i == NULL) { 519255570Strasz login_send_error(request, 0x02, 0x07); 520255570Strasz log_errx(1, "initiator requested target " 521255570Strasz "authentication, but didn't send CHAP_I"); 522255570Strasz } 523255570Strasz if (chap_c == NULL) { 524255570Strasz login_send_error(request, 0x02, 0x07); 525255570Strasz log_errx(1, "initiator requested target " 526255570Strasz "authentication, but didn't send CHAP_C"); 527255570Strasz } 528255570Strasz if (auth->a_auth_group->ag_type != AG_TYPE_CHAP_MUTUAL) { 529255570Strasz login_send_error(request, 0x02, 0x01); 530255570Strasz log_errx(1, "initiator requests target authentication " 531255570Strasz "for user \"%s\", but mutual user/secret " 532255570Strasz "is not set", auth->a_user); 533255570Strasz } 534255570Strasz 535255570Strasz id = strtoul(chap_i, NULL, 10); 536255570Strasz error = login_hex2bin(chap_c, &challenge, &challenge_len); 537255570Strasz if (error != 0) { 538255570Strasz login_send_error(request, 0x02, 0x07); 539255570Strasz log_errx(1, "received CHAP Login PDU with malformed " 540255570Strasz "CHAP_C"); 541255570Strasz } 542255570Strasz 543255570Strasz log_debugx("performing mutual authentication as user \"%s\"", 544255570Strasz auth->a_mutual_user); 545255570Strasz login_compute_md5(id, auth->a_mutual_secret, challenge, 546255570Strasz challenge_len, response_bin, sizeof(response_bin)); 547255570Strasz 548255570Strasz chap_r = login_bin2hex(response_bin, 549255570Strasz sizeof(response_bin)); 550255570Strasz response_keys = keys_new(); 551255570Strasz keys_add(response_keys, "CHAP_N", auth->a_mutual_user); 552255570Strasz keys_add(response_keys, "CHAP_R", chap_r); 553255570Strasz free(chap_r); 554255570Strasz keys_save(response_keys, response); 555255570Strasz keys_delete(response_keys); 556255570Strasz } else { 557255570Strasz log_debugx("initiator did not request target authentication"); 558255570Strasz } 559255570Strasz 560255570Strasz keys_delete(request_keys); 561255570Strasz pdu_send(response); 562256192Strasz pdu_delete(response); 563255570Strasz} 564255570Strasz 565255570Straszstatic void 566255570Straszlogin_chap(struct connection *conn, struct auth_group *ag) 567255570Strasz{ 568255570Strasz const struct auth *auth; 569255570Strasz struct pdu *request; 570255570Strasz char challenge_bin[LOGIN_CHALLENGE_LEN]; 571255570Strasz unsigned char id; 572255570Strasz int rv; 573255570Strasz 574255570Strasz /* 575255570Strasz * Receive CHAP_A PDU. 576255570Strasz */ 577255570Strasz log_debugx("beginning CHAP authentication; waiting for CHAP_A"); 578255570Strasz request = login_receive_chap_a(conn); 579255570Strasz 580255570Strasz /* 581255570Strasz * Generate the challenge. 582255570Strasz */ 583255570Strasz rv = RAND_bytes(challenge_bin, sizeof(challenge_bin)); 584255570Strasz if (rv != 1) { 585255570Strasz login_send_error(request, 0x03, 0x02); 586255570Strasz log_errx(1, "RAND_bytes failed: %s", 587255570Strasz ERR_error_string(ERR_get_error(), NULL)); 588255570Strasz } 589255570Strasz rv = RAND_bytes(&id, sizeof(id)); 590255570Strasz if (rv != 1) { 591255570Strasz login_send_error(request, 0x03, 0x02); 592255570Strasz log_errx(1, "RAND_bytes failed: %s", 593255570Strasz ERR_error_string(ERR_get_error(), NULL)); 594255570Strasz } 595255570Strasz 596255570Strasz /* 597255570Strasz * Send the challenge. 598255570Strasz */ 599255570Strasz log_debugx("sending CHAP_C, binary challenge size is %zd bytes", 600255570Strasz sizeof(challenge_bin)); 601255570Strasz login_send_chap_c(request, id, challenge_bin, 602255570Strasz sizeof(challenge_bin)); 603255570Strasz pdu_delete(request); 604255570Strasz 605255570Strasz /* 606255570Strasz * Receive CHAP_N/CHAP_R PDU and authenticate. 607255570Strasz */ 608255570Strasz log_debugx("waiting for CHAP_N/CHAP_R"); 609255570Strasz request = login_receive_chap_r(conn, ag, id, challenge_bin, 610255570Strasz sizeof(challenge_bin), &auth); 611255570Strasz 612255570Strasz /* 613255570Strasz * Yay, authentication succeeded! 614255570Strasz */ 615255570Strasz log_debugx("authentication succeeded for user \"%s\"; " 616255570Strasz "transitioning to Negotiation Phase", auth->a_user); 617255570Strasz login_send_chap_success(request, auth); 618255570Strasz pdu_delete(request); 619255570Strasz} 620255570Strasz 621255570Straszstatic void 622255570Straszlogin_negotiate_key(struct pdu *request, const char *name, 623255570Strasz const char *value, bool skipped_security, struct keys *response_keys) 624255570Strasz{ 625255570Strasz int which, tmp; 626255570Strasz struct connection *conn; 627255570Strasz 628255570Strasz conn = request->pdu_connection; 629255570Strasz 630255570Strasz if (strcmp(name, "InitiatorName") == 0) { 631255570Strasz if (!skipped_security) 632255570Strasz log_errx(1, "initiator resent InitiatorName"); 633255570Strasz } else if (strcmp(name, "SessionType") == 0) { 634255570Strasz if (!skipped_security) 635255570Strasz log_errx(1, "initiator resent SessionType"); 636255570Strasz } else if (strcmp(name, "TargetName") == 0) { 637255570Strasz if (!skipped_security) 638255570Strasz log_errx(1, "initiator resent TargetName"); 639255570Strasz } else if (strcmp(name, "InitiatorAlias") == 0) { 640255570Strasz if (conn->conn_initiator_alias != NULL) 641255570Strasz free(conn->conn_initiator_alias); 642255570Strasz conn->conn_initiator_alias = checked_strdup(value); 643255570Strasz } else if (strcmp(value, "Irrelevant") == 0) { 644255570Strasz /* Ignore. */ 645255570Strasz } else if (strcmp(name, "HeaderDigest") == 0) { 646255570Strasz /* 647255570Strasz * We don't handle digests for discovery sessions. 648255570Strasz */ 649255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { 650255570Strasz log_debugx("discovery session; digests disabled"); 651255570Strasz keys_add(response_keys, name, "None"); 652255570Strasz return; 653255570Strasz } 654255570Strasz 655255570Strasz which = login_list_prefers(value, "CRC32C", "None"); 656255570Strasz switch (which) { 657255570Strasz case 1: 658255570Strasz log_debugx("initiator prefers CRC32C " 659255570Strasz "for header digest; we'll use it"); 660255570Strasz conn->conn_header_digest = CONN_DIGEST_CRC32C; 661255570Strasz keys_add(response_keys, name, "CRC32C"); 662255570Strasz break; 663255570Strasz case 2: 664255570Strasz log_debugx("initiator prefers not to do " 665255570Strasz "header digest; we'll comply"); 666255570Strasz keys_add(response_keys, name, "None"); 667255570Strasz break; 668255570Strasz default: 669255570Strasz log_warnx("initiator sent unrecognized " 670255570Strasz "HeaderDigest value \"%s\"; will use None", value); 671255570Strasz keys_add(response_keys, name, "None"); 672255570Strasz break; 673255570Strasz } 674255570Strasz } else if (strcmp(name, "DataDigest") == 0) { 675255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { 676255570Strasz log_debugx("discovery session; digests disabled"); 677255570Strasz keys_add(response_keys, name, "None"); 678255570Strasz return; 679255570Strasz } 680255570Strasz 681255570Strasz which = login_list_prefers(value, "CRC32C", "None"); 682255570Strasz switch (which) { 683255570Strasz case 1: 684255570Strasz log_debugx("initiator prefers CRC32C " 685255570Strasz "for data digest; we'll use it"); 686255570Strasz conn->conn_data_digest = CONN_DIGEST_CRC32C; 687255570Strasz keys_add(response_keys, name, "CRC32C"); 688255570Strasz break; 689255570Strasz case 2: 690255570Strasz log_debugx("initiator prefers not to do " 691255570Strasz "data digest; we'll comply"); 692255570Strasz keys_add(response_keys, name, "None"); 693255570Strasz break; 694255570Strasz default: 695255570Strasz log_warnx("initiator sent unrecognized " 696255570Strasz "DataDigest value \"%s\"; will use None", value); 697255570Strasz keys_add(response_keys, name, "None"); 698255570Strasz break; 699255570Strasz } 700255570Strasz } else if (strcmp(name, "MaxConnections") == 0) { 701255570Strasz keys_add(response_keys, name, "1"); 702255570Strasz } else if (strcmp(name, "InitialR2T") == 0) { 703255570Strasz keys_add(response_keys, name, "Yes"); 704255570Strasz } else if (strcmp(name, "ImmediateData") == 0) { 705255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { 706255570Strasz log_debugx("discovery session; ImmediateData irrelevant"); 707255570Strasz keys_add(response_keys, name, "Irrelevant"); 708255570Strasz } else { 709255570Strasz if (strcmp(value, "Yes") == 0) { 710255570Strasz conn->conn_immediate_data = true; 711255570Strasz keys_add(response_keys, name, "Yes"); 712255570Strasz } else { 713255570Strasz conn->conn_immediate_data = false; 714255570Strasz keys_add(response_keys, name, "No"); 715255570Strasz } 716255570Strasz } 717255570Strasz } else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) { 718255570Strasz tmp = strtoul(value, NULL, 10); 719255570Strasz if (tmp <= 0) { 720255570Strasz login_send_error(request, 0x02, 0x00); 721255570Strasz log_errx(1, "received invalid " 722255570Strasz "MaxRecvDataSegmentLength"); 723255570Strasz } 724255570Strasz if (tmp > MAX_DATA_SEGMENT_LENGTH) { 725255570Strasz log_debugx("capping MaxDataSegmentLength from %d to %d", 726255570Strasz tmp, MAX_DATA_SEGMENT_LENGTH); 727255570Strasz tmp = MAX_DATA_SEGMENT_LENGTH; 728255570Strasz } 729255570Strasz conn->conn_max_data_segment_length = tmp; 730255570Strasz keys_add_int(response_keys, name, tmp); 731255570Strasz } else if (strcmp(name, "MaxBurstLength") == 0) { 732255570Strasz tmp = strtoul(value, NULL, 10); 733255570Strasz if (tmp <= 0) { 734255570Strasz login_send_error(request, 0x02, 0x00); 735255570Strasz log_errx(1, "received invalid MaxBurstLength"); 736255570Strasz } 737255570Strasz if (tmp > MAX_BURST_LENGTH) { 738255570Strasz log_debugx("capping MaxBurstLength from %d to %d", 739255570Strasz tmp, MAX_BURST_LENGTH); 740255570Strasz tmp = MAX_BURST_LENGTH; 741255570Strasz } 742255570Strasz conn->conn_max_burst_length = tmp; 743255570Strasz keys_add(response_keys, name, value); 744255570Strasz } else if (strcmp(name, "FirstBurstLength") == 0) { 745255570Strasz tmp = strtoul(value, NULL, 10); 746255570Strasz if (tmp <= 0) { 747255570Strasz login_send_error(request, 0x02, 0x00); 748255570Strasz log_errx(1, "received invalid " 749255570Strasz "FirstBurstLength"); 750255570Strasz } 751255570Strasz if (tmp > MAX_DATA_SEGMENT_LENGTH) { 752255570Strasz log_debugx("capping FirstBurstLength from %d to %d", 753255570Strasz tmp, MAX_DATA_SEGMENT_LENGTH); 754255570Strasz tmp = MAX_DATA_SEGMENT_LENGTH; 755255570Strasz } 756255570Strasz /* 757255570Strasz * We don't pass the value to the kernel; it only enforces 758255570Strasz * hardcoded limit anyway. 759255570Strasz */ 760255570Strasz keys_add_int(response_keys, name, tmp); 761255570Strasz } else if (strcmp(name, "DefaultTime2Wait") == 0) { 762255570Strasz keys_add(response_keys, name, value); 763255570Strasz } else if (strcmp(name, "DefaultTime2Retain") == 0) { 764255570Strasz keys_add(response_keys, name, "0"); 765255570Strasz } else if (strcmp(name, "MaxOutstandingR2T") == 0) { 766255570Strasz keys_add(response_keys, name, "1"); 767255570Strasz } else if (strcmp(name, "DataPDUInOrder") == 0) { 768255570Strasz keys_add(response_keys, name, "Yes"); 769255570Strasz } else if (strcmp(name, "DataSequenceInOrder") == 0) { 770255570Strasz keys_add(response_keys, name, "Yes"); 771255570Strasz } else if (strcmp(name, "ErrorRecoveryLevel") == 0) { 772255570Strasz keys_add(response_keys, name, "0"); 773255570Strasz } else if (strcmp(name, "OFMarker") == 0) { 774255570Strasz keys_add(response_keys, name, "No"); 775255570Strasz } else if (strcmp(name, "IFMarker") == 0) { 776255570Strasz keys_add(response_keys, name, "No"); 777255570Strasz } else { 778255570Strasz log_debugx("unknown key \"%s\"; responding " 779255570Strasz "with NotUnderstood", name); 780255570Strasz keys_add(response_keys, name, "NotUnderstood"); 781255570Strasz } 782255570Strasz} 783255570Strasz 784255570Straszstatic void 785255570Straszlogin_negotiate(struct connection *conn, struct pdu *request) 786255570Strasz{ 787255570Strasz struct pdu *response; 788255570Strasz struct iscsi_bhs_login_response *bhslr2; 789255570Strasz struct keys *request_keys, *response_keys; 790255570Strasz int i; 791255570Strasz bool skipped_security; 792255570Strasz 793255570Strasz if (request == NULL) { 794255570Strasz log_debugx("beginning parameter negotiation; " 795255570Strasz "waiting for Login PDU"); 796255570Strasz request = login_receive(conn, false); 797255570Strasz skipped_security = false; 798255570Strasz } else 799255570Strasz skipped_security = true; 800255570Strasz 801255570Strasz request_keys = keys_new(); 802255570Strasz keys_load(request_keys, request); 803255570Strasz 804255570Strasz response = login_new_response(request); 805255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 806255570Strasz bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; 807255570Strasz bhslr2->bhslr_tsih = htons(0xbadd); 808255570Strasz login_set_csg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); 809255570Strasz login_set_nsg(response, BHSLR_STAGE_FULL_FEATURE_PHASE); 810255570Strasz response_keys = keys_new(); 811255570Strasz for (i = 0; i < KEYS_MAX; i++) { 812255570Strasz if (request_keys->keys_names[i] == NULL) 813255570Strasz break; 814255570Strasz 815255570Strasz login_negotiate_key(request, request_keys->keys_names[i], 816255570Strasz request_keys->keys_values[i], skipped_security, 817255570Strasz response_keys); 818255570Strasz } 819255570Strasz 820255570Strasz log_debugx("parameter negotiation done; " 821255570Strasz "transitioning to Full Feature Phase"); 822255570Strasz 823255570Strasz keys_save(response_keys, response); 824255570Strasz pdu_send(response); 825255570Strasz pdu_delete(response); 826255570Strasz keys_delete(response_keys); 827255570Strasz pdu_delete(request); 828255570Strasz keys_delete(request_keys); 829255570Strasz} 830255570Strasz 831255570Straszvoid 832255570Straszlogin(struct connection *conn) 833255570Strasz{ 834255570Strasz struct pdu *request, *response; 835255570Strasz struct iscsi_bhs_login_request *bhslr; 836255570Strasz struct iscsi_bhs_login_response *bhslr2; 837255570Strasz struct keys *request_keys, *response_keys; 838255570Strasz struct auth_group *ag; 839255570Strasz const char *initiator_name, *initiator_alias, *session_type, 840255570Strasz *target_name, *auth_method; 841255570Strasz char *portal_group_tag; 842255570Strasz int rv; 843255570Strasz 844255570Strasz /* 845255570Strasz * Handle the initial Login Request - figure out required authentication 846255570Strasz * method and either transition to the next phase, if no authentication 847255570Strasz * is required, or call appropriate authentication code. 848255570Strasz */ 849255570Strasz log_debugx("beginning Login Phase; waiting for Login PDU"); 850255570Strasz request = login_receive(conn, true); 851255570Strasz bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; 852255570Strasz if (bhslr->bhslr_tsih != 0) { 853255570Strasz login_send_error(request, 0x02, 0x0a); 854255570Strasz log_errx(1, "received Login PDU with non-zero TSIH"); 855255570Strasz } 856255570Strasz 857255570Strasz /* 858255570Strasz * XXX: Implement the C flag some day. 859255570Strasz */ 860255570Strasz request_keys = keys_new(); 861255570Strasz keys_load(request_keys, request); 862255570Strasz 863255570Strasz assert(conn->conn_initiator_name == NULL); 864255570Strasz initiator_name = keys_find(request_keys, "InitiatorName"); 865255570Strasz if (initiator_name == NULL) { 866255570Strasz login_send_error(request, 0x02, 0x07); 867255570Strasz log_errx(1, "received Login PDU without InitiatorName"); 868255570Strasz } 869255570Strasz if (valid_iscsi_name(initiator_name) == false) { 870255570Strasz login_send_error(request, 0x02, 0x00); 871255570Strasz log_errx(1, "received Login PDU with invalid InitiatorName"); 872255570Strasz } 873255570Strasz conn->conn_initiator_name = checked_strdup(initiator_name); 874255570Strasz log_set_peer_name(conn->conn_initiator_name); 875255570Strasz /* 876255570Strasz * XXX: This doesn't work (does nothing) because of Capsicum. 877255570Strasz */ 878255570Strasz setproctitle("%s (%s)", conn->conn_initiator_addr, conn->conn_initiator_name); 879255570Strasz 880255570Strasz initiator_alias = keys_find(request_keys, "InitiatorAlias"); 881255570Strasz if (initiator_alias != NULL) 882255570Strasz conn->conn_initiator_alias = checked_strdup(initiator_alias); 883255570Strasz 884255570Strasz assert(conn->conn_session_type == CONN_SESSION_TYPE_NONE); 885255570Strasz session_type = keys_find(request_keys, "SessionType"); 886255570Strasz if (session_type != NULL) { 887255570Strasz if (strcmp(session_type, "Normal") == 0) { 888255570Strasz conn->conn_session_type = CONN_SESSION_TYPE_NORMAL; 889255570Strasz } else if (strcmp(session_type, "Discovery") == 0) { 890255570Strasz conn->conn_session_type = CONN_SESSION_TYPE_DISCOVERY; 891255570Strasz } else { 892255570Strasz login_send_error(request, 0x02, 0x00); 893255570Strasz log_errx(1, "received Login PDU with invalid " 894255570Strasz "SessionType \"%s\"", session_type); 895255570Strasz } 896255570Strasz } else 897255570Strasz conn->conn_session_type = CONN_SESSION_TYPE_NORMAL; 898255570Strasz 899255570Strasz assert(conn->conn_target == NULL); 900255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { 901255570Strasz target_name = keys_find(request_keys, "TargetName"); 902255570Strasz if (target_name == NULL) { 903255570Strasz login_send_error(request, 0x02, 0x07); 904255570Strasz log_errx(1, "received Login PDU without TargetName"); 905255570Strasz } 906255570Strasz 907255570Strasz conn->conn_target = 908255570Strasz target_find(conn->conn_portal->p_portal_group->pg_conf, 909255570Strasz target_name); 910255570Strasz if (conn->conn_target == NULL) { 911255570Strasz login_send_error(request, 0x02, 0x03); 912255570Strasz log_errx(1, "requested target \"%s\" not found", 913255570Strasz target_name); 914255570Strasz } 915255570Strasz } 916255570Strasz 917255570Strasz /* 918255570Strasz * At this point we know what kind of authentication we need. 919255570Strasz */ 920255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { 921255570Strasz ag = conn->conn_target->t_auth_group; 922255570Strasz if (ag->ag_name != NULL) { 923255570Strasz log_debugx("initiator requests to connect " 924255570Strasz "to target \"%s\"; auth-group \"%s\"", 925255570Strasz conn->conn_target->t_iqn, 926255570Strasz conn->conn_target->t_auth_group->ag_name); 927255570Strasz } else { 928255570Strasz log_debugx("initiator requests to connect " 929255570Strasz "to target \"%s\"", conn->conn_target->t_iqn); 930255570Strasz } 931255570Strasz } else { 932255570Strasz assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY); 933255570Strasz ag = conn->conn_portal->p_portal_group->pg_discovery_auth_group; 934255570Strasz if (ag->ag_name != NULL) { 935255570Strasz log_debugx("initiator requests " 936255570Strasz "discovery session; auth-group \"%s\"", ag->ag_name); 937255570Strasz } else { 938255570Strasz log_debugx("initiator requests discovery session"); 939255570Strasz } 940255570Strasz } 941255570Strasz 942255570Strasz /* 943255570Strasz * Let's see if the initiator intends to do any kind of authentication 944255570Strasz * at all. 945255570Strasz */ 946255570Strasz if (login_csg(request) == BHSLR_STAGE_OPERATIONAL_NEGOTIATION) { 947255570Strasz if (ag->ag_type != AG_TYPE_NO_AUTHENTICATION) { 948255570Strasz login_send_error(request, 0x02, 0x01); 949255570Strasz log_errx(1, "initiator skipped the authentication, " 950255570Strasz "but authentication is required"); 951255570Strasz } 952255570Strasz 953255570Strasz keys_delete(request_keys); 954255570Strasz 955255570Strasz log_debugx("initiator skipped the authentication, " 956255570Strasz "and we don't need it; proceeding with negotiation"); 957255570Strasz login_negotiate(conn, request); 958255570Strasz return; 959255570Strasz } 960255570Strasz 961255570Strasz if (ag->ag_type == AG_TYPE_NO_AUTHENTICATION) { 962255570Strasz /* 963255570Strasz * Initiator might want to to authenticate, 964255570Strasz * but we don't need it. 965255570Strasz */ 966255570Strasz log_debugx("authentication not required; " 967255570Strasz "transitioning to parameter negotiation"); 968255570Strasz 969255570Strasz if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) == 0) 970255570Strasz log_warnx("initiator did not set the \"T\" flag; " 971255570Strasz "transitioning anyway"); 972255570Strasz 973255570Strasz response = login_new_response(request); 974255570Strasz bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; 975255570Strasz bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; 976255570Strasz login_set_nsg(response, 977255570Strasz BHSLR_STAGE_OPERATIONAL_NEGOTIATION); 978255570Strasz response_keys = keys_new(); 979255570Strasz /* 980255570Strasz * Required by Linux initiator. 981255570Strasz */ 982255570Strasz auth_method = keys_find(request_keys, "AuthMethod"); 983255570Strasz if (auth_method != NULL && 984255570Strasz login_list_contains(auth_method, "None")) 985255570Strasz keys_add(response_keys, "AuthMethod", "None"); 986255570Strasz 987255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { 988255570Strasz if (conn->conn_target->t_alias != NULL) 989255570Strasz keys_add(response_keys, 990255570Strasz "TargetAlias", conn->conn_target->t_alias); 991255570Strasz rv = asprintf(&portal_group_tag, "%d", 992255570Strasz conn->conn_portal->p_portal_group->pg_tag); 993255570Strasz if (rv <= 0) 994255570Strasz log_err(1, "asprintf"); 995255570Strasz keys_add(response_keys, 996255570Strasz "TargetPortalGroupTag", portal_group_tag); 997255570Strasz free(portal_group_tag); 998255570Strasz } 999255570Strasz keys_save(response_keys, response); 1000255570Strasz pdu_send(response); 1001255570Strasz pdu_delete(response); 1002255570Strasz keys_delete(response_keys); 1003255570Strasz pdu_delete(request); 1004255570Strasz keys_delete(request_keys); 1005255570Strasz 1006255570Strasz login_negotiate(conn, NULL); 1007255570Strasz return; 1008255570Strasz } 1009255570Strasz 1010259333Strasz if (ag->ag_type == AG_TYPE_UNKNOWN) { 1011259333Strasz /* 1012259333Strasz * This can happen with empty auth-group. 1013259333Strasz */ 1014259333Strasz login_send_error(request, 0x02, 0x01); 1015259333Strasz log_errx(1, "auth-group type not set, denying access"); 1016259333Strasz } 1017259333Strasz 1018255570Strasz log_debugx("CHAP authentication required"); 1019255570Strasz 1020255570Strasz auth_method = keys_find(request_keys, "AuthMethod"); 1021255570Strasz if (auth_method == NULL) { 1022255570Strasz login_send_error(request, 0x02, 0x07); 1023255570Strasz log_errx(1, "received Login PDU without AuthMethod"); 1024255570Strasz } 1025255570Strasz /* 1026255570Strasz * XXX: This should be Reject, not just a login failure (5.3.2). 1027255570Strasz */ 1028255570Strasz if (login_list_contains(auth_method, "CHAP") == 0) { 1029255570Strasz login_send_error(request, 0x02, 0x01); 1030255570Strasz log_errx(1, "initiator requests unsupported AuthMethod \"%s\" " 1031255570Strasz "instead of \"CHAP\"", auth_method); 1032255570Strasz } 1033255570Strasz 1034255570Strasz response = login_new_response(request); 1035255570Strasz 1036255570Strasz response_keys = keys_new(); 1037255570Strasz keys_add(response_keys, "AuthMethod", "CHAP"); 1038255570Strasz if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { 1039255570Strasz rv = asprintf(&portal_group_tag, "%d", 1040255570Strasz conn->conn_portal->p_portal_group->pg_tag); 1041255570Strasz if (rv <= 0) 1042255570Strasz log_err(1, "asprintf"); 1043255570Strasz keys_add(response_keys, 1044255570Strasz "TargetPortalGroupTag", portal_group_tag); 1045255570Strasz free(portal_group_tag); 1046255570Strasz if (conn->conn_target->t_alias != NULL) 1047255570Strasz keys_add(response_keys, 1048255570Strasz "TargetAlias", conn->conn_target->t_alias); 1049255570Strasz } 1050255570Strasz keys_save(response_keys, response); 1051255570Strasz 1052255570Strasz pdu_send(response); 1053255570Strasz pdu_delete(response); 1054255570Strasz keys_delete(response_keys); 1055255570Strasz pdu_delete(request); 1056255570Strasz keys_delete(request_keys); 1057255570Strasz 1058255570Strasz login_chap(conn, ag); 1059255570Strasz 1060255570Strasz login_negotiate(conn, NULL); 1061255570Strasz} 1062