162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*******************************************************************************
362306a36Sopenharmony_ci * This file houses the main functions for the iSCSI CHAP support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (c) Copyright 2007-2013 Datera, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci ******************************************************************************/
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <crypto/hash.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/string.h>
1462306a36Sopenharmony_ci#include <linux/err.h>
1562306a36Sopenharmony_ci#include <linux/random.h>
1662306a36Sopenharmony_ci#include <linux/scatterlist.h>
1762306a36Sopenharmony_ci#include <target/iscsi/iscsi_target_core.h>
1862306a36Sopenharmony_ci#include "iscsi_target_nego.h"
1962306a36Sopenharmony_ci#include "iscsi_target_auth.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic char *chap_get_digest_name(const int digest_type)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	switch (digest_type) {
2462306a36Sopenharmony_ci	case CHAP_DIGEST_MD5:
2562306a36Sopenharmony_ci		return "md5";
2662306a36Sopenharmony_ci	case CHAP_DIGEST_SHA1:
2762306a36Sopenharmony_ci		return "sha1";
2862306a36Sopenharmony_ci	case CHAP_DIGEST_SHA256:
2962306a36Sopenharmony_ci		return "sha256";
3062306a36Sopenharmony_ci	case CHAP_DIGEST_SHA3_256:
3162306a36Sopenharmony_ci		return "sha3-256";
3262306a36Sopenharmony_ci	default:
3362306a36Sopenharmony_ci		return NULL;
3462306a36Sopenharmony_ci	}
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic int chap_gen_challenge(
3862306a36Sopenharmony_ci	struct iscsit_conn *conn,
3962306a36Sopenharmony_ci	int caller,
4062306a36Sopenharmony_ci	char *c_str,
4162306a36Sopenharmony_ci	unsigned int *c_len)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	int ret;
4462306a36Sopenharmony_ci	unsigned char *challenge_asciihex;
4562306a36Sopenharmony_ci	struct iscsi_chap *chap = conn->auth_protocol;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	challenge_asciihex = kzalloc(chap->challenge_len * 2 + 1, GFP_KERNEL);
4862306a36Sopenharmony_ci	if (!challenge_asciihex)
4962306a36Sopenharmony_ci		return -ENOMEM;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	memset(chap->challenge, 0, MAX_CHAP_CHALLENGE_LEN);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	ret = get_random_bytes_wait(chap->challenge, chap->challenge_len);
5462306a36Sopenharmony_ci	if (unlikely(ret))
5562306a36Sopenharmony_ci		goto out;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	bin2hex(challenge_asciihex, chap->challenge,
5862306a36Sopenharmony_ci				chap->challenge_len);
5962306a36Sopenharmony_ci	/*
6062306a36Sopenharmony_ci	 * Set CHAP_C, and copy the generated challenge into c_str.
6162306a36Sopenharmony_ci	 */
6262306a36Sopenharmony_ci	*c_len += sprintf(c_str + *c_len, "CHAP_C=0x%s", challenge_asciihex);
6362306a36Sopenharmony_ci	*c_len += 1;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	pr_debug("[%s] Sending CHAP_C=0x%s\n\n", (caller) ? "server" : "client",
6662306a36Sopenharmony_ci			challenge_asciihex);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciout:
6962306a36Sopenharmony_ci	kfree(challenge_asciihex);
7062306a36Sopenharmony_ci	return ret;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic int chap_test_algorithm(const char *name)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct crypto_shash *tfm;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	tfm = crypto_alloc_shash(name, 0, 0);
7862306a36Sopenharmony_ci	if (IS_ERR(tfm))
7962306a36Sopenharmony_ci		return -1;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	crypto_free_shash(tfm);
8262306a36Sopenharmony_ci	return 0;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic int chap_check_algorithm(const char *a_str)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	char *tmp, *orig, *token, *digest_name;
8862306a36Sopenharmony_ci	long digest_type;
8962306a36Sopenharmony_ci	int r = CHAP_DIGEST_UNKNOWN;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	tmp = kstrdup(a_str, GFP_KERNEL);
9262306a36Sopenharmony_ci	if (!tmp) {
9362306a36Sopenharmony_ci		pr_err("Memory allocation failed for CHAP_A temporary buffer\n");
9462306a36Sopenharmony_ci		return CHAP_DIGEST_UNKNOWN;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci	orig = tmp;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	token = strsep(&tmp, "=");
9962306a36Sopenharmony_ci	if (!token)
10062306a36Sopenharmony_ci		goto out;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (strcmp(token, "CHAP_A")) {
10362306a36Sopenharmony_ci		pr_err("Unable to locate CHAP_A key\n");
10462306a36Sopenharmony_ci		goto out;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci	while (token) {
10762306a36Sopenharmony_ci		token = strsep(&tmp, ",");
10862306a36Sopenharmony_ci		if (!token)
10962306a36Sopenharmony_ci			goto out;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		if (kstrtol(token, 10, &digest_type))
11262306a36Sopenharmony_ci			continue;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		digest_name = chap_get_digest_name(digest_type);
11562306a36Sopenharmony_ci		if (!digest_name)
11662306a36Sopenharmony_ci			continue;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci		pr_debug("Selected %s Algorithm\n", digest_name);
11962306a36Sopenharmony_ci		if (chap_test_algorithm(digest_name) < 0) {
12062306a36Sopenharmony_ci			pr_err("failed to allocate %s algo\n", digest_name);
12162306a36Sopenharmony_ci		} else {
12262306a36Sopenharmony_ci			r = digest_type;
12362306a36Sopenharmony_ci			goto out;
12462306a36Sopenharmony_ci		}
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ciout:
12762306a36Sopenharmony_ci	kfree(orig);
12862306a36Sopenharmony_ci	return r;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic void chap_close(struct iscsit_conn *conn)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	kfree(conn->auth_protocol);
13462306a36Sopenharmony_ci	conn->auth_protocol = NULL;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic struct iscsi_chap *chap_server_open(
13862306a36Sopenharmony_ci	struct iscsit_conn *conn,
13962306a36Sopenharmony_ci	struct iscsi_node_auth *auth,
14062306a36Sopenharmony_ci	const char *a_str,
14162306a36Sopenharmony_ci	char *aic_str,
14262306a36Sopenharmony_ci	unsigned int *aic_len)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	int digest_type;
14562306a36Sopenharmony_ci	struct iscsi_chap *chap;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (!(auth->naf_flags & NAF_USERID_SET) ||
14862306a36Sopenharmony_ci	    !(auth->naf_flags & NAF_PASSWORD_SET)) {
14962306a36Sopenharmony_ci		pr_err("CHAP user or password not set for"
15062306a36Sopenharmony_ci				" Initiator ACL\n");
15162306a36Sopenharmony_ci		return NULL;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	conn->auth_protocol = kzalloc(sizeof(struct iscsi_chap), GFP_KERNEL);
15562306a36Sopenharmony_ci	if (!conn->auth_protocol)
15662306a36Sopenharmony_ci		return NULL;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	chap = conn->auth_protocol;
15962306a36Sopenharmony_ci	digest_type = chap_check_algorithm(a_str);
16062306a36Sopenharmony_ci	switch (digest_type) {
16162306a36Sopenharmony_ci	case CHAP_DIGEST_MD5:
16262306a36Sopenharmony_ci		chap->digest_size = MD5_SIGNATURE_SIZE;
16362306a36Sopenharmony_ci		break;
16462306a36Sopenharmony_ci	case CHAP_DIGEST_SHA1:
16562306a36Sopenharmony_ci		chap->digest_size = SHA1_SIGNATURE_SIZE;
16662306a36Sopenharmony_ci		break;
16762306a36Sopenharmony_ci	case CHAP_DIGEST_SHA256:
16862306a36Sopenharmony_ci		chap->digest_size = SHA256_SIGNATURE_SIZE;
16962306a36Sopenharmony_ci		break;
17062306a36Sopenharmony_ci	case CHAP_DIGEST_SHA3_256:
17162306a36Sopenharmony_ci		chap->digest_size = SHA3_256_SIGNATURE_SIZE;
17262306a36Sopenharmony_ci		break;
17362306a36Sopenharmony_ci	case CHAP_DIGEST_UNKNOWN:
17462306a36Sopenharmony_ci	default:
17562306a36Sopenharmony_ci		pr_err("Unsupported CHAP_A value\n");
17662306a36Sopenharmony_ci		chap_close(conn);
17762306a36Sopenharmony_ci		return NULL;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	chap->digest_name = chap_get_digest_name(digest_type);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* Tie the challenge length to the digest size */
18362306a36Sopenharmony_ci	chap->challenge_len = chap->digest_size;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	pr_debug("[server] Got CHAP_A=%d\n", digest_type);
18662306a36Sopenharmony_ci	*aic_len = sprintf(aic_str, "CHAP_A=%d", digest_type);
18762306a36Sopenharmony_ci	*aic_len += 1;
18862306a36Sopenharmony_ci	pr_debug("[server] Sending CHAP_A=%d\n", digest_type);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/*
19162306a36Sopenharmony_ci	 * Set Identifier.
19262306a36Sopenharmony_ci	 */
19362306a36Sopenharmony_ci	chap->id = conn->tpg->tpg_chap_id++;
19462306a36Sopenharmony_ci	*aic_len += sprintf(aic_str + *aic_len, "CHAP_I=%d", chap->id);
19562306a36Sopenharmony_ci	*aic_len += 1;
19662306a36Sopenharmony_ci	pr_debug("[server] Sending CHAP_I=%d\n", chap->id);
19762306a36Sopenharmony_ci	/*
19862306a36Sopenharmony_ci	 * Generate Challenge.
19962306a36Sopenharmony_ci	 */
20062306a36Sopenharmony_ci	if (chap_gen_challenge(conn, 1, aic_str, aic_len) < 0) {
20162306a36Sopenharmony_ci		chap_close(conn);
20262306a36Sopenharmony_ci		return NULL;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return chap;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic const char base64_lookup_table[] =
20962306a36Sopenharmony_ci	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic int chap_base64_decode(u8 *dst, const char *src, size_t len)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	int i, bits = 0, ac = 0;
21462306a36Sopenharmony_ci	const char *p;
21562306a36Sopenharmony_ci	u8 *cp = dst;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
21862306a36Sopenharmony_ci		if (src[i] == '=')
21962306a36Sopenharmony_ci			return cp - dst;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		p = strchr(base64_lookup_table, src[i]);
22262306a36Sopenharmony_ci		if (p == NULL || src[i] == 0)
22362306a36Sopenharmony_ci			return -2;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		ac <<= 6;
22662306a36Sopenharmony_ci		ac += (p - base64_lookup_table);
22762306a36Sopenharmony_ci		bits += 6;
22862306a36Sopenharmony_ci		if (bits >= 8) {
22962306a36Sopenharmony_ci			*cp++ = (ac >> (bits - 8)) & 0xff;
23062306a36Sopenharmony_ci			ac &= ~(BIT(16) - BIT(bits - 8));
23162306a36Sopenharmony_ci			bits -= 8;
23262306a36Sopenharmony_ci		}
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci	if (ac)
23562306a36Sopenharmony_ci		return -1;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return cp - dst;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic int chap_server_compute_hash(
24162306a36Sopenharmony_ci	struct iscsit_conn *conn,
24262306a36Sopenharmony_ci	struct iscsi_node_auth *auth,
24362306a36Sopenharmony_ci	char *nr_in_ptr,
24462306a36Sopenharmony_ci	char *nr_out_ptr,
24562306a36Sopenharmony_ci	unsigned int *nr_out_len)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	unsigned long id;
24862306a36Sopenharmony_ci	unsigned char id_as_uchar;
24962306a36Sopenharmony_ci	unsigned char type;
25062306a36Sopenharmony_ci	unsigned char identifier[10], *initiatorchg = NULL;
25162306a36Sopenharmony_ci	unsigned char *initiatorchg_binhex = NULL;
25262306a36Sopenharmony_ci	unsigned char *digest = NULL;
25362306a36Sopenharmony_ci	unsigned char *response = NULL;
25462306a36Sopenharmony_ci	unsigned char *client_digest = NULL;
25562306a36Sopenharmony_ci	unsigned char *server_digest = NULL;
25662306a36Sopenharmony_ci	unsigned char chap_n[MAX_CHAP_N_SIZE], chap_r[MAX_RESPONSE_LENGTH];
25762306a36Sopenharmony_ci	size_t compare_len;
25862306a36Sopenharmony_ci	struct iscsi_chap *chap = conn->auth_protocol;
25962306a36Sopenharmony_ci	struct crypto_shash *tfm = NULL;
26062306a36Sopenharmony_ci	struct shash_desc *desc = NULL;
26162306a36Sopenharmony_ci	int auth_ret = -1, ret, initiatorchg_len;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	digest = kzalloc(chap->digest_size, GFP_KERNEL);
26462306a36Sopenharmony_ci	if (!digest) {
26562306a36Sopenharmony_ci		pr_err("Unable to allocate the digest buffer\n");
26662306a36Sopenharmony_ci		goto out;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	response = kzalloc(chap->digest_size * 2 + 2, GFP_KERNEL);
27062306a36Sopenharmony_ci	if (!response) {
27162306a36Sopenharmony_ci		pr_err("Unable to allocate the response buffer\n");
27262306a36Sopenharmony_ci		goto out;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	client_digest = kzalloc(chap->digest_size, GFP_KERNEL);
27662306a36Sopenharmony_ci	if (!client_digest) {
27762306a36Sopenharmony_ci		pr_err("Unable to allocate the client_digest buffer\n");
27862306a36Sopenharmony_ci		goto out;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	server_digest = kzalloc(chap->digest_size, GFP_KERNEL);
28262306a36Sopenharmony_ci	if (!server_digest) {
28362306a36Sopenharmony_ci		pr_err("Unable to allocate the server_digest buffer\n");
28462306a36Sopenharmony_ci		goto out;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	memset(identifier, 0, 10);
28862306a36Sopenharmony_ci	memset(chap_n, 0, MAX_CHAP_N_SIZE);
28962306a36Sopenharmony_ci	memset(chap_r, 0, MAX_RESPONSE_LENGTH);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	initiatorchg = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL);
29262306a36Sopenharmony_ci	if (!initiatorchg) {
29362306a36Sopenharmony_ci		pr_err("Unable to allocate challenge buffer\n");
29462306a36Sopenharmony_ci		goto out;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	initiatorchg_binhex = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL);
29862306a36Sopenharmony_ci	if (!initiatorchg_binhex) {
29962306a36Sopenharmony_ci		pr_err("Unable to allocate initiatorchg_binhex buffer\n");
30062306a36Sopenharmony_ci		goto out;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci	/*
30362306a36Sopenharmony_ci	 * Extract CHAP_N.
30462306a36Sopenharmony_ci	 */
30562306a36Sopenharmony_ci	if (extract_param(nr_in_ptr, "CHAP_N", MAX_CHAP_N_SIZE, chap_n,
30662306a36Sopenharmony_ci				&type) < 0) {
30762306a36Sopenharmony_ci		pr_err("Could not find CHAP_N.\n");
30862306a36Sopenharmony_ci		goto out;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci	if (type == HEX) {
31162306a36Sopenharmony_ci		pr_err("Could not find CHAP_N.\n");
31262306a36Sopenharmony_ci		goto out;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* Include the terminating NULL in the compare */
31662306a36Sopenharmony_ci	compare_len = strlen(auth->userid) + 1;
31762306a36Sopenharmony_ci	if (strncmp(chap_n, auth->userid, compare_len) != 0) {
31862306a36Sopenharmony_ci		pr_err("CHAP_N values do not match!\n");
31962306a36Sopenharmony_ci		goto out;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci	pr_debug("[server] Got CHAP_N=%s\n", chap_n);
32262306a36Sopenharmony_ci	/*
32362306a36Sopenharmony_ci	 * Extract CHAP_R.
32462306a36Sopenharmony_ci	 */
32562306a36Sopenharmony_ci	if (extract_param(nr_in_ptr, "CHAP_R", MAX_RESPONSE_LENGTH, chap_r,
32662306a36Sopenharmony_ci				&type) < 0) {
32762306a36Sopenharmony_ci		pr_err("Could not find CHAP_R.\n");
32862306a36Sopenharmony_ci		goto out;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	switch (type) {
33262306a36Sopenharmony_ci	case HEX:
33362306a36Sopenharmony_ci		if (strlen(chap_r) != chap->digest_size * 2) {
33462306a36Sopenharmony_ci			pr_err("Malformed CHAP_R\n");
33562306a36Sopenharmony_ci			goto out;
33662306a36Sopenharmony_ci		}
33762306a36Sopenharmony_ci		if (hex2bin(client_digest, chap_r, chap->digest_size) < 0) {
33862306a36Sopenharmony_ci			pr_err("Malformed CHAP_R: invalid HEX\n");
33962306a36Sopenharmony_ci			goto out;
34062306a36Sopenharmony_ci		}
34162306a36Sopenharmony_ci		break;
34262306a36Sopenharmony_ci	case BASE64:
34362306a36Sopenharmony_ci		if (chap_base64_decode(client_digest, chap_r, strlen(chap_r)) !=
34462306a36Sopenharmony_ci		    chap->digest_size) {
34562306a36Sopenharmony_ci			pr_err("Malformed CHAP_R: invalid BASE64\n");
34662306a36Sopenharmony_ci			goto out;
34762306a36Sopenharmony_ci		}
34862306a36Sopenharmony_ci		break;
34962306a36Sopenharmony_ci	default:
35062306a36Sopenharmony_ci		pr_err("Could not find CHAP_R\n");
35162306a36Sopenharmony_ci		goto out;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	pr_debug("[server] Got CHAP_R=%s\n", chap_r);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	tfm = crypto_alloc_shash(chap->digest_name, 0, 0);
35762306a36Sopenharmony_ci	if (IS_ERR(tfm)) {
35862306a36Sopenharmony_ci		tfm = NULL;
35962306a36Sopenharmony_ci		pr_err("Unable to allocate struct crypto_shash\n");
36062306a36Sopenharmony_ci		goto out;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
36462306a36Sopenharmony_ci	if (!desc) {
36562306a36Sopenharmony_ci		pr_err("Unable to allocate struct shash_desc\n");
36662306a36Sopenharmony_ci		goto out;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	desc->tfm = tfm;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	ret = crypto_shash_init(desc);
37262306a36Sopenharmony_ci	if (ret < 0) {
37362306a36Sopenharmony_ci		pr_err("crypto_shash_init() failed\n");
37462306a36Sopenharmony_ci		goto out;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	ret = crypto_shash_update(desc, &chap->id, 1);
37862306a36Sopenharmony_ci	if (ret < 0) {
37962306a36Sopenharmony_ci		pr_err("crypto_shash_update() failed for id\n");
38062306a36Sopenharmony_ci		goto out;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	ret = crypto_shash_update(desc, (char *)&auth->password,
38462306a36Sopenharmony_ci				  strlen(auth->password));
38562306a36Sopenharmony_ci	if (ret < 0) {
38662306a36Sopenharmony_ci		pr_err("crypto_shash_update() failed for password\n");
38762306a36Sopenharmony_ci		goto out;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	ret = crypto_shash_finup(desc, chap->challenge,
39162306a36Sopenharmony_ci				 chap->challenge_len, server_digest);
39262306a36Sopenharmony_ci	if (ret < 0) {
39362306a36Sopenharmony_ci		pr_err("crypto_shash_finup() failed for challenge\n");
39462306a36Sopenharmony_ci		goto out;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	bin2hex(response, server_digest, chap->digest_size);
39862306a36Sopenharmony_ci	pr_debug("[server] %s Server Digest: %s\n",
39962306a36Sopenharmony_ci		chap->digest_name, response);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (memcmp(server_digest, client_digest, chap->digest_size) != 0) {
40262306a36Sopenharmony_ci		pr_debug("[server] %s Digests do not match!\n\n",
40362306a36Sopenharmony_ci			chap->digest_name);
40462306a36Sopenharmony_ci		goto out;
40562306a36Sopenharmony_ci	} else
40662306a36Sopenharmony_ci		pr_debug("[server] %s Digests match, CHAP connection"
40762306a36Sopenharmony_ci				" successful.\n\n", chap->digest_name);
40862306a36Sopenharmony_ci	/*
40962306a36Sopenharmony_ci	 * One way authentication has succeeded, return now if mutual
41062306a36Sopenharmony_ci	 * authentication is not enabled.
41162306a36Sopenharmony_ci	 */
41262306a36Sopenharmony_ci	if (!auth->authenticate_target) {
41362306a36Sopenharmony_ci		auth_ret = 0;
41462306a36Sopenharmony_ci		goto out;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci	/*
41762306a36Sopenharmony_ci	 * Get CHAP_I.
41862306a36Sopenharmony_ci	 */
41962306a36Sopenharmony_ci	ret = extract_param(nr_in_ptr, "CHAP_I", 10, identifier, &type);
42062306a36Sopenharmony_ci	if (ret == -ENOENT) {
42162306a36Sopenharmony_ci		pr_debug("Could not find CHAP_I. Initiator uses One way authentication.\n");
42262306a36Sopenharmony_ci		auth_ret = 0;
42362306a36Sopenharmony_ci		goto out;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci	if (ret < 0) {
42662306a36Sopenharmony_ci		pr_err("Could not find CHAP_I.\n");
42762306a36Sopenharmony_ci		goto out;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (type == HEX)
43162306a36Sopenharmony_ci		ret = kstrtoul(&identifier[2], 0, &id);
43262306a36Sopenharmony_ci	else
43362306a36Sopenharmony_ci		ret = kstrtoul(identifier, 0, &id);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (ret < 0) {
43662306a36Sopenharmony_ci		pr_err("kstrtoul() failed for CHAP identifier: %d\n", ret);
43762306a36Sopenharmony_ci		goto out;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci	if (id > 255) {
44062306a36Sopenharmony_ci		pr_err("chap identifier: %lu greater than 255\n", id);
44162306a36Sopenharmony_ci		goto out;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci	/*
44462306a36Sopenharmony_ci	 * RFC 1994 says Identifier is no more than octet (8 bits).
44562306a36Sopenharmony_ci	 */
44662306a36Sopenharmony_ci	pr_debug("[server] Got CHAP_I=%lu\n", id);
44762306a36Sopenharmony_ci	/*
44862306a36Sopenharmony_ci	 * Get CHAP_C.
44962306a36Sopenharmony_ci	 */
45062306a36Sopenharmony_ci	if (extract_param(nr_in_ptr, "CHAP_C", CHAP_CHALLENGE_STR_LEN,
45162306a36Sopenharmony_ci			initiatorchg, &type) < 0) {
45262306a36Sopenharmony_ci		pr_err("Could not find CHAP_C.\n");
45362306a36Sopenharmony_ci		goto out;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	switch (type) {
45762306a36Sopenharmony_ci	case HEX:
45862306a36Sopenharmony_ci		initiatorchg_len = DIV_ROUND_UP(strlen(initiatorchg), 2);
45962306a36Sopenharmony_ci		if (!initiatorchg_len) {
46062306a36Sopenharmony_ci			pr_err("Unable to convert incoming challenge\n");
46162306a36Sopenharmony_ci			goto out;
46262306a36Sopenharmony_ci		}
46362306a36Sopenharmony_ci		if (initiatorchg_len > 1024) {
46462306a36Sopenharmony_ci			pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n");
46562306a36Sopenharmony_ci			goto out;
46662306a36Sopenharmony_ci		}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		if (hex2bin(initiatorchg_binhex, initiatorchg,
46962306a36Sopenharmony_ci			    initiatorchg_len) < 0) {
47062306a36Sopenharmony_ci			pr_err("Malformed CHAP_C: invalid HEX\n");
47162306a36Sopenharmony_ci			goto out;
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci		break;
47462306a36Sopenharmony_ci	case BASE64:
47562306a36Sopenharmony_ci		initiatorchg_len = chap_base64_decode(initiatorchg_binhex,
47662306a36Sopenharmony_ci						      initiatorchg,
47762306a36Sopenharmony_ci						      strlen(initiatorchg));
47862306a36Sopenharmony_ci		if (initiatorchg_len < 0) {
47962306a36Sopenharmony_ci			pr_err("Malformed CHAP_C: invalid BASE64\n");
48062306a36Sopenharmony_ci			goto out;
48162306a36Sopenharmony_ci		}
48262306a36Sopenharmony_ci		if (!initiatorchg_len) {
48362306a36Sopenharmony_ci			pr_err("Unable to convert incoming challenge\n");
48462306a36Sopenharmony_ci			goto out;
48562306a36Sopenharmony_ci		}
48662306a36Sopenharmony_ci		if (initiatorchg_len > 1024) {
48762306a36Sopenharmony_ci			pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n");
48862306a36Sopenharmony_ci			goto out;
48962306a36Sopenharmony_ci		}
49062306a36Sopenharmony_ci		break;
49162306a36Sopenharmony_ci	default:
49262306a36Sopenharmony_ci		pr_err("Could not find CHAP_C.\n");
49362306a36Sopenharmony_ci		goto out;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	pr_debug("[server] Got CHAP_C=%s\n", initiatorchg);
49762306a36Sopenharmony_ci	/*
49862306a36Sopenharmony_ci	 * During mutual authentication, the CHAP_C generated by the
49962306a36Sopenharmony_ci	 * initiator must not match the original CHAP_C generated by
50062306a36Sopenharmony_ci	 * the target.
50162306a36Sopenharmony_ci	 */
50262306a36Sopenharmony_ci	if (initiatorchg_len == chap->challenge_len &&
50362306a36Sopenharmony_ci				!memcmp(initiatorchg_binhex, chap->challenge,
50462306a36Sopenharmony_ci				initiatorchg_len)) {
50562306a36Sopenharmony_ci		pr_err("initiator CHAP_C matches target CHAP_C, failing"
50662306a36Sopenharmony_ci		       " login attempt\n");
50762306a36Sopenharmony_ci		goto out;
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci	/*
51062306a36Sopenharmony_ci	 * Generate CHAP_N and CHAP_R for mutual authentication.
51162306a36Sopenharmony_ci	 */
51262306a36Sopenharmony_ci	ret = crypto_shash_init(desc);
51362306a36Sopenharmony_ci	if (ret < 0) {
51462306a36Sopenharmony_ci		pr_err("crypto_shash_init() failed\n");
51562306a36Sopenharmony_ci		goto out;
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/* To handle both endiannesses */
51962306a36Sopenharmony_ci	id_as_uchar = id;
52062306a36Sopenharmony_ci	ret = crypto_shash_update(desc, &id_as_uchar, 1);
52162306a36Sopenharmony_ci	if (ret < 0) {
52262306a36Sopenharmony_ci		pr_err("crypto_shash_update() failed for id\n");
52362306a36Sopenharmony_ci		goto out;
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	ret = crypto_shash_update(desc, auth->password_mutual,
52762306a36Sopenharmony_ci				  strlen(auth->password_mutual));
52862306a36Sopenharmony_ci	if (ret < 0) {
52962306a36Sopenharmony_ci		pr_err("crypto_shash_update() failed for"
53062306a36Sopenharmony_ci				" password_mutual\n");
53162306a36Sopenharmony_ci		goto out;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci	/*
53462306a36Sopenharmony_ci	 * Convert received challenge to binary hex.
53562306a36Sopenharmony_ci	 */
53662306a36Sopenharmony_ci	ret = crypto_shash_finup(desc, initiatorchg_binhex, initiatorchg_len,
53762306a36Sopenharmony_ci				 digest);
53862306a36Sopenharmony_ci	if (ret < 0) {
53962306a36Sopenharmony_ci		pr_err("crypto_shash_finup() failed for ma challenge\n");
54062306a36Sopenharmony_ci		goto out;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	/*
54462306a36Sopenharmony_ci	 * Generate CHAP_N and CHAP_R.
54562306a36Sopenharmony_ci	 */
54662306a36Sopenharmony_ci	*nr_out_len = sprintf(nr_out_ptr, "CHAP_N=%s", auth->userid_mutual);
54762306a36Sopenharmony_ci	*nr_out_len += 1;
54862306a36Sopenharmony_ci	pr_debug("[server] Sending CHAP_N=%s\n", auth->userid_mutual);
54962306a36Sopenharmony_ci	/*
55062306a36Sopenharmony_ci	 * Convert response from binary hex to ascii hext.
55162306a36Sopenharmony_ci	 */
55262306a36Sopenharmony_ci	bin2hex(response, digest, chap->digest_size);
55362306a36Sopenharmony_ci	*nr_out_len += sprintf(nr_out_ptr + *nr_out_len, "CHAP_R=0x%s",
55462306a36Sopenharmony_ci			response);
55562306a36Sopenharmony_ci	*nr_out_len += 1;
55662306a36Sopenharmony_ci	pr_debug("[server] Sending CHAP_R=0x%s\n", response);
55762306a36Sopenharmony_ci	auth_ret = 0;
55862306a36Sopenharmony_ciout:
55962306a36Sopenharmony_ci	kfree_sensitive(desc);
56062306a36Sopenharmony_ci	if (tfm)
56162306a36Sopenharmony_ci		crypto_free_shash(tfm);
56262306a36Sopenharmony_ci	kfree(initiatorchg);
56362306a36Sopenharmony_ci	kfree(initiatorchg_binhex);
56462306a36Sopenharmony_ci	kfree(digest);
56562306a36Sopenharmony_ci	kfree(response);
56662306a36Sopenharmony_ci	kfree(server_digest);
56762306a36Sopenharmony_ci	kfree(client_digest);
56862306a36Sopenharmony_ci	return auth_ret;
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ciu32 chap_main_loop(
57262306a36Sopenharmony_ci	struct iscsit_conn *conn,
57362306a36Sopenharmony_ci	struct iscsi_node_auth *auth,
57462306a36Sopenharmony_ci	char *in_text,
57562306a36Sopenharmony_ci	char *out_text,
57662306a36Sopenharmony_ci	int *in_len,
57762306a36Sopenharmony_ci	int *out_len)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct iscsi_chap *chap = conn->auth_protocol;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (!chap) {
58262306a36Sopenharmony_ci		chap = chap_server_open(conn, auth, in_text, out_text, out_len);
58362306a36Sopenharmony_ci		if (!chap)
58462306a36Sopenharmony_ci			return 2;
58562306a36Sopenharmony_ci		chap->chap_state = CHAP_STAGE_SERVER_AIC;
58662306a36Sopenharmony_ci		return 0;
58762306a36Sopenharmony_ci	} else if (chap->chap_state == CHAP_STAGE_SERVER_AIC) {
58862306a36Sopenharmony_ci		convert_null_to_semi(in_text, *in_len);
58962306a36Sopenharmony_ci		if (chap_server_compute_hash(conn, auth, in_text, out_text,
59062306a36Sopenharmony_ci				out_len) < 0) {
59162306a36Sopenharmony_ci			chap_close(conn);
59262306a36Sopenharmony_ci			return 2;
59362306a36Sopenharmony_ci		}
59462306a36Sopenharmony_ci		if (auth->authenticate_target)
59562306a36Sopenharmony_ci			chap->chap_state = CHAP_STAGE_SERVER_NR;
59662306a36Sopenharmony_ci		else
59762306a36Sopenharmony_ci			*out_len = 0;
59862306a36Sopenharmony_ci		chap_close(conn);
59962306a36Sopenharmony_ci		return 1;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	return 2;
60362306a36Sopenharmony_ci}
604