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