root/drivers/target/iscsi/iscsi_target_auth.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. chap_gen_challenge
  2. chap_check_algorithm
  3. chap_close
  4. chap_server_open
  5. chap_server_compute_md5
  6. chap_got_response
  7. chap_main_loop

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*******************************************************************************
   3  * This file houses the main functions for the iSCSI CHAP support
   4  *
   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 
  21 static int chap_gen_challenge(
  22         struct iscsi_conn *conn,
  23         int caller,
  24         char *c_str,
  25         unsigned int *c_len)
  26 {
  27         int ret;
  28         unsigned char challenge_asciihex[CHAP_CHALLENGE_LENGTH * 2 + 1];
  29         struct iscsi_chap *chap = conn->auth_protocol;
  30 
  31         memset(challenge_asciihex, 0, CHAP_CHALLENGE_LENGTH * 2 + 1);
  32 
  33         ret = get_random_bytes_wait(chap->challenge, CHAP_CHALLENGE_LENGTH);
  34         if (unlikely(ret))
  35                 return ret;
  36         bin2hex(challenge_asciihex, chap->challenge,
  37                                 CHAP_CHALLENGE_LENGTH);
  38         /*
  39          * Set CHAP_C, and copy the generated challenge into c_str.
  40          */
  41         *c_len += sprintf(c_str + *c_len, "CHAP_C=0x%s", challenge_asciihex);
  42         *c_len += 1;
  43 
  44         pr_debug("[%s] Sending CHAP_C=0x%s\n\n", (caller) ? "server" : "client",
  45                         challenge_asciihex);
  46         return 0;
  47 }
  48 
  49 static int chap_check_algorithm(const char *a_str)
  50 {
  51         char *tmp, *orig, *token;
  52 
  53         tmp = kstrdup(a_str, GFP_KERNEL);
  54         if (!tmp) {
  55                 pr_err("Memory allocation failed for CHAP_A temporary buffer\n");
  56                 return CHAP_DIGEST_UNKNOWN;
  57         }
  58         orig = tmp;
  59 
  60         token = strsep(&tmp, "=");
  61         if (!token)
  62                 goto out;
  63 
  64         if (strcmp(token, "CHAP_A")) {
  65                 pr_err("Unable to locate CHAP_A key\n");
  66                 goto out;
  67         }
  68         while (token) {
  69                 token = strsep(&tmp, ",");
  70                 if (!token)
  71                         goto out;
  72 
  73                 if (!strcmp(token, "5")) {
  74                         pr_debug("Selected MD5 Algorithm\n");
  75                         kfree(orig);
  76                         return CHAP_DIGEST_MD5;
  77                 }
  78         }
  79 out:
  80         kfree(orig);
  81         return CHAP_DIGEST_UNKNOWN;
  82 }
  83 
  84 static void chap_close(struct iscsi_conn *conn)
  85 {
  86         kfree(conn->auth_protocol);
  87         conn->auth_protocol = NULL;
  88 }
  89 
  90 static struct iscsi_chap *chap_server_open(
  91         struct iscsi_conn *conn,
  92         struct iscsi_node_auth *auth,
  93         const char *a_str,
  94         char *aic_str,
  95         unsigned int *aic_len)
  96 {
  97         int ret;
  98         struct iscsi_chap *chap;
  99 
 100         if (!(auth->naf_flags & NAF_USERID_SET) ||
 101             !(auth->naf_flags & NAF_PASSWORD_SET)) {
 102                 pr_err("CHAP user or password not set for"
 103                                 " Initiator ACL\n");
 104                 return NULL;
 105         }
 106 
 107         conn->auth_protocol = kzalloc(sizeof(struct iscsi_chap), GFP_KERNEL);
 108         if (!conn->auth_protocol)
 109                 return NULL;
 110 
 111         chap = conn->auth_protocol;
 112         ret = chap_check_algorithm(a_str);
 113         switch (ret) {
 114         case CHAP_DIGEST_MD5:
 115                 pr_debug("[server] Got CHAP_A=5\n");
 116                 /*
 117                  * Send back CHAP_A set to MD5.
 118                 */
 119                 *aic_len = sprintf(aic_str, "CHAP_A=5");
 120                 *aic_len += 1;
 121                 chap->digest_type = CHAP_DIGEST_MD5;
 122                 pr_debug("[server] Sending CHAP_A=%d\n", chap->digest_type);
 123                 break;
 124         case CHAP_DIGEST_UNKNOWN:
 125         default:
 126                 pr_err("Unsupported CHAP_A value\n");
 127                 chap_close(conn);
 128                 return NULL;
 129         }
 130 
 131         /*
 132          * Set Identifier.
 133          */
 134         chap->id = conn->tpg->tpg_chap_id++;
 135         *aic_len += sprintf(aic_str + *aic_len, "CHAP_I=%d", chap->id);
 136         *aic_len += 1;
 137         pr_debug("[server] Sending CHAP_I=%d\n", chap->id);
 138         /*
 139          * Generate Challenge.
 140          */
 141         if (chap_gen_challenge(conn, 1, aic_str, aic_len) < 0) {
 142                 chap_close(conn);
 143                 return NULL;
 144         }
 145 
 146         return chap;
 147 }
 148 
 149 static int chap_server_compute_md5(
 150         struct iscsi_conn *conn,
 151         struct iscsi_node_auth *auth,
 152         char *nr_in_ptr,
 153         char *nr_out_ptr,
 154         unsigned int *nr_out_len)
 155 {
 156         unsigned long id;
 157         unsigned char id_as_uchar;
 158         unsigned char digest[MD5_SIGNATURE_SIZE];
 159         unsigned char type, response[MD5_SIGNATURE_SIZE * 2 + 2];
 160         unsigned char identifier[10], *challenge = NULL;
 161         unsigned char *challenge_binhex = NULL;
 162         unsigned char client_digest[MD5_SIGNATURE_SIZE];
 163         unsigned char server_digest[MD5_SIGNATURE_SIZE];
 164         unsigned char chap_n[MAX_CHAP_N_SIZE], chap_r[MAX_RESPONSE_LENGTH];
 165         size_t compare_len;
 166         struct iscsi_chap *chap = conn->auth_protocol;
 167         struct crypto_shash *tfm = NULL;
 168         struct shash_desc *desc = NULL;
 169         int auth_ret = -1, ret, challenge_len;
 170 
 171         memset(identifier, 0, 10);
 172         memset(chap_n, 0, MAX_CHAP_N_SIZE);
 173         memset(chap_r, 0, MAX_RESPONSE_LENGTH);
 174         memset(digest, 0, MD5_SIGNATURE_SIZE);
 175         memset(response, 0, MD5_SIGNATURE_SIZE * 2 + 2);
 176         memset(client_digest, 0, MD5_SIGNATURE_SIZE);
 177         memset(server_digest, 0, MD5_SIGNATURE_SIZE);
 178 
 179         challenge = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL);
 180         if (!challenge) {
 181                 pr_err("Unable to allocate challenge buffer\n");
 182                 goto out;
 183         }
 184 
 185         challenge_binhex = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL);
 186         if (!challenge_binhex) {
 187                 pr_err("Unable to allocate challenge_binhex buffer\n");
 188                 goto out;
 189         }
 190         /*
 191          * Extract CHAP_N.
 192          */
 193         if (extract_param(nr_in_ptr, "CHAP_N", MAX_CHAP_N_SIZE, chap_n,
 194                                 &type) < 0) {
 195                 pr_err("Could not find CHAP_N.\n");
 196                 goto out;
 197         }
 198         if (type == HEX) {
 199                 pr_err("Could not find CHAP_N.\n");
 200                 goto out;
 201         }
 202 
 203         /* Include the terminating NULL in the compare */
 204         compare_len = strlen(auth->userid) + 1;
 205         if (strncmp(chap_n, auth->userid, compare_len) != 0) {
 206                 pr_err("CHAP_N values do not match!\n");
 207                 goto out;
 208         }
 209         pr_debug("[server] Got CHAP_N=%s\n", chap_n);
 210         /*
 211          * Extract CHAP_R.
 212          */
 213         if (extract_param(nr_in_ptr, "CHAP_R", MAX_RESPONSE_LENGTH, chap_r,
 214                                 &type) < 0) {
 215                 pr_err("Could not find CHAP_R.\n");
 216                 goto out;
 217         }
 218         if (type != HEX) {
 219                 pr_err("Could not find CHAP_R.\n");
 220                 goto out;
 221         }
 222         if (strlen(chap_r) != MD5_SIGNATURE_SIZE * 2) {
 223                 pr_err("Malformed CHAP_R\n");
 224                 goto out;
 225         }
 226         if (hex2bin(client_digest, chap_r, MD5_SIGNATURE_SIZE) < 0) {
 227                 pr_err("Malformed CHAP_R\n");
 228                 goto out;
 229         }
 230 
 231         pr_debug("[server] Got CHAP_R=%s\n", chap_r);
 232 
 233         tfm = crypto_alloc_shash("md5", 0, 0);
 234         if (IS_ERR(tfm)) {
 235                 tfm = NULL;
 236                 pr_err("Unable to allocate struct crypto_shash\n");
 237                 goto out;
 238         }
 239 
 240         desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
 241         if (!desc) {
 242                 pr_err("Unable to allocate struct shash_desc\n");
 243                 goto out;
 244         }
 245 
 246         desc->tfm = tfm;
 247 
 248         ret = crypto_shash_init(desc);
 249         if (ret < 0) {
 250                 pr_err("crypto_shash_init() failed\n");
 251                 goto out;
 252         }
 253 
 254         ret = crypto_shash_update(desc, &chap->id, 1);
 255         if (ret < 0) {
 256                 pr_err("crypto_shash_update() failed for id\n");
 257                 goto out;
 258         }
 259 
 260         ret = crypto_shash_update(desc, (char *)&auth->password,
 261                                   strlen(auth->password));
 262         if (ret < 0) {
 263                 pr_err("crypto_shash_update() failed for password\n");
 264                 goto out;
 265         }
 266 
 267         ret = crypto_shash_finup(desc, chap->challenge,
 268                                  CHAP_CHALLENGE_LENGTH, server_digest);
 269         if (ret < 0) {
 270                 pr_err("crypto_shash_finup() failed for challenge\n");
 271                 goto out;
 272         }
 273 
 274         bin2hex(response, server_digest, MD5_SIGNATURE_SIZE);
 275         pr_debug("[server] MD5 Server Digest: %s\n", response);
 276 
 277         if (memcmp(server_digest, client_digest, MD5_SIGNATURE_SIZE) != 0) {
 278                 pr_debug("[server] MD5 Digests do not match!\n\n");
 279                 goto out;
 280         } else
 281                 pr_debug("[server] MD5 Digests match, CHAP connection"
 282                                 " successful.\n\n");
 283         /*
 284          * One way authentication has succeeded, return now if mutual
 285          * authentication is not enabled.
 286          */
 287         if (!auth->authenticate_target) {
 288                 auth_ret = 0;
 289                 goto out;
 290         }
 291         /*
 292          * Get CHAP_I.
 293          */
 294         if (extract_param(nr_in_ptr, "CHAP_I", 10, identifier, &type) < 0) {
 295                 pr_err("Could not find CHAP_I.\n");
 296                 goto out;
 297         }
 298 
 299         if (type == HEX)
 300                 ret = kstrtoul(&identifier[2], 0, &id);
 301         else
 302                 ret = kstrtoul(identifier, 0, &id);
 303 
 304         if (ret < 0) {
 305                 pr_err("kstrtoul() failed for CHAP identifier: %d\n", ret);
 306                 goto out;
 307         }
 308         if (id > 255) {
 309                 pr_err("chap identifier: %lu greater than 255\n", id);
 310                 goto out;
 311         }
 312         /*
 313          * RFC 1994 says Identifier is no more than octet (8 bits).
 314          */
 315         pr_debug("[server] Got CHAP_I=%lu\n", id);
 316         /*
 317          * Get CHAP_C.
 318          */
 319         if (extract_param(nr_in_ptr, "CHAP_C", CHAP_CHALLENGE_STR_LEN,
 320                         challenge, &type) < 0) {
 321                 pr_err("Could not find CHAP_C.\n");
 322                 goto out;
 323         }
 324 
 325         if (type != HEX) {
 326                 pr_err("Could not find CHAP_C.\n");
 327                 goto out;
 328         }
 329         challenge_len = DIV_ROUND_UP(strlen(challenge), 2);
 330         if (!challenge_len) {
 331                 pr_err("Unable to convert incoming challenge\n");
 332                 goto out;
 333         }
 334         if (challenge_len > 1024) {
 335                 pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n");
 336                 goto out;
 337         }
 338         if (hex2bin(challenge_binhex, challenge, challenge_len) < 0) {
 339                 pr_err("Malformed CHAP_C\n");
 340                 goto out;
 341         }
 342         pr_debug("[server] Got CHAP_C=%s\n", challenge);
 343         /*
 344          * During mutual authentication, the CHAP_C generated by the
 345          * initiator must not match the original CHAP_C generated by
 346          * the target.
 347          */
 348         if (!memcmp(challenge_binhex, chap->challenge, CHAP_CHALLENGE_LENGTH)) {
 349                 pr_err("initiator CHAP_C matches target CHAP_C, failing"
 350                        " login attempt\n");
 351                 goto out;
 352         }
 353         /*
 354          * Generate CHAP_N and CHAP_R for mutual authentication.
 355          */
 356         ret = crypto_shash_init(desc);
 357         if (ret < 0) {
 358                 pr_err("crypto_shash_init() failed\n");
 359                 goto out;
 360         }
 361 
 362         /* To handle both endiannesses */
 363         id_as_uchar = id;
 364         ret = crypto_shash_update(desc, &id_as_uchar, 1);
 365         if (ret < 0) {
 366                 pr_err("crypto_shash_update() failed for id\n");
 367                 goto out;
 368         }
 369 
 370         ret = crypto_shash_update(desc, auth->password_mutual,
 371                                   strlen(auth->password_mutual));
 372         if (ret < 0) {
 373                 pr_err("crypto_shash_update() failed for"
 374                                 " password_mutual\n");
 375                 goto out;
 376         }
 377         /*
 378          * Convert received challenge to binary hex.
 379          */
 380         ret = crypto_shash_finup(desc, challenge_binhex, challenge_len,
 381                                  digest);
 382         if (ret < 0) {
 383                 pr_err("crypto_shash_finup() failed for ma challenge\n");
 384                 goto out;
 385         }
 386 
 387         /*
 388          * Generate CHAP_N and CHAP_R.
 389          */
 390         *nr_out_len = sprintf(nr_out_ptr, "CHAP_N=%s", auth->userid_mutual);
 391         *nr_out_len += 1;
 392         pr_debug("[server] Sending CHAP_N=%s\n", auth->userid_mutual);
 393         /*
 394          * Convert response from binary hex to ascii hext.
 395          */
 396         bin2hex(response, digest, MD5_SIGNATURE_SIZE);
 397         *nr_out_len += sprintf(nr_out_ptr + *nr_out_len, "CHAP_R=0x%s",
 398                         response);
 399         *nr_out_len += 1;
 400         pr_debug("[server] Sending CHAP_R=0x%s\n", response);
 401         auth_ret = 0;
 402 out:
 403         kzfree(desc);
 404         if (tfm)
 405                 crypto_free_shash(tfm);
 406         kfree(challenge);
 407         kfree(challenge_binhex);
 408         return auth_ret;
 409 }
 410 
 411 static int chap_got_response(
 412         struct iscsi_conn *conn,
 413         struct iscsi_node_auth *auth,
 414         char *nr_in_ptr,
 415         char *nr_out_ptr,
 416         unsigned int *nr_out_len)
 417 {
 418         struct iscsi_chap *chap = conn->auth_protocol;
 419 
 420         switch (chap->digest_type) {
 421         case CHAP_DIGEST_MD5:
 422                 if (chap_server_compute_md5(conn, auth, nr_in_ptr,
 423                                 nr_out_ptr, nr_out_len) < 0)
 424                         return -1;
 425                 return 0;
 426         default:
 427                 pr_err("Unknown CHAP digest type %d!\n",
 428                                 chap->digest_type);
 429                 return -1;
 430         }
 431 }
 432 
 433 u32 chap_main_loop(
 434         struct iscsi_conn *conn,
 435         struct iscsi_node_auth *auth,
 436         char *in_text,
 437         char *out_text,
 438         int *in_len,
 439         int *out_len)
 440 {
 441         struct iscsi_chap *chap = conn->auth_protocol;
 442 
 443         if (!chap) {
 444                 chap = chap_server_open(conn, auth, in_text, out_text, out_len);
 445                 if (!chap)
 446                         return 2;
 447                 chap->chap_state = CHAP_STAGE_SERVER_AIC;
 448                 return 0;
 449         } else if (chap->chap_state == CHAP_STAGE_SERVER_AIC) {
 450                 convert_null_to_semi(in_text, *in_len);
 451                 if (chap_got_response(conn, auth, in_text, out_text,
 452                                 out_len) < 0) {
 453                         chap_close(conn);
 454                         return 2;
 455                 }
 456                 if (auth->authenticate_target)
 457                         chap->chap_state = CHAP_STAGE_SERVER_NR;
 458                 else
 459                         *out_len = 0;
 460                 chap_close(conn);
 461                 return 1;
 462         }
 463 
 464         return 2;
 465 }

/* [<][>][^][v][top][bottom][index][help] */