From mboxrd@z Thu Jan 1 00:00:00 1970 From: Mike Christie Subject: [PATCH 8/10][RFC] linux-iscsi driver Date: Mon, 10 Jan 2005 15:02:50 -0800 Message-ID: <41E3099A.9020108@cs.wisc.edu> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------050501070106080704030001" Return-path: Received: from sabe.cs.wisc.edu ([128.105.6.20]:732 "EHLO sabe.cs.wisc.edu") by vger.kernel.org with ESMTP id S262753AbVAJXC5 (ORCPT ); Mon, 10 Jan 2005 18:02:57 -0500 Received: from [192.168.1.7] ([199.108.226.254]) (authenticated bits=0) by sabe.cs.wisc.edu (8.13.1/8.13.1) with ESMTP id j0AN2thB024994 (version=TLSv1/SSLv3 cipher=RC4-MD5 bits=128 verify=NO) for ; Mon, 10 Jan 2005 17:02:55 -0600 Sender: linux-scsi-owner@vger.kernel.org List-Id: linux-scsi@vger.kernel.org To: linux-scsi@vger.kernel.org This is a multi-part message in MIME format. --------------050501070106080704030001 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit iSCSI Authentication. Sorry about the blkdev include in iscsi-auth.h. will correct when we figure out what file (or correct order of files), to include when using scatterlists and sg_init_one. Can we include an asm file before a linux one in this case, or does linux/scatterlist.h need a forward declaration, or is there sone other file we should use? --------------050501070106080704030001 Content-Type: text/x-patch; name="08-iscsi-auth.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="08-iscsi-auth.patch" diff -Naurp scsi-misc-2.6.orig/drivers/scsi/iscsi-sfnet/iscsi-auth.c scsi-misc-2.6.patch/drivers/scsi/iscsi-sfnet/iscsi-auth.c --- scsi-misc-2.6.orig/drivers/scsi/iscsi-sfnet/iscsi-auth.c 1969-12-31 16:00:00.000000000 -0800 +++ scsi-misc-2.6.patch/drivers/scsi/iscsi-sfnet/iscsi-auth.c 2005-01-10 12:32:30.131322392 -0800 @@ -0,0 +1,142 @@ +/* + * iSCSI driver for Linux + * Copyright (C) 2001 Cisco Systems, Inc. + * maintained by linux-iscsi-devel@lists.sourceforge.net + * + * 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. + * + * See the file COPYING included with this distribution for more details. + * + * This file contains kernel wrappers around the iscsi auth common code. + */ +#include +#include +#include + +#include "iscsi-sfnet.h" +#include "iscsi-protocol.h" +#include "iscsi-session.h" + +/* + * Authenticate a target's CHAP response. + * + * Use the kernel crypto API + */ +enum auth_dbg_status +acl_chap_compute_rsp(struct iscsi_acl *client, int rmt_auth, u32 id, + u8 *challenge_data, u32 challenge_length, + u8 *response_data) +{ + struct iscsi_session *session = client->session_handle; + u8 id_data[1]; + struct scatterlist sg; + struct crypto_tfm *tfm = session->md5_tfm; + u8 out_data[AUTH_STR_MAX_LEN]; + u32 out_length = AUTH_STR_MAX_LEN; + + if (!client->passwd_present) + return AUTH_DBG_STATUS_LOCAL_PASSWD_NOT_SET; + + crypto_digest_init(tfm); + /* id byte */ + id_data[0] = id; + sg_init_one(&sg, &id_data[0], 1); + crypto_digest_update(tfm, &sg, 1); + + /* decrypt password */ + if (acl_data(out_data, &out_length, client->passwd_data, + client->passwd_length)) + return AUTH_DBG_STATUS_PASSWD_DECRYPT_FAILED; + + if (!rmt_auth && !client->ip_sec && out_length < 12) + return AUTH_DBG_STATUS_PASSWD_TOO_SHORT_WITH_NO_IPSEC; + + /* shared secret */ + sg_init_one(&sg, out_data, out_length); + crypto_digest_update(tfm, &sg, 1); + + /* clear decrypted password */ + memset(out_data, 0, AUTH_STR_MAX_LEN); + + /* challenge value */ + sg_init_one(&sg, challenge_data, challenge_length); + crypto_digest_update(tfm, &sg, 1); + crypto_digest_final(tfm, response_data); + + return AUTH_DBG_STATUS_NOT_SET; /* no error */ +} + +int +acl_chap_auth_request(struct iscsi_acl *client, char *username, unsigned int id, + unsigned char *challenge_data, + unsigned int challenge_length, + unsigned char *response_data, + unsigned int rsp_length) +{ + struct iscsi_session *session = client->session_handle; + struct crypto_tfm *tfm = session->md5_tfm; + struct scatterlist sg[3]; + unsigned char id_byte = id; + unsigned char verify_data[16]; + + /* the expected credentials are in the session */ + if (session->username_in == NULL) { + iscsi_err("Failing authentication, no incoming username " + "configured to authenticate target %s\n", + session->target_name); + return AUTH_STATUS_FAIL; + } + if (strcmp(username, session->username_in) != 0) { + iscsi_err("Failing authentication, received incorrect username " + "from target %s\n", session->target_name); + return AUTH_STATUS_FAIL; + } + + if ((session->password_length_in < 1) || + (session->password_in == NULL) || + (session->password_in[0] == '\0')) { + iscsi_err("Failing authentication, no incoming password " + "configured to authenticate target %s\n", + session->target_name); + return AUTH_STATUS_FAIL; + } + + /* challenge length is I->T, and shouldn't need to be checked */ + + if (rsp_length != sizeof(verify_data)) { + iscsi_err("Failing authentication, received incorrect CHAP " + "response length %u from target %s\n", rsp_length, + session->target_name); + return AUTH_STATUS_FAIL; + } + + /* id byte */ + id_byte = id; + sg_init_one(&sg[0], &id_byte, 1); + + /* shared secret */ + sg_init_one(&sg[1], session->password_in, session->password_length_in); + + /* challenge value */ + sg_init_one(&sg[2], challenge_data, challenge_length); + + memset(verify_data, 0, sizeof(verify_data)); + crypto_digest_init(tfm); + crypto_digest_digest(tfm, sg, 3, verify_data); + + if (memcmp(response_data, verify_data, sizeof(verify_data)) == 0) + return AUTH_STATUS_PASS; + + iscsi_err("Failing authentication, received incorrect CHAP response " + "from target %s\n", session->target_name); + + return AUTH_STATUS_FAIL; +} diff -Naurp scsi-misc-2.6.orig/drivers/scsi/iscsi-sfnet/iscsi-auth-client.c scsi-misc-2.6.patch/drivers/scsi/iscsi-sfnet/iscsi-auth-client.c --- scsi-misc-2.6.orig/drivers/scsi/iscsi-sfnet/iscsi-auth-client.c 1969-12-31 16:00:00.000000000 -0800 +++ scsi-misc-2.6.patch/drivers/scsi/iscsi-sfnet/iscsi-auth-client.c 2005-01-10 12:32:30.131322392 -0800 @@ -0,0 +1,1839 @@ +/* + * iSCSI driver for Linux + * Copyright (C) 2001 Cisco Systems, Inc. + * maintained by linux-iscsi-devel@lists.sourceforge.net + * + * 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. + * + * See the file COPYING included with this distribution for more details. + * + * This file implements the iSCSI CHAP authentication method based on + * RFC 3720. The code in this file is meant to be common for both kernel and + * user level and makes use of only limited library functions, presently only + * string.h. Routines specific to kernel, user level are implemented in + * seperate files under the appropriate directories. + * This code in this files assumes a single thread of execution + * for each iscsi_acl structure, and does no locking. + */ +#include "iscsi-auth-client.h" +#include "iscsi-session.h" +#include "iscsi-protocol.h" +#include "iscsi-sfnet.h" + +static const char acl_hexstring[] = "0123456789abcdefABCDEF"; +static const char acl_base64_string[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char acl_authmethod_set_chap_alg_list[] = "CHAP"; +static const char acl_reject_option_name[] = "Reject"; +static const char acl_none_option_name[] = "None"; + +static int +acl_text_to_number(const char *text, unsigned long *num) +{ + char *end; + unsigned long number = *num; + + if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) + number = simple_strtoul(text + 2, &end, 16); + else + number = simple_strtoul(text, &end, 10); + + if (*text != '\0' && *end == '\0') { + *num = number; + return 0; /* No error */ + } else + return 1; /* Error */ +} + +static int +acl_chk_string(const char *s, unsigned int max_len, unsigned int *out_len) +{ + unsigned int len; + + if (!s) + return 1; + + for (len = 0; len < max_len; len++) + if (*s++ == '\0') { + if (out_len) + *out_len = len; + return 0; + } + + return 1; +} + +static int +acl_str_index(const char *s, int c) +{ + char *str = strchr(s, c); + + if (str) + return (str - s); + else + return -1; +} + +static int +acl_chk_auth_mthd_optn(int val) +{ + if (val == AUTH_OPTION_NONE || val == AUTH_METHOD_CHAP) + return 0; + + return 1; +} + +static const char * +acl_authmethod_optn_to_text(int value) +{ + const char *s; + switch (value) { + case AUTH_OPTION_REJECT: + s = acl_reject_option_name; + break; + case AUTH_OPTION_NONE: + s = acl_none_option_name; + break; + case AUTH_METHOD_CHAP: + s = acl_authmethod_set_chap_alg_list; + break; + default: + s = 0; + } + return s; +} + +static int +acl_chk_chap_alg_optn(int chap_algorithm) +{ + if (chap_algorithm == AUTH_OPTION_NONE || + chap_algorithm == AUTH_CHAP_ALG_MD5) + return 0; + + return 1; +} + +static int +acl_data_to_text(unsigned char *data, unsigned int data_length, char *text, + unsigned int text_length) +{ + unsigned long n; + + if (!text || text_length == 0) + return 1; + + if (!data || data_length == 0) { + *text = '\0'; + return 1; + } + + if (text_length < 3) { + *text = '\0'; + return 1; + } + + *text++ = '0'; + *text++ = 'x'; + + text_length -= 2; + + while (data_length > 0) { + + if (text_length < 3) { + *text = '\0'; + return 1; + } + + n = *data++; + data_length--; + + *text++ = acl_hexstring[(n >> 4) & 0xf]; + *text++ = acl_hexstring[n & 0xf]; + + text_length -= 2; + } + + *text = '\0'; + + return 0; +} + +static int +acl_hex_to_data(const char *text, unsigned int text_length, unsigned char *data, + unsigned int *data_lenp) +{ + int i; + unsigned int n1; + unsigned int n2; + unsigned int data_length = *data_lenp; + + if ((text_length % 2) == 1) { + + i = acl_str_index(acl_hexstring, *text++); + if (i < 0) + return 1; /* error, bad character */ + + if (i > 15) + i -= 6; + n2 = i; + + if (data_length < 1) + return 1; /* error, too much data */ + + *data++ = n2; + data_length--; + } + + while (*text != '\0') { + i = acl_str_index(acl_hexstring, *text++); + if (i < 0) + return 1; /* error, bad character */ + + if (i > 15) + i -= 6; + n1 = i; + + if (*text == '\0') + return 1; /* error, odd string length */ + + i = acl_str_index(acl_hexstring, *text++); + if (i < 0) + return 1; /* error, bad character */ + + if (i > 15) + i -= 6; + n2 = i; + + if (data_length < 1) + return 1; /* error, too much data */ + + *data++ = (n1 << 4) | n2; + data_length--; + } + + if (data_length >= *data_lenp) + return 1; /* error, no data */ + + *data_lenp = *data_lenp - data_length; + + return 0; /* no error */ +} + +static int +acl_base64_to_data(const char *text, unsigned char *data, + unsigned int *data_lenp) +{ + int i; + unsigned int n; + unsigned int count; + unsigned int data_length = *data_lenp; + + n = 0; + count = 0; + + while (*text != '\0' && *text != '=') { + + i = acl_str_index(acl_base64_string, *text++); + if (i < 0) + return 1; /* error, bad character */ + + n = (n << 6 | (unsigned int)i); + count++; + + if (count >= 4) { + if (data_length < 3) + return 1; /* error, too much data */ + *data++ = n >> 16; + *data++ = n >> 8; + *data++ = n; + data_length -= 3; + n = 0; + count = 0; + } + } + + while (*text != '\0') + if (*text++ != '=') + return 1; /* error, bad pad */ + + if (count == 0) { + /* do nothing */ + } else if (count == 2) { + if (data_length < 1) + return 1; /* error, too much data */ + n = n >> 4; + *data++ = n; + data_length--; + } else if (count == 3) { + if (data_length < 2) + return 1; /* error, too much data */ + n = n >> 2; + *data++ = n >> 8; + *data++ = n; + data_length -= 2; + } else + return 1; /* bad encoding */ + + if (data_length >= *data_lenp) + return 1; /* error, no data */ + + *data_lenp = *data_lenp - data_length; + + return 0; /* no error */ +} + +static int +acl_text_to_data(const char *text, unsigned char *data, + unsigned int *data_length) +{ + int status; + unsigned int text_length; + + status = acl_chk_string(text, 2 + 2 * AUTH_LARGE_BINARY_MAX_LEN + 1, + &text_length); + if (status) + return status; + + if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) { + /* skip prefix */ + text += 2; + text_length -= 2; + status = acl_hex_to_data(text, text_length, data, data_length); + } else if (text[0] == '0' && (text[1] == 'b' || text[1] == 'B')) { + /* skip prefix */ + text += 2; + text_length -= 2; + status = acl_base64_to_data(text, data, data_length); + } else + status = 1; /* prefix not recognized. */ + + return status; +} + +static void +acl_init_key_blk(struct auth_key_block *key_blk) +{ + char *str_block = key_blk->str_block; + + memset(key_blk, 0, sizeof(*key_blk)); + key_blk->str_block = str_block; +} + +static void +acl_set_key_value(struct auth_key_block *key_blk, int key_type, + const char *key_val) +{ + unsigned int length; + char *string; + + if (key_blk->key[key_type].value_set) { + key_blk->dup_set = 1; + return; + } + + key_blk->key[key_type].value_set = 1; + + if (!key_val) + return; + + if (acl_chk_string(key_val, AUTH_STR_MAX_LEN, &length)) { + key_blk->str_too_long = 1; + return; + } + + length += 1; + + if ((key_blk->blk_length + length) > AUTH_STR_BLOCK_MAX_LEN) { + key_blk->too_much_data = 1; + return; + } + + string = &key_blk->str_block[key_blk->blk_length]; + + if (strlcpy(string, key_val, length) >= length) { + key_blk->too_much_data = 1; + return; + } + key_blk->blk_length += length; + + key_blk->key[key_type].string = string; + key_blk->key[key_type].present = 1; +} + +static const char * +acl_get_key_val(struct auth_key_block *key_blk, int key_type) +{ + key_blk->key[key_type].processed = 1; + + if (!key_blk->key[key_type].present) + return 0; + + return key_blk->key[key_type].string; +} + +static void +acl_chk_key(struct iscsi_acl *client, int key_type, int *negotiated_option, + unsigned int option_count, int *option_list, + const char *(*value_to_text) (int)) +{ + const char *key_val; + int length; + unsigned int i; + + key_val = acl_get_key_val(&client->recv_key_block, key_type); + if (!key_val) { + *negotiated_option = AUTH_OPTION_NOT_PRESENT; + return; + } + + while (*key_val != '\0') { + + length = 0; + + while (*key_val != '\0' && *key_val != ',') + client->scratch_key_value[length++] = *key_val++; + + if (*key_val == ',') + key_val++; + client->scratch_key_value[length++] = '\0'; + + for (i = 0; i < option_count; i++) { + const char *s = (*value_to_text)(option_list[i]); + + if (!s) + continue; + + if (strcmp(client->scratch_key_value, s) == 0) { + *negotiated_option = option_list[i]; + return; + } + } + } + + *negotiated_option = AUTH_OPTION_REJECT; +} + +static void +acl_set_key(struct iscsi_acl *client, int key_type, unsigned int option_count, + int *option_list, const char *(*value_to_text)(int)) +{ + unsigned int i; + + if (option_count == 0) { + /* + * No valid options to send, but we always want to + * send something. + */ + acl_set_key_value(&client->send_key_block, key_type, + acl_none_option_name); + return; + } + + if (option_count == 1 && option_list[0] == AUTH_OPTION_NOT_PRESENT) { + acl_set_key_value(&client->send_key_block, key_type, 0); + return; + } + + for (i = 0; i < option_count; i++) { + const char *s = (*value_to_text)(option_list[i]); + + if (!s) + continue; + + if (i == 0) + strlcpy(client->scratch_key_value, s, + AUTH_STR_MAX_LEN); + else { + strlcat(client->scratch_key_value, ",", + AUTH_STR_MAX_LEN); + strlcat(client->scratch_key_value, s, + AUTH_STR_MAX_LEN); + } + } + + acl_set_key_value(&client->send_key_block, key_type, + client->scratch_key_value); +} + +static void +acl_chk_auth_method_key(struct iscsi_acl *client) +{ + acl_chk_key(client, AUTH_KEY_TYPE_AUTH_METHOD, + &client->negotiated_auth_method, + client->auth_method_valid_count, + client->auth_method_valid_list, + acl_authmethod_optn_to_text); +} + +static void +acl_set_auth_method_key(struct iscsi_acl *client, + unsigned int auth_method_count, int *auth_method_list) +{ + acl_set_key(client, AUTH_KEY_TYPE_AUTH_METHOD, auth_method_count, + auth_method_list, acl_authmethod_optn_to_text); +} + +static void +acl_chk_chap_alg_key(struct iscsi_acl *client) +{ + const char *key_val; + int length; + unsigned long number; + unsigned int i; + + key_val = acl_get_key_val(&client->recv_key_block, + AUTH_KEY_TYPE_CHAP_ALG); + if (!key_val) { + client->negotiated_chap_alg = AUTH_OPTION_NOT_PRESENT; + return; + } + + while (*key_val != '\0') { + + length = 0; + + while (*key_val != '\0' && *key_val != ',') + client->scratch_key_value[length++] = *key_val++; + + if (*key_val == ',') + key_val++; + client->scratch_key_value[length++] = '\0'; + + if (acl_text_to_number(client->scratch_key_value, &number)) + continue; + + + for (i = 0; i < client->chap_alg_count; i++) + if (number == (unsigned long)client->chap_alg_list[i]) + { + client->negotiated_chap_alg = number; + return; + } + } + + client->negotiated_chap_alg = AUTH_OPTION_REJECT; +} + +static void +acl_set_chap_alg_key(struct iscsi_acl *client, unsigned int chap_alg_count, + int *chap_alg_list) +{ + unsigned int i; + + if (chap_alg_count == 0) { + acl_set_key_value(&client->send_key_block, + AUTH_KEY_TYPE_CHAP_ALG, 0); + return; + } + + if (chap_alg_count == 1 && + chap_alg_list[0] == AUTH_OPTION_NOT_PRESENT) { + acl_set_key_value(&client->send_key_block, + AUTH_KEY_TYPE_CHAP_ALG, 0); + return; + } + + if (chap_alg_count == 1 && chap_alg_list[0] == AUTH_OPTION_REJECT) { + acl_set_key_value(&client->send_key_block, + AUTH_KEY_TYPE_CHAP_ALG, + acl_reject_option_name); + return; + } + + for (i = 0; i < chap_alg_count; i++) { + char s[20]; + + snprintf(s, sizeof(s), "%lu",(unsigned long)chap_alg_list[i]); + + if (i == 0) + strlcpy(client->scratch_key_value, s, + AUTH_STR_MAX_LEN); + else { + strlcat(client->scratch_key_value, ",", + AUTH_STR_MAX_LEN); + strlcat(client->scratch_key_value, s, + AUTH_STR_MAX_LEN); + } + } + + acl_set_key_value(&client->send_key_block, AUTH_KEY_TYPE_CHAP_ALG, + client->scratch_key_value); +} + +static void +acl_next_phase(struct iscsi_acl *client) +{ + switch (client->phase) { + case AUTH_PHASE_CONFIGURE: + client->phase = AUTH_PHASE_NEGOTIATE; + break; + case AUTH_PHASE_NEGOTIATE: + client->phase = AUTH_PHASE_AUTHENTICATE; + + if (client->negotiated_auth_method == AUTH_OPTION_REJECT || + client->negotiated_auth_method == AUTH_OPTION_NOT_PRESENT || + client->negotiated_auth_method == AUTH_OPTION_NONE) { + + client->local_state = AUTH_LOCAL_STATE_DONE; + client->rmt_state = AUTH_RMT_STATE_DONE; + + if (client->auth_rmt) { + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->phase = AUTH_PHASE_DONE; + } else + client->rmt_auth_status = AUTH_STATUS_PASS; + + switch (client->negotiated_auth_method) { + case AUTH_OPTION_REJECT: + client->dbg_status = + AUTH_DBG_STATUS_AUTH_METHOD_REJECT; + break; + case AUTH_OPTION_NOT_PRESENT: + client->dbg_status = + AUTH_DBG_STATUS_AUTH_METHOD_NOT_PRESENT; + break; + case AUTH_OPTION_NONE: + client->dbg_status = + AUTH_DBG_STATUS_AUTH_METHOD_NONE; + } + + } else if (client->negotiated_auth_method == AUTH_METHOD_CHAP) { + client->local_state = AUTH_LOCAL_STATE_SEND_ALG; + client->rmt_state = AUTH_RMT_STATE_SEND_ALG; + } else { + + client->local_state = AUTH_LOCAL_STATE_DONE; + client->rmt_state = AUTH_RMT_STATE_DONE; + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->dbg_status = AUTH_DBG_STATUS_AUTH_METHOD_BAD; + } + break; + case AUTH_PHASE_AUTHENTICATE: + client->phase = AUTH_PHASE_DONE; + break; + case AUTH_PHASE_DONE: + case AUTH_PHASE_ERROR: + default: + client->phase = AUTH_PHASE_ERROR; + } +} + +static void +acl_local_auth(struct iscsi_acl *client) +{ + unsigned int chap_identifier; + unsigned char response_data[AUTH_CHAP_RSP_LEN]; + unsigned long number; + int status; + enum auth_dbg_status dbg_status; + const char *chap_identifier_key_val; + const char *chap_challenge_key_val; + + switch (client->local_state) { + case AUTH_LOCAL_STATE_SEND_ALG: + if (client->node_type == TYPE_INITIATOR) { + acl_set_chap_alg_key(client, client->chap_alg_count, + client->chap_alg_list); + client->local_state = AUTH_LOCAL_STATE_RECV_ALG; + break; + } + /* Fall through */ + case AUTH_LOCAL_STATE_RECV_ALG: + acl_chk_chap_alg_key(client); + + if (client->node_type == TYPE_TARGET) + acl_set_chap_alg_key(client, 1, + &client->negotiated_chap_alg); + + /* Make sure only supported CHAP algorithm is used. */ + if (client->negotiated_chap_alg == AUTH_OPTION_NOT_PRESENT) { + client->local_state = AUTH_LOCAL_STATE_ERROR; + client->dbg_status = AUTH_DBG_STATUS_CHAP_ALG_EXPECTED; + break; + } else if (client->negotiated_chap_alg == AUTH_OPTION_REJECT) { + client->local_state = AUTH_LOCAL_STATE_ERROR; + client->dbg_status = AUTH_DBG_STATUS_CHAP_ALG_REJECT; + break; + } else if (client->negotiated_chap_alg != AUTH_CHAP_ALG_MD5) { + client->local_state = AUTH_LOCAL_STATE_ERROR; + client->dbg_status = AUTH_DBG_STATUS_CHAP_ALG_BAD; + break; + } + if (client->node_type == TYPE_TARGET) { + client->local_state = AUTH_LOCAL_STATE_RECV_CHALLENGE; + break; + } + /* Fall through */ + case AUTH_LOCAL_STATE_RECV_CHALLENGE: + chap_identifier_key_val = acl_get_key_val(&client->recv_key_block, + AUTH_KEY_TYPE_CHAP_IDENTIFIER); + chap_challenge_key_val = acl_get_key_val(&client->recv_key_block, + AUTH_KEY_TYPE_CHAP_CHALLENGE); + if (client->node_type == TYPE_TARGET) { + if (!chap_identifier_key_val && + !chap_challenge_key_val) { + client->local_state = AUTH_LOCAL_STATE_DONE; + break; + } + } + + if (!chap_identifier_key_val) { + client->local_state = AUTH_LOCAL_STATE_ERROR; + client->dbg_status = + AUTH_DBG_STATUS_CHAP_IDENTIFIER_EXPECTED; + break; + } + + if (!chap_challenge_key_val) { + client->local_state = AUTH_LOCAL_STATE_ERROR; + client->dbg_status = + AUTH_DBG_STATUS_CHAP_CHALLENGE_EXPECTED; + break; + } + + status = acl_text_to_number(chap_identifier_key_val, &number); + if (status || (255 < number)) { + client->local_state = AUTH_LOCAL_STATE_ERROR; + client->dbg_status = AUTH_DBG_STATUS_CHAP_IDENTIFIER_BAD; + break; + } + chap_identifier = number; + + if (client->recv_chap_challenge_status) { + client->local_state = AUTH_LOCAL_STATE_ERROR; + client->dbg_status = AUTH_DBG_STATUS_CHALLENGE_BAD; + break; + } + + if (client->node_type == TYPE_TARGET && + client->recv_chap_challenge.length == + client->send_chap_challenge.length && + memcmp(client->recv_chap_challenge.large_binary, + client->send_chap_challenge.large_binary, + client->send_chap_challenge.length) == 0) { + client->local_state = AUTH_LOCAL_STATE_ERROR; + client->dbg_status = + AUTH_DBG_STATUS_CHAP_CHALLENGE_REFLECTED; + break; + } + + dbg_status = acl_chap_compute_rsp(client, 0, + chap_identifier, + client->recv_chap_challenge.large_binary, + client->recv_chap_challenge.length, + response_data); + + if (dbg_status != AUTH_DBG_STATUS_NOT_SET) { + client->local_state = AUTH_LOCAL_STATE_ERROR; + client->dbg_status = dbg_status; + break; + } + + acl_data_to_text(response_data, + AUTH_CHAP_RSP_LEN, client->scratch_key_value, + AUTH_STR_MAX_LEN); + acl_set_key_value(&client->send_key_block, + AUTH_KEY_TYPE_CHAP_RSP, + client->scratch_key_value); + acl_set_key_value(&client->send_key_block, + AUTH_KEY_TYPE_CHAP_USERNAME, + client->username); + + client->local_state = AUTH_LOCAL_STATE_DONE; + break; + case AUTH_LOCAL_STATE_DONE: + break; + case AUTH_LOCAL_STATE_ERROR: + default: + client->phase = AUTH_PHASE_ERROR; + } +} + +static void +acl_rmt_auth(struct iscsi_acl *client) +{ + unsigned char id_data[1]; + unsigned char response_data[AUTH_STR_MAX_LEN]; + unsigned int rsp_len = AUTH_STR_MAX_LEN; + unsigned char my_rsp_data[AUTH_CHAP_RSP_LEN]; + int status; + enum auth_dbg_status dbg_status; + const char *chap_rsp_key_val; + const char *chap_username_key_val; + + switch (client->rmt_state) { + case AUTH_RMT_STATE_SEND_ALG: + if (client->node_type == TYPE_INITIATOR) { + client->rmt_state = AUTH_RMT_STATE_SEND_CHALLENGE; + break; + } + /* Fall through */ + case AUTH_RMT_STATE_SEND_CHALLENGE: + if (!client->auth_rmt) { + client->rmt_auth_status = AUTH_STATUS_PASS; + client->dbg_status = AUTH_DBG_STATUS_AUTH_RMT_FALSE; + client->rmt_state = AUTH_RMT_STATE_DONE; + break; + } + get_random_bytes(id_data, 1); + client->send_chap_identifier = id_data[0]; + snprintf(client->scratch_key_value, AUTH_STR_MAX_LEN, "%lu", + (unsigned long)client->send_chap_identifier); + acl_set_key_value(&client->send_key_block, + AUTH_KEY_TYPE_CHAP_IDENTIFIER, + client->scratch_key_value); + + client->send_chap_challenge.length = client->chap_challenge_len; + get_random_bytes(client->send_chap_challenge.large_binary, + client->send_chap_challenge.length); + acl_set_key_value(&client->send_key_block, + AUTH_KEY_TYPE_CHAP_CHALLENGE, ""); + + client->rmt_state = AUTH_RMT_STATE_RECV_RSP; + break; + case AUTH_RMT_STATE_RECV_RSP: + chap_rsp_key_val = acl_get_key_val(&client->recv_key_block, + AUTH_KEY_TYPE_CHAP_RSP); + chap_username_key_val = acl_get_key_val(&client->recv_key_block, + AUTH_KEY_TYPE_CHAP_USERNAME); + + if (!chap_rsp_key_val) { + client->rmt_state = AUTH_RMT_STATE_ERROR; + client->dbg_status = AUTH_DBG_STATUS_CHAP_RSP_EXPECTED; + break; + } + + if (!chap_username_key_val) { + client->rmt_state = AUTH_RMT_STATE_ERROR; + client->dbg_status = AUTH_DBG_STATUS_CHAP_USERNAME_EXPECTED; + break; + } + + status = acl_text_to_data(chap_rsp_key_val, response_data, + &rsp_len); + + if (status) { + client->rmt_state = AUTH_RMT_STATE_ERROR; + client->dbg_status = AUTH_DBG_STATUS_CHAP_RSP_BAD; + break; + } + + if (rsp_len == AUTH_CHAP_RSP_LEN) { + dbg_status = acl_chap_compute_rsp(client, 1, + client->send_chap_identifier, + client->send_chap_challenge.large_binary, + client->send_chap_challenge.length, + my_rsp_data); + + if (dbg_status == AUTH_DBG_STATUS_NOT_SET && + memcmp(my_rsp_data, response_data, + AUTH_CHAP_RSP_LEN) == 0) { + client->rmt_state = AUTH_RMT_STATE_ERROR; + client->dbg_status = AUTH_DBG_STATUS_PASSWD_IDENTICAL; + break; + } + } + + strlcpy(client->chap_username, chap_username_key_val, + AUTH_STR_MAX_LEN); + + status = acl_chap_auth_request(client, client->chap_username, + client->send_chap_identifier, + client->send_chap_challenge. + large_binary, + client->send_chap_challenge. + length, response_data, + rsp_len); + + client->rmt_auth_status = (enum auth_status) status; + client->auth_rsp_flag = 1; + + if (client->auth_server_error_flag) { + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->dbg_status = AUTH_DBG_STATUS_AUTH_SERVER_ERROR; + } else if (client->rmt_auth_status == AUTH_STATUS_PASS) + client->dbg_status = AUTH_DBG_STATUS_AUTH_PASS; + else if (client->rmt_auth_status == AUTH_STATUS_FAIL) + client->dbg_status = AUTH_DBG_STATUS_AUTH_FAIL; + else { + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->dbg_status = AUTH_DBG_STATUS_AUTH_STATUS_BAD; + } + client->rmt_state = AUTH_RMT_STATE_DONE; + + /* Fall through */ + case AUTH_RMT_STATE_DONE: + break; + case AUTH_RMT_STATE_ERROR: + default: + client->phase = AUTH_PHASE_ERROR; + } +} + +static void +acl_hand_shake(struct iscsi_acl *client) +{ + if (client->phase == AUTH_PHASE_DONE) + + /* + * Should only happen if authentication + * protocol error occured. + */ + return; + + if (client->node_type == TYPE_INITIATOR) + + /* + * Target should only have set T bit on response if + * initiator set it on previous message. + */ + if (client->recv_key_block.transit_bit && + !client->transit_bit_sent_flag) { + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->phase = AUTH_PHASE_DONE; + client->dbg_status = + AUTH_DBG_STATUS_T_BIT_SET_ILLEGAL; + return; + } + + if (client->phase == AUTH_PHASE_NEGOTIATE) { + /* + * Should only happen if waiting for peer + * to send AuthMethod key or set Transit Bit. + */ + if (client->node_type == TYPE_INITIATOR) + client->send_key_block.transit_bit = 1; + return; + } + + if (client->rmt_state == AUTH_RMT_STATE_RECV_RSP || + client->rmt_state == AUTH_RMT_STATE_DONE) { + if (client->node_type == TYPE_INITIATOR) { + if (client->recv_key_block.transit_bit) { + if (client->rmt_state != + AUTH_RMT_STATE_DONE) + goto recv_transit_bit_err; + acl_next_phase(client); + } else + client->send_key_block.transit_bit = 1; + } else { + if (client->rmt_state == AUTH_RMT_STATE_DONE && + client->rmt_auth_status != AUTH_STATUS_PASS) + /* + * Authentication failed, don't do T bit + * handshake. + */ + acl_next_phase(client); + else { + /* + * Target can only set T bit on response if + * initiator set it on current message. + */ + if (client->recv_key_block.transit_bit) { + client->send_key_block.transit_bit = 1; + acl_next_phase(client); + } + } + } + } else + if (client->node_type == TYPE_INITIATOR) + if (client->recv_key_block.transit_bit) + goto recv_transit_bit_err; + return; + + recv_transit_bit_err: + /* + * Target set T bit on response but + * initiator was not done with authentication. + */ + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->phase = AUTH_PHASE_DONE; + client->dbg_status = AUTH_DBG_STATUS_T_BIT_SET_PREMATURE; +} + +static int +acl_rcv_end_status(struct iscsi_acl *client) +{ + int auth_status; + int key_type; + + if (client->phase == AUTH_PHASE_ERROR) + return AUTH_STATUS_ERROR; + + if (client->phase == AUTH_PHASE_DONE) { + + /* Perform sanity check against configured parameters. */ + if (client->auth_rmt && !client->auth_rsp_flag && + client->rmt_auth_status == AUTH_STATUS_PASS) { + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->dbg_status = AUTH_DBG_STATUS_AUTHPASS_NOT_VALID; + } + + auth_status = client->rmt_auth_status; + + } else + auth_status = AUTH_STATUS_CONTINUE; + + if (auth_status == AUTH_STATUS_CONTINUE || + auth_status == AUTH_STATUS_PASS) { + if (client->send_key_block.dup_set) { + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->phase = AUTH_PHASE_DONE; + client->dbg_status = + AUTH_DBG_STATUS_SEND_DUP_SET_KEY_VALUE; + auth_status = AUTH_STATUS_FAIL; + } else if (client->send_key_block.str_too_long) { + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->phase = AUTH_PHASE_DONE; + client->dbg_status = + AUTH_DBG_STATUS_SEND_STR_TOO_LONG; + auth_status = AUTH_STATUS_FAIL; + } else if (client->send_key_block.too_much_data) { + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->phase = AUTH_PHASE_DONE; + client->dbg_status = + AUTH_DBG_STATUS_SEND_TOO_MUCH_DATA; + auth_status = AUTH_STATUS_FAIL; + } else { + /* Check that all incoming keys have been processed. */ + + for (key_type = AUTH_KEY_TYPE_FIRST; + key_type < AUTH_KEY_TYPE_MAX_COUNT; key_type++) + if (client->recv_key_block.key[key_type].present && + !client->recv_key_block.key[key_type]. + processed) + break; + + if (key_type < AUTH_KEY_TYPE_MAX_COUNT) { + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->phase = AUTH_PHASE_DONE; + client->dbg_status = + AUTH_DBG_STATUS_UNEXPECTED_KEY_PRESENT; + auth_status = AUTH_STATUS_FAIL; + } + } + } + + if (auth_status != AUTH_STATUS_PASS && + auth_status != AUTH_STATUS_CONTINUE) { + int auth_method_key_present = 0; + int chap_alg_key_present = 0; + + /* + * Suppress send keys on error, + * except for AuthMethod and CHAP_A. + */ + if (client->node_type == TYPE_TARGET) { + if (acl_get_key_val(&client->send_key_block, + AUTH_KEY_TYPE_AUTH_METHOD)) + auth_method_key_present = 1; + else if (acl_get_key_val(&client->send_key_block, + AUTH_KEY_TYPE_CHAP_ALG)) + chap_alg_key_present = 1; + } + + acl_init_key_blk(&client->send_key_block); + + if (client->node_type == TYPE_TARGET) { + if (auth_method_key_present && + client->negotiated_auth_method == + AUTH_OPTION_REJECT) + acl_set_key_value(&client->send_key_block, + AUTH_KEY_TYPE_AUTH_METHOD, + acl_reject_option_name); + else if (chap_alg_key_present && + client->negotiated_chap_alg == + AUTH_OPTION_REJECT) + acl_set_key_value(&client->send_key_block, + AUTH_KEY_TYPE_CHAP_ALG, + acl_reject_option_name); + } + } + client->recv_in_progress_flag = 0; + + return auth_status; +} + +int +acl_recv_begin(struct iscsi_acl *client) +{ + if (!client || client->signature != ACL_SIGNATURE) + return AUTH_STATUS_ERROR; + + if (client->phase == AUTH_PHASE_ERROR) + return AUTH_STATUS_ERROR; + + if (client->phase == AUTH_PHASE_DONE) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + if (client->recv_in_progress_flag) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + client->recv_in_progress_flag = 1; + + if (client->phase == AUTH_PHASE_CONFIGURE) + acl_next_phase(client); + + client->transit_bit_sent_flag = client->send_key_block.transit_bit; + + acl_init_key_blk(&client->recv_key_block); + acl_init_key_blk(&client->send_key_block); + + return AUTH_STATUS_NO_ERROR; +} + +int +acl_recv_end(struct iscsi_acl *client) +{ + int next_phase_flag = 0; + + if (!client || client->signature != ACL_SIGNATURE) + return AUTH_STATUS_ERROR; + + if (client->phase == AUTH_PHASE_ERROR) + return AUTH_STATUS_ERROR; + + if (!client->recv_in_progress_flag) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + if (client->recv_end_count > AUTH_RECV_END_MAX_COUNT) { + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->phase = AUTH_PHASE_DONE; + client->dbg_status = AUTH_DBG_STATUS_RECV_MSG_COUNT_LIMIT; + } else if (client->recv_key_block.dup_set) { + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->phase = AUTH_PHASE_DONE; + client->dbg_status = AUTH_DBG_STATUS_RECV_DUP_SET_KEY_VALUE; + } else if (client->recv_key_block.str_too_long) { + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->phase = AUTH_PHASE_DONE; + client->dbg_status = AUTH_DBG_STATUS_RECV_STR_TOO_LONG; + } else if (client->recv_key_block.too_much_data) { + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->phase = AUTH_PHASE_DONE; + client->dbg_status = AUTH_DBG_STATUS_RECV_TOO_MUCH_DATA; + } + + client->recv_end_count++; + + switch (client->phase) { + case AUTH_PHASE_NEGOTIATE: + acl_chk_auth_method_key(client); + if (client->auth_method_valid_neg_role == + AUTH_NEG_ROLE_RESPONDER) { + if (client->negotiated_auth_method == + AUTH_OPTION_NOT_PRESENT) { + if (client->auth_rmt || + !client->recv_key_block.transit_bit) { + /* + * No AuthMethod key from peer on + * first message, try moving the + * process along by sending the + * AuthMethod key. + */ + + client->auth_method_valid_neg_role = + AUTH_NEG_ROLE_ORIGINATOR; + acl_set_auth_method_key(client, + client->auth_method_valid_count, + client->auth_method_valid_list); + break; + } + + /* + * Special case if peer sent no AuthMethod key, + * but did set Transit Bit, allowing this side + * to do a null authentication, and compelete + * the iSCSI security phase without either side + * sending the AuthMethod key. + */ + } else + /* Send response to AuthMethod key. */ + acl_set_auth_method_key(client, 1, + &client->negotiated_auth_method); + + if (client->node_type == TYPE_INITIATOR) + acl_next_phase(client); + else + next_phase_flag = 1; + } else { + + if (client->negotiated_auth_method == + AUTH_OPTION_NOT_PRESENT) { + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->phase = AUTH_PHASE_DONE; + client->dbg_status = + AUTH_DBG_STATUS_AUTH_METHOD_EXPECTED; + break; + } + + acl_next_phase(client); + } + break; + case AUTH_PHASE_AUTHENTICATE: + case AUTH_PHASE_DONE: + break; + default: + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + switch (client->phase) { + case AUTH_PHASE_NEGOTIATE: + if (next_phase_flag) + acl_next_phase(client); + break; + case AUTH_PHASE_AUTHENTICATE: + /* + * Must call acl_local_auth() + * before acl_rmt_auth() + * to insure processing of the CHAP algorithm key, + * and to avoid leaving an in progress request to the + * authentication service. + */ + acl_local_auth(client); + + if (client->local_state != AUTH_LOCAL_STATE_ERROR) + acl_rmt_auth(client); + + if (client->local_state == AUTH_LOCAL_STATE_ERROR || + client->rmt_state == AUTH_RMT_STATE_ERROR) { + + client->rmt_auth_status = AUTH_STATUS_FAIL; + client->phase = AUTH_PHASE_DONE; + /* client->dbg_status should already be set. */ + } + break; + case AUTH_PHASE_DONE: + break; + default: + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + acl_hand_shake(client); + + return acl_rcv_end_status(client); +} + +const char * +acl_get_key_name(int key_type) +{ + /* + * Note: The ordering of this table must match the order + * defined by enum auth_key_type in iscsi-auth-client.h. + */ + static char *const key_names[AUTH_KEY_TYPE_MAX_COUNT] = { + "AuthMethod", + "CHAP_A", + "CHAP_N", + "CHAP_R", + "CHAP_I", + "CHAP_C" + }; + + if (key_type < AUTH_KEY_TYPE_FIRST || key_type > AUTH_KEY_TYPE_LAST) + return 0; + + return key_names[key_type]; +} + +int +acl_get_next_key_type(int *key_type) +{ + if (*key_type >= AUTH_KEY_TYPE_LAST) + return AUTH_STATUS_ERROR; + + if (*key_type < AUTH_KEY_TYPE_FIRST) + *key_type = AUTH_KEY_TYPE_FIRST; + else + (*key_type)++; + + return AUTH_STATUS_NO_ERROR; +} + +int +acl_recv_key_value(struct iscsi_acl *client, int key_type, + const char *user_key_val) +{ + if (!client || client->signature != ACL_SIGNATURE) + return AUTH_STATUS_ERROR; + + if (client->phase != AUTH_PHASE_NEGOTIATE && + client->phase != AUTH_PHASE_AUTHENTICATE) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + if (key_type < AUTH_KEY_TYPE_FIRST || key_type > AUTH_KEY_TYPE_LAST) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + if (key_type == AUTH_KEY_TYPE_CHAP_CHALLENGE) { + client->recv_chap_challenge.length = + AUTH_LARGE_BINARY_MAX_LEN; + client->recv_chap_challenge_status = + acl_text_to_data(user_key_val, + client->recv_chap_challenge.large_binary, + &client->recv_chap_challenge.length); + user_key_val = ""; + } + + acl_set_key_value(&client->recv_key_block, key_type, user_key_val); + + return AUTH_STATUS_NO_ERROR; +} + +int +acl_send_key_val(struct iscsi_acl *client, int key_type, int *key_present, + char *user_key_val, unsigned int max_length) +{ + const char *key_val; + + if (!client || client->signature != ACL_SIGNATURE) + return AUTH_STATUS_ERROR; + + if (client->phase != AUTH_PHASE_CONFIGURE && + client->phase != AUTH_PHASE_NEGOTIATE && + client->phase != AUTH_PHASE_AUTHENTICATE && + client->phase != AUTH_PHASE_DONE) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + if (key_type < AUTH_KEY_TYPE_FIRST || key_type > AUTH_KEY_TYPE_LAST) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + key_val = acl_get_key_val(&client->send_key_block, key_type); + if (key_val) { + if (key_type == AUTH_KEY_TYPE_CHAP_CHALLENGE) { + if (acl_data_to_text(client->send_chap_challenge.large_binary, + client->send_chap_challenge.length, user_key_val, + max_length)) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + } else if (strlcpy(user_key_val, key_val, max_length) >= + max_length) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + *key_present = 1; + } else + *key_present = 0; + + return AUTH_STATUS_NO_ERROR; +} + +int +acl_recv_transit_bit(struct iscsi_acl *client, int value) +{ + if (!client || client->signature != ACL_SIGNATURE) + return AUTH_STATUS_ERROR; + + if (client->phase != AUTH_PHASE_NEGOTIATE && + client->phase != AUTH_PHASE_AUTHENTICATE) { + + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + if (value) + client->recv_key_block.transit_bit = 1; + else + client->recv_key_block.transit_bit = 0; + + return AUTH_STATUS_NO_ERROR; +} + +int +acl_send_transit_bit(struct iscsi_acl *client, int *value) +{ + if (!client || client->signature != ACL_SIGNATURE) + return AUTH_STATUS_ERROR; + + if (client->phase != AUTH_PHASE_CONFIGURE && + client->phase != AUTH_PHASE_NEGOTIATE && + client->phase != AUTH_PHASE_AUTHENTICATE && + client->phase != AUTH_PHASE_DONE) { + + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + *value = client->send_key_block.transit_bit; + + return AUTH_STATUS_NO_ERROR; +} + +static int +acl_set_option_list(struct iscsi_acl *client, unsigned int opt_count, + const int *opt_list, unsigned int *clnt_optn_count, + int *clnt_optn_list, unsigned int optn_max_count, + int (*chk_option)(int), + int (*chk_list)(unsigned int opt_count, const int *opt_list)) +{ + unsigned int i, j; + + if (!client || client->signature != ACL_SIGNATURE) + return AUTH_STATUS_ERROR; + + if (client->phase != AUTH_PHASE_CONFIGURE || + opt_count > optn_max_count) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + for (i = 0; i < opt_count; i++) + if (chk_option(opt_list[i])) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + /* Check for duplicate entries. */ + for (i = 0; i < opt_count; i++) + for (j = 0; j < opt_count; j++) { + if (j == i) + continue; + if (opt_list[i] == opt_list[j]) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + } + + /* Check for key specific constraints. */ + if (chk_list) + if (chk_list(opt_count, opt_list)) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + for (i = 0; i < opt_count; i++) + clnt_optn_list[i] = opt_list[i]; + + *clnt_optn_count = opt_count; + + return AUTH_STATUS_NO_ERROR; +} + +static int +acl_chk_auth_method_list(unsigned int option_count, const int *option_list) +{ + unsigned int i; + + if (!option_list || option_count < 2) + return 1; + + if (option_list[option_count - 1] != AUTH_OPTION_NONE) + return 1; + + for (i = 0; i < (option_count - 1); i++) + if (option_list[i] != AUTH_OPTION_NONE) + return 0; + + return 0; +} + +static void +acl_set_auth_method_valid(struct iscsi_acl *client) +{ + unsigned int i, j = 0; + int option = 0; + + /* + * Following checks may need to be revised if + * authentication options other than CHAP and none + * are supported. + */ + if (client->node_type == TYPE_INITIATOR) { + if (client->auth_rmt) + /* + * If initiator doing authentication, + * don't offer authentication option none. + */ + option = 1; + else if (!client->passwd_present) + /* + * If initiator password not set, + * only offer authentication option none. + */ + option = 2; + } + + if (client->node_type == TYPE_TARGET) { + if (client->auth_rmt) + /* + * If target doing authentication, + * don't accept authentication option none. + */ + option = 1; + else + /* + * If target not doing authentication, + * only accept authentication option none. + */ + option = 2; + } + + for (i = 0; i < client->auth_method_count; i++) { + if (option == 1) { + if (client->auth_method_list[i] == AUTH_OPTION_NONE) + continue; + } else if (option == 2) + if (client->auth_method_list[i] != AUTH_OPTION_NONE) + continue; + client->auth_method_valid_list[j++] = client->auth_method_list[i]; + } + + client->auth_method_valid_count = j; + + acl_init_key_blk(&client->send_key_block); + + if (client->node_type == TYPE_INITIATOR) { + if (client->auth_rmt) { + /* + * Initiator wants to authenticate target, + * always send AuthMethod key. + */ + client->send_key_block.transit_bit = 0; + client->auth_method_valid_neg_role = + AUTH_NEG_ROLE_ORIGINATOR; + } else { + client->send_key_block.transit_bit = 1; + client->auth_method_valid_neg_role = + client->auth_method_neg_role; + } + } else { + client->send_key_block.transit_bit = 0; + client->auth_method_valid_neg_role = AUTH_NEG_ROLE_RESPONDER; + } + + if (client->auth_method_valid_neg_role == AUTH_NEG_ROLE_ORIGINATOR) + acl_set_auth_method_key(client, client->auth_method_valid_count, + client->auth_method_valid_list); + else { + int value = AUTH_OPTION_NOT_PRESENT; + acl_set_auth_method_key(client, 1, &value); + } +} + +static int +acl_set_auth_method_list(struct iscsi_acl *client, unsigned int option_count, + const int *option_list) +{ + int status; + + status = acl_set_option_list(client, option_count, option_list, + &client->auth_method_count, + client->auth_method_list, + AUTH_METHOD_MAX_COUNT, + acl_chk_auth_mthd_optn, + acl_chk_auth_method_list); + + if (status != AUTH_STATUS_NO_ERROR) + return status; + + /* Setting authMethod affects auth_method_valid. */ + acl_set_auth_method_valid(client); + + return AUTH_STATUS_NO_ERROR; +} + +static int +acl_chk_chap_alg_list(unsigned int option_count, const int *option_list) +{ + if (!option_list || option_count < 1) + return 1; + + return 0; +} + +static int +acl_set_chap_alg_list(struct iscsi_acl *client, unsigned int option_count, + const int *option_list) +{ + return acl_set_option_list(client, option_count, option_list, + &client->chap_alg_count, + client->chap_alg_list, + AUTH_CHAP_ALG_MAX_COUNT, + acl_chk_chap_alg_optn, + acl_chk_chap_alg_list); +} + +int +acl_init(int node_type, struct iscsi_session *session) +{ + struct iscsi_acl *client; + struct auth_str_block *rcv_str_blk; + struct auth_str_block *snd_str_blk; + struct auth_large_binary *rcv_chap_chlng; + struct auth_large_binary *snd_chap_chlng; + int value_list[2]; + + if (!session->auth_client_block) + return AUTH_STATUS_ERROR; + client = session->auth_client_block; + + if (!session->auth_recv_string_block) + return AUTH_STATUS_ERROR; + rcv_str_blk = session->auth_recv_string_block; + + if (!session->auth_send_string_block) + return AUTH_STATUS_ERROR; + snd_str_blk = session->auth_send_string_block; + + if (!session->auth_recv_binary_block) + return AUTH_STATUS_ERROR; + rcv_chap_chlng = session->auth_recv_binary_block; + + if (!session->auth_send_binary_block) + return AUTH_STATUS_ERROR; + snd_chap_chlng = session->auth_send_binary_block; + + memset(client, 0, sizeof(*client)); + memset(rcv_str_blk, 0, sizeof(*rcv_str_blk)); + memset(snd_str_blk, 0, sizeof(*snd_str_blk)); + memset(rcv_chap_chlng, 0, sizeof(*rcv_chap_chlng)); + memset(snd_chap_chlng, 0, sizeof(*snd_chap_chlng)); + + client->recv_key_block.str_block = rcv_str_blk->str_block; + client->send_key_block.str_block = snd_str_blk->str_block; + client->recv_chap_challenge.large_binary = rcv_chap_chlng->large_binary; + client->send_chap_challenge.large_binary = snd_chap_chlng->large_binary; + + if (node_type != TYPE_INITIATOR && node_type != TYPE_TARGET) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + client->signature = ACL_SIGNATURE; + client->node_type = (enum auth_node_type) node_type; + client->auth_rmt = 1; + client->passwd_present = 0; + client->chap_challenge_len = AUTH_CHAP_RSP_LEN; + client->ip_sec = 0; + client->session_handle = session; + + client->phase = AUTH_PHASE_CONFIGURE; + client->negotiated_auth_method = AUTH_OPTION_NOT_PRESENT; + client->negotiated_chap_alg = AUTH_OPTION_NOT_PRESENT; + + if (client->node_type == TYPE_INITIATOR) + client->auth_method_neg_role = AUTH_NEG_ROLE_ORIGINATOR; + else + /* Initial value ignored for Target. */ + client->auth_method_neg_role = AUTH_NEG_ROLE_RESPONDER; + + value_list[0] = AUTH_METHOD_CHAP; + value_list[1] = AUTH_OPTION_NONE; + + /* + * Must call after setting auth_rmt, password, + * and auth_method_neg_role + */ + if (acl_set_auth_method_list(client, 2, value_list) != + AUTH_STATUS_NO_ERROR) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + value_list[0] = AUTH_CHAP_ALG_MD5; + + if (acl_set_chap_alg_list(client, 1, value_list) != + AUTH_STATUS_NO_ERROR) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + return AUTH_STATUS_NO_ERROR; +} + +int +acl_finish(struct iscsi_acl *client) +{ + if (!client || client->signature != ACL_SIGNATURE) + return AUTH_STATUS_ERROR; + + memset(client, 0, sizeof(*client)); + + return AUTH_STATUS_NO_ERROR; +} + +int +acl_set_user_name(struct iscsi_acl *client, const char *username) +{ + if (!client || client->signature != ACL_SIGNATURE) + return AUTH_STATUS_ERROR; + + if (client->phase != AUTH_PHASE_CONFIGURE || + acl_chk_string(username, AUTH_STR_MAX_LEN, 0)) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + if (strlcpy(client->username, username, AUTH_STR_MAX_LEN) >= + AUTH_STR_MAX_LEN) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + return AUTH_STATUS_NO_ERROR; +} + +int +acl_set_passwd(struct iscsi_acl *client, const unsigned char *passwd_data, + unsigned int passwd_length) +{ + if (!client || client->signature != ACL_SIGNATURE) + return AUTH_STATUS_ERROR; + + if (client->phase != AUTH_PHASE_CONFIGURE || + passwd_length > AUTH_STR_MAX_LEN) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + memcpy(client->passwd_data, passwd_data, passwd_length); + client->passwd_length = passwd_length; + client->passwd_present = 1; + + /* Setting password may affect auth_method_valid. */ + acl_set_auth_method_valid(client); + + return AUTH_STATUS_NO_ERROR; +} + +int +acl_set_auth_rmt(struct iscsi_acl *client, int auth_rmt) +{ + if (!client || client->signature != ACL_SIGNATURE) + return AUTH_STATUS_ERROR; + + if (client->phase != AUTH_PHASE_CONFIGURE) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + client->auth_rmt = auth_rmt; + + /* Setting auth_rmt may affect auth_method_valid. */ + acl_set_auth_method_valid(client); + + return AUTH_STATUS_NO_ERROR; +} + +int +acl_set_ip_sec(struct iscsi_acl *client, int ip_sec) +{ + if (!client || client->signature != ACL_SIGNATURE) + return AUTH_STATUS_ERROR; + + if (client->phase != AUTH_PHASE_CONFIGURE) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + client->ip_sec = ip_sec; + + return AUTH_STATUS_NO_ERROR; +} + +int +acl_get_dbg_status(struct iscsi_acl *client, int *value) +{ + if (!client || client->signature != ACL_SIGNATURE) + return AUTH_STATUS_ERROR; + + if (client->phase != AUTH_PHASE_DONE) { + client->phase = AUTH_PHASE_ERROR; + return AUTH_STATUS_ERROR; + } + + *value = client->dbg_status; + + return AUTH_STATUS_NO_ERROR; +} + +const char * +acl_dbg_status_to_text(int dbg_status) +{ + /* + * Note: The ordering of this table must match the order + * defined by enum auth_dbg_status in iscsi-auth-client.h. + */ + static char *const dbg_text[AUTH_DBG_STATUS_MAX_COUNT] = { + "Debug status not set", + "Authentication request passed", + "Authentication not enabled", + "Authentication request failed", + "AuthMethod bad", + "CHAP algorithm bad", + "Decrypt password failed", + "Local password too short with no IPSec", + "Unexpected error from authentication server", + "Authentication request status bad", + "Authentication pass status not valid", + "Same key set more than once on send", + "Key value too long on send", + "Too much data on send", + "AuthMethod key expected", + "CHAP algorithm key expected", + "CHAP identifier expected", + "CHAP challenge expected", + "CHAP response expected", + "CHAP username expected", + "AuthMethod key not present", + "AuthMethod negotiation failed", + "AuthMethod negotiated to none", + "CHAP algorithm negotiation failed", + "CHAP challange reflected", + "Local password same as remote", + "Local password not set", + "CHAP identifier bad", + "CHAP challenge bad", + "CHAP response bad", + "Unexpected key present", + "T bit set on response, but not on previous message", + "T bit set on response, but authenticaton not complete", + "Message count limit reached on receive", + "Same key set more than once on receive", + "Key value too long on receive", + "Too much data on receive" + }; + + if (dbg_status < 0 || dbg_status >= AUTH_DBG_STATUS_MAX_COUNT) + return "Unknown error"; + + return dbg_text[dbg_status]; +} + +int +acl_data(unsigned char *out_data, unsigned int *out_length, + unsigned char *in_data, unsigned int in_length) +{ + if (*out_length < in_length) + return 1; /* error */ + + memcpy(out_data, in_data, in_length); + *out_length = in_length; + + return 0; /* no error */ +} + diff -Naurp scsi-misc-2.6.orig/drivers/scsi/iscsi-sfnet/iscsi-auth-client.h scsi-misc-2.6.patch/drivers/scsi/iscsi-sfnet/iscsi-auth-client.h --- scsi-misc-2.6.orig/drivers/scsi/iscsi-sfnet/iscsi-auth-client.h 1969-12-31 16:00:00.000000000 -0800 +++ scsi-misc-2.6.patch/drivers/scsi/iscsi-sfnet/iscsi-auth-client.h 2005-01-10 12:32:30.131322392 -0800 @@ -0,0 +1,277 @@ +/* + * iSCSI driver for Linux + * Copyright (C) 2001 Cisco Systems, Inc. + * maintained by linux-iscsi-devel@lists.sourceforge.net + * + * 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. + * + * See the file COPYING included with this distribution for more details. + * + * This file is the include file for for iscsi-auth-client.c + */ +#ifndef ISCSIAUTHCLIENT_H +#define ISCSIAUTHCLIENT_H + +struct iscsi_session; + +enum { + AUTH_STR_MAX_LEN = 256, + AUTH_STR_BLOCK_MAX_LEN = 1024, + AUTH_LARGE_BINARY_MAX_LEN = 1024, + AUTH_RECV_END_MAX_COUNT = 10, + ACL_SIGNATURE = 0x5984B2E3, + AUTH_CHAP_RSP_LEN = 16, +}; + +/* + * Note: The ordering of these values are chosen to match + * the ordering of the keys as shown in the iSCSI spec. + * The order of table key_names in acl_get_key_name() + * must match the order defined by enum auth_key_type. + */ +enum auth_key_type { + AUTH_KEY_TYPE_NONE = -1, + AUTH_KEY_TYPE_FIRST = 0, + AUTH_KEY_TYPE_AUTH_METHOD = AUTH_KEY_TYPE_FIRST, + AUTH_KEY_TYPE_CHAP_ALG, + AUTH_KEY_TYPE_CHAP_USERNAME, + AUTH_KEY_TYPE_CHAP_RSP, + AUTH_KEY_TYPE_CHAP_IDENTIFIER, + AUTH_KEY_TYPE_CHAP_CHALLENGE, + AUTH_KEY_TYPE_MAX_COUNT, + AUTH_KEY_TYPE_LAST = AUTH_KEY_TYPE_MAX_COUNT - 1 +}; + +enum { + /* Common options for all keys. */ + AUTH_OPTION_REJECT = -2, + AUTH_OPTION_NOT_PRESENT = -1, + AUTH_OPTION_NONE = 1, + + AUTH_METHOD_CHAP = 2, + AUTH_METHOD_MAX_COUNT = 2, + + AUTH_CHAP_ALG_MD5 = 5, + AUTH_CHAP_ALG_MAX_COUNT = 2 +}; + +enum auth_neg_role { + AUTH_NEG_ROLE_ORIGINATOR = 1, + AUTH_NEG_ROLE_RESPONDER = 2 +}; + +enum auth_status { + AUTH_STATUS_NO_ERROR = 0, + AUTH_STATUS_ERROR, + AUTH_STATUS_PASS, + AUTH_STATUS_FAIL, + AUTH_STATUS_CONTINUE, +}; + +/* + * Note: The order of table dbg_text in acl_dbg_status_to_text() + * must match the ordered defined by enum auth_dbg_status. + */ +enum auth_dbg_status { + AUTH_DBG_STATUS_NOT_SET = 0, + + AUTH_DBG_STATUS_AUTH_PASS, + AUTH_DBG_STATUS_AUTH_RMT_FALSE, + + AUTH_DBG_STATUS_AUTH_FAIL, + + AUTH_DBG_STATUS_AUTH_METHOD_BAD, + AUTH_DBG_STATUS_CHAP_ALG_BAD, + AUTH_DBG_STATUS_PASSWD_DECRYPT_FAILED, + AUTH_DBG_STATUS_PASSWD_TOO_SHORT_WITH_NO_IPSEC, + AUTH_DBG_STATUS_AUTH_SERVER_ERROR, + AUTH_DBG_STATUS_AUTH_STATUS_BAD, + AUTH_DBG_STATUS_AUTHPASS_NOT_VALID, + AUTH_DBG_STATUS_SEND_DUP_SET_KEY_VALUE, + AUTH_DBG_STATUS_SEND_STR_TOO_LONG, + AUTH_DBG_STATUS_SEND_TOO_MUCH_DATA, + + AUTH_DBG_STATUS_AUTH_METHOD_EXPECTED, + AUTH_DBG_STATUS_CHAP_ALG_EXPECTED, + AUTH_DBG_STATUS_CHAP_IDENTIFIER_EXPECTED, + AUTH_DBG_STATUS_CHAP_CHALLENGE_EXPECTED, + AUTH_DBG_STATUS_CHAP_RSP_EXPECTED, + AUTH_DBG_STATUS_CHAP_USERNAME_EXPECTED, + + AUTH_DBG_STATUS_AUTH_METHOD_NOT_PRESENT, + AUTH_DBG_STATUS_AUTH_METHOD_REJECT, + AUTH_DBG_STATUS_AUTH_METHOD_NONE, + AUTH_DBG_STATUS_CHAP_ALG_REJECT, + AUTH_DBG_STATUS_CHAP_CHALLENGE_REFLECTED, + AUTH_DBG_STATUS_PASSWD_IDENTICAL, + + AUTH_DBG_STATUS_LOCAL_PASSWD_NOT_SET, + + AUTH_DBG_STATUS_CHAP_IDENTIFIER_BAD, + AUTH_DBG_STATUS_CHALLENGE_BAD, + AUTH_DBG_STATUS_CHAP_RSP_BAD, + AUTH_DBG_STATUS_UNEXPECTED_KEY_PRESENT, + AUTH_DBG_STATUS_T_BIT_SET_ILLEGAL, + AUTH_DBG_STATUS_T_BIT_SET_PREMATURE, + + AUTH_DBG_STATUS_RECV_MSG_COUNT_LIMIT, + AUTH_DBG_STATUS_RECV_DUP_SET_KEY_VALUE, + AUTH_DBG_STATUS_RECV_STR_TOO_LONG, + AUTH_DBG_STATUS_RECV_TOO_MUCH_DATA, + AUTH_DBG_STATUS_MAX_COUNT +}; + +enum auth_node_type { + TYPE_INITIATOR = 1, + TYPE_TARGET = 2 +}; + +enum auth_phase { + AUTH_PHASE_CONFIGURE = 1, + AUTH_PHASE_NEGOTIATE, + AUTH_PHASE_AUTHENTICATE, + AUTH_PHASE_DONE, + AUTH_PHASE_ERROR +}; + +enum auth_local_state { + AUTH_LOCAL_STATE_SEND_ALG = 1, + AUTH_LOCAL_STATE_RECV_ALG, + AUTH_LOCAL_STATE_RECV_CHALLENGE, + AUTH_LOCAL_STATE_DONE, + AUTH_LOCAL_STATE_ERROR +}; + +enum auth_rmt_state { + AUTH_RMT_STATE_SEND_ALG = 1, + AUTH_RMT_STATE_SEND_CHALLENGE, + AUTH_RMT_STATE_RECV_RSP, + AUTH_RMT_STATE_DONE, + AUTH_RMT_STATE_ERROR +}; + +struct auth_key { + unsigned int present:1; + unsigned int processed:1; + unsigned int value_set:1; + char *string; +}; + +struct auth_large_binary_key { + unsigned int length; + unsigned char *large_binary; +}; + +struct auth_key_block { + unsigned int transit_bit:1; + unsigned int dup_set:1; + unsigned int str_too_long:1; + unsigned int too_much_data:1; + unsigned int blk_length:16; + char *str_block; + struct auth_key key[AUTH_KEY_TYPE_MAX_COUNT]; +}; + +struct auth_str_block { + char str_block[AUTH_STR_BLOCK_MAX_LEN]; +}; + +struct auth_large_binary { + unsigned char large_binary[AUTH_LARGE_BINARY_MAX_LEN]; +}; + +struct iscsi_acl { + unsigned long signature; + + enum auth_node_type node_type; + unsigned int auth_method_count; + int auth_method_list[AUTH_METHOD_MAX_COUNT]; + enum auth_neg_role auth_method_neg_role; + unsigned int chap_alg_count; + int chap_alg_list[AUTH_CHAP_ALG_MAX_COUNT]; + int auth_rmt; + char username[AUTH_STR_MAX_LEN]; + int passwd_present; + unsigned int passwd_length; + unsigned char passwd_data[AUTH_STR_MAX_LEN]; + unsigned int chap_challenge_len; + int ip_sec; + + unsigned int auth_method_valid_count; + int auth_method_valid_list[AUTH_METHOD_MAX_COUNT]; + int auth_method_valid_neg_role; + + int recv_in_progress_flag; + int recv_end_count; + /* + * session for callbacks + */ + struct iscsi_session *session_handle; + enum auth_phase phase; + enum auth_local_state local_state; + enum auth_rmt_state rmt_state; + enum auth_status rmt_auth_status; + enum auth_dbg_status dbg_status; + int negotiated_auth_method; + int negotiated_chap_alg; + int auth_rsp_flag; + int auth_server_error_flag; + int transit_bit_sent_flag; + + unsigned int send_chap_identifier; + struct auth_large_binary_key send_chap_challenge; + char chap_username[AUTH_STR_MAX_LEN]; + + int recv_chap_challenge_status; + struct auth_large_binary_key recv_chap_challenge; + + char scratch_key_value[AUTH_STR_MAX_LEN]; + + struct auth_key_block recv_key_block; + struct auth_key_block send_key_block; +}; + +extern int acl_init(int node_type, struct iscsi_session *session); +extern int acl_finish(struct iscsi_acl *client); + +extern int acl_recv_begin(struct iscsi_acl *client); +extern int acl_recv_end(struct iscsi_acl *client); +extern const char *acl_get_key_name(int key_type); +extern int acl_get_next_key_type(int *key_type); +extern int acl_recv_key_value(struct iscsi_acl *client, int key_type, + const char *user_key_val); +extern int acl_send_key_val(struct iscsi_acl *client, int key_type, + int *key_present, char *user_key_val, + unsigned int max_length); +extern int acl_recv_transit_bit(struct iscsi_acl *client, int value); +extern int acl_send_transit_bit(struct iscsi_acl *client, int *value); +extern int acl_set_user_name(struct iscsi_acl *client, const char *username); +extern int acl_set_passwd(struct iscsi_acl *client, + const unsigned char *pw_data, unsigned int pw_len); +extern int acl_set_auth_rmt(struct iscsi_acl *client, int auth_rmt); +extern int acl_set_ip_sec(struct iscsi_acl *client, int ip_sec); +extern int acl_get_dbg_status(struct iscsi_acl *client, int *value); +extern const char *acl_dbg_status_to_text(int dbg_status); +extern enum auth_dbg_status acl_chap_compute_rsp(struct iscsi_acl *client, + int rmt_auth, + unsigned int id, + unsigned char *challenge_data, + unsigned int challenge_len, + unsigned char *response_data); +extern int acl_chap_auth_request(struct iscsi_acl *client, char *username, + unsigned int id, + unsigned char *challenge_data, + unsigned int challenge_length, + unsigned char *response_data, + unsigned int rsp_length); +extern int acl_data(unsigned char *out_data, unsigned int *out_length, + unsigned char *in_data, unsigned int in_length); +#endif /* #ifndef ISCSIAUTHCLIENT_H */ --------------050501070106080704030001--