Blame SOURCES/0001-configuration-support-for-CHAP-algorithms.patch

46c2f0
From 9d36121534183195bd2892447ca07724013cef57 Mon Sep 17 00:00:00 2001
46c2f0
From: Chris Leech <cleech@redhat.com>
46c2f0
Date: Sun, 24 Nov 2019 13:51:09 -0800
46c2f0
Subject: [PATCH 1/1] configuration support for CHAP algorithms
46c2f0
46c2f0
Introduces support for preference lists in configuration files, and uses
46c2f0
that for the 'node.session.auth.chap_algs' setting.
46c2f0
46c2f0
This is also re-used for discovery authentication, rather than have two
46c2f0
different configurations.
46c2f0
---
46c2f0
 etc/iscsid.conf               |   7 ++
46c2f0
 libopeniscsiusr/default.c     |   3 +
46c2f0
 libopeniscsiusr/idbm.c        |  95 +++++++++++++++++++++++++
46c2f0
 libopeniscsiusr/idbm.h        |   9 +++
46c2f0
 libopeniscsiusr/idbm_fields.h |   1 +
46c2f0
 usr/auth.c                    |  64 ++++++++++++-----
46c2f0
 usr/auth.h                    |   3 +
46c2f0
 usr/config.h                  |   1 +
46c2f0
 usr/idbm.c                    | 126 ++++++++++++++++++++++++++++++----
46c2f0
 usr/idbm.h                    |   2 +
46c2f0
 usr/idbm_fields.h             |   1 +
46c2f0
 usr/initiator.h               |   1 +
46c2f0
 usr/initiator_common.c        |   2 +
46c2f0
 usr/login.c                   |  11 +++
46c2f0
 14 files changed, 294 insertions(+), 32 deletions(-)
46c2f0
46c2f0
diff --git a/etc/iscsid.conf b/etc/iscsid.conf
46c2f0
index 2f3a28c..420145b 100644
46c2f0
--- a/etc/iscsid.conf
46c2f0
+++ b/etc/iscsid.conf
46c2f0
@@ -57,6 +57,13 @@ node.leading_login = No
46c2f0
 # to CHAP. The default is None.
46c2f0
 #node.session.auth.authmethod = CHAP
46c2f0
 
46c2f0
+# To configure which CHAP algorithms to enable set
46c2f0
+# node.session.auth.chap_algs to a comma seperated list.
46c2f0
+# The algorithms should be listen with most prefered first.
46c2f0
+# Valid values are MD5, SHA1, SHA256, and SHA3-256.
46c2f0
+# The default is MD5.
46c2f0
+#node.session.auth.chap_algs = SHA3-256,SHA256,SHA1,MD5
46c2f0
+
46c2f0
 # To set a CHAP username and password for initiator
46c2f0
 # authentication by the target(s), uncomment the following lines:
46c2f0
 #node.session.auth.username = username
46c2f0
diff --git a/libopeniscsiusr/default.c b/libopeniscsiusr/default.c
46c2f0
index d01d892..d3b3da3 100644
46c2f0
--- a/libopeniscsiusr/default.c
46c2f0
+++ b/libopeniscsiusr/default.c
46c2f0
@@ -78,6 +78,9 @@ void _default_node(struct iscsi_node *node)
46c2f0
 	node->session.initial_login_retry_max = DEF_INITIAL_LOGIN_RETRIES_MAX;
46c2f0
 	node->session.reopen_max = DEF_SESSION_REOPEN_MAX;
46c2f0
 	node->session.auth.authmethod = 0;
46c2f0
+	/* TYPE_INT_LIST fields should be initialized to ~0 to indicate unset values */
46c2f0
+	memset(node->session.auth.chap_algs, ~0, sizeof(node->session.auth.chap_algs));
46c2f0
+	node->session.auth.chap_algs[0] = ISCSI_AUTH_CHAP_ALG_MD5;
46c2f0
 	node->session.auth.password_length = 0;
46c2f0
 	node->session.auth.password_in_length = 0;
46c2f0
 	node->session.err_tmo.abort_timeout = DEF_ABORT_TIMEO;
46c2f0
diff --git a/libopeniscsiusr/idbm.c b/libopeniscsiusr/idbm.c
46c2f0
index 342aab5..d1fc03d 100644
46c2f0
--- a/libopeniscsiusr/idbm.c
46c2f0
+++ b/libopeniscsiusr/idbm.c
46c2f0
@@ -73,6 +73,7 @@
46c2f0
 #define TYPE_INT32	6
46c2f0
 #define TYPE_INT64	7
46c2f0
 #define TYPE_BOOL	8
46c2f0
+#define TYPE_INT_LIST	9
46c2f0
 #define MAX_KEYS	256   /* number of keys total(including CNX_MAX) */
46c2f0
 #define NAME_MAXVAL	128   /* the maximum length of key name */
46c2f0
 #define VALUE_MAXVAL	256   /* the maximum length of 223 bytes in the RFC. */
46c2f0
@@ -248,6 +249,39 @@ do { \
46c2f0
 	_n++; \
46c2f0
 } while(0)
46c2f0
 
46c2f0
+#define ARRAY_LEN(x) ( sizeof(x) / sizeof((x)[0]) )
46c2f0
+
46c2f0
+/* Options list type, rather than matching a single value this populates an
46c2f0
+ * array with a list of values in user specified order.
46c2f0
+ * Requires a table matching config strings to values.
46c2f0
+ **/
46c2f0
+#define _rec_int_list(_key, _recs, _org, _name, _show, _tbl, _n, _mod) \
46c2f0
+do {\
46c2f0
+	_recs[_n].type = TYPE_INT_LIST; \
46c2f0
+	_strncpy(_recs[_n].name, _key, NAME_MAXVAL); \
46c2f0
+	for (unsigned int _i = 0; _i < ARRAY_LEN(_org->_name); _i++) { \
46c2f0
+		if (_org->_name[_i] != ~0UL) { \
46c2f0
+			for (unsigned int _j = 0; _j < ARRAY_LEN(_tbl); _j++) { \
46c2f0
+				if (_tbl[_j].value == _org->_name[_i]) { \
46c2f0
+					strcat(_recs[_n].value, _tbl[_j].name); \
46c2f0
+					strcat(_recs[_n].value, ","); \
46c2f0
+					break; \
46c2f0
+				} \
46c2f0
+			} \
46c2f0
+		} \
46c2f0
+	} \
46c2f0
+	/* delete traling ',' */ \
46c2f0
+	if (strrchr(_recs[_n].value, ',')) \
46c2f0
+		*strrchr(_recs[_n].value, ',') = '\0'; \
46c2f0
+	_recs[_n].data = &_org->_name; \
46c2f0
+	_recs[_n].data_len = sizeof(_org->_name); \
46c2f0
+	_recs[_n].visible = _show; \
46c2f0
+	_recs[_n].opts[0] = (void *)&_tbl; \
46c2f0
+	_recs[_n].numopts = ARRAY_LEN(_tbl); \
46c2f0
+	_recs[_n].can_modify = _mod; \
46c2f0
+	_n++; \
46c2f0
+} while(0)
46c2f0
+
46c2f0
 enum modify_mode {
46c2f0
 	_CANNOT_MODIFY,
46c2f0
 	_CAN_MODIFY,
46c2f0
@@ -557,6 +591,11 @@ void _idbm_node_print(struct iscsi_node *node, FILE *f, bool show_secret)
46c2f0
 	_idbm_recs_free(recs);
46c2f0
 }
46c2f0
 
46c2f0
+struct int_list_tbl {
46c2f0
+	const char *name;
46c2f0
+	unsigned int value;
46c2f0
+};
46c2f0
+
46c2f0
 static int _idbm_rec_update_param(struct iscsi_context *ctx,
46c2f0
 				  struct idbm_rec *recs, char *name,
46c2f0
 				  char *value, int line_number)
46c2f0
@@ -564,8 +603,14 @@ static int _idbm_rec_update_param(struct iscsi_context *ctx,
46c2f0
 	int rc = LIBISCSI_OK;
46c2f0
 	int i = 0;
46c2f0
 	int j = 0;
46c2f0
+	int k = 0;
46c2f0
 	int passwd_done = 0;
46c2f0
 	char passwd_len[8];
46c2f0
+	struct int_list_tbl *tbl = NULL;
46c2f0
+	char *tmp_value;
46c2f0
+	int *tmp_data;
46c2f0
+	bool *found;
46c2f0
+	char *token;
46c2f0
 
46c2f0
 	assert(ctx != NULL);
46c2f0
 	assert(recs != NULL);
46c2f0
@@ -642,6 +687,47 @@ setup_passwd_len:
46c2f0
 				else
46c2f0
 					goto unknown_value;
46c2f0
 				goto updated;
46c2f0
+			case TYPE_INT_LIST:
46c2f0
+				if (!recs[i].data)
46c2f0
+					continue;
46c2f0
+				tbl = (void *)recs[i].opts[0];
46c2f0
+				/* strsep is destructive, make a copy to work with */
46c2f0
+				tmp_value = strdup(value);
46c2f0
+				k = 0;
46c2f0
+				tmp_data = malloc(recs[i].data_len);
46c2f0
+				memset(tmp_data, ~0, recs[i].data_len);
46c2f0
+				found = calloc(recs[i].numopts, sizeof(bool));
46c2f0
+next_token:			while ((token = strsep(&tmp_value, ", \n"))) {
46c2f0
+					if (!strlen(token))
46c2f0
+						continue;
46c2f0
+					if ((k * (int)sizeof(int)) >= (recs[i].data_len)) {
46c2f0
+						_warn(ctx, "Too many values set for '%s'"
46c2f0
+						      ", continuing without processing them all",
46c2f0
+						      recs[i].name);
46c2f0
+						break;
46c2f0
+					}
46c2f0
+					for (j = 0; j < recs[i].numopts; j++) {
46c2f0
+						if (!strcmp(token, tbl[j].name)) {
46c2f0
+							if ((found[j])) {
46c2f0
+								_warn(ctx, "Ignoring repeated value '%s'"
46c2f0
+								      " for '%s'", token, recs[i].name);
46c2f0
+								goto next_token;
46c2f0
+							}
46c2f0
+							((unsigned *)tmp_data)[k++] = tbl[j].value;
46c2f0
+							found[j] = true;
46c2f0
+							goto next_token;
46c2f0
+						}
46c2f0
+					}
46c2f0
+					_warn(ctx, "Ignoring unknown value '%s'"
46c2f0
+					      " for '%s'", token, recs[i].name);
46c2f0
+				}
46c2f0
+				memcpy(recs[i].data, tmp_data, recs[i].data_len);
46c2f0
+				free(tmp_value);
46c2f0
+				free(tmp_data);
46c2f0
+				tmp_value = NULL;
46c2f0
+				tmp_data = NULL;
46c2f0
+				token = NULL;
46c2f0
+				goto updated;
46c2f0
 			default:
46c2f0
 unknown_value:
46c2f0
 				_error(ctx, "Got unknown data type %d "
46c2f0
@@ -881,6 +967,13 @@ void _idbm_free(struct idbm *db)
46c2f0
 	free(db);
46c2f0
 }
46c2f0
 
46c2f0
+static struct int_list_tbl chap_algs[] = {
46c2f0
+	{ "MD5", ISCSI_AUTH_CHAP_ALG_MD5 },
46c2f0
+	{ "SHA1", ISCSI_AUTH_CHAP_ALG_SHA1 },
46c2f0
+	{ "SHA256", ISCSI_AUTH_CHAP_ALG_SHA256 },
46c2f0
+	{ "SHA3-256", ISCSI_AUTH_CHAP_ALG_SHA3_256 },
46c2f0
+};
46c2f0
+
46c2f0
 static void _idbm_node_rec_link(struct iscsi_node *node, struct idbm_rec *recs)
46c2f0
 {
46c2f0
 	int num = 0;
46c2f0
@@ -943,6 +1036,8 @@ static void _idbm_node_rec_link(struct iscsi_node *node, struct idbm_rec *recs)
46c2f0
 	_rec_uint32(SESSION_PASSWORD_IN_LEN, recs, node,
46c2f0
 		    session.auth.password_in_length, IDBM_HIDE, num,
46c2f0
 		    _CAN_MODIFY);
46c2f0
+	_rec_int_list(SESSION_CHAP_ALGS, recs, node, session.auth.chap_algs,
46c2f0
+		IDBM_SHOW, chap_algs, num, _CAN_MODIFY);
46c2f0
 	_rec_int64(SESSION_REPLACEMENT_TMO, recs, node,
46c2f0
 		   session.tmo.replacement_timeout, IDBM_SHOW, num,
46c2f0
 		   _CAN_MODIFY);
46c2f0
diff --git a/libopeniscsiusr/idbm.h b/libopeniscsiusr/idbm.h
46c2f0
index c84d332..5a4d2fa 100644
46c2f0
--- a/libopeniscsiusr/idbm.h
46c2f0
+++ b/libopeniscsiusr/idbm.h
46c2f0
@@ -49,6 +49,14 @@ enum iscsi_auth_method {
46c2f0
 	ISCSI_AUTH_METHOD_CHAP,
46c2f0
 };
46c2f0
 
46c2f0
+enum iscsi_chap_algs {
46c2f0
+	ISCSI_AUTH_CHAP_ALG_MD5 = 5,
46c2f0
+	ISCSI_AUTH_CHAP_ALG_SHA1 = 6,
46c2f0
+	ISCSI_AUTH_CHAP_ALG_SHA256 = 7,
46c2f0
+	ISCSI_AUTH_CHAP_ALG_SHA3_256 = 8,
46c2f0
+	AUTH_CHAP_ALG_MAX_COUNT = 5,
46c2f0
+};
46c2f0
+
46c2f0
 enum iscsi_startup_type {
46c2f0
 	ISCSI_STARTUP_MANUAL,
46c2f0
 	ISCSI_STARTUP_AUTOMATIC,
46c2f0
@@ -93,6 +101,7 @@ struct iscsi_auth_config {
46c2f0
 	char					username_in[AUTH_STR_MAX_LEN];
46c2f0
 	unsigned char				password_in[AUTH_STR_MAX_LEN];
46c2f0
 	uint32_t				password_in_length;
46c2f0
+	unsigned int				chap_algs[AUTH_CHAP_ALG_MAX_COUNT];
46c2f0
 };
46c2f0
 
46c2f0
 /* all TCP options go in this structure.
46c2f0
diff --git a/libopeniscsiusr/idbm_fields.h b/libopeniscsiusr/idbm_fields.h
46c2f0
index 29a2090..8bf17b0 100644
46c2f0
--- a/libopeniscsiusr/idbm_fields.h
46c2f0
+++ b/libopeniscsiusr/idbm_fields.h
46c2f0
@@ -120,6 +120,7 @@
46c2f0
 #define SESSION_USERNAME_IN	"node.session.auth.username_in"
46c2f0
 #define SESSION_PASSWORD_IN	"node.session.auth.password_in"
46c2f0
 #define SESSION_PASSWORD_IN_LEN	"node.session.auth.password_in_length"
46c2f0
+#define SESSION_CHAP_ALGS	"node.session.auth.chap_algs"
46c2f0
 #define SESSION_REPLACEMENT_TMO	"node.session.timeo.replacement_timeout"
46c2f0
 #define SESSION_ABORT_TMO	"node.session.err_timeo.abort_timeout"
46c2f0
 #define SESSION_LU_RESET_TMO	"node.session.err_timeo.lu_reset_timeout"
46c2f0
diff --git a/usr/auth.c b/usr/auth.c
46c2f0
index 5c819c2..a222c53 100644
46c2f0
--- a/usr/auth.c
46c2f0
+++ b/usr/auth.c
46c2f0
@@ -1806,7 +1806,7 @@ acl_chk_chap_alg_list(unsigned int option_count, const int *option_list)
46c2f0
 	return 0;
46c2f0
 }
46c2f0
 
46c2f0
-static int
46c2f0
+int
46c2f0
 acl_set_chap_alg_list(struct iscsi_acl *client, unsigned int option_count,
46c2f0
 		      const int *option_list)
46c2f0
 {
46c2f0
@@ -1819,22 +1819,54 @@ acl_set_chap_alg_list(struct iscsi_acl *client, unsigned int option_count,
46c2f0
 }
46c2f0
 
46c2f0
 int
46c2f0
-acl_init_chap_digests(int *value_list) {
46c2f0
+acl_init_chap_digests(int *value_list, unsigned *chap_algs, int conf_count) {
46c2f0
 	EVP_MD_CTX *context = EVP_MD_CTX_new();
46c2f0
 	int i = 0;
46c2f0
 
46c2f0
-	if (EVP_DigestInit_ex(context, EVP_sha3_256(), NULL)) {
46c2f0
-		value_list[i++] = AUTH_CHAP_ALG_SHA3_256;
46c2f0
-	}
46c2f0
-	if (EVP_DigestInit_ex(context, EVP_sha256(), NULL)) {
46c2f0
-		value_list[i++] = AUTH_CHAP_ALG_SHA256;
46c2f0
-	}
46c2f0
-	if (EVP_DigestInit_ex(context, EVP_sha1(), NULL)) {
46c2f0
-		value_list[i++] = AUTH_CHAP_ALG_SHA1;
46c2f0
-	}
46c2f0
-	if (EVP_DigestInit_ex(context, EVP_md5(), NULL)) {
46c2f0
-		value_list[i++] = AUTH_CHAP_ALG_MD5;
46c2f0
+	for (int j = 0; j < conf_count; j++) {
46c2f0
+		switch (chap_algs[j]) {
46c2f0
+		case AUTH_CHAP_ALG_MD5:
46c2f0
+			if (EVP_DigestInit_ex(context, EVP_md5(), NULL)) {
46c2f0
+				value_list[i++] = AUTH_CHAP_ALG_MD5;
46c2f0
+			} else {
46c2f0
+				log_warning("Ignoring CHAP algorthm request for "
46c2f0
+				            "MD5 due to crypto lib configuration");
46c2f0
+			}
46c2f0
+			break;
46c2f0
+		case AUTH_CHAP_ALG_SHA1:
46c2f0
+			if (EVP_DigestInit_ex(context, EVP_sha1(), NULL)) {
46c2f0
+				value_list[i++] = AUTH_CHAP_ALG_SHA1;
46c2f0
+			} else {
46c2f0
+				log_warning("Ignoring CHAP algorthm request for "
46c2f0
+				            "SHA1 due to crypto lib configuration");
46c2f0
+			}
46c2f0
+			break;
46c2f0
+		case AUTH_CHAP_ALG_SHA256:
46c2f0
+			if (EVP_DigestInit_ex(context, EVP_sha256(), NULL)) {
46c2f0
+				value_list[i++] = AUTH_CHAP_ALG_SHA256;
46c2f0
+			} else {
46c2f0
+				log_warning("Ignoring CHAP algorthm request for "
46c2f0
+				            "SHA256 due to crypto lib configuration");
46c2f0
+			}
46c2f0
+			break;
46c2f0
+		case AUTH_CHAP_ALG_SHA3_256:
46c2f0
+			if (EVP_DigestInit_ex(context, EVP_sha3_256(), NULL)) {
46c2f0
+				value_list[i++] = AUTH_CHAP_ALG_SHA3_256;
46c2f0
+			} else {
46c2f0
+				log_warning("Ignoring CHAP algorthm request for "
46c2f0
+				            "SHA3-256 due to crypto lib configuration");
46c2f0
+			}
46c2f0
+			break;
46c2f0
+		case ~0:
46c2f0
+			/* unset value in array, just ignore */
46c2f0
+			break;
46c2f0
+		default:
46c2f0
+			log_warning("Ignoring unknown CHAP algorithm request "
46c2f0
+				    "'%d'", chap_algs[j]);
46c2f0
+			break;
46c2f0
+		}
46c2f0
 	}
46c2f0
+
46c2f0
 	return i;
46c2f0
 }
46c2f0
 
46c2f0
@@ -1926,12 +1958,6 @@ acl_init(int node_type, int buf_desc_count, struct auth_buffer_desc *buff_desc)
46c2f0
 		return AUTH_STATUS_ERROR;
46c2f0
 	}
46c2f0
 
46c2f0
-	if (acl_set_chap_alg_list(client, acl_init_chap_digests(value_list),
46c2f0
-					value_list) != AUTH_STATUS_NO_ERROR) {
46c2f0
-		client->phase = AUTH_PHASE_ERROR;
46c2f0
-		return AUTH_STATUS_ERROR;
46c2f0
-	}
46c2f0
-
46c2f0
 	return AUTH_STATUS_NO_ERROR;
46c2f0
 }
46c2f0
 
46c2f0
diff --git a/usr/auth.h b/usr/auth.h
46c2f0
index f6dbbe4..16cdb24 100644
46c2f0
--- a/usr/auth.h
46c2f0
+++ b/usr/auth.h
46c2f0
@@ -271,6 +271,9 @@ extern int acl_send_transit_bit(struct iscsi_acl *client, int *value);
46c2f0
 extern int acl_set_user_name(struct iscsi_acl *client, const char *username);
46c2f0
 extern int acl_set_passwd(struct iscsi_acl *client,
46c2f0
 			  const unsigned char *pw_data, unsigned int pw_len);
46c2f0
+extern int acl_set_chap_alg_list(struct iscsi_acl *client, unsigned int option_count,
46c2f0
+		      const int *option_list);
46c2f0
+extern int acl_init_chap_digests(int *value_list, unsigned int *chap_algs, int count);
46c2f0
 extern int acl_set_auth_rmt(struct iscsi_acl *client, int auth_rmt);
46c2f0
 extern int acl_set_ip_sec(struct iscsi_acl *client, int ip_sec);
46c2f0
 extern int acl_get_dbg_status(struct iscsi_acl *client, int *value);
46c2f0
diff --git a/usr/config.h b/usr/config.h
46c2f0
index 250879d..79059ec 100644
46c2f0
--- a/usr/config.h
46c2f0
+++ b/usr/config.h
46c2f0
@@ -58,6 +58,7 @@ struct iscsi_auth_config {
46c2f0
 	char username_in[AUTH_STR_MAX_LEN];
46c2f0
 	unsigned char password_in[AUTH_STR_MAX_LEN];
46c2f0
 	unsigned int password_in_length;
46c2f0
+	unsigned int chap_algs[AUTH_CHAP_ALG_MAX_COUNT];
46c2f0
 };
46c2f0
 
46c2f0
 /* all per-connection timeouts go in this structure.
46c2f0
diff --git a/usr/idbm.c b/usr/idbm.c
46c2f0
index 0c6870c..749203a 100644
46c2f0
--- a/usr/idbm.c
46c2f0
+++ b/usr/idbm.c
46c2f0
@@ -51,6 +51,8 @@
46c2f0
 
46c2f0
 static struct idbm *db;
46c2f0
 
46c2f0
+#define ARRAY_LEN(x) ( sizeof(x) / sizeof((x)[0]) )
46c2f0
+
46c2f0
 #define __recinfo_str(_key, _info, _rec, _name, _show, _n, _mod) do { \
46c2f0
 	_info[_n].type = TYPE_STR; \
46c2f0
 	strlcpy(_info[_n].name, _key, NAME_MAXVAL); \
46c2f0
@@ -165,6 +167,42 @@ static struct idbm *db;
46c2f0
 	_n++; \
46c2f0
 } while(0)
46c2f0
 
46c2f0
+#define __recinfo_int_list(_key,_info,_rec,_name,_show,_tbl,_n,_mod) do { \
46c2f0
+	_info[_n].type = TYPE_INT_LIST; \
46c2f0
+	strlcpy(_info[_n].name, _key, NAME_MAXVAL); \
46c2f0
+	for(int _i = 0; _i < ARRAY_LEN(_rec->_name); _i++) { \
46c2f0
+		if (_rec->_name[_i] != ~0) { \
46c2f0
+			for (int _j = 0; _j < ARRAY_LEN(_tbl); _j++) { \
46c2f0
+				if (_tbl[_j].value == _rec->_name[_i]) { \
46c2f0
+					strcat(_info[_n].value, _tbl[_j].name); \
46c2f0
+					strcat(_info[_n].value, ","); \
46c2f0
+					break; \
46c2f0
+				} \
46c2f0
+			} \
46c2f0
+		} \
46c2f0
+	} \
46c2f0
+	/* delete trailing ',' */ \
46c2f0
+	if (strrchr(_info[_n].value, ',')) \
46c2f0
+		*strrchr(_info[_n].value, ',') = '\0'; \
46c2f0
+	_info[_n].data = &_rec->_name; \
46c2f0
+	_info[_n].data_len = sizeof(_rec->_name); \
46c2f0
+	_info[_n].visible = _show; \
46c2f0
+	_info[_n].opts[0] = (void *)&_tbl; \
46c2f0
+	_info[_n].numopts = ARRAY_LEN(_tbl); \
46c2f0
+	_info[_n].can_modify = _mod; \
46c2f0
+	_n++; \
46c2f0
+} while (0)
46c2f0
+
46c2f0
+static struct int_list_tbl {
46c2f0
+	const char *name;
46c2f0
+	int value;
46c2f0
+} chap_algs [] = {
46c2f0
+	{ "MD5", AUTH_CHAP_ALG_MD5 },
46c2f0
+	{ "SHA1", AUTH_CHAP_ALG_SHA1 },
46c2f0
+	{ "SHA256", AUTH_CHAP_ALG_SHA256 },
46c2f0
+	{ "SHA3-256", AUTH_CHAP_ALG_SHA3_256 },
46c2f0
+};
46c2f0
+
46c2f0
 static int idbm_remove_disc_to_node_link(node_rec_t *rec, char *portal);
46c2f0
 
46c2f0
 static void
46c2f0
@@ -199,6 +237,10 @@ idbm_recinfo_discovery(discovery_rec_t *r, recinfo_t *ri)
46c2f0
 		__recinfo_int(DISC_ST_PASSWORD_IN_LEN, ri, r,
46c2f0
 			u.sendtargets.auth.password_in_length, IDBM_HIDE,
46c2f0
 			num, 1);
46c2f0
+		/* reusing SESSION_CHAP_ALGS */
46c2f0
+		__recinfo_int_list(SESSION_CHAP_ALGS, ri, r,
46c2f0
+				   u.sendtargets.auth.chap_algs,
46c2f0
+				   IDBM_SHOW, chap_algs, num, 1);
46c2f0
 		__recinfo_int(DISC_ST_LOGIN_TMO, ri, r,
46c2f0
 			u.sendtargets.conn_timeo.login_timeout,
46c2f0
 			IDBM_SHOW, num, 1);
46c2f0
@@ -431,6 +473,8 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri)
46c2f0
 		      session.auth.password_in, IDBM_MASKED, num, 1);
46c2f0
 	__recinfo_int(SESSION_PASSWORD_IN_LEN, ri, r,
46c2f0
 		      session.auth.password_in_length, IDBM_HIDE, num, 1);
46c2f0
+	__recinfo_int_list(SESSION_CHAP_ALGS, ri, r,
46c2f0
+			   session.auth.chap_algs, IDBM_SHOW, chap_algs, num, 1);
46c2f0
 	__recinfo_int(SESSION_REPLACEMENT_TMO, ri, r,
46c2f0
 		      session.timeo.replacement_timeout,
46c2f0
 		      IDBM_SHOW, num, 1);
46c2f0
@@ -940,6 +984,9 @@ idbm_discovery_setup_defaults(discovery_rec_t *rec, discovery_type_e type)
46c2f0
 		rec->u.sendtargets.auth.authmethod = 0;
46c2f0
 		rec->u.sendtargets.auth.password_length = 0;
46c2f0
 		rec->u.sendtargets.auth.password_in_length = 0;
46c2f0
+		/* TYPE_INT_LIST fields should be initialized to ~0 to indicate unset values */
46c2f0
+		memset(rec->u.sendtargets.auth.chap_algs, ~0, sizeof(rec->u.sendtargets.auth.chap_algs));
46c2f0
+		rec->u.sendtargets.auth.chap_algs[0] = AUTH_CHAP_ALG_MD5;
46c2f0
 		rec->u.sendtargets.conn_timeo.login_timeout=15;
46c2f0
 		rec->u.sendtargets.conn_timeo.auth_timeout = 45;
46c2f0
 		rec->u.sendtargets.conn_timeo.active_timeout=30;
46c2f0
@@ -973,59 +1020,109 @@ int idbm_rec_update_param(recinfo_t *info, char *name, char *value,
46c2f0
 	int i;
46c2f0
 	int passwd_done = 0;
46c2f0
 	char passwd_len[8];
46c2f0
+	char *tmp_value, *token;
46c2f0
+	bool *found;
46c2f0
+	int *tmp_data;
46c2f0
 
46c2f0
 setup_passwd_len:
46c2f0
 	for (i=0; i
46c2f0
 		if (!strcmp(name, info[i].name)) {
46c2f0
-			int j;
46c2f0
+			int j,k;
46c2f0
+			struct int_list_tbl *tbl;
46c2f0
 
46c2f0
 			log_debug(7, "updated '%s', '%s' => '%s'", name,
46c2f0
 				  info[i].value, value);
46c2f0
 			/* parse recinfo by type */
46c2f0
-			if (info[i].type == TYPE_INT) {
46c2f0
+			switch (info[i].type) {
46c2f0
+			case TYPE_INT:
46c2f0
 				if (!info[i].data)
46c2f0
 					continue;
46c2f0
 
46c2f0
 				*(int*)info[i].data =
46c2f0
 					strtoul(value, NULL, 10);
46c2f0
 				goto updated;
46c2f0
-			} else if (info[i].type == TYPE_UINT8) {
46c2f0
+			case TYPE_UINT8:
46c2f0
 				if (!info[i].data)
46c2f0
 					continue;
46c2f0
 
46c2f0
 				*(uint8_t *)info[i].data =
46c2f0
 					strtoul(value, NULL, 10);
46c2f0
 				goto updated;
46c2f0
-			} else if (info[i].type == TYPE_UINT16) {
46c2f0
+			case TYPE_UINT16:
46c2f0
 				if (!info[i].data)
46c2f0
 					continue;
46c2f0
 
46c2f0
 				*(uint16_t *)info[i].data =
46c2f0
 					strtoul(value, NULL, 10);
46c2f0
 				goto updated;
46c2f0
-			} else if (info[i].type == TYPE_UINT32) {
46c2f0
+			case TYPE_UINT32:
46c2f0
 				if (!info[i].data)
46c2f0
 					continue;
46c2f0
 
46c2f0
 				*(uint32_t *)info[i].data =
46c2f0
 					strtoul(value, NULL, 10);
46c2f0
 				goto updated;
46c2f0
-			} else if (info[i].type == TYPE_STR) {
46c2f0
+			case TYPE_STR:
46c2f0
 				if (!info[i].data)
46c2f0
 					continue;
46c2f0
 
46c2f0
 				strlcpy((char*)info[i].data,
46c2f0
 					value, info[i].data_len);
46c2f0
 				goto updated;
46c2f0
-			}
46c2f0
-			for (j=0; j
46c2f0
-				if (!strcmp(value, info[i].opts[j])) {
46c2f0
-					if (!info[i].data)
46c2f0
+			case TYPE_INT_O:
46c2f0
+				for (j=0; j
46c2f0
+					if (!strcmp(value, info[i].opts[j])) {
46c2f0
+						if (!info[i].data)
46c2f0
+							continue;
46c2f0
+
46c2f0
+						*(int*)info[i].data = j;
46c2f0
+						goto updated;
46c2f0
+					}
46c2f0
+				}
46c2f0
+			case TYPE_INT_LIST:
46c2f0
+				if (!info[i].data)
46c2f0
+					continue;
46c2f0
+				tbl = (void *)info[i].opts[0];
46c2f0
+				/* strsep is destructive, make a copy to work with */
46c2f0
+				tmp_value = strdup(value);
46c2f0
+				k = 0;
46c2f0
+				tmp_data = malloc(info[i].data_len);
46c2f0
+				memset(tmp_data, ~0, info[i].data_len);
46c2f0
+				found = calloc(info[i].numopts, sizeof(bool));
46c2f0
+
46c2f0
+next_token:			while ((token = strsep(&tmp_value, ", \n"))) {
46c2f0
+					if (!strlen(token))
46c2f0
 						continue;
46c2f0
-
46c2f0
-					*(int*)info[i].data = j;
46c2f0
-					goto updated;
46c2f0
+					if ((k * (int)sizeof(int)) >= (info[i].data_len)) {
46c2f0
+						log_warning("Too many values set for '%s'"
46c2f0
+						            ", continuing without processing them all",
46c2f0
+						            info[i].name);
46c2f0
+						break;
46c2f0
+					}
46c2f0
+					for (j = 0; j < info[i].numopts; j++) {
46c2f0
+						if (!strcmp(token, tbl[j].name)) {
46c2f0
+							if ((found[j])) {
46c2f0
+								log_warning("Ignoring repeated "
46c2f0
+								            "value '%s' "
46c2f0
+								            "for '%s'", token,
46c2f0
+								            info[i].name);
46c2f0
+								goto next_token;
46c2f0
+							}
46c2f0
+							((int*)tmp_data)[k++] = tbl[j].value;
46c2f0
+							found[j] = true;
46c2f0
+							goto next_token;
46c2f0
+						}
46c2f0
+					}
46c2f0
+					log_warning("Ignoring unknown value '%s'"
46c2f0
+					            " for '%s'", token, info[i].name);
46c2f0
 				}
46c2f0
+				memcpy(info[i].data, tmp_data, info[i].data_len);
46c2f0
+				free(tmp_value);
46c2f0
+				free(tmp_data);
46c2f0
+				tmp_value = NULL;
46c2f0
+				tmp_data = NULL;
46c2f0
+				token = NULL;
46c2f0
+				goto updated;
46c2f0
 			}
46c2f0
 			if (line_number) {
46c2f0
 				log_warning("config file line %d contains "
46c2f0
@@ -3098,6 +3195,9 @@ void idbm_node_setup_defaults(node_rec_t *rec)
46c2f0
 	rec->session.initial_login_retry_max = DEF_INITIAL_LOGIN_RETRIES_MAX;
46c2f0
 	rec->session.reopen_max = DEF_SESSION_REOPEN_MAX;
46c2f0
 	rec->session.auth.authmethod = 0;
46c2f0
+	/* TYPE_INT_LIST fields should be initialized to ~0 to indicate unset values */
46c2f0
+	memset(rec->session.auth.chap_algs, ~0, sizeof(rec->session.auth.chap_algs));
46c2f0
+	rec->session.auth.chap_algs[0] = AUTH_CHAP_ALG_MD5;
46c2f0
 	rec->session.auth.password_length = 0;
46c2f0
 	rec->session.auth.password_in_length = 0;
46c2f0
 	rec->session.err_timeo.abort_timeout = DEF_ABORT_TIMEO;
46c2f0
diff --git a/usr/idbm.h b/usr/idbm.h
46c2f0
index c6982e4..d1a7f63 100644
46c2f0
--- a/usr/idbm.h
46c2f0
+++ b/usr/idbm.h
46c2f0
@@ -46,6 +46,8 @@
46c2f0
 #define TYPE_UINT8	3
46c2f0
 #define TYPE_UINT16	4
46c2f0
 #define TYPE_UINT32	5
46c2f0
+#define TYPE_INT_LIST	6
46c2f0
+
46c2f0
 #define MAX_KEYS	256   /* number of keys total(including CNX_MAX) */
46c2f0
 #define NAME_MAXVAL	128   /* the maximum length of key name */
46c2f0
 #define VALUE_MAXVAL	256   /* the maximum length of 223 bytes in the RFC. */
46c2f0
diff --git a/usr/idbm_fields.h b/usr/idbm_fields.h
46c2f0
index 142c7ae..4a967fc 100644
46c2f0
--- a/usr/idbm_fields.h
46c2f0
+++ b/usr/idbm_fields.h
46c2f0
@@ -30,6 +30,7 @@
46c2f0
 #define SESSION_USERNAME_IN	"node.session.auth.username_in"
46c2f0
 #define SESSION_PASSWORD_IN	"node.session.auth.password_in"
46c2f0
 #define SESSION_PASSWORD_IN_LEN	"node.session.auth.password_in_length"
46c2f0
+#define SESSION_CHAP_ALGS	"node.session.auth.chap_algs"
46c2f0
 #define SESSION_REPLACEMENT_TMO	"node.session.timeo.replacement_timeout"
46c2f0
 #define SESSION_ABORT_TMO	"node.session.err_timeo.abort_timeout"
46c2f0
 #define SESSION_LU_RESET_TMO	"node.session.err_timeo.lu_reset_timeout"
46c2f0
diff --git a/usr/initiator.h b/usr/initiator.h
46c2f0
index eccafb9..6a49ea6 100644
46c2f0
--- a/usr/initiator.h
46c2f0
+++ b/usr/initiator.h
46c2f0
@@ -243,6 +243,7 @@ typedef struct iscsi_session {
46c2f0
 	char username_in[AUTH_STR_MAX_LEN];
46c2f0
 	uint8_t password_in[AUTH_STR_MAX_LEN];
46c2f0
 	int password_in_length;
46c2f0
+	unsigned int chap_algs[AUTH_CHAP_ALG_MAX_COUNT];
46c2f0
 	iscsi_conn_t conn[ISCSI_CONN_MAX];
46c2f0
 	uint64_t param_mask;
46c2f0
 
46c2f0
diff --git a/usr/initiator_common.c b/usr/initiator_common.c
46c2f0
index 790f13d..81da8fd 100644
46c2f0
--- a/usr/initiator_common.c
46c2f0
+++ b/usr/initiator_common.c
46c2f0
@@ -94,6 +94,8 @@ int iscsi_setup_authentication(struct iscsi_session *session,
46c2f0
 		memcpy(session->password_in, auth_cfg->password_in,
46c2f0
 		       session->password_in_length);
46c2f0
 
46c2f0
+	memcpy(session->chap_algs, auth_cfg->chap_algs, sizeof(auth_cfg->chap_algs));
46c2f0
+
46c2f0
 	if (session->password_length || session->password_in_length) {
46c2f0
 		/* setup the auth buffers */
46c2f0
 		session->auth_buffers[0].address = &session->auth_client_block;
46c2f0
diff --git a/usr/login.c b/usr/login.c
46c2f0
index d7dad21..1251e61 100644
46c2f0
--- a/usr/login.c
46c2f0
+++ b/usr/login.c
46c2f0
@@ -1262,6 +1262,17 @@ check_for_authentication(iscsi_session_t *session,
46c2f0
 		goto end;
46c2f0
 	}
46c2f0
 
46c2f0
+	int value_list[AUTH_CHAP_ALG_MAX_COUNT];
46c2f0
+
46c2f0
+	if (acl_set_chap_alg_list(auth_client,
46c2f0
+				acl_init_chap_digests(value_list,
46c2f0
+					session->chap_algs,
46c2f0
+					AUTH_CHAP_ALG_MAX_COUNT),
46c2f0
+				value_list) != AUTH_STATUS_NO_ERROR) {
46c2f0
+		log_error("Couldn't set CHAP algorithm list");
46c2f0
+		goto end;
46c2f0
+	}
46c2f0
+
46c2f0
 	if (acl_set_ip_sec(auth_client, 1) != AUTH_STATUS_NO_ERROR) {
46c2f0
 		log_error("Couldn't set IPSec");
46c2f0
 		goto end;
46c2f0
-- 
46c2f0
2.21.1
46c2f0