2011-07-23 14:43:04 +08:00
|
|
|
/*******************************************************************************
|
|
|
|
* This file houses the main functions for the iSCSI CHAP support
|
|
|
|
*
|
2013-09-06 06:29:12 +08:00
|
|
|
* (c) Copyright 2007-2013 Datera, Inc.
|
2011-07-23 14:43:04 +08:00
|
|
|
*
|
|
|
|
* Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
******************************************************************************/
|
|
|
|
|
2016-01-24 21:19:52 +08:00
|
|
|
#include <crypto/hash.h>
|
2011-09-30 19:39:54 +08:00
|
|
|
#include <linux/kernel.h>
|
2011-07-23 14:43:04 +08:00
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/err.h>
|
2016-11-15 07:47:14 +08:00
|
|
|
#include <linux/random.h>
|
2011-07-23 14:43:04 +08:00
|
|
|
#include <linux/scatterlist.h>
|
2015-01-07 20:57:31 +08:00
|
|
|
#include <target/iscsi/iscsi_target_core.h>
|
2011-07-23 14:43:04 +08:00
|
|
|
#include "iscsi_target_nego.h"
|
|
|
|
#include "iscsi_target_auth.h"
|
|
|
|
|
|
|
|
static void chap_binaryhex_to_asciihex(char *dst, char *src, int src_len)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < src_len; i++) {
|
|
|
|
sprintf(&dst[i*2], "%02x", (int) src[i] & 0xff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-08 10:34:26 +08:00
|
|
|
static int chap_gen_challenge(
|
2011-07-23 14:43:04 +08:00
|
|
|
struct iscsi_conn *conn,
|
|
|
|
int caller,
|
|
|
|
char *c_str,
|
|
|
|
unsigned int *c_len)
|
|
|
|
{
|
2017-06-08 10:34:26 +08:00
|
|
|
int ret;
|
2011-07-23 14:43:04 +08:00
|
|
|
unsigned char challenge_asciihex[CHAP_CHALLENGE_LENGTH * 2 + 1];
|
target: remove useless casts
A reader should spend an extra moment whenever noticing a cast,
because either something special is going on that deserves extra
attention or, as is all too often the case, the code is wrong.
These casts, afaics, have all been useless. They cast a foo* to a
foo*, cast a void* to the assigned type, cast a foo* to void*, before
assigning it to a void* variable, etc.
In a few cases I also removed an additional &...[0], which is equally
useless.
Lastly I added three FIXMEs where, to the best of my judgement, the
code appears to have a bug. It would be good if someone could check
these.
Signed-off-by: Joern Engel <joern@logfs.org>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
2011-11-24 09:05:51 +08:00
|
|
|
struct iscsi_chap *chap = conn->auth_protocol;
|
2011-07-23 14:43:04 +08:00
|
|
|
|
|
|
|
memset(challenge_asciihex, 0, CHAP_CHALLENGE_LENGTH * 2 + 1);
|
|
|
|
|
2017-06-08 10:34:26 +08:00
|
|
|
ret = get_random_bytes_wait(chap->challenge, CHAP_CHALLENGE_LENGTH);
|
|
|
|
if (unlikely(ret))
|
|
|
|
return ret;
|
2011-07-23 14:43:04 +08:00
|
|
|
chap_binaryhex_to_asciihex(challenge_asciihex, chap->challenge,
|
|
|
|
CHAP_CHALLENGE_LENGTH);
|
|
|
|
/*
|
|
|
|
* Set CHAP_C, and copy the generated challenge into c_str.
|
|
|
|
*/
|
|
|
|
*c_len += sprintf(c_str + *c_len, "CHAP_C=0x%s", challenge_asciihex);
|
|
|
|
*c_len += 1;
|
|
|
|
|
|
|
|
pr_debug("[%s] Sending CHAP_C=0x%s\n\n", (caller) ? "server" : "client",
|
|
|
|
challenge_asciihex);
|
2017-06-08 10:34:26 +08:00
|
|
|
return 0;
|
2011-07-23 14:43:04 +08:00
|
|
|
}
|
|
|
|
|
2014-05-30 13:43:47 +08:00
|
|
|
static int chap_check_algorithm(const char *a_str)
|
|
|
|
{
|
|
|
|
char *tmp, *orig, *token;
|
|
|
|
|
|
|
|
tmp = kstrdup(a_str, GFP_KERNEL);
|
|
|
|
if (!tmp) {
|
|
|
|
pr_err("Memory allocation failed for CHAP_A temporary buffer\n");
|
|
|
|
return CHAP_DIGEST_UNKNOWN;
|
|
|
|
}
|
|
|
|
orig = tmp;
|
|
|
|
|
|
|
|
token = strsep(&tmp, "=");
|
|
|
|
if (!token)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (strcmp(token, "CHAP_A")) {
|
|
|
|
pr_err("Unable to locate CHAP_A key\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
while (token) {
|
|
|
|
token = strsep(&tmp, ",");
|
|
|
|
if (!token)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (!strncmp(token, "5", 1)) {
|
|
|
|
pr_debug("Selected MD5 Algorithm\n");
|
|
|
|
kfree(orig);
|
|
|
|
return CHAP_DIGEST_MD5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
kfree(orig);
|
|
|
|
return CHAP_DIGEST_UNKNOWN;
|
|
|
|
}
|
2011-07-23 14:43:04 +08:00
|
|
|
|
|
|
|
static struct iscsi_chap *chap_server_open(
|
|
|
|
struct iscsi_conn *conn,
|
|
|
|
struct iscsi_node_auth *auth,
|
|
|
|
const char *a_str,
|
|
|
|
char *aic_str,
|
|
|
|
unsigned int *aic_len)
|
|
|
|
{
|
2014-05-30 13:43:47 +08:00
|
|
|
int ret;
|
2011-07-23 14:43:04 +08:00
|
|
|
struct iscsi_chap *chap;
|
|
|
|
|
|
|
|
if (!(auth->naf_flags & NAF_USERID_SET) ||
|
|
|
|
!(auth->naf_flags & NAF_PASSWORD_SET)) {
|
|
|
|
pr_err("CHAP user or password not set for"
|
|
|
|
" Initiator ACL\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn->auth_protocol = kzalloc(sizeof(struct iscsi_chap), GFP_KERNEL);
|
|
|
|
if (!conn->auth_protocol)
|
|
|
|
return NULL;
|
|
|
|
|
target: remove useless casts
A reader should spend an extra moment whenever noticing a cast,
because either something special is going on that deserves extra
attention or, as is all too often the case, the code is wrong.
These casts, afaics, have all been useless. They cast a foo* to a
foo*, cast a void* to the assigned type, cast a foo* to void*, before
assigning it to a void* variable, etc.
In a few cases I also removed an additional &...[0], which is equally
useless.
Lastly I added three FIXMEs where, to the best of my judgement, the
code appears to have a bug. It would be good if someone could check
these.
Signed-off-by: Joern Engel <joern@logfs.org>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
2011-11-24 09:05:51 +08:00
|
|
|
chap = conn->auth_protocol;
|
2014-05-30 13:43:47 +08:00
|
|
|
ret = chap_check_algorithm(a_str);
|
|
|
|
switch (ret) {
|
|
|
|
case CHAP_DIGEST_MD5:
|
|
|
|
pr_debug("[server] Got CHAP_A=5\n");
|
|
|
|
/*
|
|
|
|
* Send back CHAP_A set to MD5.
|
|
|
|
*/
|
|
|
|
*aic_len = sprintf(aic_str, "CHAP_A=5");
|
|
|
|
*aic_len += 1;
|
|
|
|
chap->digest_type = CHAP_DIGEST_MD5;
|
|
|
|
pr_debug("[server] Sending CHAP_A=%d\n", chap->digest_type);
|
|
|
|
break;
|
|
|
|
case CHAP_DIGEST_UNKNOWN:
|
|
|
|
default:
|
|
|
|
pr_err("Unsupported CHAP_A value\n");
|
2017-06-08 10:34:26 +08:00
|
|
|
kfree(conn->auth_protocol);
|
2011-07-23 14:43:04 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-05-30 13:43:47 +08:00
|
|
|
|
2011-07-23 14:43:04 +08:00
|
|
|
/*
|
|
|
|
* Set Identifier.
|
|
|
|
*/
|
2013-10-10 02:05:58 +08:00
|
|
|
chap->id = conn->tpg->tpg_chap_id++;
|
2011-07-23 14:43:04 +08:00
|
|
|
*aic_len += sprintf(aic_str + *aic_len, "CHAP_I=%d", chap->id);
|
|
|
|
*aic_len += 1;
|
|
|
|
pr_debug("[server] Sending CHAP_I=%d\n", chap->id);
|
|
|
|
/*
|
|
|
|
* Generate Challenge.
|
|
|
|
*/
|
2017-06-08 10:34:26 +08:00
|
|
|
if (chap_gen_challenge(conn, 1, aic_str, aic_len) < 0) {
|
|
|
|
kfree(conn->auth_protocol);
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-07-23 14:43:04 +08:00
|
|
|
|
|
|
|
return chap;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void chap_close(struct iscsi_conn *conn)
|
|
|
|
{
|
|
|
|
kfree(conn->auth_protocol);
|
|
|
|
conn->auth_protocol = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int chap_server_compute_md5(
|
|
|
|
struct iscsi_conn *conn,
|
|
|
|
struct iscsi_node_auth *auth,
|
|
|
|
char *nr_in_ptr,
|
|
|
|
char *nr_out_ptr,
|
|
|
|
unsigned int *nr_out_len)
|
|
|
|
{
|
2011-11-28 17:02:07 +08:00
|
|
|
unsigned long id;
|
2013-03-05 05:52:09 +08:00
|
|
|
unsigned char id_as_uchar;
|
2011-11-28 17:02:07 +08:00
|
|
|
unsigned char digest[MD5_SIGNATURE_SIZE];
|
2011-07-23 14:43:04 +08:00
|
|
|
unsigned char type, response[MD5_SIGNATURE_SIZE * 2 + 2];
|
|
|
|
unsigned char identifier[10], *challenge = NULL;
|
|
|
|
unsigned char *challenge_binhex = NULL;
|
|
|
|
unsigned char client_digest[MD5_SIGNATURE_SIZE];
|
|
|
|
unsigned char server_digest[MD5_SIGNATURE_SIZE];
|
|
|
|
unsigned char chap_n[MAX_CHAP_N_SIZE], chap_r[MAX_RESPONSE_LENGTH];
|
2013-11-21 06:19:52 +08:00
|
|
|
size_t compare_len;
|
target: remove useless casts
A reader should spend an extra moment whenever noticing a cast,
because either something special is going on that deserves extra
attention or, as is all too often the case, the code is wrong.
These casts, afaics, have all been useless. They cast a foo* to a
foo*, cast a void* to the assigned type, cast a foo* to void*, before
assigning it to a void* variable, etc.
In a few cases I also removed an additional &...[0], which is equally
useless.
Lastly I added three FIXMEs where, to the best of my judgement, the
code appears to have a bug. It would be good if someone could check
these.
Signed-off-by: Joern Engel <joern@logfs.org>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
2011-11-24 09:05:51 +08:00
|
|
|
struct iscsi_chap *chap = conn->auth_protocol;
|
2016-01-24 21:19:52 +08:00
|
|
|
struct crypto_shash *tfm = NULL;
|
|
|
|
struct shash_desc *desc = NULL;
|
2011-07-23 14:43:04 +08:00
|
|
|
int auth_ret = -1, ret, challenge_len;
|
|
|
|
|
|
|
|
memset(identifier, 0, 10);
|
|
|
|
memset(chap_n, 0, MAX_CHAP_N_SIZE);
|
|
|
|
memset(chap_r, 0, MAX_RESPONSE_LENGTH);
|
|
|
|
memset(digest, 0, MD5_SIGNATURE_SIZE);
|
|
|
|
memset(response, 0, MD5_SIGNATURE_SIZE * 2 + 2);
|
|
|
|
memset(client_digest, 0, MD5_SIGNATURE_SIZE);
|
|
|
|
memset(server_digest, 0, MD5_SIGNATURE_SIZE);
|
|
|
|
|
|
|
|
challenge = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL);
|
|
|
|
if (!challenge) {
|
|
|
|
pr_err("Unable to allocate challenge buffer\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
challenge_binhex = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL);
|
|
|
|
if (!challenge_binhex) {
|
|
|
|
pr_err("Unable to allocate challenge_binhex buffer\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Extract CHAP_N.
|
|
|
|
*/
|
|
|
|
if (extract_param(nr_in_ptr, "CHAP_N", MAX_CHAP_N_SIZE, chap_n,
|
|
|
|
&type) < 0) {
|
|
|
|
pr_err("Could not find CHAP_N.\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (type == HEX) {
|
|
|
|
pr_err("Could not find CHAP_N.\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2013-11-21 06:19:52 +08:00
|
|
|
/* Include the terminating NULL in the compare */
|
|
|
|
compare_len = strlen(auth->userid) + 1;
|
|
|
|
if (strncmp(chap_n, auth->userid, compare_len) != 0) {
|
2011-07-23 14:43:04 +08:00
|
|
|
pr_err("CHAP_N values do not match!\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
pr_debug("[server] Got CHAP_N=%s\n", chap_n);
|
|
|
|
/*
|
|
|
|
* Extract CHAP_R.
|
|
|
|
*/
|
|
|
|
if (extract_param(nr_in_ptr, "CHAP_R", MAX_RESPONSE_LENGTH, chap_r,
|
|
|
|
&type) < 0) {
|
|
|
|
pr_err("Could not find CHAP_R.\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (type != HEX) {
|
|
|
|
pr_err("Could not find CHAP_R.\n");
|
|
|
|
goto out;
|
|
|
|
}
|
scsi: target: iscsi: Use hex2bin instead of a re-implementation
This change has the following effects, in order of descreasing importance:
1) Prevent a stack buffer overflow
2) Do not append an unnecessary NULL to an anyway binary buffer, which
is writing one byte past client_digest when caller is:
chap_string_to_hex(client_digest, chap_r, strlen(chap_r));
The latter was found by KASAN (see below) when input value hes expected size
(32 hex chars), and further analysis revealed a stack buffer overflow can
happen when network-received value is longer, allowing an unauthenticated
remote attacker to smash up to 17 bytes after destination buffer (16 bytes
attacker-controlled and one null). As switching to hex2bin requires
specifying destination buffer length, and does not internally append any null,
it solves both issues.
This addresses CVE-2018-14633.
Beyond this:
- Validate received value length and check hex2bin accepted the input, to log
this rejection reason instead of just failing authentication.
- Only log received CHAP_R and CHAP_C values once they passed sanity checks.
==================================================================
BUG: KASAN: stack-out-of-bounds in chap_string_to_hex+0x32/0x60 [iscsi_target_mod]
Write of size 1 at addr ffff8801090ef7c8 by task kworker/0:0/1021
CPU: 0 PID: 1021 Comm: kworker/0:0 Tainted: G O 4.17.8kasan.sess.connops+ #2
Hardware name: To be filled by O.E.M. To be filled by O.E.M./Aptio CRB, BIOS 5.6.5 05/19/2014
Workqueue: events iscsi_target_do_login_rx [iscsi_target_mod]
Call Trace:
dump_stack+0x71/0xac
print_address_description+0x65/0x22e
? chap_string_to_hex+0x32/0x60 [iscsi_target_mod]
kasan_report.cold.6+0x241/0x2fd
chap_string_to_hex+0x32/0x60 [iscsi_target_mod]
chap_server_compute_md5.isra.2+0x2cb/0x860 [iscsi_target_mod]
? chap_binaryhex_to_asciihex.constprop.5+0x50/0x50 [iscsi_target_mod]
? ftrace_caller_op_ptr+0xe/0xe
? __orc_find+0x6f/0xc0
? unwind_next_frame+0x231/0x850
? kthread+0x1a0/0x1c0
? ret_from_fork+0x35/0x40
? ret_from_fork+0x35/0x40
? iscsi_target_do_login_rx+0x3bc/0x4c0 [iscsi_target_mod]
? deref_stack_reg+0xd0/0xd0
? iscsi_target_do_login_rx+0x3bc/0x4c0 [iscsi_target_mod]
? is_module_text_address+0xa/0x11
? kernel_text_address+0x4c/0x110
? __save_stack_trace+0x82/0x100
? ret_from_fork+0x35/0x40
? save_stack+0x8c/0xb0
? 0xffffffffc1660000
? iscsi_target_do_login+0x155/0x8d0 [iscsi_target_mod]
? iscsi_target_do_login_rx+0x3bc/0x4c0 [iscsi_target_mod]
? process_one_work+0x35c/0x640
? worker_thread+0x66/0x5d0
? kthread+0x1a0/0x1c0
? ret_from_fork+0x35/0x40
? iscsi_update_param_value+0x80/0x80 [iscsi_target_mod]
? iscsit_release_cmd+0x170/0x170 [iscsi_target_mod]
chap_main_loop+0x172/0x570 [iscsi_target_mod]
? chap_server_compute_md5.isra.2+0x860/0x860 [iscsi_target_mod]
? rx_data+0xd6/0x120 [iscsi_target_mod]
? iscsit_print_session_params+0xd0/0xd0 [iscsi_target_mod]
? cyc2ns_read_begin.part.2+0x90/0x90
? _raw_spin_lock_irqsave+0x25/0x50
? memcmp+0x45/0x70
iscsi_target_do_login+0x875/0x8d0 [iscsi_target_mod]
? iscsi_target_check_first_request.isra.5+0x1a0/0x1a0 [iscsi_target_mod]
? del_timer+0xe0/0xe0
? memset+0x1f/0x40
? flush_sigqueue+0x29/0xd0
iscsi_target_do_login_rx+0x3bc/0x4c0 [iscsi_target_mod]
? iscsi_target_nego_release+0x80/0x80 [iscsi_target_mod]
? iscsi_target_restore_sock_callbacks+0x130/0x130 [iscsi_target_mod]
process_one_work+0x35c/0x640
worker_thread+0x66/0x5d0
? flush_rcu_work+0x40/0x40
kthread+0x1a0/0x1c0
? kthread_bind+0x30/0x30
ret_from_fork+0x35/0x40
The buggy address belongs to the page:
page:ffffea0004243bc0 count:0 mapcount:0 mapping:0000000000000000 index:0x0
flags: 0x17fffc000000000()
raw: 017fffc000000000 0000000000000000 0000000000000000 00000000ffffffff
raw: ffffea0004243c20 ffffea0004243ba0 0000000000000000 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff8801090ef680: f2 f2 f2 f2 f2 f2 f2 01 f2 f2 f2 f2 f2 f2 f2 00
ffff8801090ef700: f2 f2 f2 f2 f2 f2 f2 00 02 f2 f2 f2 f2 f2 f2 00
>ffff8801090ef780: 00 f2 f2 f2 f2 f2 f2 00 00 f2 f2 f2 f2 f2 f2 00
^
ffff8801090ef800: 00 f2 f2 f2 f2 f2 f2 00 00 00 00 02 f2 f2 f2 f2
ffff8801090ef880: f2 f2 f2 00 00 00 00 00 00 00 00 f2 f2 f2 f2 00
==================================================================
Signed-off-by: Vincent Pelletier <plr.vincent@gmail.com>
Reviewed-by: Mike Christie <mchristi@redhat.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2018-09-09 12:09:26 +08:00
|
|
|
if (strlen(chap_r) != MD5_SIGNATURE_SIZE * 2) {
|
|
|
|
pr_err("Malformed CHAP_R\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (hex2bin(client_digest, chap_r, MD5_SIGNATURE_SIZE) < 0) {
|
|
|
|
pr_err("Malformed CHAP_R\n");
|
|
|
|
goto out;
|
|
|
|
}
|
2011-07-23 14:43:04 +08:00
|
|
|
|
|
|
|
pr_debug("[server] Got CHAP_R=%s\n", chap_r);
|
|
|
|
|
2016-01-24 21:19:52 +08:00
|
|
|
tfm = crypto_alloc_shash("md5", 0, 0);
|
2011-07-23 14:43:04 +08:00
|
|
|
if (IS_ERR(tfm)) {
|
2016-01-24 21:19:52 +08:00
|
|
|
tfm = NULL;
|
|
|
|
pr_err("Unable to allocate struct crypto_shash\n");
|
2011-07-23 14:43:04 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2016-01-24 21:19:52 +08:00
|
|
|
desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
|
|
|
|
if (!desc) {
|
|
|
|
pr_err("Unable to allocate struct shash_desc\n");
|
2011-07-23 14:43:04 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2016-01-24 21:19:52 +08:00
|
|
|
desc->tfm = tfm;
|
|
|
|
desc->flags = 0;
|
|
|
|
|
|
|
|
ret = crypto_shash_init(desc);
|
2011-07-23 14:43:04 +08:00
|
|
|
if (ret < 0) {
|
2016-01-24 21:19:52 +08:00
|
|
|
pr_err("crypto_shash_init() failed\n");
|
2011-07-23 14:43:04 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2016-01-24 21:19:52 +08:00
|
|
|
ret = crypto_shash_update(desc, &chap->id, 1);
|
2011-07-23 14:43:04 +08:00
|
|
|
if (ret < 0) {
|
2016-01-24 21:19:52 +08:00
|
|
|
pr_err("crypto_shash_update() failed for id\n");
|
2011-07-23 14:43:04 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2016-01-24 21:19:52 +08:00
|
|
|
ret = crypto_shash_update(desc, (char *)&auth->password,
|
|
|
|
strlen(auth->password));
|
2011-07-23 14:43:04 +08:00
|
|
|
if (ret < 0) {
|
2016-01-24 21:19:52 +08:00
|
|
|
pr_err("crypto_shash_update() failed for password\n");
|
2011-07-23 14:43:04 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2016-01-24 21:19:52 +08:00
|
|
|
ret = crypto_shash_finup(desc, chap->challenge,
|
|
|
|
CHAP_CHALLENGE_LENGTH, server_digest);
|
2011-07-23 14:43:04 +08:00
|
|
|
if (ret < 0) {
|
2016-01-24 21:19:52 +08:00
|
|
|
pr_err("crypto_shash_finup() failed for challenge\n");
|
2011-07-23 14:43:04 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
chap_binaryhex_to_asciihex(response, server_digest, MD5_SIGNATURE_SIZE);
|
|
|
|
pr_debug("[server] MD5 Server Digest: %s\n", response);
|
|
|
|
|
|
|
|
if (memcmp(server_digest, client_digest, MD5_SIGNATURE_SIZE) != 0) {
|
|
|
|
pr_debug("[server] MD5 Digests do not match!\n\n");
|
|
|
|
goto out;
|
|
|
|
} else
|
2016-04-19 23:27:33 +08:00
|
|
|
pr_debug("[server] MD5 Digests match, CHAP connection"
|
2011-07-23 14:43:04 +08:00
|
|
|
" successful.\n\n");
|
|
|
|
/*
|
|
|
|
* One way authentication has succeeded, return now if mutual
|
|
|
|
* authentication is not enabled.
|
|
|
|
*/
|
|
|
|
if (!auth->authenticate_target) {
|
2016-01-24 21:19:52 +08:00
|
|
|
auth_ret = 0;
|
|
|
|
goto out;
|
2011-07-23 14:43:04 +08:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Get CHAP_I.
|
|
|
|
*/
|
|
|
|
if (extract_param(nr_in_ptr, "CHAP_I", 10, identifier, &type) < 0) {
|
|
|
|
pr_err("Could not find CHAP_I.\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == HEX)
|
2014-06-13 12:05:16 +08:00
|
|
|
ret = kstrtoul(&identifier[2], 0, &id);
|
2011-07-23 14:43:04 +08:00
|
|
|
else
|
2014-06-13 12:05:16 +08:00
|
|
|
ret = kstrtoul(identifier, 0, &id);
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_err("kstrtoul() failed for CHAP identifier: %d\n", ret);
|
|
|
|
goto out;
|
|
|
|
}
|
2011-11-28 17:02:07 +08:00
|
|
|
if (id > 255) {
|
|
|
|
pr_err("chap identifier: %lu greater than 255\n", id);
|
|
|
|
goto out;
|
|
|
|
}
|
2011-07-23 14:43:04 +08:00
|
|
|
/*
|
|
|
|
* RFC 1994 says Identifier is no more than octet (8 bits).
|
|
|
|
*/
|
2011-11-28 17:02:07 +08:00
|
|
|
pr_debug("[server] Got CHAP_I=%lu\n", id);
|
2011-07-23 14:43:04 +08:00
|
|
|
/*
|
|
|
|
* Get CHAP_C.
|
|
|
|
*/
|
|
|
|
if (extract_param(nr_in_ptr, "CHAP_C", CHAP_CHALLENGE_STR_LEN,
|
|
|
|
challenge, &type) < 0) {
|
|
|
|
pr_err("Could not find CHAP_C.\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type != HEX) {
|
|
|
|
pr_err("Could not find CHAP_C.\n");
|
|
|
|
goto out;
|
|
|
|
}
|
scsi: target: iscsi: Use hex2bin instead of a re-implementation
This change has the following effects, in order of descreasing importance:
1) Prevent a stack buffer overflow
2) Do not append an unnecessary NULL to an anyway binary buffer, which
is writing one byte past client_digest when caller is:
chap_string_to_hex(client_digest, chap_r, strlen(chap_r));
The latter was found by KASAN (see below) when input value hes expected size
(32 hex chars), and further analysis revealed a stack buffer overflow can
happen when network-received value is longer, allowing an unauthenticated
remote attacker to smash up to 17 bytes after destination buffer (16 bytes
attacker-controlled and one null). As switching to hex2bin requires
specifying destination buffer length, and does not internally append any null,
it solves both issues.
This addresses CVE-2018-14633.
Beyond this:
- Validate received value length and check hex2bin accepted the input, to log
this rejection reason instead of just failing authentication.
- Only log received CHAP_R and CHAP_C values once they passed sanity checks.
==================================================================
BUG: KASAN: stack-out-of-bounds in chap_string_to_hex+0x32/0x60 [iscsi_target_mod]
Write of size 1 at addr ffff8801090ef7c8 by task kworker/0:0/1021
CPU: 0 PID: 1021 Comm: kworker/0:0 Tainted: G O 4.17.8kasan.sess.connops+ #2
Hardware name: To be filled by O.E.M. To be filled by O.E.M./Aptio CRB, BIOS 5.6.5 05/19/2014
Workqueue: events iscsi_target_do_login_rx [iscsi_target_mod]
Call Trace:
dump_stack+0x71/0xac
print_address_description+0x65/0x22e
? chap_string_to_hex+0x32/0x60 [iscsi_target_mod]
kasan_report.cold.6+0x241/0x2fd
chap_string_to_hex+0x32/0x60 [iscsi_target_mod]
chap_server_compute_md5.isra.2+0x2cb/0x860 [iscsi_target_mod]
? chap_binaryhex_to_asciihex.constprop.5+0x50/0x50 [iscsi_target_mod]
? ftrace_caller_op_ptr+0xe/0xe
? __orc_find+0x6f/0xc0
? unwind_next_frame+0x231/0x850
? kthread+0x1a0/0x1c0
? ret_from_fork+0x35/0x40
? ret_from_fork+0x35/0x40
? iscsi_target_do_login_rx+0x3bc/0x4c0 [iscsi_target_mod]
? deref_stack_reg+0xd0/0xd0
? iscsi_target_do_login_rx+0x3bc/0x4c0 [iscsi_target_mod]
? is_module_text_address+0xa/0x11
? kernel_text_address+0x4c/0x110
? __save_stack_trace+0x82/0x100
? ret_from_fork+0x35/0x40
? save_stack+0x8c/0xb0
? 0xffffffffc1660000
? iscsi_target_do_login+0x155/0x8d0 [iscsi_target_mod]
? iscsi_target_do_login_rx+0x3bc/0x4c0 [iscsi_target_mod]
? process_one_work+0x35c/0x640
? worker_thread+0x66/0x5d0
? kthread+0x1a0/0x1c0
? ret_from_fork+0x35/0x40
? iscsi_update_param_value+0x80/0x80 [iscsi_target_mod]
? iscsit_release_cmd+0x170/0x170 [iscsi_target_mod]
chap_main_loop+0x172/0x570 [iscsi_target_mod]
? chap_server_compute_md5.isra.2+0x860/0x860 [iscsi_target_mod]
? rx_data+0xd6/0x120 [iscsi_target_mod]
? iscsit_print_session_params+0xd0/0xd0 [iscsi_target_mod]
? cyc2ns_read_begin.part.2+0x90/0x90
? _raw_spin_lock_irqsave+0x25/0x50
? memcmp+0x45/0x70
iscsi_target_do_login+0x875/0x8d0 [iscsi_target_mod]
? iscsi_target_check_first_request.isra.5+0x1a0/0x1a0 [iscsi_target_mod]
? del_timer+0xe0/0xe0
? memset+0x1f/0x40
? flush_sigqueue+0x29/0xd0
iscsi_target_do_login_rx+0x3bc/0x4c0 [iscsi_target_mod]
? iscsi_target_nego_release+0x80/0x80 [iscsi_target_mod]
? iscsi_target_restore_sock_callbacks+0x130/0x130 [iscsi_target_mod]
process_one_work+0x35c/0x640
worker_thread+0x66/0x5d0
? flush_rcu_work+0x40/0x40
kthread+0x1a0/0x1c0
? kthread_bind+0x30/0x30
ret_from_fork+0x35/0x40
The buggy address belongs to the page:
page:ffffea0004243bc0 count:0 mapcount:0 mapping:0000000000000000 index:0x0
flags: 0x17fffc000000000()
raw: 017fffc000000000 0000000000000000 0000000000000000 00000000ffffffff
raw: ffffea0004243c20 ffffea0004243ba0 0000000000000000 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff8801090ef680: f2 f2 f2 f2 f2 f2 f2 01 f2 f2 f2 f2 f2 f2 f2 00
ffff8801090ef700: f2 f2 f2 f2 f2 f2 f2 00 02 f2 f2 f2 f2 f2 f2 00
>ffff8801090ef780: 00 f2 f2 f2 f2 f2 f2 00 00 f2 f2 f2 f2 f2 f2 00
^
ffff8801090ef800: 00 f2 f2 f2 f2 f2 f2 00 00 00 00 02 f2 f2 f2 f2
ffff8801090ef880: f2 f2 f2 00 00 00 00 00 00 00 00 f2 f2 f2 f2 00
==================================================================
Signed-off-by: Vincent Pelletier <plr.vincent@gmail.com>
Reviewed-by: Mike Christie <mchristi@redhat.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2018-09-09 12:09:26 +08:00
|
|
|
challenge_len = DIV_ROUND_UP(strlen(challenge), 2);
|
2011-07-23 14:43:04 +08:00
|
|
|
if (!challenge_len) {
|
|
|
|
pr_err("Unable to convert incoming challenge\n");
|
|
|
|
goto out;
|
|
|
|
}
|
2014-06-13 12:28:31 +08:00
|
|
|
if (challenge_len > 1024) {
|
|
|
|
pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n");
|
|
|
|
goto out;
|
|
|
|
}
|
scsi: target: iscsi: Use hex2bin instead of a re-implementation
This change has the following effects, in order of descreasing importance:
1) Prevent a stack buffer overflow
2) Do not append an unnecessary NULL to an anyway binary buffer, which
is writing one byte past client_digest when caller is:
chap_string_to_hex(client_digest, chap_r, strlen(chap_r));
The latter was found by KASAN (see below) when input value hes expected size
(32 hex chars), and further analysis revealed a stack buffer overflow can
happen when network-received value is longer, allowing an unauthenticated
remote attacker to smash up to 17 bytes after destination buffer (16 bytes
attacker-controlled and one null). As switching to hex2bin requires
specifying destination buffer length, and does not internally append any null,
it solves both issues.
This addresses CVE-2018-14633.
Beyond this:
- Validate received value length and check hex2bin accepted the input, to log
this rejection reason instead of just failing authentication.
- Only log received CHAP_R and CHAP_C values once they passed sanity checks.
==================================================================
BUG: KASAN: stack-out-of-bounds in chap_string_to_hex+0x32/0x60 [iscsi_target_mod]
Write of size 1 at addr ffff8801090ef7c8 by task kworker/0:0/1021
CPU: 0 PID: 1021 Comm: kworker/0:0 Tainted: G O 4.17.8kasan.sess.connops+ #2
Hardware name: To be filled by O.E.M. To be filled by O.E.M./Aptio CRB, BIOS 5.6.5 05/19/2014
Workqueue: events iscsi_target_do_login_rx [iscsi_target_mod]
Call Trace:
dump_stack+0x71/0xac
print_address_description+0x65/0x22e
? chap_string_to_hex+0x32/0x60 [iscsi_target_mod]
kasan_report.cold.6+0x241/0x2fd
chap_string_to_hex+0x32/0x60 [iscsi_target_mod]
chap_server_compute_md5.isra.2+0x2cb/0x860 [iscsi_target_mod]
? chap_binaryhex_to_asciihex.constprop.5+0x50/0x50 [iscsi_target_mod]
? ftrace_caller_op_ptr+0xe/0xe
? __orc_find+0x6f/0xc0
? unwind_next_frame+0x231/0x850
? kthread+0x1a0/0x1c0
? ret_from_fork+0x35/0x40
? ret_from_fork+0x35/0x40
? iscsi_target_do_login_rx+0x3bc/0x4c0 [iscsi_target_mod]
? deref_stack_reg+0xd0/0xd0
? iscsi_target_do_login_rx+0x3bc/0x4c0 [iscsi_target_mod]
? is_module_text_address+0xa/0x11
? kernel_text_address+0x4c/0x110
? __save_stack_trace+0x82/0x100
? ret_from_fork+0x35/0x40
? save_stack+0x8c/0xb0
? 0xffffffffc1660000
? iscsi_target_do_login+0x155/0x8d0 [iscsi_target_mod]
? iscsi_target_do_login_rx+0x3bc/0x4c0 [iscsi_target_mod]
? process_one_work+0x35c/0x640
? worker_thread+0x66/0x5d0
? kthread+0x1a0/0x1c0
? ret_from_fork+0x35/0x40
? iscsi_update_param_value+0x80/0x80 [iscsi_target_mod]
? iscsit_release_cmd+0x170/0x170 [iscsi_target_mod]
chap_main_loop+0x172/0x570 [iscsi_target_mod]
? chap_server_compute_md5.isra.2+0x860/0x860 [iscsi_target_mod]
? rx_data+0xd6/0x120 [iscsi_target_mod]
? iscsit_print_session_params+0xd0/0xd0 [iscsi_target_mod]
? cyc2ns_read_begin.part.2+0x90/0x90
? _raw_spin_lock_irqsave+0x25/0x50
? memcmp+0x45/0x70
iscsi_target_do_login+0x875/0x8d0 [iscsi_target_mod]
? iscsi_target_check_first_request.isra.5+0x1a0/0x1a0 [iscsi_target_mod]
? del_timer+0xe0/0xe0
? memset+0x1f/0x40
? flush_sigqueue+0x29/0xd0
iscsi_target_do_login_rx+0x3bc/0x4c0 [iscsi_target_mod]
? iscsi_target_nego_release+0x80/0x80 [iscsi_target_mod]
? iscsi_target_restore_sock_callbacks+0x130/0x130 [iscsi_target_mod]
process_one_work+0x35c/0x640
worker_thread+0x66/0x5d0
? flush_rcu_work+0x40/0x40
kthread+0x1a0/0x1c0
? kthread_bind+0x30/0x30
ret_from_fork+0x35/0x40
The buggy address belongs to the page:
page:ffffea0004243bc0 count:0 mapcount:0 mapping:0000000000000000 index:0x0
flags: 0x17fffc000000000()
raw: 017fffc000000000 0000000000000000 0000000000000000 00000000ffffffff
raw: ffffea0004243c20 ffffea0004243ba0 0000000000000000 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff8801090ef680: f2 f2 f2 f2 f2 f2 f2 01 f2 f2 f2 f2 f2 f2 f2 00
ffff8801090ef700: f2 f2 f2 f2 f2 f2 f2 00 02 f2 f2 f2 f2 f2 f2 00
>ffff8801090ef780: 00 f2 f2 f2 f2 f2 f2 00 00 f2 f2 f2 f2 f2 f2 00
^
ffff8801090ef800: 00 f2 f2 f2 f2 f2 f2 00 00 00 00 02 f2 f2 f2 f2
ffff8801090ef880: f2 f2 f2 00 00 00 00 00 00 00 00 f2 f2 f2 f2 00
==================================================================
Signed-off-by: Vincent Pelletier <plr.vincent@gmail.com>
Reviewed-by: Mike Christie <mchristi@redhat.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2018-09-09 12:09:26 +08:00
|
|
|
if (hex2bin(challenge_binhex, challenge, challenge_len) < 0) {
|
|
|
|
pr_err("Malformed CHAP_C\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
pr_debug("[server] Got CHAP_C=%s\n", challenge);
|
2014-06-06 09:08:57 +08:00
|
|
|
/*
|
|
|
|
* During mutual authentication, the CHAP_C generated by the
|
|
|
|
* initiator must not match the original CHAP_C generated by
|
|
|
|
* the target.
|
|
|
|
*/
|
|
|
|
if (!memcmp(challenge_binhex, chap->challenge, CHAP_CHALLENGE_LENGTH)) {
|
|
|
|
pr_err("initiator CHAP_C matches target CHAP_C, failing"
|
|
|
|
" login attempt\n");
|
|
|
|
goto out;
|
|
|
|
}
|
2011-07-23 14:43:04 +08:00
|
|
|
/*
|
|
|
|
* Generate CHAP_N and CHAP_R for mutual authentication.
|
|
|
|
*/
|
2016-01-24 21:19:52 +08:00
|
|
|
ret = crypto_shash_init(desc);
|
2011-07-23 14:43:04 +08:00
|
|
|
if (ret < 0) {
|
2016-01-24 21:19:52 +08:00
|
|
|
pr_err("crypto_shash_init() failed\n");
|
2011-07-23 14:43:04 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2013-03-05 05:52:09 +08:00
|
|
|
/* To handle both endiannesses */
|
|
|
|
id_as_uchar = id;
|
2016-01-24 21:19:52 +08:00
|
|
|
ret = crypto_shash_update(desc, &id_as_uchar, 1);
|
2011-07-23 14:43:04 +08:00
|
|
|
if (ret < 0) {
|
2016-01-24 21:19:52 +08:00
|
|
|
pr_err("crypto_shash_update() failed for id\n");
|
2011-07-23 14:43:04 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2016-01-24 21:19:52 +08:00
|
|
|
ret = crypto_shash_update(desc, auth->password_mutual,
|
|
|
|
strlen(auth->password_mutual));
|
2011-07-23 14:43:04 +08:00
|
|
|
if (ret < 0) {
|
2016-01-24 21:19:52 +08:00
|
|
|
pr_err("crypto_shash_update() failed for"
|
2011-07-23 14:43:04 +08:00
|
|
|
" password_mutual\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Convert received challenge to binary hex.
|
|
|
|
*/
|
2016-01-24 21:19:52 +08:00
|
|
|
ret = crypto_shash_finup(desc, challenge_binhex, challenge_len,
|
|
|
|
digest);
|
2011-07-23 14:43:04 +08:00
|
|
|
if (ret < 0) {
|
2016-01-24 21:19:52 +08:00
|
|
|
pr_err("crypto_shash_finup() failed for ma challenge\n");
|
2011-07-23 14:43:04 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate CHAP_N and CHAP_R.
|
|
|
|
*/
|
|
|
|
*nr_out_len = sprintf(nr_out_ptr, "CHAP_N=%s", auth->userid_mutual);
|
|
|
|
*nr_out_len += 1;
|
|
|
|
pr_debug("[server] Sending CHAP_N=%s\n", auth->userid_mutual);
|
|
|
|
/*
|
|
|
|
* Convert response from binary hex to ascii hext.
|
|
|
|
*/
|
|
|
|
chap_binaryhex_to_asciihex(response, digest, MD5_SIGNATURE_SIZE);
|
|
|
|
*nr_out_len += sprintf(nr_out_ptr + *nr_out_len, "CHAP_R=0x%s",
|
|
|
|
response);
|
|
|
|
*nr_out_len += 1;
|
|
|
|
pr_debug("[server] Sending CHAP_R=0x%s\n", response);
|
|
|
|
auth_ret = 0;
|
|
|
|
out:
|
2016-01-24 21:19:52 +08:00
|
|
|
kzfree(desc);
|
2017-12-14 01:22:30 +08:00
|
|
|
if (tfm)
|
|
|
|
crypto_free_shash(tfm);
|
2011-07-23 14:43:04 +08:00
|
|
|
kfree(challenge);
|
|
|
|
kfree(challenge_binhex);
|
|
|
|
return auth_ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int chap_got_response(
|
|
|
|
struct iscsi_conn *conn,
|
|
|
|
struct iscsi_node_auth *auth,
|
|
|
|
char *nr_in_ptr,
|
|
|
|
char *nr_out_ptr,
|
|
|
|
unsigned int *nr_out_len)
|
|
|
|
{
|
target: remove useless casts
A reader should spend an extra moment whenever noticing a cast,
because either something special is going on that deserves extra
attention or, as is all too often the case, the code is wrong.
These casts, afaics, have all been useless. They cast a foo* to a
foo*, cast a void* to the assigned type, cast a foo* to void*, before
assigning it to a void* variable, etc.
In a few cases I also removed an additional &...[0], which is equally
useless.
Lastly I added three FIXMEs where, to the best of my judgement, the
code appears to have a bug. It would be good if someone could check
these.
Signed-off-by: Joern Engel <joern@logfs.org>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
2011-11-24 09:05:51 +08:00
|
|
|
struct iscsi_chap *chap = conn->auth_protocol;
|
2011-07-23 14:43:04 +08:00
|
|
|
|
|
|
|
switch (chap->digest_type) {
|
|
|
|
case CHAP_DIGEST_MD5:
|
|
|
|
if (chap_server_compute_md5(conn, auth, nr_in_ptr,
|
|
|
|
nr_out_ptr, nr_out_len) < 0)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
pr_err("Unknown CHAP digest type %d!\n",
|
|
|
|
chap->digest_type);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 chap_main_loop(
|
|
|
|
struct iscsi_conn *conn,
|
|
|
|
struct iscsi_node_auth *auth,
|
|
|
|
char *in_text,
|
|
|
|
char *out_text,
|
|
|
|
int *in_len,
|
|
|
|
int *out_len)
|
|
|
|
{
|
target: remove useless casts
A reader should spend an extra moment whenever noticing a cast,
because either something special is going on that deserves extra
attention or, as is all too often the case, the code is wrong.
These casts, afaics, have all been useless. They cast a foo* to a
foo*, cast a void* to the assigned type, cast a foo* to void*, before
assigning it to a void* variable, etc.
In a few cases I also removed an additional &...[0], which is equally
useless.
Lastly I added three FIXMEs where, to the best of my judgement, the
code appears to have a bug. It would be good if someone could check
these.
Signed-off-by: Joern Engel <joern@logfs.org>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
2011-11-24 09:05:51 +08:00
|
|
|
struct iscsi_chap *chap = conn->auth_protocol;
|
2011-07-23 14:43:04 +08:00
|
|
|
|
|
|
|
if (!chap) {
|
|
|
|
chap = chap_server_open(conn, auth, in_text, out_text, out_len);
|
|
|
|
if (!chap)
|
|
|
|
return 2;
|
|
|
|
chap->chap_state = CHAP_STAGE_SERVER_AIC;
|
|
|
|
return 0;
|
|
|
|
} else if (chap->chap_state == CHAP_STAGE_SERVER_AIC) {
|
|
|
|
convert_null_to_semi(in_text, *in_len);
|
|
|
|
if (chap_got_response(conn, auth, in_text, out_text,
|
|
|
|
out_len) < 0) {
|
|
|
|
chap_close(conn);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
if (auth->authenticate_target)
|
|
|
|
chap->chap_state = CHAP_STAGE_SERVER_NR;
|
|
|
|
else
|
|
|
|
*out_len = 0;
|
|
|
|
chap_close(conn);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 2;
|
|
|
|
}
|