Certain iSCSI commands such as NOP and LOGOUT commands are discarded instead of re-queued when we have a session failure and reconnect. Change the LOGOUT command to instead fail with SCSI_STATUS_ERROR when this happens. Otherwise, IF we are in iscsi_logout_sync() and we get a session failure at the same-ish time we may end up automatically re-connecting the session, but since we have discarded the logout command we will never get a reply and will hang indefinitely in the event loop for synchronous commands. Arguably, we could also just return SCSI_STATUS_GOOD here since when we perform a logout, we probably don't care too much about how we disconnected from the server, only that we did disconnect from the server. That is academic anyway since this only affects the sync API which is only meant for trivial applications, which will likely not inspect the result and just do a: ... iscsi_logout_sync() iscsi_destroy_context() ... anyway. Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
1323 lines
32 KiB
C
1323 lines
32 KiB
C
/*
|
|
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_ARPA_INET_H
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
#if defined(WIN32)
|
|
#include <winsock2.h>
|
|
#include "win32/win32_compat.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "iscsi.h"
|
|
#include "iscsi-private.h"
|
|
#include "scsi-lowlevel.h"
|
|
#include "md5.h"
|
|
#ifdef HAVE_LIBGCRYPT
|
|
#include <gcrypt.h>
|
|
#endif
|
|
|
|
static int
|
|
iscsi_login_add_initiatorname(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send InitiatorName during opneg or the first leg of secneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG
|
|
&& iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) {
|
|
return 0;
|
|
}
|
|
|
|
if (snprintf(str, MAX_STRING_SIZE, "InitiatorName=%s", iscsi->initiator_name) == -1) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: aprintf failed.");
|
|
return -1;
|
|
}
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_alias(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send InitiatorAlias during opneg or the first leg of secneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG
|
|
&& iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) {
|
|
return 0;
|
|
}
|
|
|
|
if (snprintf(str, MAX_STRING_SIZE, "InitiatorAlias=%s", iscsi->alias) == -1) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: aprintf failed.");
|
|
return -1;
|
|
}
|
|
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_targetname(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send TargetName during opneg or the first leg of secneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG
|
|
&& iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) {
|
|
return 0;
|
|
}
|
|
|
|
if (!iscsi->target_name[0]) {
|
|
iscsi_set_error(iscsi, "Trying normal connect but "
|
|
"target name not set.");
|
|
return -1;
|
|
}
|
|
|
|
if (snprintf(str, MAX_STRING_SIZE, "TargetName=%s", iscsi->target_name) == -1) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: aprintf failed.");
|
|
return -1;
|
|
}
|
|
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_sessiontype(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send TargetName during opneg or the first leg of secneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG
|
|
&& iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) {
|
|
return 0;
|
|
}
|
|
|
|
switch (iscsi->session_type) {
|
|
case ISCSI_SESSION_DISCOVERY:
|
|
strncpy(str,"SessionType=Discovery",MAX_STRING_SIZE);
|
|
break;
|
|
case ISCSI_SESSION_NORMAL:
|
|
strncpy(str,"SessionType=Normal",MAX_STRING_SIZE);
|
|
break;
|
|
default:
|
|
iscsi_set_error(iscsi, "Can not handle sessions %d yet.",
|
|
iscsi->session_type);
|
|
return -1;
|
|
}
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_headerdigest(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send HeaderDigest during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
switch (iscsi->want_header_digest) {
|
|
case ISCSI_HEADER_DIGEST_NONE:
|
|
strncpy(str,"HeaderDigest=None",MAX_STRING_SIZE);
|
|
break;
|
|
case ISCSI_HEADER_DIGEST_NONE_CRC32C:
|
|
strncpy(str,"HeaderDigest=None,CRC32C",MAX_STRING_SIZE);
|
|
break;
|
|
case ISCSI_HEADER_DIGEST_CRC32C_NONE:
|
|
strncpy(str,"HeaderDigest=CRC32C,None",MAX_STRING_SIZE);
|
|
break;
|
|
case ISCSI_HEADER_DIGEST_CRC32C:
|
|
strncpy(str,"HeaderDigest=CRC32C",MAX_STRING_SIZE);
|
|
break;
|
|
default:
|
|
iscsi_set_error(iscsi, "invalid header digest value");
|
|
return -1;
|
|
}
|
|
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_datadigest(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send DataDigest during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
strncpy(str,"DataDigest=None",MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_initialr2t(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send InitialR2T during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
if (snprintf(str, MAX_STRING_SIZE, "InitialR2T=%s", iscsi->want_initial_r2t == ISCSI_INITIAL_R2T_NO ?
|
|
"No" : "Yes") == -1) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: aprintf failed.");
|
|
return -1;
|
|
}
|
|
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_immediatedata(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send ImmediateData during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
if (snprintf(str, MAX_STRING_SIZE, "ImmediateData=%s", iscsi->want_immediate_data == ISCSI_IMMEDIATE_DATA_NO ?
|
|
"No" : "Yes") == -1) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: aprintf failed.");
|
|
return -1;
|
|
}
|
|
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_maxburstlength(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send MaxBurstLength during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
if (snprintf(str, MAX_STRING_SIZE, "MaxBurstLength=%d", iscsi->max_burst_length) == -1) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: aprintf failed.");
|
|
return -1;
|
|
}
|
|
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_firstburstlength(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send FirstBurstLength during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
if (snprintf(str, MAX_STRING_SIZE, "FirstBurstLength=%d", iscsi->first_burst_length) == -1) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: aprintf failed.");
|
|
return -1;
|
|
}
|
|
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_maxrecvdatasegmentlength(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send MaxRecvDataSegmentLength during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
if (snprintf(str, MAX_STRING_SIZE, "MaxRecvDataSegmentLength=%d", iscsi->initiator_max_recv_data_segment_length) == -1) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: aprintf failed.");
|
|
return -1;
|
|
}
|
|
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_datapduinorder(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send DataPduInOrder during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
strncpy(str,"DataPDUInOrder=Yes",MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_defaulttime2wait(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send DefaultTime2Wait during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
strncpy(str,"DefaultTime2Wait=2",MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_defaulttime2retain(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send DefaultTime2Retain during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
strncpy(str,"DefaultTime2Retain=0",MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_ifmarker(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send IFMarker during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
strncpy(str,"IFMarker=No",MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_ofmarker(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send OFMarker during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
strncpy(str,"OFMarker=No",MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_maxconnections(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send MaxConnections during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
strncpy(str,"MaxConnections=1",MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_maxoutstandingr2t(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send MaxOutstandingR2T during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
strncpy(str,"MaxOutstandingR2T=1",MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_errorrecoverylevel(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send ErrorRecoveryLevel during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
strncpy(str,"ErrorRecoveryLevel=0",MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_datasequenceinorder(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
/* We only send DataSequenceInOrder during opneg */
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
return 0;
|
|
}
|
|
|
|
strncpy(str,"DataSequenceInOrder=Yes",MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_authmethod(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG
|
|
|| iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) {
|
|
return 0;
|
|
}
|
|
|
|
strncpy(str,"AuthMethod=CHAP,None",MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_authalgorithm(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG
|
|
|| iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SELECT_ALGORITHM) {
|
|
return 0;
|
|
}
|
|
|
|
strncpy(str,"CHAP_A=5",MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
iscsi_login_add_chap_username(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG
|
|
|| iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) {
|
|
return 0;
|
|
}
|
|
|
|
strncpy(str,"CHAP_N=",MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str))
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
if (iscsi_pdu_add_data(iscsi, pdu,
|
|
(unsigned char *)iscsi->user,
|
|
strlen(iscsi->user) +1) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
|
"failed.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
h2i(int h)
|
|
{
|
|
if (h >= 'a' && h <= 'f') {
|
|
return h - 'a' + 10;
|
|
}
|
|
if (h >= 'A' && h <= 'F') {
|
|
return h - 'A' + 10;
|
|
}
|
|
return h - '0';
|
|
}
|
|
|
|
static int
|
|
i2h(int i)
|
|
{
|
|
if (i >= 10) {
|
|
return i - 10 + 'A';
|
|
}
|
|
|
|
return i + '0';
|
|
}
|
|
|
|
#ifndef HAVE_LIBGCRYPT
|
|
typedef struct MD5Context *gcry_md_hd_t;
|
|
#define gcry_md_write MD5Update
|
|
#define GCRY_MD_MD5 1
|
|
|
|
static void gcry_md_open(gcry_md_hd_t *hd, int algo, unsigned int flags)
|
|
{
|
|
assert(algo == GCRY_MD_MD5 && flags == 0);
|
|
*hd = malloc(sizeof(struct MD5Context));
|
|
if (*hd) {
|
|
MD5Init(*hd);
|
|
}
|
|
}
|
|
|
|
static void gcry_md_putc(gcry_md_hd_t h, unsigned char c)
|
|
{
|
|
MD5Update(h, &c, 1);
|
|
}
|
|
|
|
static char *gcry_md_read(gcry_md_hd_t h, int algo)
|
|
{
|
|
unsigned char digest[16];
|
|
assert(algo == 0 || algo == GCRY_MD_MD5);
|
|
|
|
MD5Final(digest, h);
|
|
return memcpy(h->buf, digest, sizeof(digest));
|
|
}
|
|
|
|
static void gcry_md_close(gcry_md_hd_t h)
|
|
{
|
|
memset(h, 0, sizeof(*h));
|
|
free(h);
|
|
}
|
|
#endif
|
|
|
|
/* size of the challenge used for bidirectional chap */
|
|
#define TARGET_CHAP_C_SIZE 32
|
|
|
|
static int
|
|
iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
|
{
|
|
char str[MAX_STRING_SIZE+1];
|
|
char * strp;
|
|
unsigned char c, cc[2];
|
|
unsigned char digest[CHAP_R_SIZE];
|
|
gcry_md_hd_t ctx;
|
|
int i;
|
|
|
|
if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG
|
|
|| iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) {
|
|
return 0;
|
|
}
|
|
|
|
if (!iscsi->chap_c[0]) {
|
|
iscsi_set_error(iscsi, "No CHAP challenge found");
|
|
return -1;
|
|
}
|
|
|
|
gcry_md_open(&ctx, GCRY_MD_MD5, 0);
|
|
if (ctx == NULL) {
|
|
iscsi_set_error(iscsi, "Cannot create MD5 algorithm");
|
|
return -1;
|
|
}
|
|
gcry_md_putc(ctx, iscsi->chap_i);
|
|
gcry_md_write(ctx, (unsigned char *)iscsi->passwd, strlen(iscsi->passwd));
|
|
|
|
strp = iscsi->chap_c;
|
|
while (*strp != 0) {
|
|
c = (h2i(strp[0]) << 4) | h2i(strp[1]);
|
|
strp += 2;
|
|
gcry_md_putc(ctx, c);
|
|
}
|
|
memcpy(digest, gcry_md_read(ctx, 0), sizeof(digest));
|
|
gcry_md_close(ctx);
|
|
|
|
strncpy(str,"CHAP_R=0x",MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str))
|
|
!= 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < CHAP_R_SIZE; i++) {
|
|
c = digest[i];
|
|
cc[0] = i2h((c >> 4)&0x0f);
|
|
cc[1] = i2h((c )&0x0f);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, &cc[0], 2) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
|
"failed.");
|
|
return -1;
|
|
}
|
|
}
|
|
c = 0;
|
|
if (iscsi_pdu_add_data(iscsi, pdu, &c, 1) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
|
"failed.");
|
|
return -1;
|
|
}
|
|
|
|
/* bidirectional chap */
|
|
if (iscsi->target_user[0]) {
|
|
unsigned char target_chap_c[TARGET_CHAP_C_SIZE];
|
|
|
|
iscsi->target_chap_i++;
|
|
snprintf(str, MAX_STRING_SIZE, "CHAP_I=%d",
|
|
iscsi->target_chap_i);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str,
|
|
strlen(str) + 1) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add "
|
|
"data failed.");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < TARGET_CHAP_C_SIZE; i++) {
|
|
target_chap_c[i] = rand()&0xff;
|
|
}
|
|
strncpy(str, "CHAP_C=0x", MAX_STRING_SIZE);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str,
|
|
strlen(str)) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
|
"failed.");
|
|
return -1;
|
|
}
|
|
for (i = 0; i < TARGET_CHAP_C_SIZE; i++) {
|
|
c = target_chap_c[i];
|
|
cc[0] = i2h((c >> 4)&0x0f);
|
|
cc[1] = i2h((c )&0x0f);
|
|
if (iscsi_pdu_add_data(iscsi, pdu, &cc[0], 2) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add "
|
|
"data failed.");
|
|
return -1;
|
|
}
|
|
}
|
|
c = 0;
|
|
if (iscsi_pdu_add_data(iscsi, pdu, &c, 1) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
|
"failed.");
|
|
return -1;
|
|
}
|
|
|
|
gcry_md_open(&ctx, GCRY_MD_MD5, 0);
|
|
if (ctx == NULL) {
|
|
iscsi_set_error(iscsi, "Cannot create MD5 algorithm");
|
|
return -1;
|
|
}
|
|
gcry_md_putc(ctx, iscsi->target_chap_i);
|
|
gcry_md_write(ctx, (unsigned char *)iscsi->target_passwd,
|
|
strlen(iscsi->target_passwd));
|
|
gcry_md_write(ctx, (unsigned char *)target_chap_c,
|
|
TARGET_CHAP_C_SIZE);
|
|
|
|
memcpy(iscsi->target_chap_r, gcry_md_read(ctx, 0),
|
|
sizeof(iscsi->target_chap_r));
|
|
gcry_md_close(ctx);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
|
|
void *private_data)
|
|
{
|
|
struct iscsi_pdu *pdu;
|
|
int transit;
|
|
|
|
if (iscsi->login_attempts++ > 10) {
|
|
iscsi_set_error(iscsi, "login took too many tries."
|
|
" giving up.");
|
|
return -1;
|
|
}
|
|
|
|
if (iscsi->is_loggedin != 0) {
|
|
iscsi_set_error(iscsi, "Trying to login while already logged "
|
|
"in.");
|
|
return -1;
|
|
}
|
|
|
|
switch (iscsi->session_type) {
|
|
case ISCSI_SESSION_DISCOVERY:
|
|
case ISCSI_SESSION_NORMAL:
|
|
break;
|
|
default:
|
|
iscsi_set_error(iscsi, "trying to login without setting "
|
|
"session type.");
|
|
return -1;
|
|
}
|
|
|
|
/* randomize cmdsn and itt */
|
|
if (!iscsi->current_phase && !iscsi->secneg_phase) {
|
|
iscsi->itt = (uint32_t) rand();
|
|
iscsi->cmdsn = (uint32_t) rand();
|
|
iscsi->expcmdsn = iscsi->maxcmdsn = iscsi->min_cmdsn_waiting = iscsi->cmdsn;
|
|
}
|
|
|
|
pdu = iscsi_allocate_pdu(iscsi,
|
|
ISCSI_PDU_LOGIN_REQUEST,
|
|
ISCSI_PDU_LOGIN_RESPONSE,
|
|
iscsi->itt,
|
|
ISCSI_PDU_DROP_ON_RECONNECT);
|
|
if (pdu == NULL) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate "
|
|
"login pdu.");
|
|
return -1;
|
|
}
|
|
|
|
/* login request */
|
|
iscsi_pdu_set_immediate(pdu);
|
|
|
|
/* cmdsn is not increased if Immediate delivery*/
|
|
iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn);
|
|
|
|
if (!iscsi->user[0]) {
|
|
iscsi->current_phase = ISCSI_PDU_LOGIN_CSG_OPNEG;
|
|
}
|
|
|
|
if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_SECNEG) {
|
|
iscsi->next_phase = ISCSI_PDU_LOGIN_NSG_OPNEG;
|
|
}
|
|
if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
iscsi->next_phase = ISCSI_PDU_LOGIN_NSG_FF;
|
|
}
|
|
|
|
transit = 0;
|
|
if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_OPNEG) {
|
|
transit = ISCSI_PDU_LOGIN_TRANSIT;
|
|
}
|
|
if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_SECNEG) {
|
|
if (iscsi->secneg_phase == ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) {
|
|
transit = ISCSI_PDU_LOGIN_TRANSIT;
|
|
}
|
|
if (iscsi->secneg_phase == ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) {
|
|
transit = ISCSI_PDU_LOGIN_TRANSIT;
|
|
}
|
|
}
|
|
|
|
/* flags */
|
|
iscsi_pdu_set_pduflags(pdu, transit
|
|
| iscsi->current_phase
|
|
| iscsi->next_phase);
|
|
|
|
|
|
/* initiator name */
|
|
if (iscsi_login_add_initiatorname(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* optional alias */
|
|
if (iscsi->alias[0]) {
|
|
if (iscsi_login_add_alias(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* target name */
|
|
if (iscsi->session_type == ISCSI_SESSION_NORMAL) {
|
|
if (iscsi_login_add_targetname(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* session type */
|
|
if (iscsi_login_add_sessiontype(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* header digest */
|
|
if (iscsi_login_add_headerdigest(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* auth method */
|
|
if (iscsi_login_add_authmethod(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* auth algorithm */
|
|
if (iscsi_login_add_authalgorithm(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* chap username */
|
|
if (iscsi_login_add_chap_username(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* chap response */
|
|
if (iscsi_login_add_chap_response(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* data digest */
|
|
if (iscsi_login_add_datadigest(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* initial r2t */
|
|
if (iscsi_login_add_initialr2t(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* immediate data */
|
|
if (iscsi_login_add_immediatedata(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* max burst length */
|
|
if (iscsi_login_add_maxburstlength(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* first burst length */
|
|
if (iscsi_login_add_firstburstlength(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* default time 2 wait */
|
|
if (iscsi_login_add_defaulttime2wait(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* default time 2 retain */
|
|
if (iscsi_login_add_defaulttime2retain(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* max outstanding r2t */
|
|
if (iscsi_login_add_maxoutstandingr2t(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* errorrecoverylevel */
|
|
if (iscsi_login_add_errorrecoverylevel(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* ifmarker */
|
|
if (iscsi_login_add_ifmarker(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* ofmarker */
|
|
if (iscsi_login_add_ofmarker(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* maxconnections */
|
|
if (iscsi_login_add_maxconnections(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* max recv data segment length */
|
|
if (iscsi_login_add_maxrecvdatasegmentlength(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* data pdu in order */
|
|
if (iscsi_login_add_datapduinorder(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
/* data sequence in order */
|
|
if (iscsi_login_add_datasequenceinorder(iscsi, pdu) != 0) {
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
|
|
pdu->callback = cb;
|
|
pdu->private_data = private_data;
|
|
|
|
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
|
|
"pdu.");
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char *login_error_str(int status)
|
|
{
|
|
switch (status) {
|
|
case 0x0100: return "Target moved (unknown)"; /* Some don't set the detail */
|
|
case 0x0101: return "Target moved temporarily";
|
|
case 0x0102: return "Target moved permanently";
|
|
case 0x0200: return "Initiator error";
|
|
case 0x0201: return "Authentication failure";
|
|
case 0x0202: return "Authorization failure";
|
|
case 0x0203: return "Target not found";
|
|
case 0x0204: return "Target removed";
|
|
case 0x0205: return "Unsupported version";
|
|
case 0x0206: return "Too many connections";
|
|
case 0x0207: return "Missing parameter";
|
|
case 0x0208: return "Can't include in session";
|
|
case 0x0209: return "Session type not supported";
|
|
case 0x020a: return "Session does not exist";
|
|
case 0x020b: return "Invalid during login";
|
|
case 0x0300: return "Target error";
|
|
case 0x0301: return "Service unavailable";
|
|
case 0x0302: return "Out of resources";
|
|
}
|
|
return "Unknown login error";
|
|
}
|
|
|
|
|
|
int
|
|
iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
|
|
struct iscsi_in_pdu *in)
|
|
{
|
|
uint32_t status;
|
|
char *ptr = (char *)in->data;
|
|
int size = in->data_pos;
|
|
int must_have_chap_n = 0;
|
|
int must_have_chap_r = 0;
|
|
|
|
status = scsi_get_uint16(&in->hdr[36]);
|
|
|
|
// Status-Class is 0
|
|
if (!(status >> 8)) {
|
|
if (!iscsi->current_phase && !iscsi->secneg_phase) {
|
|
iscsi->statsn = scsi_get_uint32(&in->hdr[24]);
|
|
}
|
|
}
|
|
|
|
/* Using bidirectional CHAP? Then we must see a chap_n and chap_r
|
|
* field in this PDU
|
|
*/
|
|
if ((in->hdr[1] & ISCSI_PDU_LOGIN_TRANSIT)
|
|
&& (in->hdr[1] & ISCSI_PDU_LOGIN_CSG_FF) == ISCSI_PDU_LOGIN_CSG_SECNEG
|
|
&& iscsi->target_user[0]) {
|
|
must_have_chap_n = 1;
|
|
must_have_chap_r = 1;
|
|
}
|
|
|
|
/* XXX here we should parse the data returned in case the target
|
|
* renegotiated some some parameters.
|
|
* we should also do proper handshaking if the target is not yet
|
|
* prepared to transition to the next stage
|
|
*/
|
|
|
|
while (size > 0) {
|
|
char *end;
|
|
int len;
|
|
|
|
end = memchr(ptr, 0, size);
|
|
if (end == NULL) {
|
|
iscsi_set_error(iscsi, "NUL not found after offset %ld "
|
|
"when parsing login data",
|
|
(long)((unsigned char *)ptr - in->data));
|
|
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
|
pdu->private_data);
|
|
return -1;
|
|
}
|
|
|
|
len = end - ptr;
|
|
if (len == 0) {
|
|
break;
|
|
}
|
|
|
|
/* parse the strings */
|
|
if (!strncmp(ptr, "TargetAddress=", 14)) {
|
|
strncpy(iscsi->target_address,ptr+14,MAX_STRING_SIZE);
|
|
}
|
|
|
|
if (!strncmp(ptr, "HeaderDigest=", 13)) {
|
|
if (!strcmp(ptr + 13, "CRC32C")) {
|
|
iscsi->want_header_digest
|
|
= ISCSI_HEADER_DIGEST_CRC32C;
|
|
} else {
|
|
iscsi->want_header_digest
|
|
= ISCSI_HEADER_DIGEST_NONE;
|
|
}
|
|
}
|
|
|
|
if (!strncmp(ptr, "FirstBurstLength=", 17)) {
|
|
iscsi->first_burst_length = strtol(ptr + 17, NULL, 10);
|
|
}
|
|
|
|
if (!strncmp(ptr, "InitialR2T=", 11)) {
|
|
if (!strcmp(ptr + 11, "No")) {
|
|
iscsi->use_initial_r2t = ISCSI_INITIAL_R2T_NO;
|
|
} else {
|
|
iscsi->use_initial_r2t = ISCSI_INITIAL_R2T_YES;
|
|
}
|
|
}
|
|
|
|
if (!strncmp(ptr, "ImmediateData=", 14)) {
|
|
if (!strcmp(ptr + 14, "No")) {
|
|
iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_NO;
|
|
} else if (iscsi->want_immediate_data == ISCSI_IMMEDIATE_DATA_NO) {
|
|
/* If we negotiated NO, it doesnt matter what
|
|
* the target said. ImmediateData is NO.
|
|
*/
|
|
iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_NO;
|
|
|
|
} else {
|
|
iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_YES;
|
|
}
|
|
}
|
|
|
|
if (!strncmp(ptr, "MaxBurstLength=", 15)) {
|
|
iscsi->max_burst_length = strtol(ptr + 15, NULL, 10);
|
|
}
|
|
|
|
if (!strncmp(ptr, "MaxRecvDataSegmentLength=", 25)) {
|
|
iscsi->target_max_recv_data_segment_length = strtol(ptr + 25, NULL, 10);
|
|
}
|
|
|
|
if (!strncmp(ptr, "AuthMethod=", 11)) {
|
|
if (!strcmp(ptr + 11, "CHAP")) {
|
|
iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SELECT_ALGORITHM;
|
|
}
|
|
}
|
|
|
|
if (!strncmp(ptr, "CHAP_A=", 7)) {
|
|
iscsi->chap_a = atoi(ptr+7);
|
|
iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE;
|
|
}
|
|
|
|
if (!strncmp(ptr, "CHAP_I=", 7)) {
|
|
iscsi->chap_i = atoi(ptr+7);
|
|
iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE;
|
|
}
|
|
|
|
if (!strncmp(ptr, "CHAP_C=0x", 9)) {
|
|
strncpy(iscsi->chap_c,ptr+9,MAX_STRING_SIZE);
|
|
iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE;
|
|
}
|
|
|
|
if (!strncmp(ptr, "CHAP_N=", 7)) {
|
|
if (strcmp(iscsi->target_user, ptr + 7)) {
|
|
iscsi_set_error(iscsi, "Failed to log in to"
|
|
" target. Wrong CHAP targetname"
|
|
" received: %s", ptr + 7);
|
|
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
|
pdu->private_data);
|
|
return 0;
|
|
}
|
|
must_have_chap_n = 0;
|
|
}
|
|
|
|
if (!strncmp(ptr, "CHAP_R=0x", 9)) {
|
|
int i;
|
|
|
|
if (len != 9 + 2 * CHAP_R_SIZE) {
|
|
iscsi_set_error(iscsi, "Wrong size of CHAP_R"
|
|
" received from target.");
|
|
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
|
pdu->private_data);
|
|
return 0;
|
|
}
|
|
for (i = 0; i < CHAP_R_SIZE; i++) {
|
|
unsigned char c;
|
|
c = ((h2i(ptr[9 + 2 * i]) << 4) | h2i(ptr[9 + 2 * i + 1]));
|
|
if (c != iscsi->target_chap_r[i]) {
|
|
iscsi_set_error(iscsi, "Authentication "
|
|
"failed. Invalid CHAP_R "
|
|
"response from the target");
|
|
pdu->callback(iscsi, SCSI_STATUS_ERROR,
|
|
NULL, pdu->private_data);
|
|
return 0;
|
|
}
|
|
}
|
|
must_have_chap_r = 0;
|
|
}
|
|
|
|
ptr += len + 1;
|
|
size -= len + 1;
|
|
}
|
|
|
|
if (status == SCSI_STATUS_REDIRECT && iscsi->target_address[0]) {
|
|
ISCSI_LOG(iscsi, 2, "target requests redirect to %s",iscsi->target_address);
|
|
pdu->callback(iscsi, SCSI_STATUS_REDIRECT, NULL,
|
|
pdu->private_data);
|
|
return 0;
|
|
}
|
|
|
|
if (status != 0) {
|
|
iscsi_set_error(iscsi, "Failed to log in to target. Status: %s(%d)",
|
|
login_error_str(status), status);
|
|
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
|
pdu->private_data);
|
|
return 0;
|
|
}
|
|
|
|
if (must_have_chap_n) {
|
|
iscsi_set_error(iscsi, "Failed to log in to target. "
|
|
"It did not return CHAP_N during SECNEG.");
|
|
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
|
pdu->private_data);
|
|
return 0;
|
|
}
|
|
|
|
if (must_have_chap_r) {
|
|
iscsi_set_error(iscsi, "Failed to log in to target. "
|
|
"It did not return CHAP_R during SECNEG.");
|
|
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
|
pdu->private_data);
|
|
return 0;
|
|
}
|
|
|
|
if (in->hdr[1] & ISCSI_PDU_LOGIN_TRANSIT) {
|
|
iscsi->current_phase = (in->hdr[1] & ISCSI_PDU_LOGIN_NSG_FF) << 2;
|
|
}
|
|
|
|
if ((in->hdr[1] & ISCSI_PDU_LOGIN_TRANSIT)
|
|
&& (in->hdr[1] & ISCSI_PDU_LOGIN_NSG_FF) == ISCSI_PDU_LOGIN_NSG_FF) {
|
|
iscsi->is_loggedin = 1;
|
|
iscsi_itt_post_increment(iscsi);
|
|
iscsi->header_digest = iscsi->want_header_digest;
|
|
ISCSI_LOG(iscsi, 2, "login successful");
|
|
pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data);
|
|
} else {
|
|
if (iscsi_login_async(iscsi, pdu->callback, pdu->private_data) != 0) {
|
|
iscsi_set_error(iscsi, "Failed to send continuation login pdu");
|
|
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
|
|
void *private_data)
|
|
{
|
|
struct iscsi_pdu *pdu;
|
|
|
|
iscsi->login_attempts = 0;
|
|
|
|
if (iscsi->is_loggedin == 0) {
|
|
iscsi_set_error(iscsi, "Trying to logout while not logged in.");
|
|
return -1;
|
|
}
|
|
|
|
pdu = iscsi_allocate_pdu(iscsi,
|
|
ISCSI_PDU_LOGOUT_REQUEST,
|
|
ISCSI_PDU_LOGOUT_RESPONSE,
|
|
iscsi_itt_post_increment(iscsi),
|
|
ISCSI_PDU_ERROR_ON_RECONNECT|ISCSI_PDU_CORK_WHEN_SENT);
|
|
if (pdu == NULL) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate "
|
|
"logout pdu.");
|
|
return -1;
|
|
}
|
|
|
|
/* logout request has the immediate flag set */
|
|
iscsi_pdu_set_immediate(pdu);
|
|
|
|
/* flags : close the session */
|
|
iscsi_pdu_set_pduflags(pdu, 0x80);
|
|
|
|
/* cmdsn is not increased if Immediate delivery*/
|
|
iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn);
|
|
|
|
pdu->callback = cb;
|
|
pdu->private_data = private_data;
|
|
|
|
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
|
|
iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
|
|
"logout pdu.");
|
|
iscsi_free_pdu(iscsi, pdu);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
|
|
struct iscsi_in_pdu *in _U_)
|
|
{
|
|
iscsi->is_loggedin = 0;
|
|
ISCSI_LOG(iscsi, 2, "logout successful");
|
|
pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
iscsi_set_session_type(struct iscsi_context *iscsi,
|
|
enum iscsi_session_type session_type)
|
|
{
|
|
if (iscsi->is_loggedin) {
|
|
iscsi_set_error(iscsi, "trying to set session type while "
|
|
"logged in");
|
|
return -1;
|
|
}
|
|
|
|
iscsi->session_type = session_type;
|
|
|
|
return 0;
|
|
}
|