linux/drivers/target/iscsi/iscsi_target_auth.c
<<
>>
Prefs
   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
  21static 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
  49static 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 (!strncmp(token, "5", 1)) {
  74                        pr_debug("Selected MD5 Algorithm\n");
  75                        kfree(orig);
  76                        return CHAP_DIGEST_MD5;
  77                }
  78        }
  79out:
  80        kfree(orig);
  81        return CHAP_DIGEST_UNKNOWN;
  82}
  83
  84static void chap_close(struct iscsi_conn *conn)
  85{
  86        kfree(conn->auth_protocol);
  87        conn->auth_protocol = NULL;
  88}
  89
  90static 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
 149static 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;
 402out:
 403        kzfree(desc);
 404        if (tfm)
 405                crypto_free_shash(tfm);
 406        kfree(challenge);
 407        kfree(challenge_binhex);
 408        return auth_ret;
 409}
 410
 411static 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
 433u32 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}
 466