Adapt to bignum representation of target.

Fix negotiation for mutual authentication.
Prepare for more CHAP types.
Fix crashes for invalid socket descriptors passed to kernel.
Protect usecount with connection mutex, avoids race on connection close.
Minor cosmetics.
This commit is contained in:
mlelstv 2023-11-25 10:08:27 +00:00
parent 7f1bdbd373
commit 4ccc61761f
6 changed files with 145 additions and 70 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: iscsi.h,v 1.4 2016/06/15 04:30:30 mlelstv Exp $ */
/* $NetBSD: iscsi.h,v 1.5 2023/11/25 10:08:27 mlelstv Exp $ */
/*-
* Copyright (c) 2004,2006,2011 The NetBSD Foundation, Inc.
@ -54,6 +54,13 @@ typedef enum {
Indicates SRP authentication (for future use).
*/
typedef enum {
ISCSI_CHAP_MD5 = 5,
ISCSI_CHAP_SHA1 = 6,
ISCSI_CHAP_SHA256 = 7,
ISCSI_CHAP_SHA3_256 = 8
} iscsi_chap_types_t;
typedef struct {
unsigned int mutual_auth:1;
unsigned int is_secure:1;

View File

@ -1,4 +1,4 @@
/* $NetBSD: iscsi_globals.h,v 1.27 2022/09/13 13:09:16 mlelstv Exp $ */
/* $NetBSD: iscsi_globals.h,v 1.28 2023/11/25 10:08:27 mlelstv Exp $ */
/*-
* Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@ -135,11 +135,10 @@
/* Connection state */
typedef enum {
/* first three correspond to CSG/NSG coding */
ST_SEC_NEG = 0, /* security negotiation phase */
ST_OP_NEG = 1, /* operational negotiation phase */
ST_SEC_FIN = 1, /* switch from SEC after mutual CHAP */
ST_OP_NEG = 2, /* operational negotiation phase */
ST_FULL_FEATURE = 3, /* full feature phase */
/* rest is internal */
ST_WINDING_DOWN = 4, /* connection termination initiated, logging out */
ST_LOGOUT_SENT = 5, /* logout has been sent */
ST_SETTLING = 6, /* waiting for things to settle down */

View File

@ -1,4 +1,4 @@
/* $NetBSD: iscsi_ioctl.c,v 1.33 2022/09/13 13:09:16 mlelstv Exp $ */
/* $NetBSD: iscsi_ioctl.c,v 1.34 2023/11/25 10:08:27 mlelstv Exp $ */
/*-
* Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@ -728,18 +728,11 @@ create_connection(iscsi_login_parameters_t *par, session_t *sess,
}
rc = get_socket(par->socket, &conn->c_sock);
if (rc != EBADF)
fd_close(par->socket);
if (rc) {
DEBOUT(("Invalid socket %d\n", par->socket));
callout_destroy(&conn->c_timeout);
rw_destroy(&conn->c_sock_rw);
cv_destroy(&conn->c_idle_cv);
cv_destroy(&conn->c_ccb_cv);
cv_destroy(&conn->c_pdu_cv);
cv_destroy(&conn->c_conn_cv);
mutex_destroy(&conn->c_lock);
free(conn, M_DEVBUF);
par->status = ISCSI_STATUS_INVALID_SOCKET;
return rc;
@ -901,11 +894,13 @@ recreate_connection(iscsi_login_parameters_t *par, session_t *sess,
DEBOUT(("Too many connections (max = %d, curr = %d)\n",
sess->s_MaxConnections, sess->s_active_connections));
/* Always close the desecriptor */
fd_close(par->socket);
/* Close the desecriptor */
rc = EIO;
if (fd_getfile(par->socket) != NULL)
rc = fd_close(par->socket);
par->status = ISCSI_STATUS_MAXED_CONNECTIONS;
return EIO;
return rc;
}
rw_enter(&conn->c_sock_rw, RW_WRITER);
@ -915,6 +910,7 @@ recreate_connection(iscsi_login_parameters_t *par, session_t *sess,
}
rc = get_socket(par->socket, &conn->c_sock);
rw_exit(&conn->c_sock_rw);
if (rc != EBADF)
fd_close(par->socket);
if (rc) {
@ -1739,19 +1735,23 @@ iscsi_cleanup_thread(void *par)
* the send/recv threads have been killed
*/
DEBC(conn, 5, ("Cleanup: Waiting for threads to exit\n"));
mutex_enter(&conn->c_lock);
while (conn->c_sendproc || conn->c_rcvproc)
kpause("threads", false, hz, NULL);
kpause("threads", false, hz, &conn->c_lock);
for (s=1; conn->c_usecount > 0 && s < 3; ++s)
kpause("usecount", false, hz, NULL);
kpause("usecount", false, hz, &conn->c_lock);
if (conn->c_usecount > 0) {
DEBC(conn, 5, ("Cleanup: %d CCBs busy\n", conn->c_usecount));
mutex_exit(&conn->c_lock);
/* retry later */
mutex_enter(&iscsi_cleanup_mtx);
TAILQ_INSERT_HEAD(&iscsi_cleanupc_list, conn, c_connections);
continue;
}
mutex_exit(&conn->c_lock);
KASSERT(!conn->c_in_session);
@ -1838,7 +1838,7 @@ iscsi_cleanup_thread(void *par)
conn->c_timedout = TOUT_NONE;
}
/* Go to sleep, but wake up every 30 seconds to
/* Go to sleep, but wake up every 120 seconds to
* check for dead event handlers */
rc = cv_timedwait(&iscsi_cleanup_cv, &iscsi_cleanup_mtx,
(TAILQ_FIRST(&event_handlers)) ? 120 * hz : 0);

View File

@ -1,4 +1,4 @@
/* $NetBSD: iscsi_send.c,v 1.39 2022/09/13 13:09:16 mlelstv Exp $ */
/* $NetBSD: iscsi_send.c,v 1.40 2023/11/25 10:08:27 mlelstv Exp $ */
/*-
* Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@ -34,7 +34,6 @@
#include <sys/filedesc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/atomic.h>
/*#define LUN_1 1 */
@ -242,10 +241,6 @@ reassign_tasks(connection_t *oldconn)
/* reset timeouts */
ccb->ccb_num_timeouts = 0;
/* fixup reference counts */
oldconn->c_usecount--;
atomic_inc_uint(&conn->c_usecount);
DEBC(conn, 1, ("CCB %p: Copied PDU %p to %p\n",
ccb, opdu, pdu));
@ -255,9 +250,17 @@ reassign_tasks(connection_t *oldconn)
/* and free the old PDU */
free_pdu(opdu);
/* put ready CCB into waiting list of new connection */
mutex_enter(&conn->c_lock);
/* fixup reference counts */
mutex_enter(&oldconn->c_lock);
oldconn->c_usecount--;
conn->c_usecount++;
mutex_exit(&oldconn->c_lock);
/* put ready CCB into waiting list of new connection */
suspend_ccb(ccb, TRUE);
mutex_exit(&conn->c_lock);
}
@ -683,6 +686,11 @@ init_login_pdu(connection_t *conn, ccb_t *ccb, pdu_t *ppdu, bool next)
NEXT_PHASE(c_phase);
}
DEB(99, ("InitLoginPdu: Flags=%x Phase=%x->%x\n",
hpdu->pduh_Flags,
(hpdu->pduh_Flags >> CSG_SHIFT) & SG_MASK,
hpdu->pduh_Flags & SG_MASK));
memcpy(isid, &iscsi_InitiatorISID, 6);
isid->TSIH = conn->c_session->s_TSIH;
@ -748,6 +756,27 @@ negotiate_login(connection_t *conn, pdu_t *rx_pdu, ccb_t *tx_ccb)
break;
case SG_LOGIN_OPERATIONAL_NEGOTIATION:
if (conn->c_state == ST_SEC_FIN) {
/*
* Both sides announced to continue with
* operational negotation, but this is the
* last target packet from mutual CHAP
* that needs to be validated.
*/
rc = assemble_security_parameters(conn, tx_ccb, rx_pdu, tx_pdu);
if (rc)
break;
/*
* Response was valid, drop (security) parameters
* so that we start negotiating operational
* parameters.
*/
rx_pdu->pdu_temp_data = NULL;
}
rc = assemble_negotiation_parameters(conn, tx_ccb, rx_pdu, tx_pdu);
break;
@ -1699,13 +1728,22 @@ ccb_timeout(ccb_t *ccb)
DEBC(conn, 0, ("ccb_timeout: num=%d total=%d disp=%d\n",
ccb->ccb_num_timeouts+1, ccb->ccb_total_tries, ccb->ccb_disp));
/*
* XXX can we time out after connection is closed ?
*/
if (conn == NULL) {
wake_ccb(ccb, ISCSI_STATUS_TIMEOUT);
return;
}
if (++ccb->ccb_num_timeouts > MAX_CCB_TIMEOUTS ||
ccb->ccb_total_tries > MAX_CCB_TRIES ||
ccb->ccb_disp <= CCBDISP_FREE ||
!ccb->ccb_session->s_ErrorRecoveryLevel) {
wake_ccb(ccb, ISCSI_STATUS_TIMEOUT);
handle_connection_error(conn, ISCSI_STATUS_TIMEOUT, RECOVER_CONNECTION);
handle_connection_error(conn,
ISCSI_STATUS_TIMEOUT, RECOVER_CONNECTION);
} else {
if (ccb->ccb_data_in && ccb->ccb_xfer_len < ccb->ccb_data_len) {
/* request resend of all missing data */

View File

@ -1,4 +1,4 @@
/* $NetBSD: iscsi_text.c,v 1.13 2019/04/21 11:45:08 maya Exp $ */
/* $NetBSD: iscsi_text.c,v 1.14 2023/11/25 10:08:27 mlelstv Exp $ */
/*-
* Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
@ -133,7 +133,7 @@ typedef struct
STATIC key_entry_t entries[] = {
{"AuthMethod", T_AUTH, 0},
{"CHAP_A", T_NUM, 5},
{"CHAP_A", T_NUM, ISCSI_CHAP_MD5},
{"CHAP_C", T_BIGNUM, 0},
{"CHAP_I", T_NUM, 0},
{"CHAP_N", T_STRING, 0},
@ -172,8 +172,8 @@ typedef struct
{
text_key_t key; /* the key */
int list_num; /* number of elements in list, doubles as */
bool hex_bignums; /* whether to encode in hex or base64 */
/* data size for large numeric values */
bool hex_bignums; /* whether to encode in hex or base64 */
union
{
uint32_t nval[MAX_LIST];/* numeric or enumeration values */
@ -220,7 +220,6 @@ typedef struct
/*****************************************************************************/
STATIC void
chap_md5_response(uint8_t *buffer, uint8_t identifier, uint8_t *secret,
uint8_t *challenge, int challenge_size)
@ -234,7 +233,6 @@ chap_md5_response(uint8_t *buffer, uint8_t identifier, uint8_t *secret,
MD5Final(buffer, &md5);
}
/*****************************************************************************/
/*
@ -322,6 +320,7 @@ get_bignumval(uint8_t *buf, negotiation_parameter_t *par)
}
buf++;
par->list_num = dp - par->val.sval;
par->hex_bignums = true;
} else if (buf[0] == '0' && (buf[1] == 'b' || buf[1] == 'B')) {
buf = base64_decode(&buf[2], par->val.sval, &par->list_num);
} else {
@ -340,12 +339,13 @@ get_bignumval(uint8_t *buf, negotiation_parameter_t *par)
* Parameter:
* buf The buffer pointer
* pval The pointer to the result.
* sep Separator to next value.
*
* Returns: The pointer to the next parameter, NULL on error.
*/
STATIC uint8_t *
get_numval(uint8_t *buf, uint32_t *pval)
get_numval(uint8_t *buf, uint32_t *pval, const uint8_t sep)
{
uint32_t val = 0;
char c;
@ -389,11 +389,11 @@ STATIC uint8_t *
get_range(uint8_t *buf, uint32_t *pval1, uint32_t *pval2)
{
if ((buf = get_numval(buf, pval1)) == NULL)
if ((buf = get_numval(buf, pval1, '~')) == NULL)
return NULL;
if (!*buf)
return NULL;
if ((buf = get_numval(buf, pval2)) == NULL)
if ((buf = get_numval(buf, pval2, '~')) == NULL)
return NULL;
return buf;
}
@ -539,6 +539,7 @@ get_parameter(uint8_t *buf, negotiation_parameter_t *par)
par->key = i;
par->list_num = 1;
par->hex_bignums = false; /* set by get_bignumval */
if (i > MAX_KEY) {
DEBOUT(("get_parameter: unrecognized key <%s>\n", buf));
@ -556,7 +557,7 @@ get_parameter(uint8_t *buf, negotiation_parameter_t *par)
switch (entries[i].val) {
case T_NUM:
bp = get_numval(bp, &par->val.nval[0]);
bp = get_numval(bp, &par->val.nval[0], '\0');
break;
case T_BIGNUM:
@ -927,6 +928,12 @@ complete_pars(negotiation_state_t *state, pdu_t *pdu)
DEB(10, ("complete_pars: n=%d, len=%d\n", state->num_pars, len));
if (len == 0) {
pdu->pdu_temp_data = NULL;
pdu->pdu_temp_data_len = 0;
return 0;
}
if ((bp = malloc(len, M_TEMP, M_WAITOK)) == NULL) {
DEBOUT(("*** Out of memory in complete_pars\n"));
return ISCSI_STATUS_NO_RESOURCES;
@ -1313,7 +1320,6 @@ assemble_login_parameters(connection_t *conn, ccb_t *ccb, pdu_t *pdu)
return (next) ? 0 : -1;
}
/*
* assemble_security_parameters:
* Assemble the security negotiation parameters.
@ -1342,6 +1348,7 @@ assemble_security_parameters(connection_t *conn, ccb_t *ccb, pdu_t *rx_pdu,
int challenge_size = 0;
uint8_t *response = NULL;
int response_size = 0;
bool challenge_hex = iscsi_hex_bignums;
state->num_pars = 0;
next = 0;
@ -1385,7 +1392,7 @@ assemble_security_parameters(connection_t *conn, ccb_t *ccb, pdu_t *rx_pdu,
case K_Auth_CHAP_Algorithm:
if (state->auth_state != AUTH_CHAP_ALG_SENT ||
rxp.val.nval[0] != 5) {
rxp.val.nval[0] != ISCSI_CHAP_MD5) {
DEBOUT(("Bad algorithm, auth_state = %d, alg %d\n",
state->auth_state, rxp.val.nval[0]));
return ISCSI_STATUS_NEGOTIATION_ERROR;
@ -1400,6 +1407,8 @@ assemble_security_parameters(connection_t *conn, ccb_t *ccb, pdu_t *rx_pdu,
}
challenge = rxp.val.sval;
challenge_size = rxp.list_num;
/* respond in the same format as the challenge */
challenge_hex = rxp.hex_bignums;
break;
case K_Auth_CHAP_Identifier:
@ -1428,8 +1437,11 @@ assemble_security_parameters(connection_t *conn, ccb_t *ccb, pdu_t *rx_pdu,
}
response = rxp.val.sval;
response_size = rxp.list_num;
if (response_size != CHAP_MD5_SIZE)
if (response_size != CHAP_MD5_SIZE) {
DEBOUT(("CHAP Response, bad size %d\n",
response_size));
return ISCSI_STATUS_NEGOTIATION_ERROR;
}
break;
default:
@ -1446,7 +1458,7 @@ assemble_security_parameters(connection_t *conn, ccb_t *ccb, pdu_t *rx_pdu,
return ISCSI_STATUS_NEGOTIATION_ERROR;
case AUTH_METHOD_SELECTED:
set_key_n(state, K_Auth_CHAP_Algorithm, 5);
set_key_n(state, K_Auth_CHAP_Algorithm, ISCSI_CHAP_MD5);
state->auth_state = AUTH_CHAP_ALG_SENT;
next = -1;
break;
@ -1461,12 +1473,15 @@ assemble_security_parameters(connection_t *conn, ccb_t *ccb, pdu_t *rx_pdu,
set_key_s(state, K_Auth_CHAP_Name, state->user_name);
chap_md5_response(state->temp_buf, identifier, state->password,
challenge, challenge_size);
chap_md5_response(state->temp_buf, identifier,
state->password, challenge, challenge_size);
cpar = set_key_s(state, K_Auth_CHAP_Response, state->temp_buf);
if (cpar != NULL)
if (cpar != NULL) {
cpar->list_num = CHAP_MD5_SIZE;
/* respond in same format as challenge */
cpar->hex_bignums = challenge_hex;
}
if (par->auth_info.mutual_auth) {
if (!state->target_password[0]) {
@ -1481,9 +1496,14 @@ assemble_security_parameters(connection_t *conn, ccb_t *ccb, pdu_t *rx_pdu,
state->temp_buf[CHAP_MD5_SIZE]);
cpar = set_key_s(state, K_Auth_CHAP_Challenge,
&state->temp_buf[CHAP_MD5_SIZE + 1]);
if (cpar != NULL)
if (cpar != NULL) {
cpar->list_num = CHAP_CHALLENGE_LEN;
next = -1;
/* use same format as target challenge */
cpar->hex_bignums = challenge_hex;
}
/* transitional state */
conn->c_state = ST_SEC_FIN;
}
state->auth_state = AUTH_CHAP_RSP_SENT;
break;
@ -1497,11 +1517,12 @@ assemble_security_parameters(connection_t *conn, ccb_t *ccb, pdu_t *rx_pdu,
chap_md5_response(state->temp_buf,
state->temp_buf[CHAP_MD5_SIZE],
state->password,
state->target_password,
&state->temp_buf[CHAP_MD5_SIZE + 1],
CHAP_CHALLENGE_LEN);
if (memcmp(state->temp_buf, response, response_size)) {
if (response_size > sizeof(state->temp_buf) ||
memcmp(state->temp_buf, response, response_size)) {
DEBOUT(("Mutual authentication mismatch\n"));
return ISCSI_STATUS_AUTHENTICATION_FAILED;
}
@ -1557,16 +1578,19 @@ set_first_opnegs(connection_t *conn, negotiation_state_t *state)
/* First connection only */
if (!conn->c_session->s_TSIH) {
state->ErrorRecoveryLevel =
(lpar->is_present.ErrorRecoveryLevel) ? lpar->ErrorRecoveryLevel
: 2;
(lpar->is_present.ErrorRecoveryLevel) ?
lpar->ErrorRecoveryLevel : 2;
/*
Negotiate InitialR2T to FALSE and ImmediateData to TRUE, should
be slightly more efficient than the default InitialR2T=TRUE.
* Negotiate InitialR2T to FALSE and ImmediateData to
* TRUE, should be slightly more efficient than the
* default InitialR2T=TRUE.
*/
state->InitialR2T = FALSE;
state->ImmediateData = TRUE;
/* We don't really care about this, so don't negotiate by default */
/* We don't really care about this, so don't negotiate
* by default
*/
state->MaxBurstLength = entries[K_MaxBurstLength].defval;
state->FirstBurstLength = entries[K_FirstBurstLength].defval;
state->MaxOutstandingR2T = entries[K_MaxOutstandingR2T].defval;
@ -1625,7 +1649,7 @@ assemble_negotiation_parameters(connection_t *conn, ccb_t *ccb, pdu_t *rx_pdu,
DEBC(conn, 10, ("AsmNegParams: connState=%d, MRDSL=%d\n",
conn->c_state, state->MaxRecvDataSegmentLength));
if (conn->c_state == ST_SEC_NEG) {
if (conn->c_state == ST_SEC_NEG || conn->c_state == ST_SEC_FIN) {
conn->c_state = ST_OP_NEG;
set_first_opnegs(conn, state);
}
@ -1709,6 +1733,7 @@ assemble_send_targets(pdu_t *pdu, uint8_t *val)
par.key = K_SendTargets;
par.list_num = 1;
par.val.sval = val;
par.hex_bignums = false;
len = parameter_size(&par);
@ -1719,8 +1744,10 @@ assemble_send_targets(pdu_t *pdu, uint8_t *val)
pdu->pdu_temp_data = buf;
pdu->pdu_temp_data_len = len;
if (put_parameter(buf, len, &par) == 0)
if (put_parameter(buf, len, &par) == 0) {
DEBOUT(("trying to put zero sized buffer\n"));
return ISCSI_STATUS_PARAMETER_INVALID;
}
return 0;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: iscsi_utils.c,v 1.28 2022/09/13 13:09:16 mlelstv Exp $ */
/* $NetBSD: iscsi_utils.c,v 1.29 2023/11/25 10:08:27 mlelstv Exp $ */
/*-
* Copyright (c) 2004,2005,2006,2008 The NetBSD Foundation, Inc.
@ -235,7 +235,9 @@ get_ccb(connection_t *conn, bool waitok)
ccb->ccb_disp = CCBDISP_NOWAIT;
ccb->ccb_connection = conn;
ccb->ccb_num_timeouts = 0;
atomic_inc_uint(&conn->c_usecount);
mutex_enter(&conn->c_lock);
conn->c_usecount++;
mutex_exit(&conn->c_lock);
DEBC(conn, 15, (
"get_ccb: ccb = %p, usecount = %d\n",
@ -264,8 +266,10 @@ free_ccb(ccb_t *ccb)
KASSERT((ccb->ccb_flags & CCBF_WAITQUEUE) == 0);
atomic_dec_uint(&conn->c_usecount);
ccb->ccb_connection = NULL;
mutex_enter(&conn->c_lock);
conn->c_usecount--;
mutex_exit(&conn->c_lock);
if (ccb->ccb_disp > CCBDISP_NOWAIT) {
DEBOUT(("Freeing CCB with disp %d\n",ccb->ccb_disp));