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