brintos

brintos / linux-shallow public Read only

0
0
Text · 13.9 KiB · c8a248b Raw
604 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*******************************************************************************3 * This file houses the main functions for the iSCSI CHAP support4 *5 * (c) Copyright 2007-2013 Datera, Inc.6 *7 * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>8 *9 ******************************************************************************/10 11#include <crypto/hash.h>12#include <linux/kernel.h>13#include <linux/string.h>14#include <linux/err.h>15#include <linux/random.h>16#include <linux/scatterlist.h>17#include <target/iscsi/iscsi_target_core.h>18#include "iscsi_target_nego.h"19#include "iscsi_target_auth.h"20 21static char *chap_get_digest_name(const int digest_type)22{23	switch (digest_type) {24	case CHAP_DIGEST_MD5:25		return "md5";26	case CHAP_DIGEST_SHA1:27		return "sha1";28	case CHAP_DIGEST_SHA256:29		return "sha256";30	case CHAP_DIGEST_SHA3_256:31		return "sha3-256";32	default:33		return NULL;34	}35}36 37static int chap_gen_challenge(38	struct iscsit_conn *conn,39	int caller,40	char *c_str,41	unsigned int *c_len)42{43	int ret;44	unsigned char *challenge_asciihex;45	struct iscsi_chap *chap = conn->auth_protocol;46 47	challenge_asciihex = kzalloc(chap->challenge_len * 2 + 1, GFP_KERNEL);48	if (!challenge_asciihex)49		return -ENOMEM;50 51	memset(chap->challenge, 0, MAX_CHAP_CHALLENGE_LEN);52 53	ret = get_random_bytes_wait(chap->challenge, chap->challenge_len);54	if (unlikely(ret))55		goto out;56 57	bin2hex(challenge_asciihex, chap->challenge,58				chap->challenge_len);59	/*60	 * Set CHAP_C, and copy the generated challenge into c_str.61	 */62	*c_len += sprintf(c_str + *c_len, "CHAP_C=0x%s", challenge_asciihex);63	*c_len += 1;64 65	pr_debug("[%s] Sending CHAP_C=0x%s\n\n", (caller) ? "server" : "client",66			challenge_asciihex);67 68out:69	kfree(challenge_asciihex);70	return ret;71}72 73static int chap_test_algorithm(const char *name)74{75	struct crypto_shash *tfm;76 77	tfm = crypto_alloc_shash(name, 0, 0);78	if (IS_ERR(tfm))79		return -1;80 81	crypto_free_shash(tfm);82	return 0;83}84 85static int chap_check_algorithm(const char *a_str)86{87	char *tmp, *orig, *token, *digest_name;88	long digest_type;89	int r = CHAP_DIGEST_UNKNOWN;90 91	tmp = kstrdup(a_str, GFP_KERNEL);92	if (!tmp) {93		pr_err("Memory allocation failed for CHAP_A temporary buffer\n");94		return CHAP_DIGEST_UNKNOWN;95	}96	orig = tmp;97 98	token = strsep(&tmp, "=");99	if (!token)100		goto out;101 102	if (strcmp(token, "CHAP_A")) {103		pr_err("Unable to locate CHAP_A key\n");104		goto out;105	}106	while (token) {107		token = strsep(&tmp, ",");108		if (!token)109			goto out;110 111		if (kstrtol(token, 10, &digest_type))112			continue;113 114		digest_name = chap_get_digest_name(digest_type);115		if (!digest_name)116			continue;117 118		pr_debug("Selected %s Algorithm\n", digest_name);119		if (chap_test_algorithm(digest_name) < 0) {120			pr_err("failed to allocate %s algo\n", digest_name);121		} else {122			r = digest_type;123			goto out;124		}125	}126out:127	kfree(orig);128	return r;129}130 131static void chap_close(struct iscsit_conn *conn)132{133	kfree(conn->auth_protocol);134	conn->auth_protocol = NULL;135}136 137static struct iscsi_chap *chap_server_open(138	struct iscsit_conn *conn,139	struct iscsi_node_auth *auth,140	const char *a_str,141	char *aic_str,142	unsigned int *aic_len)143{144	int digest_type;145	struct iscsi_chap *chap;146 147	if (!(auth->naf_flags & NAF_USERID_SET) ||148	    !(auth->naf_flags & NAF_PASSWORD_SET)) {149		pr_err("CHAP user or password not set for"150				" Initiator ACL\n");151		return NULL;152	}153 154	conn->auth_protocol = kzalloc(sizeof(struct iscsi_chap), GFP_KERNEL);155	if (!conn->auth_protocol)156		return NULL;157 158	chap = conn->auth_protocol;159	digest_type = chap_check_algorithm(a_str);160	switch (digest_type) {161	case CHAP_DIGEST_MD5:162		chap->digest_size = MD5_SIGNATURE_SIZE;163		break;164	case CHAP_DIGEST_SHA1:165		chap->digest_size = SHA1_SIGNATURE_SIZE;166		break;167	case CHAP_DIGEST_SHA256:168		chap->digest_size = SHA256_SIGNATURE_SIZE;169		break;170	case CHAP_DIGEST_SHA3_256:171		chap->digest_size = SHA3_256_SIGNATURE_SIZE;172		break;173	case CHAP_DIGEST_UNKNOWN:174	default:175		pr_err("Unsupported CHAP_A value\n");176		chap_close(conn);177		return NULL;178	}179 180	chap->digest_name = chap_get_digest_name(digest_type);181 182	/* Tie the challenge length to the digest size */183	chap->challenge_len = chap->digest_size;184 185	pr_debug("[server] Got CHAP_A=%d\n", digest_type);186	*aic_len = sprintf(aic_str, "CHAP_A=%d", digest_type);187	*aic_len += 1;188	pr_debug("[server] Sending CHAP_A=%d\n", digest_type);189 190	/*191	 * Set Identifier.192	 */193	chap->id = conn->tpg->tpg_chap_id++;194	*aic_len += sprintf(aic_str + *aic_len, "CHAP_I=%d", chap->id);195	*aic_len += 1;196	pr_debug("[server] Sending CHAP_I=%d\n", chap->id);197	/*198	 * Generate Challenge.199	 */200	if (chap_gen_challenge(conn, 1, aic_str, aic_len) < 0) {201		chap_close(conn);202		return NULL;203	}204 205	return chap;206}207 208static const char base64_lookup_table[] =209	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";210 211static int chap_base64_decode(u8 *dst, const char *src, size_t len)212{213	int i, bits = 0, ac = 0;214	const char *p;215	u8 *cp = dst;216 217	for (i = 0; i < len; i++) {218		if (src[i] == '=')219			return cp - dst;220 221		p = strchr(base64_lookup_table, src[i]);222		if (p == NULL || src[i] == 0)223			return -2;224 225		ac <<= 6;226		ac += (p - base64_lookup_table);227		bits += 6;228		if (bits >= 8) {229			*cp++ = (ac >> (bits - 8)) & 0xff;230			ac &= ~(BIT(16) - BIT(bits - 8));231			bits -= 8;232		}233	}234	if (ac)235		return -1;236 237	return cp - dst;238}239 240static int chap_server_compute_hash(241	struct iscsit_conn *conn,242	struct iscsi_node_auth *auth,243	char *nr_in_ptr,244	char *nr_out_ptr,245	unsigned int *nr_out_len)246{247	unsigned long id;248	unsigned char id_as_uchar;249	unsigned char type;250	unsigned char identifier[10], *initiatorchg = NULL;251	unsigned char *initiatorchg_binhex = NULL;252	unsigned char *digest = NULL;253	unsigned char *response = NULL;254	unsigned char *client_digest = NULL;255	unsigned char *server_digest = NULL;256	unsigned char chap_n[MAX_CHAP_N_SIZE], chap_r[MAX_RESPONSE_LENGTH];257	size_t compare_len;258	struct iscsi_chap *chap = conn->auth_protocol;259	struct crypto_shash *tfm = NULL;260	struct shash_desc *desc = NULL;261	int auth_ret = -1, ret, initiatorchg_len;262 263	digest = kzalloc(chap->digest_size, GFP_KERNEL);264	if (!digest) {265		pr_err("Unable to allocate the digest buffer\n");266		goto out;267	}268 269	response = kzalloc(chap->digest_size * 2 + 2, GFP_KERNEL);270	if (!response) {271		pr_err("Unable to allocate the response buffer\n");272		goto out;273	}274 275	client_digest = kzalloc(chap->digest_size, GFP_KERNEL);276	if (!client_digest) {277		pr_err("Unable to allocate the client_digest buffer\n");278		goto out;279	}280 281	server_digest = kzalloc(chap->digest_size, GFP_KERNEL);282	if (!server_digest) {283		pr_err("Unable to allocate the server_digest buffer\n");284		goto out;285	}286 287	memset(identifier, 0, 10);288	memset(chap_n, 0, MAX_CHAP_N_SIZE);289	memset(chap_r, 0, MAX_RESPONSE_LENGTH);290 291	initiatorchg = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL);292	if (!initiatorchg) {293		pr_err("Unable to allocate challenge buffer\n");294		goto out;295	}296 297	initiatorchg_binhex = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL);298	if (!initiatorchg_binhex) {299		pr_err("Unable to allocate initiatorchg_binhex buffer\n");300		goto out;301	}302	/*303	 * Extract CHAP_N.304	 */305	if (extract_param(nr_in_ptr, "CHAP_N", MAX_CHAP_N_SIZE, chap_n,306				&type) < 0) {307		pr_err("Could not find CHAP_N.\n");308		goto out;309	}310	if (type == HEX) {311		pr_err("Could not find CHAP_N.\n");312		goto out;313	}314 315	/* Include the terminating NULL in the compare */316	compare_len = strlen(auth->userid) + 1;317	if (strncmp(chap_n, auth->userid, compare_len) != 0) {318		pr_err("CHAP_N values do not match!\n");319		goto out;320	}321	pr_debug("[server] Got CHAP_N=%s\n", chap_n);322	/*323	 * Extract CHAP_R.324	 */325	if (extract_param(nr_in_ptr, "CHAP_R", MAX_RESPONSE_LENGTH, chap_r,326				&type) < 0) {327		pr_err("Could not find CHAP_R.\n");328		goto out;329	}330 331	switch (type) {332	case HEX:333		if (strlen(chap_r) != chap->digest_size * 2) {334			pr_err("Malformed CHAP_R\n");335			goto out;336		}337		if (hex2bin(client_digest, chap_r, chap->digest_size) < 0) {338			pr_err("Malformed CHAP_R: invalid HEX\n");339			goto out;340		}341		break;342	case BASE64:343		if (chap_base64_decode(client_digest, chap_r, strlen(chap_r)) !=344		    chap->digest_size) {345			pr_err("Malformed CHAP_R: invalid BASE64\n");346			goto out;347		}348		break;349	default:350		pr_err("Could not find CHAP_R\n");351		goto out;352	}353 354	pr_debug("[server] Got CHAP_R=%s\n", chap_r);355 356	tfm = crypto_alloc_shash(chap->digest_name, 0, 0);357	if (IS_ERR(tfm)) {358		tfm = NULL;359		pr_err("Unable to allocate struct crypto_shash\n");360		goto out;361	}362 363	desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);364	if (!desc) {365		pr_err("Unable to allocate struct shash_desc\n");366		goto out;367	}368 369	desc->tfm = tfm;370 371	ret = crypto_shash_init(desc);372	if (ret < 0) {373		pr_err("crypto_shash_init() failed\n");374		goto out;375	}376 377	ret = crypto_shash_update(desc, &chap->id, 1);378	if (ret < 0) {379		pr_err("crypto_shash_update() failed for id\n");380		goto out;381	}382 383	ret = crypto_shash_update(desc, (char *)&auth->password,384				  strlen(auth->password));385	if (ret < 0) {386		pr_err("crypto_shash_update() failed for password\n");387		goto out;388	}389 390	ret = crypto_shash_finup(desc, chap->challenge,391				 chap->challenge_len, server_digest);392	if (ret < 0) {393		pr_err("crypto_shash_finup() failed for challenge\n");394		goto out;395	}396 397	bin2hex(response, server_digest, chap->digest_size);398	pr_debug("[server] %s Server Digest: %s\n",399		chap->digest_name, response);400 401	if (memcmp(server_digest, client_digest, chap->digest_size) != 0) {402		pr_debug("[server] %s Digests do not match!\n\n",403			chap->digest_name);404		goto out;405	} else406		pr_debug("[server] %s Digests match, CHAP connection"407				" successful.\n\n", chap->digest_name);408	/*409	 * One way authentication has succeeded, return now if mutual410	 * authentication is not enabled.411	 */412	if (!auth->authenticate_target) {413		auth_ret = 0;414		goto out;415	}416	/*417	 * Get CHAP_I.418	 */419	ret = extract_param(nr_in_ptr, "CHAP_I", 10, identifier, &type);420	if (ret == -ENOENT) {421		pr_debug("Could not find CHAP_I. Initiator uses One way authentication.\n");422		auth_ret = 0;423		goto out;424	}425	if (ret < 0) {426		pr_err("Could not find CHAP_I.\n");427		goto out;428	}429 430	if (type == HEX)431		ret = kstrtoul(&identifier[2], 0, &id);432	else433		ret = kstrtoul(identifier, 0, &id);434 435	if (ret < 0) {436		pr_err("kstrtoul() failed for CHAP identifier: %d\n", ret);437		goto out;438	}439	if (id > 255) {440		pr_err("chap identifier: %lu greater than 255\n", id);441		goto out;442	}443	/*444	 * RFC 1994 says Identifier is no more than octet (8 bits).445	 */446	pr_debug("[server] Got CHAP_I=%lu\n", id);447	/*448	 * Get CHAP_C.449	 */450	if (extract_param(nr_in_ptr, "CHAP_C", CHAP_CHALLENGE_STR_LEN,451			initiatorchg, &type) < 0) {452		pr_err("Could not find CHAP_C.\n");453		goto out;454	}455 456	switch (type) {457	case HEX:458		initiatorchg_len = DIV_ROUND_UP(strlen(initiatorchg), 2);459		if (!initiatorchg_len) {460			pr_err("Unable to convert incoming challenge\n");461			goto out;462		}463		if (initiatorchg_len > 1024) {464			pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n");465			goto out;466		}467 468		if (hex2bin(initiatorchg_binhex, initiatorchg,469			    initiatorchg_len) < 0) {470			pr_err("Malformed CHAP_C: invalid HEX\n");471			goto out;472		}473		break;474	case BASE64:475		initiatorchg_len = chap_base64_decode(initiatorchg_binhex,476						      initiatorchg,477						      strlen(initiatorchg));478		if (initiatorchg_len < 0) {479			pr_err("Malformed CHAP_C: invalid BASE64\n");480			goto out;481		}482		if (!initiatorchg_len) {483			pr_err("Unable to convert incoming challenge\n");484			goto out;485		}486		if (initiatorchg_len > 1024) {487			pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n");488			goto out;489		}490		break;491	default:492		pr_err("Could not find CHAP_C.\n");493		goto out;494	}495 496	pr_debug("[server] Got CHAP_C=%s\n", initiatorchg);497	/*498	 * During mutual authentication, the CHAP_C generated by the499	 * initiator must not match the original CHAP_C generated by500	 * the target.501	 */502	if (initiatorchg_len == chap->challenge_len &&503				!memcmp(initiatorchg_binhex, chap->challenge,504				initiatorchg_len)) {505		pr_err("initiator CHAP_C matches target CHAP_C, failing"506		       " login attempt\n");507		goto out;508	}509	/*510	 * Generate CHAP_N and CHAP_R for mutual authentication.511	 */512	ret = crypto_shash_init(desc);513	if (ret < 0) {514		pr_err("crypto_shash_init() failed\n");515		goto out;516	}517 518	/* To handle both endiannesses */519	id_as_uchar = id;520	ret = crypto_shash_update(desc, &id_as_uchar, 1);521	if (ret < 0) {522		pr_err("crypto_shash_update() failed for id\n");523		goto out;524	}525 526	ret = crypto_shash_update(desc, auth->password_mutual,527				  strlen(auth->password_mutual));528	if (ret < 0) {529		pr_err("crypto_shash_update() failed for"530				" password_mutual\n");531		goto out;532	}533	/*534	 * Convert received challenge to binary hex.535	 */536	ret = crypto_shash_finup(desc, initiatorchg_binhex, initiatorchg_len,537				 digest);538	if (ret < 0) {539		pr_err("crypto_shash_finup() failed for ma challenge\n");540		goto out;541	}542 543	/*544	 * Generate CHAP_N and CHAP_R.545	 */546	*nr_out_len = sprintf(nr_out_ptr, "CHAP_N=%s", auth->userid_mutual);547	*nr_out_len += 1;548	pr_debug("[server] Sending CHAP_N=%s\n", auth->userid_mutual);549	/*550	 * Convert response from binary hex to ascii hext.551	 */552	bin2hex(response, digest, chap->digest_size);553	*nr_out_len += sprintf(nr_out_ptr + *nr_out_len, "CHAP_R=0x%s",554			response);555	*nr_out_len += 1;556	pr_debug("[server] Sending CHAP_R=0x%s\n", response);557	auth_ret = 0;558out:559	kfree_sensitive(desc);560	if (tfm)561		crypto_free_shash(tfm);562	kfree(initiatorchg);563	kfree(initiatorchg_binhex);564	kfree(digest);565	kfree(response);566	kfree(server_digest);567	kfree(client_digest);568	return auth_ret;569}570 571u32 chap_main_loop(572	struct iscsit_conn *conn,573	struct iscsi_node_auth *auth,574	char *in_text,575	char *out_text,576	int *in_len,577	int *out_len)578{579	struct iscsi_chap *chap = conn->auth_protocol;580 581	if (!chap) {582		chap = chap_server_open(conn, auth, in_text, out_text, out_len);583		if (!chap)584			return 2;585		chap->chap_state = CHAP_STAGE_SERVER_AIC;586		return 0;587	} else if (chap->chap_state == CHAP_STAGE_SERVER_AIC) {588		convert_null_to_semi(in_text, *in_len);589		if (chap_server_compute_hash(conn, auth, in_text, out_text,590				out_len) < 0) {591			chap_close(conn);592			return 2;593		}594		if (auth->authenticate_target)595			chap->chap_state = CHAP_STAGE_SERVER_NR;596		else597			*out_len = 0;598		chap_close(conn);599		return 1;600	}601 602	return 2;603}604