summaryrefslogtreecommitdiff
path: root/usr/auth.c
diff options
context:
space:
mode:
authoropen-iscsi <open-iscsi@d7303112-9cec-0310-bdd2-e83a94d6c2b6>2005-01-17 17:39:46 +0000
committeropen-iscsi <open-iscsi@d7303112-9cec-0310-bdd2-e83a94d6c2b6>2005-01-17 17:39:46 +0000
commit4d501fa95e9c6f6f5938a09d25b9dac40b52d0e1 (patch)
treead91e63b54b8dfb14cd37af17bcbcf05b8004ce4 /usr/auth.c
parent6f140d05e46a4706d8cddf87d94ecd97e0ea1d8f (diff)
downloadopen-iscsi-4d501fa95e9c6f6f5938a09d25b9dac40b52d0e1.tar.gz
redesign host. interim. commit
git-svn-id: svn://svn.berlios.de/open-iscsi@32 d7303112-9cec-0310-bdd2-e83a94d6c2b6
Diffstat (limited to 'usr/auth.c')
-rw-r--r--usr/auth.c2078
1 files changed, 2078 insertions, 0 deletions
diff --git a/usr/auth.c b/usr/auth.c
new file mode 100644
index 0000000..1411e45
--- /dev/null
+++ b/usr/auth.c
@@ -0,0 +1,2078 @@
+/*
+ * iSCSI Authorization Library
+ *
+ * maintained by open-iscsi@@googlegroups.com
+ *
+ * Originally based on:
+ * Copyright (C) 2001 Cisco Systems, Inc.
+ *
+ * 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.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "auth.h"
+#include "initiator.h"
+#include "md5.h"
+#include "log.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";
+
+void auth_md5_init(struct MD5Context *);
+void auth_md5_update(struct MD5Context *, unsigned char *, unsigned int);
+void auth_md5_final(unsigned char *, struct MD5Context *);
+void get_random_bytes(unsigned char *data, unsigned int length);
+size_t strlcpy(char *, const char *, size_t);
+size_t strlcat(char *, const char *, size_t);
+
+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_length,
+ unsigned char *response_data)
+{
+ unsigned char id_data[1];
+ struct MD5Context context;
+ unsigned char out_data[AUTH_STR_MAX_LEN];
+ unsigned int out_length = AUTH_STR_MAX_LEN;
+
+ if (!client->passwd_present)
+ return AUTH_DBG_STATUS_LOCAL_PASSWD_NOT_SET;
+
+ auth_md5_init(&context);
+
+ /* id byte */
+ id_data[0] = id;
+ auth_md5_update(&context, id_data, 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 */
+ auth_md5_update(&context, out_data, out_length);
+
+ /* clear decrypted password */
+ memset(out_data, 0, AUTH_STR_MAX_LEN);
+
+ /* challenge value */
+ auth_md5_update(&context, challenge_data, challenge_length);
+
+ auth_md5_final(response_data, &context);
+
+ return AUTH_DBG_STATUS_NOT_SET; /* no error */
+}
+
+/*
+ * Authenticate a target's CHAP response.
+ */
+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 MD5Context context;
+ unsigned char verify_data[16];
+
+ /* the expected credentials are in the session */
+ if (session->username_in == NULL) {
+ log_error("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) {
+ log_error("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')) {
+ log_error("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)) {
+ log_error("failing authentication, received incorrect "
+ "CHAP response length %u from target %s\n",
+ rsp_length, session->target_name);
+ return AUTH_STATUS_FAIL;
+ }
+
+ auth_md5_init(&context);
+
+ /* id byte */
+ verify_data[0] = id;
+ auth_md5_update(&context, verify_data, 1);
+
+ /* shared secret */
+ auth_md5_update(&context, (unsigned char *)session->password_in,
+ session->password_length_in);
+
+ /* challenge value */
+ auth_md5_update(&context, (unsigned char *)challenge_data,
+ challenge_length);
+
+ auth_md5_final(verify_data, &context);
+
+ if (memcmp(response_data, verify_data, sizeof(verify_data)) == 0) {
+ log_debug(1, "initiator authenticated target %s\n",
+ session->target_name);
+ return AUTH_STATUS_PASS;
+ }
+
+ log_error("failing authentication, received incorrect CHAP "
+ "response from target %s\n", session->target_name);
+ return AUTH_STATUS_FAIL;
+}
+
+void
+auth_md5_init(struct MD5Context *context)
+{
+ MD5Init(context);
+}
+
+void
+auth_md5_update(struct MD5Context *context, unsigned char *data,
+ unsigned int length)
+{
+ MD5Update(context, data, length);
+}
+
+void
+auth_md5_final(unsigned char *hash, struct MD5Context *context)
+{
+ MD5Final(hash, context);
+}
+
+void
+get_random_bytes(unsigned char *data, unsigned int length)
+{
+
+ long r;
+ unsigned n;
+
+ while (length > 0) {
+
+ r = rand();
+ r = r ^ (r >> 8);
+ r = r ^ (r >> 4);
+ n = r & 0x7;
+
+ r = rand();
+ r = r ^ (r >> 8);
+ r = r ^ (r >> 5);
+ n = (n << 3) | (r & 0x7);
+
+ r = rand();
+ r = r ^ (r >> 8);
+ r = r ^ (r >> 5);
+ n = (n << 2) | (r & 0x3);
+
+ *data++ = n;
+ length--;
+ }
+}
+
+/**
+ * strlcpy - Copy a %NUL terminated string into a sized buffer
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ * @size: size of destination buffer
+ *
+ * Compatible with *BSD: the result is always a valid
+ * NUL-terminated string that fits in the buffer (unless,
+ * of course, the buffer size is zero). It does not pad
+ * out the result like strncpy() does.
+ **/
+
+size_t strlcpy(char *dest, const char *src, size_t size)
+{
+ size_t ret = strlen(src);
+
+ if (size) {
+ size_t len = (ret >= size) ? size-1 : ret;
+ memcpy(dest, src, len);
+ dest[len] = '\0';
+ }
+ return ret;
+}
+
+/**
+ * strlcat - Append a length-limited, %NUL-terminated string to another
+ * @dest: The string to be appended to
+ * @src: The string to append to it
+ * @count: The size of the destination buffer.
+ **/
+
+size_t strlcat(char *dest, const char *src, size_t count)
+{
+ size_t dsize = strlen(dest);
+ size_t len = strlen(src);
+ size_t res = dsize + len;
+
+ /* This would be a bug */
+ if (dsize >= count) {
+ dest[count - 1] = 0;
+ return count;
+ }
+
+ dest += dsize;
+ count -= dsize;
+ if (len >= count)
+ len = count-1;
+ memcpy(dest, src, len);
+ dest[len] = 0;
+ return res;
+}
+
+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 = strtoul(text + 2, &end, 16);
+ else
+ number = 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, struct iscsi_session *session_handle)
+{
+ 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++;
+ client->session_handle = session_handle;
+
+ 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, int buf_desc_count, struct auth_buffer_desc *buff_desc)
+{
+ struct iscsi_acl *client;
+ struct auth_str_block *recv_str_blk;
+ struct auth_str_block *send_str_blk;
+ struct auth_large_binary *recv_chap_challenge;
+ struct auth_large_binary *send_chap_challenge;
+ int value_list[2];
+
+ if (buf_desc_count != 5 || !buff_desc)
+ return AUTH_STATUS_ERROR;
+
+ if (!buff_desc[0].address ||
+ buff_desc[0].length != sizeof(*client))
+ return AUTH_STATUS_ERROR;
+ client = (struct iscsi_acl *)buff_desc[0].address;
+
+ if (!buff_desc[1].address ||
+ buff_desc[1].length != sizeof(*recv_str_blk))
+ return AUTH_STATUS_ERROR;
+ recv_str_blk = (struct auth_str_block *)buff_desc[1].address;
+
+ if (!buff_desc[2].address ||
+ buff_desc[2].length != sizeof(*send_str_blk))
+ return AUTH_STATUS_ERROR;
+
+ send_str_blk = (struct auth_str_block *)buff_desc[2].address;
+
+ if (!buff_desc[3].address ||
+ buff_desc[3].length != sizeof(*recv_chap_challenge))
+ return AUTH_STATUS_ERROR;
+
+ recv_chap_challenge = (struct auth_large_binary *)
+ buff_desc[3].address;
+
+ if (!buff_desc[4].address ||
+ buff_desc[4].length != sizeof(*send_chap_challenge))
+ return AUTH_STATUS_ERROR;
+ send_chap_challenge = (struct auth_large_binary *)
+ buff_desc[4].address;
+ memset(client, 0, sizeof(*client));
+ memset(recv_str_blk, 0, sizeof(*recv_str_blk));
+ memset(send_str_blk, 0, sizeof(*send_str_blk));
+ memset(recv_chap_challenge, 0, sizeof(*recv_chap_challenge));
+ memset(send_chap_challenge, 0, sizeof(*send_chap_challenge));
+
+ client->recv_key_block.str_block = recv_str_blk->str_block;
+ client->send_key_block.str_block = send_str_blk->str_block;
+ client->recv_chap_challenge.large_binary = recv_chap_challenge->large_binary;
+ client->send_chap_challenge.large_binary = send_chap_challenge->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->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 */
+}
+