From 62741fb13c64bfe41ea45b353b1223a501d36c27 Mon Sep 17 00:00:00 2001
From: Chris Leech <cleech@redhat.com>
Date: Sun, 24 Nov 2019 13:51:09 -0800
Subject: [PATCH] configuration support for CHAP algorithms
Introduces support for preference lists in configuration files, and uses
that for the 'node.session.auth.chap_algs' setting.
This is also re-used for discovery authentication, rather than have two
different configurations.
(cherry picked from commit d3daa7a2bc3f5bca874d3efd072b34a657c4d492)
---
etc/iscsid.conf | 7 +++
usr/auth.c | 64 ++++++++++++++-------
usr/auth.h | 3 +
usr/config.h | 1 +
usr/idbm.c | 126 ++++++++++++++++++++++++++++++++++++-----
usr/idbm.h | 2 +
usr/idbm_fields.h | 1 +
usr/initiator.h | 1 +
usr/initiator_common.c | 2 +
usr/login.c | 11 ++++
10 files changed, 186 insertions(+), 32 deletions(-)
diff --git a/etc/iscsid.conf b/etc/iscsid.conf
index 1af8ed2..82cc7d0 100644
--- a/etc/iscsid.conf
+++ b/etc/iscsid.conf
@@ -56,6 +56,13 @@ node.leading_login = No
# to CHAP. The default is None.
#node.session.auth.authmethod = CHAP
+# To configure which CHAP algorithms to enable set
+# node.session.auth.chap_algs to a comma seperated list.
+# The algorithms should be listen with most prefered first.
+# Valid values are MD5, SHA1, SHA256, and SHA3-256.
+# The default is MD5.
+#node.session.auth.chap_algs = SHA3-256,SHA256,SHA1,MD5
+
# To set a CHAP username and password for initiator
# authentication by the target(s), uncomment the following lines:
#node.session.auth.username = username
diff --git a/usr/auth.c b/usr/auth.c
index afb4ea3..ec934e6 100644
--- a/usr/auth.c
+++ b/usr/auth.c
@@ -1806,7 +1806,7 @@ acl_chk_chap_alg_list(unsigned int option_count, const int *option_list)
return 0;
}
-static int
+int
acl_set_chap_alg_list(struct iscsi_acl *client, unsigned int option_count,
const int *option_list)
{
@@ -1819,22 +1819,54 @@ acl_set_chap_alg_list(struct iscsi_acl *client, unsigned int option_count,
}
int
-acl_init_chap_digests(int *value_list) {
+acl_init_chap_digests(int *value_list, unsigned *chap_algs, int conf_count) {
EVP_MD_CTX *context = EVP_MD_CTX_new();
int i = 0;
- if (EVP_DigestInit_ex(context, EVP_sha3_256(), NULL)) {
- value_list[i++] = AUTH_CHAP_ALG_SHA3_256;
- }
- if (EVP_DigestInit_ex(context, EVP_sha256(), NULL)) {
- value_list[i++] = AUTH_CHAP_ALG_SHA256;
- }
- if (EVP_DigestInit_ex(context, EVP_sha1(), NULL)) {
- value_list[i++] = AUTH_CHAP_ALG_SHA1;
- }
- if (EVP_DigestInit_ex(context, EVP_md5(), NULL)) {
- value_list[i++] = AUTH_CHAP_ALG_MD5;
+ for (int j = 0; j < conf_count; j++) {
+ switch (chap_algs[j]) {
+ case AUTH_CHAP_ALG_MD5:
+ if (EVP_DigestInit_ex(context, EVP_md5(), NULL)) {
+ value_list[i++] = AUTH_CHAP_ALG_MD5;
+ } else {
+ log_warning("Ignoring CHAP algorthm request for "
+ "MD5 due to crypto lib configuration");
+ }
+ break;
+ case AUTH_CHAP_ALG_SHA1:
+ if (EVP_DigestInit_ex(context, EVP_sha1(), NULL)) {
+ value_list[i++] = AUTH_CHAP_ALG_SHA1;
+ } else {
+ log_warning("Ignoring CHAP algorthm request for "
+ "SHA1 due to crypto lib configuration");
+ }
+ break;
+ case AUTH_CHAP_ALG_SHA256:
+ if (EVP_DigestInit_ex(context, EVP_sha256(), NULL)) {
+ value_list[i++] = AUTH_CHAP_ALG_SHA256;
+ } else {
+ log_warning("Ignoring CHAP algorthm request for "
+ "SHA256 due to crypto lib configuration");
+ }
+ break;
+ case AUTH_CHAP_ALG_SHA3_256:
+ if (EVP_DigestInit_ex(context, EVP_sha3_256(), NULL)) {
+ value_list[i++] = AUTH_CHAP_ALG_SHA3_256;
+ } else {
+ log_warning("Ignoring CHAP algorthm request for "
+ "SHA3-256 due to crypto lib configuration");
+ }
+ break;
+ case ~0:
+ /* unset value in array, just ignore */
+ break;
+ default:
+ log_warning("Ignoring unknown CHAP algorithm request "
+ "'%d'", chap_algs[j]);
+ break;
+ }
}
+
return i;
}
@@ -1924,12 +1956,6 @@ acl_init(int node_type, int buf_desc_count, struct auth_buffer_desc *buff_desc)
return AUTH_STATUS_ERROR;
}
- if (acl_set_chap_alg_list(client, acl_init_chap_digests(value_list),
- value_list) != AUTH_STATUS_NO_ERROR) {
- client->phase = AUTH_PHASE_ERROR;
- return AUTH_STATUS_ERROR;
- }
-
return AUTH_STATUS_NO_ERROR;
}
diff --git a/usr/auth.h b/usr/auth.h
index f6dbbe4..16cdb24 100644
--- a/usr/auth.h
+++ b/usr/auth.h
@@ -271,6 +271,9 @@ extern int acl_send_transit_bit(struct iscsi_acl *client, int *value);
extern int acl_set_user_name(struct iscsi_acl *client, const char *username);
extern int acl_set_passwd(struct iscsi_acl *client,
const unsigned char *pw_data, unsigned int pw_len);
+extern int acl_set_chap_alg_list(struct iscsi_acl *client, unsigned int option_count,
+ const int *option_list);
+extern int acl_init_chap_digests(int *value_list, unsigned int *chap_algs, int count);
extern int acl_set_auth_rmt(struct iscsi_acl *client, int auth_rmt);
extern int acl_set_ip_sec(struct iscsi_acl *client, int ip_sec);
extern int acl_get_dbg_status(struct iscsi_acl *client, int *value);
diff --git a/usr/config.h b/usr/config.h
index 8807c07..dfa4991 100644
--- a/usr/config.h
+++ b/usr/config.h
@@ -58,6 +58,7 @@ struct iscsi_auth_config {
char username_in[AUTH_STR_MAX_LEN];
unsigned char password_in[AUTH_STR_MAX_LEN];
unsigned int password_in_length;
+ unsigned int chap_algs[AUTH_CHAP_ALG_MAX_COUNT];
};
/* all per-connection timeouts go in this structure.
diff --git a/usr/idbm.c b/usr/idbm.c
index f47ff28..a9c7b40 100644
--- a/usr/idbm.c
+++ b/usr/idbm.c
@@ -49,6 +49,8 @@
static struct idbm *db;
+#define ARRAY_LEN(x) ( sizeof(x) / sizeof((x)[0]) )
+
#define __recinfo_str(_key, _info, _rec, _name, _show, _n, _mod) do { \
_info[_n].type = TYPE_STR; \
strlcpy(_info[_n].name, _key, NAME_MAXVAL); \
@@ -163,6 +165,42 @@ static struct idbm *db;
_n++; \
} while(0)
+#define __recinfo_int_list(_key,_info,_rec,_name,_show,_tbl,_n,_mod) do { \
+ _info[_n].type = TYPE_INT_LIST; \
+ strlcpy(_info[_n].name, _key, NAME_MAXVAL); \
+ for(int _i = 0; _i < ARRAY_LEN(_rec->_name); _i++) { \
+ if (_rec->_name[_i] != ~0) { \
+ for (int _j = 0; _j < ARRAY_LEN(_tbl); _j++) { \
+ if (_tbl[_j].value == _rec->_name[_i]) { \
+ strcat(_info[_n].value, _tbl[_j].name); \
+ strcat(_info[_n].value, ","); \
+ break; \
+ } \
+ } \
+ } \
+ } \
+ /* delete trailing ',' */ \
+ if (strrchr(_info[_n].value, ',')) \
+ *strrchr(_info[_n].value, ',') = '\0'; \
+ _info[_n].data = &_rec->_name; \
+ _info[_n].data_len = sizeof(_rec->_name); \
+ _info[_n].visible = _show; \
+ _info[_n].opts[0] = (void *)&_tbl; \
+ _info[_n].numopts = ARRAY_LEN(_tbl); \
+ _info[_n].can_modify = _mod; \
+ _n++; \
+} while (0)
+
+static struct int_list_tbl {
+ const char *name;
+ int value;
+} chap_algs [] = {
+ { "MD5", AUTH_CHAP_ALG_MD5 },
+ { "SHA1", AUTH_CHAP_ALG_SHA1 },
+ { "SHA256", AUTH_CHAP_ALG_SHA256 },
+ { "SHA3-256", AUTH_CHAP_ALG_SHA3_256 },
+};
+
static int idbm_remove_disc_to_node_link(node_rec_t *rec, char *portal);
static void
@@ -197,6 +235,10 @@ idbm_recinfo_discovery(discovery_rec_t *r, recinfo_t *ri)
__recinfo_int(DISC_ST_PASSWORD_IN_LEN, ri, r,
u.sendtargets.auth.password_in_length, IDBM_HIDE,
num, 1);
+ /* reusing SESSION_CHAP_ALGS */
+ __recinfo_int_list(SESSION_CHAP_ALGS, ri, r,
+ u.sendtargets.auth.chap_algs,
+ IDBM_SHOW, chap_algs, num, 1);
__recinfo_int(DISC_ST_LOGIN_TMO, ri, r,
u.sendtargets.conn_timeo.login_timeout,
IDBM_SHOW, num, 1);
@@ -427,6 +469,8 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri)
session.auth.password_in, IDBM_MASKED, num, 1);
__recinfo_int(SESSION_PASSWORD_IN_LEN, ri, r,
session.auth.password_in_length, IDBM_HIDE, num, 1);
+ __recinfo_int_list(SESSION_CHAP_ALGS, ri, r,
+ session.auth.chap_algs, IDBM_SHOW, chap_algs, num, 1);
__recinfo_int(SESSION_REPLACEMENT_TMO, ri, r,
session.timeo.replacement_timeout,
IDBM_SHOW, num, 1);
@@ -932,6 +976,9 @@ idbm_discovery_setup_defaults(discovery_rec_t *rec, discovery_type_e type)
rec->u.sendtargets.auth.authmethod = 0;
rec->u.sendtargets.auth.password_length = 0;
rec->u.sendtargets.auth.password_in_length = 0;
+ /* TYPE_INT_LIST fields should be initialized to ~0 to indicate unset values */
+ memset(rec->u.sendtargets.auth.chap_algs, ~0, sizeof(rec->u.sendtargets.auth.chap_algs));
+ rec->u.sendtargets.auth.chap_algs[0] = AUTH_CHAP_ALG_MD5;
rec->u.sendtargets.conn_timeo.login_timeout=15;
rec->u.sendtargets.conn_timeo.auth_timeout = 45;
rec->u.sendtargets.conn_timeo.active_timeout=30;
@@ -965,59 +1012,109 @@ int idbm_rec_update_param(recinfo_t *info, char *name, char *value,
int i;
int passwd_done = 0;
char passwd_len[8];
+ char *tmp_value, *token;
+ bool *found;
+ int *tmp_data;
setup_passwd_len:
for (i=0; i<MAX_KEYS; i++) {
if (!strcmp(name, info[i].name)) {
- int j;
+ int j,k;
+ struct int_list_tbl *tbl;
log_debug(7, "updated '%s', '%s' => '%s'", name,
info[i].value, value);
/* parse recinfo by type */
- if (info[i].type == TYPE_INT) {
+ switch (info[i].type) {
+ case TYPE_INT:
if (!info[i].data)
continue;
*(int*)info[i].data =
strtoul(value, NULL, 10);
goto updated;
- } else if (info[i].type == TYPE_UINT8) {
+ case TYPE_UINT8:
if (!info[i].data)
continue;
*(uint8_t *)info[i].data =
strtoul(value, NULL, 10);
goto updated;
- } else if (info[i].type == TYPE_UINT16) {
+ case TYPE_UINT16:
if (!info[i].data)
continue;
*(uint16_t *)info[i].data =
strtoul(value, NULL, 10);
goto updated;
- } else if (info[i].type == TYPE_UINT32) {
+ case TYPE_UINT32:
if (!info[i].data)
continue;
*(uint32_t *)info[i].data =
strtoul(value, NULL, 10);
goto updated;
- } else if (info[i].type == TYPE_STR) {
+ case TYPE_STR:
if (!info[i].data)
continue;
strlcpy((char*)info[i].data,
value, info[i].data_len);
goto updated;
- }
- for (j=0; j<info[i].numopts; j++) {
- if (!strcmp(value, info[i].opts[j])) {
- if (!info[i].data)
+ case TYPE_INT_O:
+ for (j=0; j<info[i].numopts; j++) {
+ if (!strcmp(value, info[i].opts[j])) {
+ if (!info[i].data)
+ continue;
+
+ *(int*)info[i].data = j;
+ goto updated;
+ }
+ }
+ case TYPE_INT_LIST:
+ if (!info[i].data)
+ continue;
+ tbl = (void *)info[i].opts[0];
+ /* strsep is destructive, make a copy to work with */
+ tmp_value = strdup(value);
+ k = 0;
+ tmp_data = malloc(info[i].data_len);
+ memset(tmp_data, ~0, info[i].data_len);
+ found = calloc(info[i].numopts, sizeof(bool));
+
+next_token: while ((token = strsep(&tmp_value, ", \n"))) {
+ if (!strlen(token))
continue;
-
- *(int*)info[i].data = j;
- goto updated;
+ if ((k * (int)sizeof(int)) >= (info[i].data_len)) {
+ log_warning("Too many values set for '%s'"
+ ", continuing without processing them all",
+ info[i].name);
+ break;
+ }
+ for (j = 0; j < info[i].numopts; j++) {
+ if (!strcmp(token, tbl[j].name)) {
+ if ((found[j])) {
+ log_warning("Ignoring repeated "
+ "value '%s' "
+ "for '%s'", token,
+ info[i].name);
+ goto next_token;
+ }
+ ((int*)tmp_data)[k++] = tbl[j].value;
+ found[j] = true;
+ goto next_token;
+ }
+ }
+ log_warning("Ignoring unknown value '%s'"
+ " for '%s'", token, info[i].name);
}
+ memcpy(info[i].data, tmp_data, info[i].data_len);
+ free(tmp_value);
+ free(tmp_data);
+ tmp_value = NULL;
+ tmp_data = NULL;
+ token = NULL;
+ goto updated;
}
if (line_number) {
log_warning("config file line %d contains "
@@ -3098,6 +3195,9 @@ void idbm_node_setup_defaults(node_rec_t *rec)
rec->session.initial_login_retry_max = DEF_INITIAL_LOGIN_RETRIES_MAX;
rec->session.reopen_max = 32;
rec->session.auth.authmethod = 0;
+ /* TYPE_INT_LIST fields should be initialized to ~0 to indicate unset values */
+ memset(rec->session.auth.chap_algs, ~0, sizeof(rec->session.auth.chap_algs));
+ rec->session.auth.chap_algs[0] = AUTH_CHAP_ALG_MD5;
rec->session.auth.password_length = 0;
rec->session.auth.password_in_length = 0;
rec->session.err_timeo.abort_timeout = DEF_ABORT_TIMEO;
diff --git a/usr/idbm.h b/usr/idbm.h
index cb5dd8f..65ea876 100644
--- a/usr/idbm.h
+++ b/usr/idbm.h
@@ -46,6 +46,8 @@
#define TYPE_UINT8 3
#define TYPE_UINT16 4
#define TYPE_UINT32 5
+#define TYPE_INT_LIST 6
+
#define MAX_KEYS 256 /* number of keys total(including CNX_MAX) */
#define NAME_MAXVAL 128 /* the maximum length of key name */
#define VALUE_MAXVAL 256 /* the maximum length of 223 bytes in the RFC. */
diff --git a/usr/idbm_fields.h b/usr/idbm_fields.h
index 4a92758..8749ef7 100644
--- a/usr/idbm_fields.h
+++ b/usr/idbm_fields.h
@@ -30,6 +30,7 @@
#define SESSION_USERNAME_IN "node.session.auth.username_in"
#define SESSION_PASSWORD_IN "node.session.auth.password_in"
#define SESSION_PASSWORD_IN_LEN "node.session.auth.password_in_length"
+#define SESSION_CHAP_ALGS "node.session.auth.chap_algs"
#define SESSION_REPLACEMENT_TMO "node.session.timeo.replacement_timeout"
#define SESSION_ABORT_TMO "node.session.err_timeo.abort_timeout"
#define SESSION_LU_RESET_TMO "node.session.err_timeo.lu_reset_timeout"
diff --git a/usr/initiator.h b/usr/initiator.h
index 3ee1454..01caf3f 100644
--- a/usr/initiator.h
+++ b/usr/initiator.h
@@ -243,6 +243,7 @@ typedef struct iscsi_session {
char username_in[AUTH_STR_MAX_LEN];
uint8_t password_in[AUTH_STR_MAX_LEN];
int password_in_length;
+ unsigned int chap_algs[AUTH_CHAP_ALG_MAX_COUNT];
iscsi_conn_t conn[ISCSI_CONN_MAX];
uint64_t param_mask;
diff --git a/usr/initiator_common.c b/usr/initiator_common.c
index d00bd9e..7cb45bc 100644
--- a/usr/initiator_common.c
+++ b/usr/initiator_common.c
@@ -94,6 +94,8 @@ int iscsi_setup_authentication(struct iscsi_session *session,
memcpy(session->password_in, auth_cfg->password_in,
session->password_in_length);
+ memcpy(session->chap_algs, auth_cfg->chap_algs, sizeof(auth_cfg->chap_algs));
+
if (session->password_length || session->password_in_length) {
/* setup the auth buffers */
session->auth_buffers[0].address = &session->auth_client_block;
diff --git a/usr/login.c b/usr/login.c
index d7dad21..1251e61 100644
--- a/usr/login.c
+++ b/usr/login.c
@@ -1262,6 +1262,17 @@ check_for_authentication(iscsi_session_t *session,
goto end;
}
+ int value_list[AUTH_CHAP_ALG_MAX_COUNT];
+
+ if (acl_set_chap_alg_list(auth_client,
+ acl_init_chap_digests(value_list,
+ session->chap_algs,
+ AUTH_CHAP_ALG_MAX_COUNT),
+ value_list) != AUTH_STATUS_NO_ERROR) {
+ log_error("Couldn't set CHAP algorithm list");
+ goto end;
+ }
+
if (acl_set_ip_sec(auth_client, 1) != AUTH_STATUS_NO_ERROR) {
log_error("Couldn't set IPSec");
goto end;
--
2.21.3