|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* iSCSI Login Library
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* Copyright (C) 2004 Dmitry Yusupov, Alex Aizman
|
|
Packit |
eace71 |
* maintained by open-iscsi@googlegroups.com
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* heavily based on code from iscsi-login.c:
|
|
Packit |
eace71 |
* Copyright (C) 2001 Cisco Systems, Inc.
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
eace71 |
* it under the terms of the GNU General Public License as published
|
|
Packit |
eace71 |
* by the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
eace71 |
* (at your option) any later version.
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* This program is distributed in the hope that it will be useful, but
|
|
Packit |
eace71 |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
eace71 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
eace71 |
* General Public License for more details.
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* See the file COPYING included with this distribution for more details.
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* Formation of iSCSI login pdu, processing the login response and other
|
|
Packit |
eace71 |
* functions are defined here
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
#include <netdb.h>
|
|
Packit |
eace71 |
#include <stdio.h>
|
|
Packit |
eace71 |
#include <stdlib.h>
|
|
Packit |
eace71 |
#include <string.h>
|
|
Packit |
eace71 |
#include <poll.h>
|
|
Packit |
eace71 |
#include <errno.h>
|
|
Packit |
eace71 |
#include <sys/param.h>
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
#include "initiator.h"
|
|
Packit |
eace71 |
#include "transport.h"
|
|
Packit |
eace71 |
#include "log.h"
|
|
Packit |
eace71 |
#include "iscsi_timer.h"
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* caller is assumed to be well-behaved and passing NUL terminated strings */
|
|
Packit |
eace71 |
int
|
|
Packit |
eace71 |
iscsi_add_text(struct iscsi_hdr *pdu, char *data, int max_data_length,
|
|
Packit |
eace71 |
char *param, char *value)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
int param_len = strlen(param);
|
|
Packit |
eace71 |
int value_len = strlen(value);
|
|
Packit |
eace71 |
int length = param_len + 1 + value_len + 1; /* param, separator,
|
|
Packit |
eace71 |
* value, and trailing
|
|
Packit |
eace71 |
* NULL
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
int pdu_length = ntoh24(pdu->dlength);
|
|
Packit |
eace71 |
char *text = data;
|
|
Packit |
eace71 |
char *end = data + max_data_length;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* find the end of the current text */
|
|
Packit |
eace71 |
text += pdu_length;
|
|
Packit |
eace71 |
pdu_length += length;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (text + length >= end) {
|
|
Packit |
eace71 |
log_warning("Failed to add login text "
|
|
Packit |
eace71 |
"'%s=%s'", param, value);
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* param */
|
|
Packit |
eace71 |
strncpy(text, param, param_len);
|
|
Packit |
eace71 |
text += param_len;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* separator */
|
|
Packit |
eace71 |
*text++ = ISCSI_TEXT_SEPARATOR;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* value */
|
|
Packit |
eace71 |
strncpy(text, value, value_len);
|
|
Packit |
eace71 |
text += value_len;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* NUL */
|
|
Packit |
eace71 |
*text++ = '\0';
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* update the length in the PDU header */
|
|
Packit |
eace71 |
hton24(pdu->dlength, pdu_length);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static int
|
|
Packit |
eace71 |
iscsi_find_key_value(char *param, char *pdu, char *pdu_end, char **value_start,
|
|
Packit |
eace71 |
char **value_end)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
char *str = param;
|
|
Packit |
eace71 |
char *text = pdu;
|
|
Packit |
eace71 |
char *value;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (value_start)
|
|
Packit |
eace71 |
*value_start = NULL;
|
|
Packit |
eace71 |
if (value_end)
|
|
Packit |
eace71 |
*value_end = NULL;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* make sure they contain the same bytes */
|
|
Packit |
eace71 |
while (*str) {
|
|
Packit |
eace71 |
if (text >= pdu_end)
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
if (*text == '\0')
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
if (*str != *text)
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
str++;
|
|
Packit |
eace71 |
text++;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if ((text >= pdu_end) || (*text == '\0')
|
|
Packit |
eace71 |
|| (*text != ISCSI_TEXT_SEPARATOR)) {
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* find the value */
|
|
Packit |
eace71 |
value = text + 1;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* find the end of the value */
|
|
Packit |
eace71 |
while ((text < pdu_end) && (*text))
|
|
Packit |
eace71 |
text++;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (value_start)
|
|
Packit |
eace71 |
*value_start = value;
|
|
Packit |
eace71 |
if (value_end)
|
|
Packit |
eace71 |
*value_end = text;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static enum iscsi_login_status
|
|
Packit |
eace71 |
get_auth_key_type(struct iscsi_acl *auth_client, char **data, char *end)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
char *key;
|
|
Packit |
eace71 |
char *value = NULL;
|
|
Packit |
eace71 |
char *value_end = NULL;
|
|
Packit |
eace71 |
char *text = *data;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
int keytype = AUTH_KEY_TYPE_NONE;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
while (acl_get_next_key_type(&keytype) == AUTH_STATUS_NO_ERROR) {
|
|
Packit |
eace71 |
key = (char *)acl_get_key_name(keytype);
|
|
Packit |
eace71 |
if (key && iscsi_find_key_value(key, text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
if (acl_recv_key_value(auth_client, keytype, value) !=
|
|
Packit |
eace71 |
AUTH_STATUS_NO_ERROR) {
|
|
Packit |
eace71 |
log_error("login negotiation failed, can't "
|
|
Packit |
eace71 |
"accept %s in security stage", text);
|
|
Packit |
eace71 |
return LOGIN_NEGOTIATION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
*data = text;
|
|
Packit |
eace71 |
return LOGIN_OK;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
log_error("Login negotiation failed, can't accept %s in security "
|
|
Packit |
eace71 |
"stage", text);
|
|
Packit |
eace71 |
return LOGIN_NEGOTIATION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
int
|
|
Packit |
eace71 |
resolve_address(char *host, char *port, struct sockaddr_storage *ss)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
struct addrinfo hints, *res;
|
|
Packit |
eace71 |
int rc;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
memset(&hints, 0, sizeof(struct addrinfo));
|
|
Packit |
eace71 |
hints.ai_family = AF_UNSPEC;
|
|
Packit |
eace71 |
hints.ai_socktype = SOCK_STREAM;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if ((rc = getaddrinfo(host, port, &hints, &res))) {
|
|
Packit |
eace71 |
log_error("Cannot resolve host %s. getaddrinfo error: "
|
|
Packit |
eace71 |
"[%s]", host, gai_strerror(rc));
|
|
Packit |
eace71 |
return rc;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
memcpy(ss, res->ai_addr, res->ai_addrlen);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
freeaddrinfo(res);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return rc;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* try to reset the session's IP address and port, based on the TargetAddress
|
|
Packit |
eace71 |
* provided
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
int
|
|
Packit |
eace71 |
iscsi_update_address(iscsi_conn_t *conn, char *address)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
char *port, *tag;
|
|
Packit |
eace71 |
char default_port[NI_MAXSERV];
|
|
Packit |
eace71 |
iscsi_session_t *session = conn->session;
|
|
Packit |
eace71 |
struct sockaddr_storage addr;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if ((tag = strrchr(address, ','))) {
|
|
Packit |
eace71 |
*tag = '\0';
|
|
Packit |
eace71 |
tag++;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
if ((port = strrchr(address, ':'))) {
|
|
Packit |
eace71 |
*port = '\0';
|
|
Packit |
eace71 |
port++;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (!port) {
|
|
Packit |
eace71 |
sprintf(default_port, "%d", ISCSI_LISTEN_PORT);
|
|
Packit |
eace71 |
port = default_port;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (*address == '[') {
|
|
Packit |
eace71 |
char *end_bracket;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (!(end_bracket = strchr(address, ']'))) {
|
|
Packit |
eace71 |
log_error("Invalid IPv6 address with opening bracket, "
|
|
Packit |
eace71 |
"but no closing bracket.");
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
*end_bracket = '\0';
|
|
Packit |
eace71 |
address++;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (resolve_address(address, port, &addr)) {
|
|
Packit |
eace71 |
log_error("cannot resolve host name %s", address);
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
conn->saddr = addr;
|
|
Packit |
eace71 |
if (tag)
|
|
Packit |
eace71 |
session->portal_group_tag = atoi(tag);
|
|
Packit |
eace71 |
return 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static enum iscsi_login_status
|
|
Packit |
eace71 |
get_security_text_keys(iscsi_session_t *session, int cid, char **data,
|
|
Packit |
eace71 |
struct iscsi_acl *auth_client, char *end)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
char *text = *data;
|
|
Packit |
eace71 |
char *value = NULL;
|
|
Packit |
eace71 |
char *value_end = NULL;
|
|
Packit |
eace71 |
size_t size;
|
|
Packit |
eace71 |
int tag;
|
|
Packit |
eace71 |
enum iscsi_login_status ret;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* a few keys are possible in Security stage
|
|
Packit |
eace71 |
* which the auth code doesn't care about, but
|
|
Packit |
eace71 |
* which we might want to see, or at least not
|
|
Packit |
eace71 |
* choke on.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
if (iscsi_find_key_value("TargetAlias", text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
size = value_end - value;
|
|
Packit |
eace71 |
session->target_alias = malloc(size + 1);
|
|
Packit |
eace71 |
if (!session->target_alias) {
|
|
Packit |
eace71 |
/* Alias not critical. So just print an error */
|
|
Packit |
eace71 |
log_error("Login failed to allocate alias");
|
|
Packit |
eace71 |
*data = value_end;
|
|
Packit |
eace71 |
return LOGIN_OK;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
memcpy(session->target_alias, value, size);
|
|
Packit |
eace71 |
session->target_alias[size] = '\0';
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("TargetAddress", text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* if possible, change the session's
|
|
Packit |
eace71 |
* ip_address and port to the new TargetAddress for
|
|
Packit |
eace71 |
* leading connection
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
if (iscsi_update_address(&session->conn[cid], value)) {
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
log_error("Login redirection failed, "
|
|
Packit |
eace71 |
"can't handle redirection to %s", value);
|
|
Packit |
eace71 |
return LOGIN_REDIRECTION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("TargetPortalGroupTag", text, end,
|
|
Packit |
eace71 |
&value, &value_end)) {
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* We should have already obtained this
|
|
Packit |
eace71 |
* via discovery, but the value could be stale.
|
|
Packit |
eace71 |
* If the target was reconfigured it will send us
|
|
Packit |
eace71 |
* the updated tpgt.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
tag = strtoul(value, NULL, 0);
|
|
Packit |
eace71 |
if (session->portal_group_tag >= 0) {
|
|
Packit |
eace71 |
if (tag != session->portal_group_tag)
|
|
Packit |
eace71 |
log_debug(2, "Portal group tag "
|
|
Packit |
eace71 |
"mismatch, expected %u, "
|
|
Packit |
eace71 |
"received %u. Updating",
|
|
Packit |
eace71 |
session->portal_group_tag, tag);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
/* we now know the tag */
|
|
Packit |
eace71 |
session->portal_group_tag = tag;
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* any key we don't recognize either
|
|
Packit |
eace71 |
* goes to the auth code, or we choke
|
|
Packit |
eace71 |
* on it
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
ret = get_auth_key_type(auth_client, &text, end);
|
|
Packit |
eace71 |
if (ret != LOGIN_OK)
|
|
Packit |
eace71 |
return ret;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
*data = text;
|
|
Packit |
eace71 |
return LOGIN_OK;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static enum iscsi_login_status
|
|
Packit |
eace71 |
get_op_params_text_keys(iscsi_session_t *session, int cid,
|
|
Packit |
eace71 |
char **data, char *end)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
char *text = *data;
|
|
Packit |
eace71 |
char *value = NULL;
|
|
Packit |
eace71 |
char *value_end = NULL;
|
|
Packit |
eace71 |
size_t size;
|
|
Packit |
eace71 |
iscsi_conn_t *conn = &session->conn[cid];
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (iscsi_find_key_value("TargetAlias", text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
size = value_end - value;
|
|
Packit |
eace71 |
if (session->target_alias &&
|
|
Packit |
eace71 |
strlen(session->target_alias) == size &&
|
|
Packit |
eace71 |
memcmp(session->target_alias, value, size) == 0) {
|
|
Packit |
eace71 |
*data = value_end;
|
|
Packit |
eace71 |
return LOGIN_OK;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
free(session->target_alias);
|
|
Packit |
eace71 |
session->target_alias = malloc(size + 1);
|
|
Packit |
eace71 |
if (!session->target_alias) {
|
|
Packit |
eace71 |
/* Alias not critical. So just print an error */
|
|
Packit |
eace71 |
log_error("Login failed to allocate alias");
|
|
Packit |
eace71 |
*data = value_end;
|
|
Packit |
eace71 |
return LOGIN_OK;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
memcpy(session->target_alias, value, size);
|
|
Packit |
eace71 |
session->target_alias[size] = '\0';
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("TargetAddress", text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
if (iscsi_update_address(conn, value))
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
else {
|
|
Packit |
eace71 |
log_error("Login redirection failed, "
|
|
Packit |
eace71 |
"can't handle redirection to %s",
|
|
Packit |
eace71 |
value);
|
|
Packit |
eace71 |
return LOGIN_REDIRECTION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("TargetPortalGroupTag", text, end,
|
|
Packit |
eace71 |
&value, &value_end)) {
|
|
Packit |
eace71 |
int tag = strtoul(value, NULL, 0);
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* We should have already obtained this
|
|
Packit |
eace71 |
* via discovery, but the value could be stale.
|
|
Packit |
eace71 |
* If the target was reconfigured it will send us
|
|
Packit |
eace71 |
* the updated tpgt.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
if (session->portal_group_tag >= 0) {
|
|
Packit |
eace71 |
if (tag != session->portal_group_tag)
|
|
Packit |
eace71 |
log_debug(2, "Portal group tag "
|
|
Packit |
eace71 |
"mismatch, expected %u, "
|
|
Packit |
eace71 |
"received %u. Updating",
|
|
Packit |
eace71 |
session->portal_group_tag, tag);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
/* we now know the tag */
|
|
Packit |
eace71 |
session->portal_group_tag = tag;
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("InitialR2T", text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
if (session->type == ISCSI_SESSION_TYPE_NORMAL) {
|
|
Packit |
eace71 |
if (value && (strcmp(value, "Yes") == 0))
|
|
Packit |
eace71 |
session->initial_r2t_en = 1;
|
|
Packit |
eace71 |
else
|
|
Packit |
eace71 |
session->initial_r2t_en = 0;
|
|
Packit |
eace71 |
} else
|
|
Packit |
eace71 |
session->irrelevant_keys_bitmap |=
|
|
Packit |
eace71 |
IRRELEVANT_INITIALR2T;
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("ImmediateData", text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
if (session->type == ISCSI_SESSION_TYPE_NORMAL) {
|
|
Packit |
eace71 |
if (value && (strcmp(value, "Yes") == 0))
|
|
Packit |
eace71 |
session->imm_data_en = 1;
|
|
Packit |
eace71 |
else
|
|
Packit |
eace71 |
session->imm_data_en = 0;
|
|
Packit |
eace71 |
} else
|
|
Packit |
eace71 |
session->irrelevant_keys_bitmap |=
|
|
Packit |
eace71 |
IRRELEVANT_IMMEDIATEDATA;
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("MaxRecvDataSegmentLength", text, end,
|
|
Packit |
eace71 |
&value, &value_end)) {
|
|
Packit |
eace71 |
if (session->type == ISCSI_SESSION_TYPE_DISCOVERY ||
|
|
Packit |
eace71 |
!session->t->template->rdma) {
|
|
Packit |
eace71 |
int tgt_max_xmit;
|
|
Packit |
eace71 |
conn_rec_t *conn_rec = &session->nrec.conn[cid];
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
tgt_max_xmit = strtoul(value, NULL, 0);
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* if the rec value is zero it means to use
|
|
Packit |
eace71 |
* what the target gave us.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
if (!conn_rec->iscsi.MaxXmitDataSegmentLength ||
|
|
Packit |
eace71 |
tgt_max_xmit < conn->max_xmit_dlength)
|
|
Packit |
eace71 |
conn->max_xmit_dlength = tgt_max_xmit;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("FirstBurstLength", text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
if (session->type == ISCSI_SESSION_TYPE_NORMAL)
|
|
Packit |
eace71 |
session->first_burst = strtoul(value, NULL, 0);
|
|
Packit |
eace71 |
else
|
|
Packit |
eace71 |
session->irrelevant_keys_bitmap |=
|
|
Packit |
eace71 |
IRRELEVANT_FIRSTBURSTLENGTH;
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("MaxBurstLength", text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* we don't really care, since it's a limit on the target's
|
|
Packit |
eace71 |
* R2Ts, but record it anwyay
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
if (session->type == ISCSI_SESSION_TYPE_NORMAL)
|
|
Packit |
eace71 |
session->max_burst = strtoul(value, NULL, 0);
|
|
Packit |
eace71 |
else
|
|
Packit |
eace71 |
session->irrelevant_keys_bitmap |=
|
|
Packit |
eace71 |
IRRELEVANT_MAXBURSTLENGTH;
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("HeaderDigest", text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
if (strcmp(value, "None") == 0) {
|
|
Packit |
eace71 |
if (conn->hdrdgst_en != ISCSI_DIGEST_CRC32C)
|
|
Packit |
eace71 |
conn->hdrdgst_en = ISCSI_DIGEST_NONE;
|
|
Packit |
eace71 |
else {
|
|
Packit |
eace71 |
log_error("Login negotiation "
|
|
Packit |
eace71 |
"failed, HeaderDigest=CRC32C "
|
|
Packit |
eace71 |
"is required, can't accept "
|
|
Packit |
eace71 |
"%s", text);
|
|
Packit |
eace71 |
return LOGIN_NEGOTIATION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
} else if (strcmp(value, "CRC32C") == 0) {
|
|
Packit |
eace71 |
if (conn->hdrdgst_en != ISCSI_DIGEST_NONE)
|
|
Packit |
eace71 |
conn->hdrdgst_en = ISCSI_DIGEST_CRC32C;
|
|
Packit |
eace71 |
else {
|
|
Packit |
eace71 |
log_error("Login negotiation "
|
|
Packit |
eace71 |
"failed, HeaderDigest=None is "
|
|
Packit |
eace71 |
"required, can't accept %s", text);
|
|
Packit |
eace71 |
return LOGIN_NEGOTIATION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
log_error("Login negotiation failed, "
|
|
Packit |
eace71 |
"can't accept %s", text);
|
|
Packit |
eace71 |
return LOGIN_NEGOTIATION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("DataDigest", text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
if (strcmp(value, "None") == 0) {
|
|
Packit |
eace71 |
if (conn->datadgst_en != ISCSI_DIGEST_CRC32C)
|
|
Packit |
eace71 |
conn->datadgst_en = ISCSI_DIGEST_NONE;
|
|
Packit |
eace71 |
else {
|
|
Packit |
eace71 |
log_error("Login negotiation "
|
|
Packit |
eace71 |
"failed, DataDigest=CRC32C "
|
|
Packit |
eace71 |
"is required, can't accept %s", text);
|
|
Packit |
eace71 |
return LOGIN_NEGOTIATION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
} else if (strcmp(value, "CRC32C") == 0) {
|
|
Packit |
eace71 |
if (conn->datadgst_en != ISCSI_DIGEST_NONE)
|
|
Packit |
eace71 |
conn->datadgst_en = ISCSI_DIGEST_CRC32C;
|
|
Packit |
eace71 |
else {
|
|
Packit |
eace71 |
log_error("Login negotiation "
|
|
Packit |
eace71 |
"failed, DataDigest=None is "
|
|
Packit |
eace71 |
"required, can't accept %s", text);
|
|
Packit |
eace71 |
return LOGIN_NEGOTIATION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
log_error("Login negotiation failed, "
|
|
Packit |
eace71 |
"can't accept %s", text);
|
|
Packit |
eace71 |
return LOGIN_NEGOTIATION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("DefaultTime2Wait", text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
session->def_time2wait = strtoul(value, NULL, 0);
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("DefaultTime2Retain", text, end,
|
|
Packit |
eace71 |
&value, &value_end)) {
|
|
Packit |
eace71 |
session->def_time2retain = strtoul(value, NULL, 0);
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("OFMarker", text, end, &value,
|
|
Packit |
eace71 |
&value_end))
|
|
Packit |
eace71 |
/* result function is AND, target must honor our No */
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
else if (iscsi_find_key_value("OFMarkInt", text, end, &value,
|
|
Packit |
eace71 |
&value_end))
|
|
Packit |
eace71 |
/* we don't do markers, so we don't care */
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
else if (iscsi_find_key_value("IFMarker", text, end, &value,
|
|
Packit |
eace71 |
&value_end))
|
|
Packit |
eace71 |
/* result function is AND, target must honor our No */
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
else if (iscsi_find_key_value("IFMarkInt", text, end, &value,
|
|
Packit |
eace71 |
&value_end))
|
|
Packit |
eace71 |
/* we don't do markers, so we don't care */
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
else if (iscsi_find_key_value("DataPDUInOrder", text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
if (session->type == ISCSI_SESSION_TYPE_NORMAL) {
|
|
Packit |
eace71 |
if (value && strcmp(value, "Yes") == 0)
|
|
Packit |
eace71 |
session->pdu_inorder_en = 1;
|
|
Packit |
eace71 |
else
|
|
Packit |
eace71 |
session->pdu_inorder_en = 0;
|
|
Packit |
eace71 |
} else
|
|
Packit |
eace71 |
session->irrelevant_keys_bitmap |=
|
|
Packit |
eace71 |
IRRELEVANT_DATAPDUINORDER;
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value ("DataSequenceInOrder", text, end,
|
|
Packit |
eace71 |
&value, &value_end)) {
|
|
Packit |
eace71 |
if (session->type == ISCSI_SESSION_TYPE_NORMAL)
|
|
Packit |
eace71 |
if (value && strcmp(value, "Yes") == 0)
|
|
Packit |
eace71 |
session->dataseq_inorder_en = 1;
|
|
Packit |
eace71 |
else
|
|
Packit |
eace71 |
session->dataseq_inorder_en = 0;
|
|
Packit |
eace71 |
else
|
|
Packit |
eace71 |
session->irrelevant_keys_bitmap |=
|
|
Packit |
eace71 |
IRRELEVANT_DATASEQUENCEINORDER;
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("MaxOutstandingR2T", text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
if (session->type == ISCSI_SESSION_TYPE_NORMAL)
|
|
Packit |
eace71 |
session->max_r2t = strtoul(value, NULL, 0);
|
|
Packit |
eace71 |
else
|
|
Packit |
eace71 |
session->irrelevant_keys_bitmap |=
|
|
Packit |
eace71 |
IRRELEVANT_MAXOUTSTANDINGR2T;
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("MaxConnections", text, end, &value,
|
|
Packit |
eace71 |
&value_end)) {
|
|
Packit |
eace71 |
if (session->type == ISCSI_SESSION_TYPE_NORMAL) {
|
|
Packit |
eace71 |
if (strcmp(value, "1")) {
|
|
Packit |
eace71 |
log_error("Login negotiation "
|
|
Packit |
eace71 |
"failed, can't accept Max"
|
|
Packit |
eace71 |
"Connections %s", value);
|
|
Packit |
eace71 |
return LOGIN_NEGOTIATION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
} else
|
|
Packit |
eace71 |
session->irrelevant_keys_bitmap |=
|
|
Packit |
eace71 |
IRRELEVANT_MAXCONNECTIONS;
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("ErrorRecoveryLevel", text, end,
|
|
Packit |
eace71 |
&value, &value_end)) {
|
|
Packit |
eace71 |
if (strcmp(value, "0")) {
|
|
Packit |
eace71 |
log_error("Login negotiation failed, "
|
|
Packit |
eace71 |
"can't accept ErrorRecovery %s", value);
|
|
Packit |
eace71 |
return LOGIN_NEGOTIATION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("RDMAExtensions", text, end,
|
|
Packit |
eace71 |
&value, &value_end)) {
|
|
Packit |
eace71 |
if (session->t->template->rdma &&
|
|
Packit |
eace71 |
strcmp(value, "Yes") != 0) {
|
|
Packit |
eace71 |
log_error("Login negotiation failed, "
|
|
Packit |
eace71 |
"Target must support RDMAExtensions");
|
|
Packit |
eace71 |
return LOGIN_NEGOTIATION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("InitiatorRecvDataSegmentLength", text,
|
|
Packit |
eace71 |
end, &value, &value_end)) {
|
|
Packit |
eace71 |
if (session->t->template->rdma) {
|
|
Packit |
eace71 |
conn->max_recv_dlength = MIN(conn->max_recv_dlength,
|
|
Packit |
eace71 |
strtoul(value, NULL, 0));
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("TargetRecvDataSegmentLength", text,
|
|
Packit |
eace71 |
end, &value, &value_end)) {
|
|
Packit |
eace71 |
if (session->t->template->rdma) {
|
|
Packit |
eace71 |
conn->max_xmit_dlength = MIN(conn->max_xmit_dlength,
|
|
Packit |
eace71 |
strtoul(value, NULL, 0));
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value ("X-com.cisco.protocol", text, end,
|
|
Packit |
eace71 |
&value, &value_end)) {
|
|
Packit |
eace71 |
if (strcmp(value, "NotUnderstood") &&
|
|
Packit |
eace71 |
strcmp(value, "Reject") &&
|
|
Packit |
eace71 |
strcmp(value, "Irrelevant") &&
|
|
Packit |
eace71 |
strcmp(value, "draft20")) {
|
|
Packit |
eace71 |
/* if we didn't get a compatible protocol, fail */
|
|
Packit |
eace71 |
log_error("Login version mismatch, "
|
|
Packit |
eace71 |
"can't accept protocol %s", value);
|
|
Packit |
eace71 |
return LOGIN_VERSION_MISMATCH;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
} else if (iscsi_find_key_value("X-com.cisco.PingTimeout", text, end,
|
|
Packit |
eace71 |
&value, &value_end))
|
|
Packit |
eace71 |
/* we don't really care what the target ends up using */
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
else if (iscsi_find_key_value("X-com.cisco.sendAsyncText", text, end,
|
|
Packit |
eace71 |
&value, &value_end))
|
|
Packit |
eace71 |
/* we don't bother for the target response */
|
|
Packit |
eace71 |
text = value_end;
|
|
Packit |
eace71 |
else {
|
|
Packit |
eace71 |
log_error("Login negotiation failed, couldn't "
|
|
Packit |
eace71 |
"recognize text %s", text);
|
|
Packit |
eace71 |
return LOGIN_NEGOTIATION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
*data = text;
|
|
Packit |
eace71 |
return LOGIN_OK;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static enum iscsi_login_status
|
|
Packit |
eace71 |
check_security_stage_status(iscsi_session_t *session,
|
|
Packit |
eace71 |
struct iscsi_acl *auth_client)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
int debug_status = 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
switch (acl_recv_end(auth_client, session)) {
|
|
Packit |
eace71 |
case AUTH_STATUS_CONTINUE:
|
|
Packit |
eace71 |
/* continue sending PDUs */
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
case AUTH_STATUS_PASS:
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
case AUTH_STATUS_NO_ERROR: /* treat this as an error,
|
|
Packit |
eace71 |
* since we should get a
|
|
Packit |
eace71 |
* different code
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
case AUTH_STATUS_ERROR:
|
|
Packit |
eace71 |
case AUTH_STATUS_FAIL:
|
|
Packit |
eace71 |
default:
|
|
Packit |
eace71 |
if (acl_get_dbg_status(auth_client, &debug_status) !=
|
|
Packit |
eace71 |
AUTH_STATUS_NO_ERROR)
|
|
Packit |
eace71 |
log_error("Login authentication failed "
|
|
Packit |
eace71 |
"with target %s, %s",
|
|
Packit |
eace71 |
session->target_name,
|
|
Packit |
eace71 |
acl_dbg_status_to_text(debug_status));
|
|
Packit |
eace71 |
else
|
|
Packit |
eace71 |
log_error("Login authentication failed "
|
|
Packit |
eace71 |
"with target %s",
|
|
Packit |
eace71 |
session->target_name);
|
|
Packit |
eace71 |
return LOGIN_AUTHENTICATION_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
return LOGIN_OK;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* this assumes the text data is always NULL terminated. The caller can
|
|
Packit |
eace71 |
* always arrange for that by using a slightly larger buffer than the max PDU
|
|
Packit |
eace71 |
* size, and then appending a NULL to the PDU.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static enum iscsi_login_status
|
|
Packit |
eace71 |
iscsi_process_login_response(iscsi_session_t *session, int cid,
|
|
Packit |
eace71 |
struct iscsi_login_rsp *login_rsp,
|
|
Packit |
eace71 |
char *data, int max_data_length)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
int transit = login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT;
|
|
Packit |
eace71 |
char *text = data;
|
|
Packit |
eace71 |
char *end;
|
|
Packit |
eace71 |
int pdu_current_stage, pdu_next_stage;
|
|
Packit |
eace71 |
enum iscsi_login_status ret;
|
|
Packit |
eace71 |
struct iscsi_acl *auth_client;
|
|
Packit |
eace71 |
iscsi_conn_t *conn = &session->conn[cid];
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
auth_client = (session->auth_buffers && session->num_auth_buffers) ?
|
|
Packit |
eace71 |
(struct iscsi_acl *)session->auth_buffers[0].address : NULL;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
end = text + ntoh24(login_rsp->dlength) + 1;
|
|
Packit |
eace71 |
if (end >= (data + max_data_length)) {
|
|
Packit |
eace71 |
log_error("Login failed, process_login_response "
|
|
Packit |
eace71 |
"buffer too small to guarantee NULL "
|
|
Packit |
eace71 |
"termination");
|
|
Packit |
eace71 |
return LOGIN_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* guarantee a trailing NUL */
|
|
Packit |
eace71 |
*end = '\0';
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* if the response status was success, sanity check the response */
|
|
Packit |
eace71 |
if (login_rsp->status_class == ISCSI_STATUS_CLS_SUCCESS) {
|
|
Packit |
eace71 |
/* check the active version */
|
|
Packit |
eace71 |
if (login_rsp->active_version != ISCSI_DRAFT20_VERSION) {
|
|
Packit |
eace71 |
log_error("Login version mismatch, "
|
|
Packit |
eace71 |
"received incompatible active iSCSI "
|
|
Packit |
eace71 |
"version 0x%02x, expected version "
|
|
Packit |
eace71 |
"0x%02x",
|
|
Packit |
eace71 |
login_rsp->active_version,
|
|
Packit |
eace71 |
ISCSI_DRAFT20_VERSION);
|
|
Packit |
eace71 |
return LOGIN_VERSION_MISMATCH;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* make sure the current stage matches */
|
|
Packit |
eace71 |
pdu_current_stage = (login_rsp->flags &
|
|
Packit |
eace71 |
ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2;
|
|
Packit |
eace71 |
if (pdu_current_stage != conn->current_stage) {
|
|
Packit |
eace71 |
log_error("Received invalid login PDU, "
|
|
Packit |
eace71 |
"current stage mismatch, session %d, "
|
|
Packit |
eace71 |
"response %d", conn->current_stage,
|
|
Packit |
eace71 |
pdu_current_stage);
|
|
Packit |
eace71 |
return LOGIN_INVALID_PDU;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* make sure that we're actually advancing if the T-bit is set
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
pdu_next_stage = login_rsp->flags &
|
|
Packit |
eace71 |
ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK;
|
|
Packit |
eace71 |
if (transit && (pdu_next_stage <= conn->current_stage))
|
|
Packit |
eace71 |
return LOGIN_INVALID_PDU;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (conn->current_stage == ISCSI_SECURITY_NEGOTIATION_STAGE) {
|
|
Packit |
eace71 |
if (acl_recv_begin(auth_client) != AUTH_STATUS_NO_ERROR) {
|
|
Packit |
eace71 |
log_error("Login failed because "
|
|
Packit |
eace71 |
"acl_recv_begin failed");
|
|
Packit |
eace71 |
return LOGIN_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (acl_recv_transit_bit(auth_client, transit) !=
|
|
Packit |
eace71 |
AUTH_STATUS_NO_ERROR) {
|
|
Packit |
eace71 |
log_error("Login failed because "
|
|
Packit |
eace71 |
"acl_recv_transit_bit failed");
|
|
Packit |
eace71 |
return LOGIN_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* scan the text data */
|
|
Packit |
eace71 |
while (text && (text < end)) {
|
|
Packit |
eace71 |
/* skip any NULs separating each text key=value pair */
|
|
Packit |
eace71 |
while ((text < end) && (*text == '\0'))
|
|
Packit |
eace71 |
text++;
|
|
Packit |
eace71 |
if (text >= end)
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* handle keys appropriate for each stage */
|
|
Packit |
eace71 |
switch (conn->current_stage) {
|
|
Packit |
eace71 |
case ISCSI_SECURITY_NEGOTIATION_STAGE:{
|
|
Packit |
eace71 |
ret = get_security_text_keys(session, cid,
|
|
Packit |
eace71 |
&text, auth_client, end);
|
|
Packit |
eace71 |
if (ret != LOGIN_OK)
|
|
Packit |
eace71 |
return ret;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
case ISCSI_OP_PARMS_NEGOTIATION_STAGE:{
|
|
Packit |
eace71 |
ret = get_op_params_text_keys(session, cid,
|
|
Packit |
eace71 |
&text, end);
|
|
Packit |
eace71 |
if (ret != LOGIN_OK)
|
|
Packit |
eace71 |
return ret;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
default:
|
|
Packit |
eace71 |
return LOGIN_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (conn->current_stage == ISCSI_SECURITY_NEGOTIATION_STAGE) {
|
|
Packit |
eace71 |
ret = check_security_stage_status(session, auth_client);
|
|
Packit |
eace71 |
if (ret != LOGIN_OK)
|
|
Packit |
eace71 |
return ret;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
/* record some of the PDU fields for later use */
|
|
Packit |
eace71 |
session->tsih = ntohs(login_rsp->tsih);
|
|
Packit |
eace71 |
session->exp_cmdsn = ntohl(login_rsp->exp_cmdsn);
|
|
Packit |
eace71 |
session->max_cmdsn = ntohl(login_rsp->max_cmdsn);
|
|
Packit |
eace71 |
if (login_rsp->status_class == ISCSI_STATUS_CLS_SUCCESS)
|
|
Packit |
eace71 |
conn->exp_statsn = ntohl(login_rsp->statsn) + 1;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (transit) {
|
|
Packit |
eace71 |
/* advance to the next stage */
|
|
Packit |
eace71 |
conn->partial_response = 0;
|
|
Packit |
eace71 |
conn->current_stage = login_rsp->flags &
|
|
Packit |
eace71 |
ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK;
|
|
Packit |
eace71 |
session->irrelevant_keys_bitmap = 0;
|
|
Packit |
eace71 |
} else
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* we got a partial response, don't advance,
|
|
Packit |
eace71 |
* more negotiation to do
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
conn->partial_response = 1;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return LOGIN_OK; /* this PDU is ok, though the login process
|
|
Packit |
eace71 |
* may not be done yet
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static int
|
|
Packit |
eace71 |
add_params_normal_session(iscsi_session_t *session, struct iscsi_hdr *pdu,
|
|
Packit |
eace71 |
char *data, int max_data_length)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
char value[AUTH_STR_MAX_LEN];
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* these are only relevant for normal sessions */
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length, "InitialR2T",
|
|
Packit |
eace71 |
session->initial_r2t_en ? "Yes" : "No"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"ImmediateData",
|
|
Packit |
eace71 |
session->imm_data_en ? "Yes" : "No"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
sprintf(value, "%d", session->max_burst);
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"MaxBurstLength", value))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
sprintf(value, "%d",session->first_burst);
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"FirstBurstLength", value))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* these we must have */
|
|
Packit |
eace71 |
sprintf(value, "%d", session->max_r2t);
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"MaxOutstandingR2T", value))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"MaxConnections", "1"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"DataPDUInOrder", "Yes"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"DataSequenceInOrder", "Yes"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
return 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static int
|
|
Packit |
eace71 |
add_params_transport_specific(iscsi_session_t *session, int cid,
|
|
Packit |
eace71 |
struct iscsi_hdr *pdu, char *data,
|
|
Packit |
eace71 |
int max_data_length)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
char value[AUTH_STR_MAX_LEN];
|
|
Packit |
eace71 |
iscsi_conn_t *conn = &session->conn[cid];
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (session->type == ISCSI_SESSION_TYPE_DISCOVERY ||
|
|
Packit |
eace71 |
!session->t->template->rdma) {
|
|
Packit |
eace71 |
sprintf(value, "%d", conn->max_recv_dlength);
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"MaxRecvDataSegmentLength", value))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
sprintf(value, "%d", conn->max_recv_dlength);
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"InitiatorRecvDataSegmentLength",
|
|
Packit |
eace71 |
value))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
sprintf(value, "%d", conn->max_xmit_dlength);
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"TargetRecvDataSegmentLength",
|
|
Packit |
eace71 |
value))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"RDMAExtensions", "Yes"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
return 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static int
|
|
Packit |
eace71 |
check_irrelevant_keys(iscsi_session_t *session, struct iscsi_hdr *pdu,
|
|
Packit |
eace71 |
char *data, int max_data_length)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
/* If you receive irrelevant keys, just check them from the irrelevant
|
|
Packit |
eace71 |
* keys bitmap and respond with the key=Irrelevant text
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (session->irrelevant_keys_bitmap & IRRELEVANT_MAXCONNECTIONS)
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"MaxConnections", "Irrelevant"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (session->irrelevant_keys_bitmap & IRRELEVANT_INITIALR2T)
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"InitialR2T", "Irrelevant"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (session->irrelevant_keys_bitmap & IRRELEVANT_IMMEDIATEDATA)
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"ImmediateData", "Irrelevant"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (session->irrelevant_keys_bitmap & IRRELEVANT_MAXBURSTLENGTH)
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"MaxBurstLength", "Irrelevant"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (session->irrelevant_keys_bitmap & IRRELEVANT_FIRSTBURSTLENGTH)
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"FirstBurstLength", "Irrelevant"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (session->irrelevant_keys_bitmap & IRRELEVANT_MAXOUTSTANDINGR2T)
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"MaxOutstandingR2T", "Irrelevant"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (session->irrelevant_keys_bitmap & IRRELEVANT_DATAPDUINORDER)
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"DataPDUInOrder", "Irrelevant"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (session->irrelevant_keys_bitmap & IRRELEVANT_DATASEQUENCEINORDER )
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"DataSequenceInOrder", "Irrelevant"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static int
|
|
Packit |
eace71 |
fill_crc_digest_text(iscsi_conn_t *conn, struct iscsi_hdr *pdu,
|
|
Packit |
eace71 |
char *data, int max_data_length)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
switch (conn->hdrdgst_en) {
|
|
Packit |
eace71 |
case ISCSI_DIGEST_NONE:
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"HeaderDigest", "None"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
case ISCSI_DIGEST_CRC32C:
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"HeaderDigest", "CRC32C"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
case ISCSI_DIGEST_CRC32C_NONE:
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"HeaderDigest", "CRC32C,None"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
default:
|
|
Packit |
eace71 |
case ISCSI_DIGEST_NONE_CRC32C:
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"HeaderDigest", "None,CRC32C"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
switch (conn->datadgst_en) {
|
|
Packit |
eace71 |
case ISCSI_DIGEST_NONE:
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"DataDigest", "None"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
case ISCSI_DIGEST_CRC32C:
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"DataDigest", "CRC32C"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
case ISCSI_DIGEST_CRC32C_NONE:
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"DataDigest", "CRC32C,None"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
default:
|
|
Packit |
eace71 |
case ISCSI_DIGEST_NONE_CRC32C:
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"DataDigest", "None,CRC32C"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
return 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static int
|
|
Packit |
eace71 |
fill_op_params_text(iscsi_session_t *session, int cid, struct iscsi_hdr *pdu,
|
|
Packit |
eace71 |
char *data, int max_data_length, int *transit)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
char value[AUTH_STR_MAX_LEN];
|
|
Packit |
eace71 |
iscsi_conn_t *conn = &session->conn[cid];
|
|
Packit |
eace71 |
int rdma;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* we always try to go from op params to full feature stage */
|
|
Packit |
eace71 |
conn->current_stage = ISCSI_OP_PARMS_NEGOTIATION_STAGE;
|
|
Packit |
eace71 |
conn->next_stage = ISCSI_FULL_FEATURE_PHASE;
|
|
Packit |
eace71 |
*transit = 1;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
rdma = (session->type == ISCSI_SESSION_TYPE_NORMAL) &&
|
|
Packit |
eace71 |
session->t->template->rdma;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* If we haven't gotten a partial response, then either we shouldn't be
|
|
Packit |
eace71 |
* here, or we just switched to this stage, and need to start offering
|
|
Packit |
eace71 |
* keys.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
if (!conn->partial_response) {
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* request the desired settings the first time
|
|
Packit |
eace71 |
* we are in this stage
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
if (!rdma &&
|
|
Packit |
eace71 |
!fill_crc_digest_text(conn, pdu, data, max_data_length))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
sprintf(value, "%d", session->def_time2wait);
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"DefaultTime2Wait", value))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
sprintf(value, "%d", session->def_time2retain);
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"DefaultTime2Retain", value))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"IFMarker", "No"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"OFMarker", "No"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"ErrorRecoveryLevel", "0"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (session->type == ISCSI_SESSION_TYPE_NORMAL) {
|
|
Packit |
eace71 |
if (!add_params_normal_session(session, pdu, data,
|
|
Packit |
eace71 |
max_data_length))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (!add_params_transport_specific(session, cid,
|
|
Packit |
eace71 |
pdu, data,
|
|
Packit |
eace71 |
max_data_length))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
sprintf(value, "%d", conn->max_recv_dlength);
|
|
Packit |
eace71 |
if (!iscsi_add_text(pdu, data, max_data_length,
|
|
Packit |
eace71 |
"MaxRecvDataSegmentLength", value))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
if (!check_irrelevant_keys(session, pdu, data, max_data_length))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (rdma &&
|
|
Packit |
eace71 |
!fill_crc_digest_text(conn, pdu, data, max_data_length))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static void
|
|
Packit |
eace71 |
enum_auth_keys(struct iscsi_acl *auth_client, struct iscsi_hdr *pdu,
|
|
Packit |
eace71 |
char *data, int max_data_length, int keytype)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
int present = 0, rc;
|
|
Packit |
eace71 |
char *key = (char *)acl_get_key_name(keytype);
|
|
Packit |
eace71 |
int key_length = key ? strlen(key) : 0;
|
|
Packit |
eace71 |
int pdu_length = ntoh24(pdu->dlength);
|
|
Packit |
eace71 |
char *auth_value = data + pdu_length + key_length + 1;
|
|
Packit |
eace71 |
unsigned int max_length = max_data_length - (pdu_length
|
|
Packit |
eace71 |
+ key_length + 1);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* add the key/value pairs the auth code wants to send
|
|
Packit |
eace71 |
* directly to the PDU, since they could in theory be large.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
rc = acl_send_key_val(auth_client, keytype, &present, auth_value,
|
|
Packit |
eace71 |
max_length);
|
|
Packit |
eace71 |
if ((rc == AUTH_STATUS_NO_ERROR) && present) {
|
|
Packit |
eace71 |
/* actually fill in the key */
|
|
Packit |
eace71 |
strncpy(&data[pdu_length], key, key_length);
|
|
Packit |
eace71 |
pdu_length += key_length;
|
|
Packit |
eace71 |
data[pdu_length] = '=';
|
|
Packit |
eace71 |
pdu_length++;
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* adjust the PDU's data segment length
|
|
Packit |
eace71 |
* to include the value and trailing NUL
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
pdu_length += strlen(auth_value) + 1;
|
|
Packit |
eace71 |
hton24(pdu->dlength, pdu_length);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static int
|
|
Packit |
eace71 |
fill_security_params_text(iscsi_session_t *session, int cid, struct iscsi_hdr *pdu,
|
|
Packit |
eace71 |
struct iscsi_acl *auth_client, char *data,
|
|
Packit |
eace71 |
int max_data_length, int *transit)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
int keytype = AUTH_KEY_TYPE_NONE;
|
|
Packit |
eace71 |
int rc = acl_send_transit_bit(auth_client, transit);
|
|
Packit |
eace71 |
iscsi_conn_t *conn = &session->conn[cid];
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* see if we're ready for a stage change */
|
|
Packit |
eace71 |
if (rc != AUTH_STATUS_NO_ERROR)
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (*transit) {
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* discovery sessions can go right to full-feature phase,
|
|
Packit |
eace71 |
* unless they want to non-standard values for the few relevant
|
|
Packit |
eace71 |
* keys, or want to offer vendor-specific keys
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
if (session->type == ISCSI_SESSION_TYPE_DISCOVERY)
|
|
Packit |
eace71 |
if ((conn->hdrdgst_en != ISCSI_DIGEST_NONE) ||
|
|
Packit |
eace71 |
(conn->datadgst_en != ISCSI_DIGEST_NONE) ||
|
|
Packit |
eace71 |
(conn->max_recv_dlength !=
|
|
Packit |
eace71 |
ISCSI_DEF_MAX_RECV_SEG_LEN))
|
|
Packit |
eace71 |
conn->next_stage =
|
|
Packit |
eace71 |
ISCSI_OP_PARMS_NEGOTIATION_STAGE;
|
|
Packit |
eace71 |
else
|
|
Packit |
eace71 |
conn->next_stage = ISCSI_FULL_FEATURE_PHASE;
|
|
Packit |
eace71 |
else
|
|
Packit |
eace71 |
conn->next_stage = ISCSI_OP_PARMS_NEGOTIATION_STAGE;
|
|
Packit |
eace71 |
} else
|
|
Packit |
eace71 |
conn->next_stage = ISCSI_SECURITY_NEGOTIATION_STAGE;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* enumerate all the keys the auth code might want to send */
|
|
Packit |
eace71 |
while (acl_get_next_key_type(&keytype) == AUTH_STATUS_NO_ERROR)
|
|
Packit |
eace71 |
enum_auth_keys(auth_client, pdu, data, max_data_length,
|
|
Packit |
eace71 |
keytype);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* iscsi_make_login_pdu - Prepare the login pdu to be sent to iSCSI target.
|
|
Packit |
eace71 |
* @session: session for which login is initiated.
|
|
Packit |
eace71 |
* @pdu: login header
|
|
Packit |
eace71 |
* @data: contains text keys to be negotiated during login
|
|
Packit |
eace71 |
* @max_data_length: data size
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* Description:
|
|
Packit |
eace71 |
* Based on whether authentication is enabled or not, corresponding text
|
|
Packit |
eace71 |
* keys are filled up in login pdu.
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
**/
|
|
Packit |
eace71 |
static int
|
|
Packit |
eace71 |
iscsi_make_login_pdu(iscsi_session_t *session, int cid, struct iscsi_hdr *hdr,
|
|
Packit |
eace71 |
char *data, int max_data_length)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
int transit = 0;
|
|
Packit |
eace71 |
int ret;
|
|
Packit |
eace71 |
struct iscsi_login *login_hdr = (struct iscsi_login *)hdr;
|
|
Packit |
eace71 |
struct iscsi_acl *auth_client;
|
|
Packit |
eace71 |
iscsi_conn_t *conn = &session->conn[cid];
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
auth_client = (session->auth_buffers && session->num_auth_buffers) ?
|
|
Packit |
eace71 |
(struct iscsi_acl *)session->auth_buffers[0].address : NULL;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* initialize the PDU header */
|
|
Packit |
eace71 |
memset(login_hdr, 0, sizeof(*login_hdr));
|
|
Packit |
eace71 |
login_hdr->opcode = ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE;
|
|
Packit |
eace71 |
login_hdr->cid = 0;
|
|
Packit |
eace71 |
memcpy(login_hdr->isid, session->isid, sizeof(session->isid));
|
|
Packit |
eace71 |
login_hdr->tsih = 0;
|
|
Packit |
eace71 |
login_hdr->cmdsn = htonl(session->cmdsn);
|
|
Packit |
eace71 |
/* don't increment on immediate */
|
|
Packit |
eace71 |
login_hdr->min_version = ISCSI_DRAFT20_VERSION;
|
|
Packit |
eace71 |
login_hdr->max_version = ISCSI_DRAFT20_VERSION;
|
|
Packit |
eace71 |
login_hdr->exp_statsn = htonl(conn->exp_statsn);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* the very first Login PDU has some additional requirements,
|
|
Packit |
eace71 |
* and we need to decide what stage to start in.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
if (conn->current_stage == ISCSI_INITIAL_LOGIN_STAGE) {
|
|
Packit |
eace71 |
if (session->initiator_name && session->initiator_name[0]) {
|
|
Packit |
eace71 |
if (!iscsi_add_text(hdr, data, max_data_length,
|
|
Packit |
eace71 |
"InitiatorName", session->initiator_name))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
log_error("InitiatorName is required "
|
|
Packit |
eace71 |
"on the first Login PDU");
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
if (session->initiator_alias && session->initiator_alias[0]) {
|
|
Packit |
eace71 |
if (!iscsi_add_text(hdr, data, max_data_length,
|
|
Packit |
eace71 |
"InitiatorAlias", session->initiator_alias))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if ((session->target_name && session->target_name[0]) &&
|
|
Packit |
eace71 |
(session->type == ISCSI_SESSION_TYPE_NORMAL)) {
|
|
Packit |
eace71 |
if (!iscsi_add_text(hdr, data, max_data_length,
|
|
Packit |
eace71 |
"TargetName", session->target_name))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (!iscsi_add_text(hdr, data, max_data_length,
|
|
Packit |
eace71 |
"SessionType", (session->type ==
|
|
Packit |
eace71 |
ISCSI_SESSION_TYPE_DISCOVERY) ? "Discovery" : "Normal"))
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (auth_client)
|
|
Packit |
eace71 |
/* we're prepared to do authentication */
|
|
Packit |
eace71 |
conn->current_stage = conn->next_stage =
|
|
Packit |
eace71 |
ISCSI_SECURITY_NEGOTIATION_STAGE;
|
|
Packit |
eace71 |
else
|
|
Packit |
eace71 |
/* can't do any authentication, skip that stage */
|
|
Packit |
eace71 |
conn->current_stage = conn->next_stage =
|
|
Packit |
eace71 |
ISCSI_OP_PARMS_NEGOTIATION_STAGE;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* fill in text based on the stage */
|
|
Packit |
eace71 |
switch (conn->current_stage) {
|
|
Packit |
eace71 |
case ISCSI_OP_PARMS_NEGOTIATION_STAGE:{
|
|
Packit |
eace71 |
ret = fill_op_params_text(session, cid, hdr, data,
|
|
Packit |
eace71 |
max_data_length, &transit);
|
|
Packit |
eace71 |
if (!ret)
|
|
Packit |
eace71 |
return ret;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
case ISCSI_SECURITY_NEGOTIATION_STAGE:{
|
|
Packit |
eace71 |
ret = fill_security_params_text(session, cid, hdr,
|
|
Packit |
eace71 |
auth_client, data, max_data_length,
|
|
Packit |
eace71 |
&transit);
|
|
Packit |
eace71 |
if (!ret)
|
|
Packit |
eace71 |
return ret;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
case ISCSI_FULL_FEATURE_PHASE:
|
|
Packit |
eace71 |
log_error("Can't send login PDUs in full "
|
|
Packit |
eace71 |
"feature phase");
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
default:
|
|
Packit |
eace71 |
log_error("Can't send login PDUs in unknown "
|
|
Packit |
eace71 |
"stage %d", conn->current_stage);
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* fill in the flags */
|
|
Packit |
eace71 |
login_hdr->flags = 0;
|
|
Packit |
eace71 |
login_hdr->flags |= conn->current_stage << 2;
|
|
Packit |
eace71 |
if (transit) {
|
|
Packit |
eace71 |
/* transit to the next stage */
|
|
Packit |
eace71 |
login_hdr->flags |= conn->next_stage;
|
|
Packit |
eace71 |
login_hdr->flags |= ISCSI_FLAG_LOGIN_TRANSIT;
|
|
Packit |
eace71 |
} else
|
|
Packit |
eace71 |
/* next == current */
|
|
Packit |
eace71 |
login_hdr->flags |= conn->current_stage;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static enum iscsi_login_status
|
|
Packit |
eace71 |
check_for_authentication(iscsi_session_t *session,
|
|
Packit |
eace71 |
struct iscsi_acl *auth_client)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
enum iscsi_login_status ret = LOGIN_FAILED;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
auth_client = (struct iscsi_acl *)session->auth_buffers[0].address;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* prepare for authentication */
|
|
Packit |
eace71 |
if (acl_init(TYPE_INITIATOR, session->num_auth_buffers,
|
|
Packit |
eace71 |
session->auth_buffers) != AUTH_STATUS_NO_ERROR) {
|
|
Packit |
eace71 |
log_error("Couldn't initialize authentication");
|
|
Packit |
eace71 |
return LOGIN_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (session->username &&
|
|
Packit |
eace71 |
(acl_set_user_name(auth_client, session->username) !=
|
|
Packit |
eace71 |
AUTH_STATUS_NO_ERROR)) {
|
|
Packit |
eace71 |
log_error("Couldn't set username");
|
|
Packit |
eace71 |
goto end;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (session->password && (acl_set_passwd(auth_client,
|
|
Packit |
eace71 |
session->password, session->password_length) !=
|
|
Packit |
eace71 |
AUTH_STATUS_NO_ERROR)) {
|
|
Packit |
eace71 |
log_error("Couldn't set password");
|
|
Packit |
eace71 |
goto end;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (acl_set_ip_sec(auth_client, 1) != AUTH_STATUS_NO_ERROR) {
|
|
Packit |
eace71 |
log_error("Couldn't set IPSec");
|
|
Packit |
eace71 |
goto end;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (acl_set_auth_rmt(auth_client, session->bidirectional_auth) !=
|
|
Packit |
eace71 |
AUTH_STATUS_NO_ERROR) {
|
|
Packit |
eace71 |
log_error("Couldn't set remote authentication");
|
|
Packit |
eace71 |
goto end;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
return LOGIN_OK;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
end:
|
|
Packit |
eace71 |
if (auth_client && acl_finish(auth_client) != AUTH_STATUS_NO_ERROR) {
|
|
Packit |
eace71 |
log_error("Login failed, error finishing auth_client");
|
|
Packit |
eace71 |
if (ret == LOGIN_OK)
|
|
Packit |
eace71 |
ret = LOGIN_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
return ret;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static enum iscsi_login_status
|
|
Packit |
eace71 |
check_status_login_response(iscsi_session_t *session, int cid,
|
|
Packit |
eace71 |
struct iscsi_login_rsp *login_rsp,
|
|
Packit |
eace71 |
char *data, int max_data_length, int *final)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
enum iscsi_login_status ret;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
switch (login_rsp->status_class) {
|
|
Packit |
eace71 |
case ISCSI_STATUS_CLS_SUCCESS:
|
|
Packit |
eace71 |
/* process this response and possibly continue sending PDUs */
|
|
Packit |
eace71 |
ret = iscsi_process_login_response(session, cid, login_rsp,
|
|
Packit |
eace71 |
data, max_data_length);
|
|
Packit |
eace71 |
if (ret != LOGIN_OK) /* pass back whatever
|
|
Packit |
eace71 |
* error we discovered
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
*final = 1;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
case ISCSI_STATUS_CLS_REDIRECT:
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* we need to process this response to get the
|
|
Packit |
eace71 |
* TargetAddress of the redirect, but we don't care
|
|
Packit |
eace71 |
* about the return code.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
iscsi_process_login_response(session, cid, login_rsp,
|
|
Packit |
eace71 |
data, max_data_length);
|
|
Packit |
eace71 |
ret = LOGIN_REDIRECT;
|
|
Packit |
eace71 |
*final = 1;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
case ISCSI_STATUS_CLS_INITIATOR_ERR:
|
|
Packit |
eace71 |
if (login_rsp->status_detail ==
|
|
Packit |
eace71 |
ISCSI_LOGIN_STATUS_AUTH_FAILED) {
|
|
Packit |
eace71 |
log_error("Login failed to authenticate "
|
|
Packit |
eace71 |
"with target %s", session->target_name);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
ret = LOGIN_OK;
|
|
Packit |
eace71 |
*final = 1;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
default:
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* some sort of error, login terminated unsuccessfully,
|
|
Packit |
eace71 |
* though this function did it's job.
|
|
Packit |
eace71 |
* the caller must check the status_class and
|
|
Packit |
eace71 |
* status_detail and decide what to do next.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
ret = LOGIN_OK;
|
|
Packit |
eace71 |
*final = 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
return ret;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
int
|
|
Packit |
eace71 |
iscsi_login_begin(iscsi_session_t *session, iscsi_login_context_t *c)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
iscsi_conn_t *conn = &session->conn[c->cid];
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
c->auth_client = NULL;
|
|
Packit |
eace71 |
c->login_rsp = (struct iscsi_login_rsp *)&c->pdu;
|
|
Packit |
eace71 |
c->received_pdu = 0;
|
|
Packit |
eace71 |
c->timeout = 0;
|
|
Packit |
eace71 |
c->final = 0;
|
|
Packit |
eace71 |
c->ret = LOGIN_FAILED;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* prepare the session of the connection is leading */
|
|
Packit |
eace71 |
if (c->cid ==0) {
|
|
Packit |
eace71 |
session->cmdsn = 1;
|
|
Packit |
eace71 |
session->exp_cmdsn = 1;
|
|
Packit |
eace71 |
session->max_cmdsn = 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
conn->current_stage = ISCSI_INITIAL_LOGIN_STAGE;
|
|
Packit |
eace71 |
conn->partial_response = 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (session->auth_buffers && session->num_auth_buffers) {
|
|
Packit |
eace71 |
c->ret = check_for_authentication(session, c->auth_client);
|
|
Packit |
eace71 |
if (c->ret != LOGIN_OK)
|
|
Packit |
eace71 |
return 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
int
|
|
Packit |
eace71 |
iscsi_login_req(iscsi_session_t *session, iscsi_login_context_t *c)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
iscsi_conn_t *conn = &session->conn[c->cid];
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
c->final = 0;
|
|
Packit |
eace71 |
c->timeout = 0;
|
|
Packit |
eace71 |
c->login_rsp = (struct iscsi_login_rsp *)&c->pdu;
|
|
Packit |
eace71 |
c->ret = LOGIN_FAILED;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
memset(c->buffer, 0, c->bufsize);
|
|
Packit |
eace71 |
c->data = c->buffer;
|
|
Packit |
eace71 |
c->max_data_length = c->bufsize;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* pick the appropriate timeout. If we know the target has
|
|
Packit |
eace71 |
* responded before, and we're in the security stage, we use a
|
|
Packit |
eace71 |
* longer timeout, since the authentication alogorithms can
|
|
Packit |
eace71 |
* take a while, especially if the target has to go talk to a
|
|
Packit |
eace71 |
* tacacs or RADIUS server (which may or may not be
|
|
Packit |
eace71 |
* responding).
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
if (c->received_pdu && (conn->current_stage ==
|
|
Packit |
eace71 |
ISCSI_SECURITY_NEGOTIATION_STAGE))
|
|
Packit |
eace71 |
c->timeout = conn->auth_timeout;
|
|
Packit |
eace71 |
else
|
|
Packit |
eace71 |
c->timeout = conn->login_timeout;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* fill in the PDU header and text data based on the login
|
|
Packit |
eace71 |
* stage that we're in
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
if (!iscsi_make_login_pdu(session, c->cid, &c->pdu, c->data,
|
|
Packit |
eace71 |
c->max_data_length)) {
|
|
Packit |
eace71 |
log_error("login failed, couldn't make a login PDU");
|
|
Packit |
eace71 |
c->ret = LOGIN_FAILED;
|
|
Packit |
eace71 |
goto done;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* send a PDU to the target */
|
|
Packit |
eace71 |
if (!iscsi_io_send_pdu(conn, &c->pdu, ISCSI_DIGEST_NONE,
|
|
Packit |
eace71 |
c->data, ISCSI_DIGEST_NONE, c->timeout)) {
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* FIXME: caller might want us to distinguish I/O
|
|
Packit |
eace71 |
* error and timeout. Might want to switch portals on
|
|
Packit |
eace71 |
* timeouts, but not I/O errors.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
log_error("Login I/O error, failed to send a PDU");
|
|
Packit |
eace71 |
c->ret = LOGIN_IO_ERROR;
|
|
Packit |
eace71 |
goto done;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
done:
|
|
Packit |
eace71 |
if (c->auth_client && acl_finish(c->auth_client) !=
|
|
Packit |
eace71 |
AUTH_STATUS_NO_ERROR) {
|
|
Packit |
eace71 |
log_error("Login failed, error finishing c->auth_client");
|
|
Packit |
eace71 |
if (c->ret == LOGIN_OK)
|
|
Packit |
eace71 |
c->ret = LOGIN_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
return 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
int
|
|
Packit |
eace71 |
iscsi_login_rsp(iscsi_session_t *session, iscsi_login_context_t *c)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
iscsi_conn_t *conn = &session->conn[c->cid];
|
|
Packit |
eace71 |
int err;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* read the target's response into the same buffer */
|
|
Packit |
eace71 |
err = iscsi_io_recv_pdu(conn, &c->pdu, ISCSI_DIGEST_NONE, c->data,
|
|
Packit |
eace71 |
c->max_data_length, ISCSI_DIGEST_NONE,
|
|
Packit |
eace71 |
c->timeout);
|
|
Packit |
eace71 |
if (err == -EAGAIN) {
|
|
Packit |
eace71 |
goto done;
|
|
Packit |
eace71 |
} else if (err < 0) {
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* FIXME: caller might want us to distinguish I/O
|
|
Packit |
eace71 |
* error and timeout. Might want to switch portals on
|
|
Packit |
eace71 |
* timeouts, but not I/O errors.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
log_error("Login I/O error, failed to receive a PDU");
|
|
Packit |
eace71 |
c->ret = LOGIN_IO_ERROR;
|
|
Packit |
eace71 |
goto done;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
err = -EIO;
|
|
Packit |
eace71 |
c->received_pdu = 1;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* check the PDU response type */
|
|
Packit |
eace71 |
if (c->pdu.opcode == (ISCSI_OP_LOGIN_RSP | 0xC0)) {
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* it's probably a draft 8 login response,
|
|
Packit |
eace71 |
* which we can't deal with
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
log_error("Received iSCSI draft 8 login "
|
|
Packit |
eace71 |
"response opcode 0x%x, expected draft "
|
|
Packit |
eace71 |
"20 login response 0x%2x",
|
|
Packit |
eace71 |
c->pdu.opcode, ISCSI_OP_LOGIN_RSP);
|
|
Packit |
eace71 |
c->ret = LOGIN_VERSION_MISMATCH;
|
|
Packit |
eace71 |
goto done;
|
|
Packit |
eace71 |
} else if (c->pdu.opcode != ISCSI_OP_LOGIN_RSP) {
|
|
Packit |
eace71 |
c->ret = LOGIN_INVALID_PDU;
|
|
Packit |
eace71 |
goto done;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* give the caller the status class and detail from the last
|
|
Packit |
eace71 |
* login response PDU received
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
c->status_class = c->login_rsp->status_class;
|
|
Packit |
eace71 |
c->status_detail = c->login_rsp->status_detail;
|
|
Packit |
eace71 |
log_debug(1, "login response status %02d%02d",
|
|
Packit |
eace71 |
c->status_class, c->status_detail);
|
|
Packit |
eace71 |
c->ret = check_status_login_response(session, c->cid,
|
|
Packit |
eace71 |
c->login_rsp, c->data, c->max_data_length,
|
|
Packit |
eace71 |
&c->final);
|
|
Packit |
eace71 |
if (c->final)
|
|
Packit |
eace71 |
goto done;
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
done:
|
|
Packit |
eace71 |
if (c->auth_client && acl_finish(c->auth_client) !=
|
|
Packit |
eace71 |
AUTH_STATUS_NO_ERROR) {
|
|
Packit |
eace71 |
log_error("Login failed, error finishing c->auth_client");
|
|
Packit |
eace71 |
if (c->ret == LOGIN_OK)
|
|
Packit |
eace71 |
c->ret = LOGIN_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
return err;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* iscsi_login - attempt to login to the target.
|
|
Packit |
eace71 |
* @session: login is initiated over this session
|
|
Packit |
eace71 |
* @buffer: holds login pdu
|
|
Packit |
eace71 |
* @bufsize: size of login pdu
|
|
Packit |
eace71 |
* @status_class: holds either success or failure as status of login
|
|
Packit |
eace71 |
* @status_detail: contains details based on the login status
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* Description:
|
|
Packit |
eace71 |
* The caller must check the status class to determine if the login
|
|
Packit |
eace71 |
* succeeded. A return of 1 does not mean the login succeeded, it just
|
|
Packit |
eace71 |
* means this function worked, and the status class is valid info.
|
|
Packit |
eace71 |
* This allows the caller to decide whether or not to retry logins, so
|
|
Packit |
eace71 |
* that we don't have any policy logic here.
|
|
Packit |
eace71 |
**/
|
|
Packit |
eace71 |
enum iscsi_login_status
|
|
Packit |
eace71 |
iscsi_login(iscsi_session_t *session, int cid, char *buffer, size_t bufsize,
|
|
Packit |
eace71 |
uint8_t *status_class, uint8_t *status_detail)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
iscsi_conn_t *conn = &session->conn[cid];
|
|
Packit |
eace71 |
iscsi_login_context_t *c = &conn->login_context;
|
|
Packit |
eace71 |
struct timeval connection_timer;
|
|
Packit |
eace71 |
struct pollfd pfd;
|
|
Packit |
eace71 |
int ret, timeout;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* assume iscsi_login is only called from discovery, so it is
|
|
Packit |
eace71 |
* safe to always set to zero
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
conn->exp_statsn = 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
c->cid = cid;
|
|
Packit |
eace71 |
c->buffer = buffer;
|
|
Packit |
eace71 |
c->bufsize = bufsize;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (iscsi_login_begin(session, c))
|
|
Packit |
eace71 |
return c->ret;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
do {
|
|
Packit |
eace71 |
if (iscsi_login_req(session, c))
|
|
Packit |
eace71 |
return c->ret;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* TODO: merge the poll and req/rsp code with the discovery
|
|
Packit |
eace71 |
* poll and text req/rsp.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
iscsi_timer_set(&connection_timer,
|
|
Packit |
eace71 |
session->conn[0].active_timeout);
|
|
Packit |
eace71 |
timeout = iscsi_timer_msecs_until(&connection_timer);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
memset(&pfd, 0, sizeof (pfd));
|
|
Packit |
eace71 |
pfd.fd = conn->socket_fd;
|
|
Packit |
eace71 |
pfd.events = POLLIN | POLLPRI;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
repoll:
|
|
Packit |
eace71 |
pfd.revents = 0;
|
|
Packit |
eace71 |
ret = poll(&pfd, 1, timeout);
|
|
Packit |
eace71 |
log_debug(7, "%s: Poll return %d", __FUNCTION__, ret);
|
|
Packit |
eace71 |
if (iscsi_timer_expired(&connection_timer)) {
|
|
Packit |
eace71 |
log_warning("Login response timeout. Waited %d "
|
|
Packit |
eace71 |
"seconds and did not get response PDU.",
|
|
Packit |
eace71 |
session->conn[0].active_timeout);
|
|
Packit |
eace71 |
c->ret = LOGIN_FAILED;
|
|
Packit |
eace71 |
return c->ret;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (ret > 0) {
|
|
Packit |
eace71 |
if (pfd.revents & (POLLIN | POLLPRI)) {
|
|
Packit |
eace71 |
ret = iscsi_login_rsp(session, c);
|
|
Packit |
eace71 |
if (ret == -EAGAIN)
|
|
Packit |
eace71 |
goto repoll;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (status_class)
|
|
Packit |
eace71 |
*status_class = c->status_class;
|
|
Packit |
eace71 |
if (status_detail)
|
|
Packit |
eace71 |
*status_detail = c->status_detail;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (ret)
|
|
Packit |
eace71 |
return c->ret;
|
|
Packit |
eace71 |
} else if (pfd.revents & POLLHUP) {
|
|
Packit |
eace71 |
log_warning("Login POLLHUP");
|
|
Packit |
eace71 |
c->ret = LOGIN_FAILED;
|
|
Packit |
eace71 |
return c->ret;
|
|
Packit |
eace71 |
} else if (pfd.revents & POLLNVAL) {
|
|
Packit |
eace71 |
log_warning("Login POLLNVAL");
|
|
Packit |
eace71 |
c->ret = LOGIN_IO_ERROR;
|
|
Packit |
eace71 |
return c->ret;
|
|
Packit |
eace71 |
} else if (pfd.revents & POLLERR) {
|
|
Packit |
eace71 |
log_warning("Login POLLERR");
|
|
Packit |
eace71 |
c->ret = LOGIN_IO_ERROR;
|
|
Packit |
eace71 |
return c->ret;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
} else if (ret < 0) {
|
|
Packit |
eace71 |
log_error("Login poll error.");
|
|
Packit |
eace71 |
c->ret = LOGIN_FAILED;
|
|
Packit |
eace71 |
return c->ret;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
} while (conn->current_stage != ISCSI_FULL_FEATURE_PHASE);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
c->ret = LOGIN_OK;
|
|
Packit |
eace71 |
if (c->auth_client && acl_finish(c->auth_client) !=
|
|
Packit |
eace71 |
AUTH_STATUS_NO_ERROR) {
|
|
Packit |
eace71 |
log_error("Login failed, error finishing c->auth_client");
|
|
Packit |
eace71 |
if (c->ret == LOGIN_OK)
|
|
Packit |
eace71 |
c->ret = LOGIN_FAILED;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return c->ret;
|
|
Packit |
eace71 |
}
|