Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Nicholas Bellinger | 5350 | 92.00% | 19 | 33.93% |
Dmitry Bogdanov | 184 | 3.16% | 5 | 8.93% |
Maurizio Lombardi | 137 | 2.36% | 3 | 5.36% |
Max Gurtovoy | 30 | 0.52% | 3 | 5.36% |
Andy Grover | 26 | 0.45% | 3 | 5.36% |
Hou Pu | 22 | 0.38% | 2 | 3.57% |
Florian Westphal | 16 | 0.28% | 1 | 1.79% |
Roland Dreier | 11 | 0.19% | 2 | 3.57% |
Christoph Hellwig | 6 | 0.10% | 3 | 5.36% |
Yang Yingliang | 5 | 0.09% | 1 | 1.79% |
Peilin Ye | 5 | 0.09% | 1 | 1.79% |
Varun Prakash | 5 | 0.09% | 1 | 1.79% |
Satoru Moriya | 3 | 0.05% | 1 | 1.79% |
Ingo Molnar | 3 | 0.05% | 1 | 1.79% |
Thomas Gleixner | 2 | 0.03% | 1 | 1.79% |
Christophe Vu-Brugier | 2 | 0.03% | 1 | 1.79% |
Bart Van Assche | 1 | 0.02% | 1 | 1.79% |
Hariprasad Kelam | 1 | 0.02% | 1 | 1.79% |
Joe Perches | 1 | 0.02% | 1 | 1.79% |
Li Yang | 1 | 0.02% | 1 | 1.79% |
Sagi Grimberg | 1 | 0.02% | 1 | 1.79% |
Kees Cook | 1 | 0.02% | 1 | 1.79% |
Masanari Iida | 1 | 0.02% | 1 | 1.79% |
Eric Seppanen | 1 | 0.02% | 1 | 1.79% |
Total | 5815 | 56 |
// SPDX-License-Identifier: GPL-2.0-or-later /******************************************************************************* * This file contains main functions related to iSCSI Parameter negotiation. * * (c) Copyright 2007-2013 Datera, Inc. * * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> * ******************************************************************************/ #include <linux/ctype.h> #include <linux/kthread.h> #include <linux/slab.h> #include <linux/sched/signal.h> #include <net/sock.h> #include <trace/events/sock.h> #include <scsi/iscsi_proto.h> #include <target/target_core_base.h> #include <target/target_core_fabric.h> #include <target/iscsi/iscsi_transport.h> #include <target/iscsi/iscsi_target_core.h> #include "iscsi_target_parameters.h" #include "iscsi_target_login.h" #include "iscsi_target_nego.h" #include "iscsi_target_tpg.h" #include "iscsi_target_util.h" #include "iscsi_target.h" #include "iscsi_target_auth.h" #define MAX_LOGIN_PDUS 7 void convert_null_to_semi(char *buf, int len) { int i; for (i = 0; i < len; i++) if (buf[i] == '\0') buf[i] = ';'; } static int strlen_semi(char *buf) { int i = 0; while (buf[i] != '\0') { if (buf[i] == ';') return i; i++; } return -1; } int extract_param( const char *in_buf, const char *pattern, unsigned int max_length, char *out_buf, unsigned char *type) { char *ptr; int len; if (!in_buf || !pattern || !out_buf || !type) return -EINVAL; ptr = strstr(in_buf, pattern); if (!ptr) return -ENOENT; ptr = strstr(ptr, "="); if (!ptr) return -EINVAL; ptr += 1; if (*ptr == '0' && (*(ptr+1) == 'x' || *(ptr+1) == 'X')) { ptr += 2; /* skip 0x */ *type = HEX; } else if (*ptr == '0' && (*(ptr+1) == 'b' || *(ptr+1) == 'B')) { ptr += 2; /* skip 0b */ *type = BASE64; } else *type = DECIMAL; len = strlen_semi(ptr); if (len < 0) return -EINVAL; if (len >= max_length) { pr_err("Length of input: %d exceeds max_length:" " %d\n", len, max_length); return -EINVAL; } memcpy(out_buf, ptr, len); out_buf[len] = '\0'; return 0; } static struct iscsi_node_auth *iscsi_get_node_auth(struct iscsit_conn *conn) { struct iscsi_portal_group *tpg; struct iscsi_node_acl *nacl; struct se_node_acl *se_nacl; if (conn->sess->sess_ops->SessionType) return &iscsit_global->discovery_acl.node_auth; se_nacl = conn->sess->se_sess->se_node_acl; if (!se_nacl) { pr_err("Unable to locate struct se_node_acl for CHAP auth\n"); return NULL; } if (se_nacl->dynamic_node_acl) { tpg = to_iscsi_tpg(se_nacl->se_tpg); return &tpg->tpg_demo_auth; } nacl = to_iscsi_nacl(se_nacl); return &nacl->node_auth; } static u32 iscsi_handle_authentication( struct iscsit_conn *conn, char *in_buf, char *out_buf, int in_length, int *out_length, unsigned char *authtype) { struct iscsi_node_auth *auth; auth = iscsi_get_node_auth(conn); if (!auth) return -1; if (strstr("CHAP", authtype)) strcpy(conn->sess->auth_type, "CHAP"); else strcpy(conn->sess->auth_type, NONE); if (strstr("None", authtype)) return 1; else if (strstr("CHAP", authtype)) return chap_main_loop(conn, auth, in_buf, out_buf, &in_length, out_length); /* SRP, SPKM1, SPKM2 and KRB5 are unsupported */ return 2; } static void iscsi_remove_failed_auth_entry(struct iscsit_conn *conn) { kfree(conn->auth_protocol); } int iscsi_target_check_login_request( struct iscsit_conn *conn, struct iscsi_login *login) { int req_csg, req_nsg; u32 payload_length; struct iscsi_login_req *login_req; login_req = (struct iscsi_login_req *) login->req; payload_length = ntoh24(login_req->dlength); switch (login_req->opcode & ISCSI_OPCODE_MASK) { case ISCSI_OP_LOGIN: break; default: pr_err("Received unknown opcode 0x%02x.\n", login_req->opcode & ISCSI_OPCODE_MASK); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_INIT_ERR); return -1; } if ((login_req->flags & ISCSI_FLAG_LOGIN_CONTINUE) && (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) { pr_err("Login request has both ISCSI_FLAG_LOGIN_CONTINUE" " and ISCSI_FLAG_LOGIN_TRANSIT set, protocol error.\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_INIT_ERR); return -1; } req_csg = ISCSI_LOGIN_CURRENT_STAGE(login_req->flags); req_nsg = ISCSI_LOGIN_NEXT_STAGE(login_req->flags); if (req_csg != login->current_stage) { pr_err("Initiator unexpectedly changed login stage" " from %d to %d, login failed.\n", login->current_stage, req_csg); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_INIT_ERR); return -1; } if ((req_nsg == 2) || (req_csg >= 2) || ((login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT) && (req_nsg <= req_csg))) { pr_err("Illegal login_req->flags Combination, CSG: %d," " NSG: %d, ISCSI_FLAG_LOGIN_TRANSIT: %d.\n", req_csg, req_nsg, (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_INIT_ERR); return -1; } if ((login_req->max_version != login->version_max) || (login_req->min_version != login->version_min)) { pr_err("Login request changed Version Max/Nin" " unexpectedly to 0x%02x/0x%02x, protocol error\n", login_req->max_version, login_req->min_version); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_INIT_ERR); return -1; } if (memcmp(login_req->isid, login->isid, 6) != 0) { pr_err("Login request changed ISID unexpectedly," " protocol error.\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_INIT_ERR); return -1; } if (login_req->itt != login->init_task_tag) { pr_err("Login request changed ITT unexpectedly to" " 0x%08x, protocol error.\n", login_req->itt); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_INIT_ERR); return -1; } if (payload_length > MAX_KEY_VALUE_PAIRS) { pr_err("Login request payload exceeds default" " MaxRecvDataSegmentLength: %u, protocol error.\n", MAX_KEY_VALUE_PAIRS); return -1; } return 0; } EXPORT_SYMBOL(iscsi_target_check_login_request); static int iscsi_target_check_first_request( struct iscsit_conn *conn, struct iscsi_login *login) { struct iscsi_param *param = NULL; struct se_node_acl *se_nacl; login->first_request = 0; list_for_each_entry(param, &conn->param_list->param_list, p_list) { if (!strncmp(param->name, SESSIONTYPE, 11)) { if (!IS_PSTATE_ACCEPTOR(param)) { pr_err("SessionType key not received" " in first login request.\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_MISSING_FIELDS); return -1; } if (!strncmp(param->value, DISCOVERY, 9)) return 0; } if (!strncmp(param->name, INITIATORNAME, 13)) { if (!IS_PSTATE_ACCEPTOR(param)) { if (!login->leading_connection) continue; pr_err("InitiatorName key not received" " in first login request.\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_MISSING_FIELDS); return -1; } /* * For non-leading connections, double check that the * received InitiatorName matches the existing session's * struct iscsi_node_acl. */ if (!login->leading_connection) { se_nacl = conn->sess->se_sess->se_node_acl; if (!se_nacl) { pr_err("Unable to locate" " struct se_node_acl\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_TGT_NOT_FOUND); return -1; } if (strcmp(param->value, se_nacl->initiatorname)) { pr_err("Incorrect" " InitiatorName: %s for this" " iSCSI Initiator Node.\n", param->value); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_TGT_NOT_FOUND); return -1; } } } } return 0; } static int iscsi_target_do_tx_login_io(struct iscsit_conn *conn, struct iscsi_login *login) { u32 padding = 0; struct iscsi_login_rsp *login_rsp; login_rsp = (struct iscsi_login_rsp *) login->rsp; login_rsp->opcode = ISCSI_OP_LOGIN_RSP; hton24(login_rsp->dlength, login->rsp_length); memcpy(login_rsp->isid, login->isid, 6); login_rsp->tsih = cpu_to_be16(login->tsih); login_rsp->itt = login->init_task_tag; login_rsp->statsn = cpu_to_be32(conn->stat_sn++); login_rsp->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); login_rsp->max_cmdsn = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn)); pr_debug("Sending Login Response, Flags: 0x%02x, ITT: 0x%08x," " ExpCmdSN; 0x%08x, MaxCmdSN: 0x%08x, StatSN: 0x%08x, Length:" " %u\n", login_rsp->flags, (__force u32)login_rsp->itt, ntohl(login_rsp->exp_cmdsn), ntohl(login_rsp->max_cmdsn), ntohl(login_rsp->statsn), login->rsp_length); padding = ((-login->rsp_length) & 3); /* * Before sending the last login response containing the transition * bit for full-feature-phase, go ahead and start up TX/RX threads * now to avoid potential resource allocation failures after the * final login response has been sent. */ if (login->login_complete) { int rc = iscsit_start_kthreads(conn); if (rc) { iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, ISCSI_LOGIN_STATUS_NO_RESOURCES); return -1; } } if (conn->conn_transport->iscsit_put_login_tx(conn, login, login->rsp_length + padding) < 0) goto err; login->rsp_length = 0; return 0; err: if (login->login_complete) { if (conn->rx_thread && conn->rx_thread_active) { send_sig(SIGINT, conn->rx_thread, 1); complete(&conn->rx_login_comp); kthread_stop(conn->rx_thread); } if (conn->tx_thread && conn->tx_thread_active) { send_sig(SIGINT, conn->tx_thread, 1); kthread_stop(conn->tx_thread); } spin_lock(&iscsit_global->ts_bitmap_lock); bitmap_release_region(iscsit_global->ts_bitmap, conn->bitmap_id, get_order(1)); spin_unlock(&iscsit_global->ts_bitmap_lock); } return -1; } static void iscsi_target_sk_data_ready(struct sock *sk) { struct iscsit_conn *conn = sk->sk_user_data; bool rc; trace_sk_data_ready(sk); pr_debug("Entering iscsi_target_sk_data_ready: conn: %p\n", conn); write_lock_bh(&sk->sk_callback_lock); if (!sk->sk_user_data) { write_unlock_bh(&sk->sk_callback_lock); return; } if (!test_bit(LOGIN_FLAGS_READY, &conn->login_flags)) { write_unlock_bh(&sk->sk_callback_lock); pr_debug("Got LOGIN_FLAGS_READY=0, conn: %p >>>>\n", conn); return; } if (test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) { write_unlock_bh(&sk->sk_callback_lock); pr_debug("Got LOGIN_FLAGS_CLOSED=1, conn: %p >>>>\n", conn); return; } if (test_and_set_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags)) { write_unlock_bh(&sk->sk_callback_lock); pr_debug("Got LOGIN_FLAGS_READ_ACTIVE=1, conn: %p >>>>\n", conn); if (iscsi_target_sk_data_ready == conn->orig_data_ready) return; conn->orig_data_ready(sk); return; } rc = schedule_delayed_work(&conn->login_work, 0); if (!rc) { pr_debug("iscsi_target_sk_data_ready, schedule_delayed_work" " got false\n"); } write_unlock_bh(&sk->sk_callback_lock); } static void iscsi_target_sk_state_change(struct sock *); static void iscsi_target_set_sock_callbacks(struct iscsit_conn *conn) { struct sock *sk; if (!conn->sock) return; sk = conn->sock->sk; pr_debug("Entering iscsi_target_set_sock_callbacks: conn: %p\n", conn); write_lock_bh(&sk->sk_callback_lock); sk->sk_user_data = conn; conn->orig_data_ready = sk->sk_data_ready; conn->orig_state_change = sk->sk_state_change; sk->sk_data_ready = iscsi_target_sk_data_ready; sk->sk_state_change = iscsi_target_sk_state_change; write_unlock_bh(&sk->sk_callback_lock); sk->sk_sndtimeo = TA_LOGIN_TIMEOUT * HZ; sk->sk_rcvtimeo = TA_LOGIN_TIMEOUT * HZ; } static void iscsi_target_restore_sock_callbacks(struct iscsit_conn *conn) { struct sock *sk; if (!conn->sock) return; sk = conn->sock->sk; pr_debug("Entering iscsi_target_restore_sock_callbacks: conn: %p\n", conn); write_lock_bh(&sk->sk_callback_lock); if (!sk->sk_user_data) { write_unlock_bh(&sk->sk_callback_lock); return; } sk->sk_user_data = NULL; sk->sk_data_ready = conn->orig_data_ready; sk->sk_state_change = conn->orig_state_change; write_unlock_bh(&sk->sk_callback_lock); sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT; sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; } static int iscsi_target_do_login(struct iscsit_conn *, struct iscsi_login *); static bool __iscsi_target_sk_check_close(struct sock *sk) { if (sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) { pr_debug("__iscsi_target_sk_check_close: TCP_CLOSE_WAIT|TCP_CLOSE," "returning TRUE\n"); return true; } return false; } static bool iscsi_target_sk_check_close(struct iscsit_conn *conn) { bool state = false; if (conn->sock) { struct sock *sk = conn->sock->sk; read_lock_bh(&sk->sk_callback_lock); state = (__iscsi_target_sk_check_close(sk) || test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)); read_unlock_bh(&sk->sk_callback_lock); } return state; } static bool iscsi_target_sk_check_flag(struct iscsit_conn *conn, unsigned int flag) { bool state = false; if (conn->sock) { struct sock *sk = conn->sock->sk; read_lock_bh(&sk->sk_callback_lock); state = test_bit(flag, &conn->login_flags); read_unlock_bh(&sk->sk_callback_lock); } return state; } static bool iscsi_target_sk_check_and_clear(struct iscsit_conn *conn, unsigned int flag) { bool state = false; if (conn->sock) { struct sock *sk = conn->sock->sk; write_lock_bh(&sk->sk_callback_lock); state = (__iscsi_target_sk_check_close(sk) || test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)); if (!state) clear_bit(flag, &conn->login_flags); write_unlock_bh(&sk->sk_callback_lock); } return state; } static void iscsi_target_login_drop(struct iscsit_conn *conn, struct iscsi_login *login) { bool zero_tsih = login->zero_tsih; iscsi_remove_failed_auth_entry(conn); iscsi_target_nego_release(conn); iscsi_target_login_sess_out(conn, zero_tsih, true); } static void iscsi_target_do_login_rx(struct work_struct *work) { struct iscsit_conn *conn = container_of(work, struct iscsit_conn, login_work.work); struct iscsi_login *login = conn->login; struct iscsi_np *np = login->np; struct iscsi_portal_group *tpg = conn->tpg; struct iscsi_tpg_np *tpg_np = conn->tpg_np; int rc, zero_tsih = login->zero_tsih; bool state; pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n", conn, current->comm, current->pid); spin_lock(&conn->login_worker_lock); set_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags); spin_unlock(&conn->login_worker_lock); /* * If iscsi_target_do_login_rx() has been invoked by ->sk_data_ready() * before initial PDU processing in iscsi_target_start_negotiation() * has completed, go ahead and retry until it's cleared. * * Otherwise if the TCP connection drops while this is occuring, * iscsi_target_start_negotiation() will detect the failure, call * cancel_delayed_work_sync(&conn->login_work), and cleanup the * remaining iscsi connection resources from iscsi_np process context. */ if (iscsi_target_sk_check_flag(conn, LOGIN_FLAGS_INITIAL_PDU)) { schedule_delayed_work(&conn->login_work, msecs_to_jiffies(10)); return; } spin_lock(&tpg->tpg_state_lock); state = (tpg->tpg_state == TPG_STATE_ACTIVE); spin_unlock(&tpg->tpg_state_lock); if (!state) { pr_debug("iscsi_target_do_login_rx: tpg_state != TPG_STATE_ACTIVE\n"); goto err; } if (iscsi_target_sk_check_close(conn)) { pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n"); goto err; } allow_signal(SIGINT); rc = iscsit_set_login_timer_kworker(conn, current); if (rc < 0) { /* The login timer has already expired */ pr_debug("iscsi_target_do_login_rx, login failed\n"); goto err; } rc = conn->conn_transport->iscsit_get_login_rx(conn, login); flush_signals(current); if (rc < 0) goto err; pr_debug("iscsi_target_do_login_rx after rx_login_io, %p, %s:%d\n", conn, current->comm, current->pid); /* * LOGIN_FLAGS_READ_ACTIVE is cleared so that sk_data_ready * could be triggered again after this. * * LOGIN_FLAGS_WRITE_ACTIVE is cleared after we successfully * process a login PDU, so that sk_state_chage can do login * cleanup as needed if the socket is closed. If a delayed work is * ongoing (LOGIN_FLAGS_WRITE_ACTIVE or LOGIN_FLAGS_READ_ACTIVE), * sk_state_change will leave the cleanup to the delayed work or * it will schedule a delayed work to do cleanup. */ if (conn->sock) { struct sock *sk = conn->sock->sk; write_lock_bh(&sk->sk_callback_lock); if (!test_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags)) { clear_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags); set_bit(LOGIN_FLAGS_WRITE_ACTIVE, &conn->login_flags); } write_unlock_bh(&sk->sk_callback_lock); } rc = iscsi_target_do_login(conn, login); if (rc < 0) { goto err; } else if (!rc) { if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_WRITE_ACTIVE)) goto err; /* * Set the login timer thread pointer to NULL to prevent the * login process from getting stuck if the initiator * stops sending data. */ rc = iscsit_set_login_timer_kworker(conn, NULL); if (rc < 0) goto err; } else if (rc == 1) { iscsit_stop_login_timer(conn); cancel_delayed_work(&conn->login_work); iscsi_target_nego_release(conn); iscsi_post_login_handler(np, conn, zero_tsih); iscsit_deaccess_np(np, tpg, tpg_np); } return; err: iscsi_target_restore_sock_callbacks(conn); iscsit_stop_login_timer(conn); cancel_delayed_work(&conn->login_work); iscsi_target_login_drop(conn, login); iscsit_deaccess_np(np, tpg, tpg_np); } static void iscsi_target_sk_state_change(struct sock *sk) { struct iscsit_conn *conn; void (*orig_state_change)(struct sock *); bool state; pr_debug("Entering iscsi_target_sk_state_change\n"); write_lock_bh(&sk->sk_callback_lock); conn = sk->sk_user_data; if (!conn) { write_unlock_bh(&sk->sk_callback_lock); return; } orig_state_change = conn->orig_state_change; if (!test_bit(LOGIN_FLAGS_READY, &conn->login_flags)) { pr_debug("Got LOGIN_FLAGS_READY=0 sk_state_change conn: %p\n", conn); write_unlock_bh(&sk->sk_callback_lock); orig_state_change(sk); return; } state = __iscsi_target_sk_check_close(sk); pr_debug("__iscsi_target_sk_close_change: state: %d\n", state); if (test_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags) || test_bit(LOGIN_FLAGS_WRITE_ACTIVE, &conn->login_flags)) { pr_debug("Got LOGIN_FLAGS_{READ|WRITE}_ACTIVE=1" " sk_state_change conn: %p\n", conn); if (state) set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags); write_unlock_bh(&sk->sk_callback_lock); orig_state_change(sk); return; } if (test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) { pr_debug("Got LOGIN_FLAGS_CLOSED=1 sk_state_change conn: %p\n", conn); write_unlock_bh(&sk->sk_callback_lock); orig_state_change(sk); return; } /* * If the TCP connection has dropped, go ahead and set LOGIN_FLAGS_CLOSED, * but only queue conn->login_work -> iscsi_target_do_login_rx() * processing if LOGIN_FLAGS_INITIAL_PDU has already been cleared. * * When iscsi_target_do_login_rx() runs, iscsi_target_sk_check_close() * will detect the dropped TCP connection from delayed workqueue context. * * If LOGIN_FLAGS_INITIAL_PDU is still set, which means the initial * iscsi_target_start_negotiation() is running, iscsi_target_do_login() * via iscsi_target_sk_check_close() or iscsi_target_start_negotiation() * via iscsi_target_sk_check_and_clear() is responsible for detecting the * dropped TCP connection in iscsi_np process context, and cleaning up * the remaining iscsi connection resources. */ if (state) { pr_debug("iscsi_target_sk_state_change got failed state\n"); set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags); state = test_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags); write_unlock_bh(&sk->sk_callback_lock); orig_state_change(sk); if (!state) schedule_delayed_work(&conn->login_work, 0); return; } write_unlock_bh(&sk->sk_callback_lock); orig_state_change(sk); } /* * NOTE: We check for existing sessions or connections AFTER the initiator * has been successfully authenticated in order to protect against faked * ISID/TSIH combinations. */ static int iscsi_target_check_for_existing_instances( struct iscsit_conn *conn, struct iscsi_login *login) { if (login->checked_for_existing) return 0; login->checked_for_existing = 1; if (!login->tsih) return iscsi_check_for_session_reinstatement(conn); else return iscsi_login_post_auth_non_zero_tsih(conn, login->cid, login->initial_exp_statsn); } static int iscsi_target_do_authentication( struct iscsit_conn *conn, struct iscsi_login *login) { int authret; u32 payload_length; struct iscsi_param *param; struct iscsi_login_req *login_req; struct iscsi_login_rsp *login_rsp; login_req = (struct iscsi_login_req *) login->req; login_rsp = (struct iscsi_login_rsp *) login->rsp; payload_length = ntoh24(login_req->dlength); param = iscsi_find_param_from_key(AUTHMETHOD, conn->param_list); if (!param) return -1; authret = iscsi_handle_authentication( conn, login->req_buf, login->rsp_buf, payload_length, &login->rsp_length, param->value); switch (authret) { case 0: pr_debug("Received OK response" " from LIO Authentication, continuing.\n"); break; case 1: pr_debug("iSCSI security negotiation" " completed successfully.\n"); login->auth_complete = 1; if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE1) && (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) { login_rsp->flags |= (ISCSI_FLAG_LOGIN_NEXT_STAGE1 | ISCSI_FLAG_LOGIN_TRANSIT); login->current_stage = 1; } return iscsi_target_check_for_existing_instances( conn, login); case 2: pr_err("Security negotiation" " failed.\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_AUTH_FAILED); return -1; default: pr_err("Received unknown error %d from LIO" " Authentication\n", authret); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, ISCSI_LOGIN_STATUS_TARGET_ERROR); return -1; } return 0; } bool iscsi_conn_auth_required(struct iscsit_conn *conn) { struct iscsi_node_acl *nacl; struct se_node_acl *se_nacl; if (conn->sess->sess_ops->SessionType) { /* * For SessionType=Discovery */ return conn->tpg->tpg_attrib.authentication; } /* * For SessionType=Normal */ se_nacl = conn->sess->se_sess->se_node_acl; if (!se_nacl) { pr_debug("Unknown ACL is trying to connect\n"); return true; } if (se_nacl->dynamic_node_acl) { pr_debug("Dynamic ACL %s is trying to connect\n", se_nacl->initiatorname); return conn->tpg->tpg_attrib.authentication; } pr_debug("Known ACL %s is trying to connect\n", se_nacl->initiatorname); nacl = to_iscsi_nacl(se_nacl); if (nacl->node_attrib.authentication == NA_AUTHENTICATION_INHERITED) return conn->tpg->tpg_attrib.authentication; return nacl->node_attrib.authentication; } static int iscsi_target_handle_csg_zero( struct iscsit_conn *conn, struct iscsi_login *login) { int ret; u32 payload_length; struct iscsi_param *param; struct iscsi_login_req *login_req; struct iscsi_login_rsp *login_rsp; login_req = (struct iscsi_login_req *) login->req; login_rsp = (struct iscsi_login_rsp *) login->rsp; payload_length = ntoh24(login_req->dlength); param = iscsi_find_param_from_key(AUTHMETHOD, conn->param_list); if (!param) return -1; ret = iscsi_decode_text_input( PHASE_SECURITY|PHASE_DECLARATIVE, SENDER_INITIATOR|SENDER_RECEIVER, login->req_buf, payload_length, conn); if (ret < 0) return -1; if (ret > 0) { if (login->auth_complete) { pr_err("Initiator has already been" " successfully authenticated, but is still" " sending %s keys.\n", param->value); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_INIT_ERR); return -1; } goto do_auth; } else if (!payload_length) { pr_err("Initiator sent zero length security payload," " login failed\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_AUTH_FAILED); return -1; } if (login->first_request) if (iscsi_target_check_first_request(conn, login) < 0) return -1; ret = iscsi_encode_text_output( PHASE_SECURITY|PHASE_DECLARATIVE, SENDER_TARGET, login->rsp_buf, &login->rsp_length, conn->param_list, conn->tpg->tpg_attrib.login_keys_workaround); if (ret < 0) return -1; if (!iscsi_check_negotiated_keys(conn->param_list)) { bool auth_required = iscsi_conn_auth_required(conn); if (auth_required) { if (!strncmp(param->value, NONE, 4)) { pr_err("Initiator sent AuthMethod=None but" " Target is enforcing iSCSI Authentication," " login failed.\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_AUTH_FAILED); return -1; } if (!login->auth_complete) return 0; if (strncmp(param->value, NONE, 4) && !login->auth_complete) return 0; } if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE1) && (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) { login_rsp->flags |= ISCSI_FLAG_LOGIN_NEXT_STAGE1 | ISCSI_FLAG_LOGIN_TRANSIT; login->current_stage = 1; } } return 0; do_auth: return iscsi_target_do_authentication(conn, login); } static bool iscsi_conn_authenticated(struct iscsit_conn *conn, struct iscsi_login *login) { if (!iscsi_conn_auth_required(conn)) return true; if (login->auth_complete) return true; return false; } static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_login *login) { int ret; u32 payload_length; struct iscsi_login_req *login_req; struct iscsi_login_rsp *login_rsp; login_req = (struct iscsi_login_req *) login->req; login_rsp = (struct iscsi_login_rsp *) login->rsp; payload_length = ntoh24(login_req->dlength); ret = iscsi_decode_text_input( PHASE_OPERATIONAL|PHASE_DECLARATIVE, SENDER_INITIATOR|SENDER_RECEIVER, login->req_buf, payload_length, conn); if (ret < 0) { iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_INIT_ERR); return -1; } if (login->first_request) if (iscsi_target_check_first_request(conn, login) < 0) return -1; if (iscsi_target_check_for_existing_instances(conn, login) < 0) return -1; ret = iscsi_encode_text_output( PHASE_OPERATIONAL|PHASE_DECLARATIVE, SENDER_TARGET, login->rsp_buf, &login->rsp_length, conn->param_list, conn->tpg->tpg_attrib.login_keys_workaround); if (ret < 0) { iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_INIT_ERR); return -1; } if (!iscsi_conn_authenticated(conn, login)) { pr_err("Initiator is requesting CSG: 1, has not been" " successfully authenticated, and the Target is" " enforcing iSCSI Authentication, login failed.\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_AUTH_FAILED); return -1; } if (!iscsi_check_negotiated_keys(conn->param_list)) if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE3) && (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) login_rsp->flags |= ISCSI_FLAG_LOGIN_NEXT_STAGE3 | ISCSI_FLAG_LOGIN_TRANSIT; return 0; } /* * RETURN VALUE: * * 1 = Login successful * -1 = Login failed * 0 = More PDU exchanges required */ static int iscsi_target_do_login(struct iscsit_conn *conn, struct iscsi_login *login) { int pdu_count = 0; struct iscsi_login_req *login_req; struct iscsi_login_rsp *login_rsp; login_req = (struct iscsi_login_req *) login->req; login_rsp = (struct iscsi_login_rsp *) login->rsp; while (1) { if (++pdu_count > MAX_LOGIN_PDUS) { pr_err("MAX_LOGIN_PDUS count reached.\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, ISCSI_LOGIN_STATUS_TARGET_ERROR); return -1; } switch (ISCSI_LOGIN_CURRENT_STAGE(login_req->flags)) { case 0: login_rsp->flags &= ~ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK; if (iscsi_target_handle_csg_zero(conn, login) < 0) return -1; break; case 1: login_rsp->flags |= ISCSI_FLAG_LOGIN_CURRENT_STAGE1; if (iscsi_target_handle_csg_one(conn, login) < 0) return -1; if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) { /* * Check to make sure the TCP connection has not * dropped asynchronously while session reinstatement * was occuring in this kthread context, before * transitioning to full feature phase operation. */ if (iscsi_target_sk_check_close(conn)) return -1; login->tsih = conn->sess->tsih; login->login_complete = 1; iscsi_target_restore_sock_callbacks(conn); if (iscsi_target_do_tx_login_io(conn, login) < 0) return -1; return 1; } break; default: pr_err("Illegal CSG: %d received from" " Initiator, protocol error.\n", ISCSI_LOGIN_CURRENT_STAGE(login_req->flags)); break; } if (iscsi_target_do_tx_login_io(conn, login) < 0) return -1; if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) { login_rsp->flags &= ~ISCSI_FLAG_LOGIN_TRANSIT; login_rsp->flags &= ~ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK; } break; } return 0; } static void iscsi_initiatorname_tolower( char *param_buf) { char *c; u32 iqn_size = strlen(param_buf), i; for (i = 0; i < iqn_size; i++) { c = ¶m_buf[i]; if (!isupper(*c)) continue; *c = tolower(*c); } } /* * Processes the first Login Request.. */ int iscsi_target_locate_portal( struct iscsi_np *np, struct iscsit_conn *conn, struct iscsi_login *login) { char *i_buf = NULL, *s_buf = NULL, *t_buf = NULL; char *tmpbuf, *start = NULL, *end = NULL, *key, *value; struct iscsit_session *sess = conn->sess; struct iscsi_tiqn *tiqn; struct iscsi_tpg_np *tpg_np = NULL; struct iscsi_login_req *login_req; struct se_node_acl *se_nacl; u32 payload_length, queue_depth = 0; int sessiontype = 0, ret = 0, tag_num, tag_size; INIT_DELAYED_WORK(&conn->login_work, iscsi_target_do_login_rx); iscsi_target_set_sock_callbacks(conn); login->np = np; conn->tpg = NULL; login_req = (struct iscsi_login_req *) login->req; payload_length = ntoh24(login_req->dlength); tmpbuf = kmemdup_nul(login->req_buf, payload_length, GFP_KERNEL); if (!tmpbuf) { pr_err("Unable to allocate memory for tmpbuf.\n"); return -1; } start = tmpbuf; end = (start + payload_length); /* * Locate the initial keys expected from the Initiator node in * the first login request in order to progress with the login phase. */ while (start < end) { if (iscsi_extract_key_value(start, &key, &value) < 0) { ret = -1; goto out; } if (!strncmp(key, "InitiatorName", 13)) i_buf = value; else if (!strncmp(key, "SessionType", 11)) s_buf = value; else if (!strncmp(key, "TargetName", 10)) t_buf = value; start += strlen(key) + strlen(value) + 2; } /* * See 5.3. Login Phase. */ if (!i_buf) { pr_err("InitiatorName key not received" " in first login request.\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_MISSING_FIELDS); ret = -1; goto out; } /* * Convert the incoming InitiatorName to lowercase following * RFC-3720 3.2.6.1. section c) that says that iSCSI IQNs * are NOT case sensitive. */ iscsi_initiatorname_tolower(i_buf); if (!s_buf) { if (!login->leading_connection) goto get_target; pr_err("SessionType key not received" " in first login request.\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_MISSING_FIELDS); ret = -1; goto out; } /* * Use default portal group for discovery sessions. */ sessiontype = strncmp(s_buf, DISCOVERY, 9); if (!sessiontype) { if (!login->leading_connection) goto get_target; sess->sess_ops->SessionType = 1; /* * Setup crc32c modules from libcrypto */ if (iscsi_login_setup_crypto(conn) < 0) { pr_err("iscsi_login_setup_crypto() failed\n"); ret = -1; goto out; } /* * Serialize access across the discovery struct iscsi_portal_group to * process login attempt. */ conn->tpg = iscsit_global->discovery_tpg; if (iscsit_access_np(np, conn->tpg) < 0) { iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE); conn->tpg = NULL; ret = -1; goto out; } ret = 0; goto alloc_tags; } get_target: if (!t_buf) { pr_err("TargetName key not received" " in first login request while" " SessionType=Normal.\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_MISSING_FIELDS); ret = -1; goto out; } /* * Locate Target IQN from Storage Node. */ tiqn = iscsit_get_tiqn_for_login(t_buf); if (!tiqn) { pr_err("Unable to locate Target IQN: %s in" " Storage Node\n", t_buf); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE); ret = -1; goto out; } pr_debug("Located Storage Object: %s\n", tiqn->tiqn); /* * Locate Target Portal Group from Storage Node. */ conn->tpg = iscsit_get_tpg_from_np(tiqn, np, &tpg_np); if (!conn->tpg) { pr_err("Unable to locate Target Portal Group" " on %s\n", tiqn->tiqn); iscsit_put_tiqn_for_login(tiqn); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE); ret = -1; goto out; } conn->tpg_np = tpg_np; pr_debug("Located Portal Group Object: %hu\n", conn->tpg->tpgt); /* * Setup crc32c modules from libcrypto */ if (iscsi_login_setup_crypto(conn) < 0) { pr_err("iscsi_login_setup_crypto() failed\n"); kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put); iscsit_put_tiqn_for_login(tiqn); conn->tpg = NULL; ret = -1; goto out; } /* * Serialize access across the struct iscsi_portal_group to * process login attempt. */ if (iscsit_access_np(np, conn->tpg) < 0) { kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put); iscsit_put_tiqn_for_login(tiqn); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE); conn->tpg = NULL; ret = -1; goto out; } /* * conn->sess->node_acl will be set when the referenced * struct iscsit_session is located from received ISID+TSIH in * iscsi_login_non_zero_tsih_s2(). */ if (!login->leading_connection) { ret = 0; goto out; } /* * This value is required in iscsi_login_zero_tsih_s2() */ sess->sess_ops->SessionType = 0; /* * Locate incoming Initiator IQN reference from Storage Node. */ sess->se_sess->se_node_acl = core_tpg_check_initiator_node_acl( &conn->tpg->tpg_se_tpg, i_buf); if (!sess->se_sess->se_node_acl) { pr_err("iSCSI Initiator Node: %s is not authorized to" " access iSCSI target portal group: %hu.\n", i_buf, conn->tpg->tpgt); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_TGT_FORBIDDEN); ret = -1; goto out; } se_nacl = sess->se_sess->se_node_acl; queue_depth = se_nacl->queue_depth; /* * Setup pre-allocated tags based upon allowed per NodeACL CmdSN * depth for non immediate commands, plus extra tags for immediate * commands. * * Also enforce a ISCSIT_MIN_TAGS to prevent unnecessary contention * in per-cpu-ida tag allocation logic + small queue_depth. */ alloc_tags: tag_num = max_t(u32, ISCSIT_MIN_TAGS, queue_depth); tag_num = (tag_num * 2) + ISCSIT_EXTRA_TAGS; tag_size = sizeof(struct iscsit_cmd) + conn->conn_transport->priv_size; ret = transport_alloc_session_tags(sess->se_sess, tag_num, tag_size); if (ret < 0) { iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, ISCSI_LOGIN_STATUS_NO_RESOURCES); ret = -1; } out: kfree(tmpbuf); return ret; } int iscsi_target_start_negotiation( struct iscsi_login *login, struct iscsit_conn *conn) { int ret; if (conn->sock) { struct sock *sk = conn->sock->sk; write_lock_bh(&sk->sk_callback_lock); set_bit(LOGIN_FLAGS_READY, &conn->login_flags); set_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags); write_unlock_bh(&sk->sk_callback_lock); } /* * If iscsi_target_do_login returns zero to signal more PDU * exchanges are required to complete the login, go ahead and * clear LOGIN_FLAGS_INITIAL_PDU but only if the TCP connection * is still active. * * Otherwise if TCP connection dropped asynchronously, go ahead * and perform connection cleanup now. */ ret = iscsi_target_do_login(conn, login); if (!ret) { spin_lock(&conn->login_worker_lock); if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU)) ret = -1; else if (!test_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags)) { if (iscsit_set_login_timer_kworker(conn, NULL) < 0) { /* * The timeout has expired already. * Schedule login_work to perform the cleanup. */ schedule_delayed_work(&conn->login_work, 0); } } spin_unlock(&conn->login_worker_lock); } if (ret < 0) { iscsi_target_restore_sock_callbacks(conn); iscsi_remove_failed_auth_entry(conn); } if (ret != 0) { iscsit_stop_login_timer(conn); cancel_delayed_work_sync(&conn->login_work); iscsi_target_nego_release(conn); } return ret; } void iscsi_target_nego_release(struct iscsit_conn *conn) { struct iscsi_login *login = conn->conn_login; if (!login) return; kfree(login->req_buf); kfree(login->rsp_buf); kfree(login); conn->conn_login = NULL; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1