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

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