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