/* * IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. By downloading, copying, installing or * using the software you agree to this license. If you do not agree to this license, do not download, install, * copy or use the software. * * Intel License Agreement * * Copyright (c) 2000, Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that * the following conditions are met: * * -Redistributions of source code must retain the above copyright notice, this list of conditions and the * following disclaimer. * * -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other materials provided with the distribution. * * -The name of Intel Corporation may not be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "compat.h" #ifdef HAVE_CTYPE_H #include <ctype.h> #endif #include <stdio.h> #include <stdlib.h> #ifdef HAVE_STRING_H #include <string.h> #endif #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif #include "iscsi-md5.h" #include "iscsiutil.h" #include "parameters.h" #include "conffile.h" int param_list_add(iscsi_parameter_t ** head, int type, const char *key, const char *dflt, const char *valid) { iscsi_parameter_t *param; /* Allocated new parameter type */ if (*head == NULL) { if ((*head = iscsi_malloc_atomic(sizeof(iscsi_parameter_t))) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "out of memory\n"); return -1; } param = *head; } else { for (param = *head; param->next != NULL; param = param->next) { } if ((param->next = iscsi_malloc_atomic(sizeof(iscsi_parameter_t))) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "out of memory\n"); return -1; } param = param->next; } /* Initilized parameter */ param->type = type; /* type */ (void) strlcpy(param->key, key, sizeof(param->key));/* key */ (void) strlcpy(param->dflt, dflt, sizeof(param->dflt)); /* default value */ (void) strlcpy(param->valid, valid, sizeof(param->valid)); /* list of valid values */ param->tx_offer = 0; /* sent offer */ param->rx_offer = 0; /* received offer */ param->tx_answer = 0; /* sent answer */ param->rx_answer = 0; /* received answer */ param->reset = 0; /* used to erase value_l on next parse */ param->next = NULL; /* terminate list */ /* Allocated space for value list and set first item to default; and */ /* set offer and answer lists to NULL */ if ((param->value_l = iscsi_malloc_atomic(sizeof(iscsi_parameter_value_t))) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n"); return -1; } param->value_l->next = NULL; (void) strlcpy(param->value_l->value, dflt, sizeof(param->value_l->value)); /* Arg check */ switch (type) { case ISCSI_PARAM_TYPE_DECLARATIVE: break; case ISCSI_PARAM_TYPE_DECLARE_MULTI: break; case ISCSI_PARAM_TYPE_BINARY_OR: if (strcmp(valid, "Yes,No") != 0 && strcmp(valid, "No,Yes") != 0 && strcmp(valid, "No") != 0 && strcmp(valid, "Yes") != 0 && strcmp(valid, "yes,no") != 0 && strcmp(valid, "no,yes") != 0 && strcmp(valid, "no") != 0 && strcmp(valid, "yes") != 0) { iscsi_trace_error(__FILE__, __LINE__, "bad <valid> field \"%s\" for ISCSI_PARAM_TYPE_BINARY\n", valid); return -1; } break; case ISCSI_PARAM_TYPE_BINARY_AND: if (strcmp(valid, "Yes,No") != 0 && strcmp(valid, "No,Yes") != 0 && strcmp(valid, "No") != 0 && strcmp(valid, "Yes") != 0 && strcmp(valid, "yes,no") != 0 && strcmp(valid, "no,yes") != 0 && strcmp(valid, "no") != 0 && strcmp(valid, "yes") != 0) { iscsi_trace_error(__FILE__, __LINE__, "bad <valid> field \"%s\" for ISCSI_PARAM_TYPE_BINARY\n", valid); return -1; } break; case ISCSI_PARAM_TYPE_NUMERICAL: break; case ISCSI_PARAM_TYPE_NUMERICAL_Z: break; case ISCSI_PARAM_TYPE_LIST: break; default: iscsi_trace_error(__FILE__, __LINE__, "unknown parameter type %d\n", type); return -1; } iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "\"%s\": valid \"%s\", default \"%s\", current \"%s\"\n", param->key, param->valid, param->dflt, param->value_l->value); return 0; } int param_list_destroy(iscsi_parameter_t * head) { iscsi_parameter_t *ptr, *tmp; iscsi_parameter_value_t *item_ptr, *next; for (ptr = head; ptr != NULL;) { tmp = ptr; ptr = ptr->next; if (tmp->value_l) { for (item_ptr = tmp->value_l; item_ptr != NULL; item_ptr = next) { next = item_ptr->next; /* * iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "freeing \"%s\" * (%p)\n", item_ptr->value, item_ptr); */ iscsi_free_atomic(item_ptr); } } /* iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "freeing %p\n", tmp); */ iscsi_free_atomic(tmp); } return 0; } iscsi_parameter_t * param_get(iscsi_parameter_t * head, const char *key) { iscsi_parameter_t *ptr; for (ptr = head; ptr != NULL; ptr = ptr->next) { if (strcmp(ptr->key, key) == 0) { return ptr; } } iscsi_trace_error(__FILE__, __LINE__, "key \"%s\" not found in param list\n", key); return NULL; } char * param_val(iscsi_parameter_t * head, const char *key) { return param_val_which(head, key, 0); } char * param_val_which(iscsi_parameter_t * head, const char *key, int which) { iscsi_parameter_t *ptr; iscsi_parameter_value_t *item_ptr; int i = 0; for (ptr = head; ptr != NULL; ptr = ptr->next) { if (strcmp(ptr->key, key) == 0) { item_ptr = ptr->value_l; for (i = 0; i != which; i++) { if (item_ptr == NULL) { iscsi_trace_error(__FILE__, __LINE__, "item %d in value list is NULL\n", i); return NULL; } item_ptr = item_ptr->next; } if (item_ptr == NULL) { iscsi_trace_error(__FILE__, __LINE__, "item %d in value list is NULL\n", which); return NULL; } return item_ptr->value; } } iscsi_trace_error(__FILE__, __LINE__, "key \"%s\" not found in param list\n", key); return NULL; } static int param_val_delete_all(iscsi_parameter_t * head, char *key) { iscsi_parameter_t *ptr; iscsi_parameter_value_t *item_ptr, *next; for (ptr = head; ptr != NULL; ptr = ptr->next) { if (strcmp(ptr->key, key) == 0) { for (item_ptr = ptr->value_l; item_ptr != NULL; item_ptr = next) { next = item_ptr->next; iscsi_free_atomic(item_ptr); } ptr->value_l = NULL; return 0; } } iscsi_trace_error(__FILE__, __LINE__, "key \"%s\" not found in param list\n", key); return -1; } int param_val_reset(iscsi_parameter_t * head, const char *key) { iscsi_parameter_t *ptr; for (ptr = head; ptr != NULL; ptr = ptr->next) { if (strcmp(ptr->key, key) == 0) { ptr->reset = 1; return 0; } } iscsi_trace_error(__FILE__, __LINE__, "key \"%s\" not found in param list\n", key); return -1; } int param_atoi(iscsi_parameter_t * head, const char *key) { iscsi_parameter_t *ptr; char *value; for (ptr = head; ptr != NULL; ptr = ptr->next) { if (strcmp(ptr->key, key) == 0) { if (ptr->value_l) { if ((value = param_val(head, key)) != NULL) { return iscsi_atoi(value); } else { iscsi_trace_error(__FILE__, __LINE__, "value is NULL\n"); return 0; } } else { iscsi_trace_error(__FILE__, __LINE__, "param \"%s\" has NULL value list\n", key); return 0; } } } iscsi_trace_error(__FILE__, __LINE__, "key \"%s\" not found in param list\n", key); return 0; } int param_equiv(iscsi_parameter_t * head, const char *key, const char *val) { iscsi_parameter_t *ptr; char *value; for (ptr = head; ptr != NULL; ptr = ptr->next) { if (strcmp(ptr->key, key) == 0) { if (ptr->value_l == NULL) { iscsi_trace_error(__FILE__, __LINE__, "param \"%s\" has NULL value list\n", key); return 0; } if ((value = param_val(head, key)) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "key \"%s\" value is NULL\n", key); return 0; } return (strcmp(value, val) == 0); } } iscsi_trace_error(__FILE__, __LINE__, "key \"%s\" not found in param list\n", key); return -1; } int param_num_vals(iscsi_parameter_t * head, char *key) { iscsi_parameter_t *ptr; iscsi_parameter_value_t *item_ptr; int num = 0; for (ptr = head; ptr != NULL; ptr = ptr->next) { if (strcmp(ptr->key, key) == 0) { for (item_ptr = ptr->value_l; item_ptr != NULL; item_ptr = item_ptr->next) { num++; } return num; } } iscsi_trace_error(__FILE__, __LINE__, "key \"%s\" not found in param list\n", key); return -1; } int param_list_print(iscsi_parameter_t * head) { iscsi_parameter_t *ptr; iscsi_parameter_value_t *item_ptr; for (ptr = head; ptr != NULL; ptr = ptr->next) { for (item_ptr = ptr->value_l; item_ptr != NULL; item_ptr = item_ptr->next) { printf("\"%s\"=\"%s\"\n", ptr->key, item_ptr->value); } } return 0; } int param_text_print(char *text, uint32_t text_len) { char key[256]; char *ptr, *eq, *value; for (ptr = text; ptr - text < text_len; ptr += (strlen(ptr) + 1)) { /* Skip over any NULLs */ while (!(*ptr) && ((ptr - text) < text_len)) ptr++; if ((ptr - text) >= text_len) break; if ((eq = strchr(ptr, '=')) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "delimiter \'=\' not found in token \"%s\"\n", ptr); return -1; } strncpy(key, ptr, (unsigned)(eq - ptr)); key[(int)(eq - ptr)] = 0x0; value = eq + 1; printf("\"%s\"=\"%s\"\n", key, value); } return 0; } /* ARGSUSED */ int param_text_add(iscsi_parameter_t * head, const char *key, const char *value, char *text, int *len, int size, int offer) { int cc; cc = snprintf(text + *len, (unsigned)(size - *len), "%s=%s", key, value); *len += cc + 1; return 0; } int driver_atoi(const char *s) { int k = 0; while (*s != 0x0 && *s >= '0' && *s <= '9') { k = 10 * k + (*s - '0'); s++; } return k; } /* find the credentials for `user' and put them in `cred' */ static int find_credentials(iscsi_cred_t *cred, char *user, const char *auth) { conffile_t conf; const char *authtype; unsigned cc; ent_t e; (void) memset(&conf, 0x0, sizeof(conf)); (void) memset(&e, 0x0, sizeof(e)); if (!conffile_open(&conf, _PATH_ISCSI_PASSWD, "r", ":", "#")) { iscsi_trace_error(__FILE__, __LINE__, "can't open `%s'\n", _PATH_ISCSI_PASSWD); exit(EXIT_FAILURE); } while (conffile_getent(&conf, &e)) { if (strcasecmp(e.sv.v[0], user) == 0) { authtype = (e.sv.c == 1) ? "none" : e.sv.v[1]; cc = strlen(authtype); if (auth == NULL || (strncasecmp(authtype, auth, cc) == 0 && cc == strlen(auth))) { cred->user = strdup(e.sv.v[0]); cred->auth_type = strdup(authtype); cred->shared_secret = (e.sv.c == 3) ? strdup(e.sv.v[2]) : NULL; conffile_close(&conf); return 1; } } } conffile_close(&conf); (void) fprintf(stderr, "No matching user configuration entry for `%s' was found\n", user); (void) fprintf(stderr, "Please add an entry for `%s' to `%s'\n", user, _PATH_ISCSI_PASSWD); return 0; } #if 0 /* free any storage allocated in `cred' */ static void free_cred(iscsi_cred_t *cred) { if (cred) { if (cred->user) { iscsi_free_atomic(cred->user); } if (cred->auth_type) { iscsi_free_atomic(cred->auth_type); } if (cred->shared_secret) { iscsi_free_atomic(cred->shared_secret); } } } #endif /* Security offering and check */ /* * ret values: =0: succeed or no security >0: security negotiation in process * <0: failed */ static int param_parse_security(iscsi_parameter_t * head, iscsi_parameter_t * param_in, iscsi_cred_t *cred, char *text_out, int *text_len_out, int textsize) { static uint8_t idData; static uint8_t chapdata[ISCSI_CHAP_DATA_LENGTH]; static uint8_t respdata[ISCSI_CHAP_DATA_LENGTH]; char *chapstring = NULL; iSCSI_MD5_CTX *context = NULL; iscsi_parameter_t *param = NULL; int ret = 1; if ((chapstring = iscsi_malloc(ISCSI_CHAP_STRING_LENGTH)) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "iscsi_malloc() failed\n"); return -1; } if ((context = iscsi_malloc(sizeof(*context))) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "iscsi_malloc() failed\n"); if (chapstring != NULL) iscsi_free(chapstring); return -1; } #define PPS_CLEANUP { if (chapstring != NULL) iscsi_free(chapstring);if (context != NULL) iscsi_free(context); } #define PPS_ERROR { PPS_CLEANUP; return (-1); }; if (strcmp(param_in->key, "AuthMethod") == 0) { if (param_in->rx_answer && strcmp(param_in->answer_rx, "None") == 0) { PPS_CLEANUP; return 0; /* Proposed None for * Authentication */ } if (param_in->rx_offer && strcmp(param_in->offer_rx, "None") == 0) { PPS_CLEANUP; return 0; } if (!param_in->rx_offer) { param = param_get(head, "CHAP_A"); if (param == NULL) PPS_ERROR; param->tx_offer = 1; /* sending an offer */ param->rx_offer = 0; /* reset */ (void) strlcpy(param->offer_tx, param->valid, sizeof(param->offer_tx)); PARAM_TEXT_ADD(head, param->key, param->valid, text_out, text_len_out, textsize, 0, PPS_ERROR); ret++; } } else if (strcmp(param_in->key, "CHAP_A") == 0) { if (param_in->rx_offer) { PARAM_TEXT_ADD(head, param_in->key, param_in->offer_rx, text_out, text_len_out, textsize, 0, PPS_ERROR); if ((param = param_get(head, "CHAP_I")) == NULL) { PPS_ERROR; } param->tx_offer = 1; /* sending an offer */ param->rx_offer = 0; /* reset */ GenRandomData(&idData, 1); (void) snprintf(chapstring, ISCSI_CHAP_STRING_LENGTH, "%d", idData); (void) strlcpy(param->offer_tx, chapstring, sizeof(param->offer_tx)); PARAM_TEXT_ADD(head, param->key, param->offer_tx, text_out, text_len_out, textsize, 0, PPS_ERROR); if ((param = param_get(head, "CHAP_C")) == NULL) { PPS_ERROR; } param->tx_offer = 1; /* sending an offer */ param->rx_offer = 0; /* reset */ GenRandomData(chapdata, ISCSI_CHAP_DATA_LENGTH); HexDataToText(chapdata, ISCSI_CHAP_DATA_LENGTH, chapstring, ISCSI_CHAP_STRING_LENGTH); (void) strlcpy(param->offer_tx, chapstring, sizeof(param->offer_tx)); PARAM_TEXT_ADD(head, param->key, param->offer_tx, text_out, text_len_out, textsize, 0, PPS_ERROR); ret++; } } else if (strcmp(param_in->key, "CHAP_I") == 0) { idData = driver_atoi((param_in->rx_offer) ? param_in->offer_rx : param_in->answer_rx); ret++; } else if (strcmp(param_in->key, "CHAP_C") == 0) { HexTextToData((param_in->rx_offer) ? param_in->offer_rx : param_in->answer_rx, ISCSI_CHAP_STRING_LENGTH, chapdata, ISCSI_CHAP_DATA_LENGTH); if ((param = param_get(head, "CHAP_N")) == NULL) { PPS_ERROR; } param->tx_offer = 1; /* sending an offer */ param->rx_offer = 0; /* reset */ if (cred->shared_secret == NULL && !find_credentials(cred, cred->user, "chap")) { iscsi_trace_error(__FILE__, __LINE__, "Unknown user `%s'\n", param_in->offer_rx); PPS_ERROR; } if (cred->user) { (void) strlcpy(param->offer_tx, cred->user, sizeof(param->offer_tx)); } else { iscsi_trace_error(__FILE__, __LINE__, "no valid user credentials\n"); PPS_ERROR; } PARAM_TEXT_ADD(head, param->key, param->offer_tx, text_out, text_len_out, textsize, 0, PPS_ERROR); if ((param = param_get(head, "CHAP_R")) == NULL) { PPS_ERROR; } param->tx_offer = 1; /* sending an offer */ param->rx_offer = 0; /* reset */ iSCSI_MD5Init(context); iSCSI_MD5Update(context, &idData, 1); if (cred->shared_secret == NULL) { iscsi_trace_error(__FILE__, __LINE__, "null shared secret\n"); PPS_ERROR; } else { iSCSI_MD5Update(context, (const uint8_t *)cred->shared_secret, strlen(cred->shared_secret)); } HexDataToText(chapdata, ISCSI_CHAP_DATA_LENGTH, param->offer_tx, ISCSI_CHAP_STRING_LENGTH); iSCSI_MD5Update(context, chapdata, ISCSI_CHAP_DATA_LENGTH); iSCSI_MD5Final(chapdata, context); HexDataToText(chapdata, ISCSI_CHAP_DATA_LENGTH, param->offer_tx, ISCSI_CHAP_STRING_LENGTH); PARAM_TEXT_ADD(head, param->key, param->offer_tx, text_out, text_len_out, textsize, 0, PPS_ERROR); if (param_in->rx_offer) { if ((param = param_get(head, "CHAP_I")) == NULL) { PPS_ERROR; } param->tx_offer = 1; /* sending an offer */ param->rx_offer = 0; /* reset */ GenRandomData(&idData, 1); (void) snprintf(chapstring, ISCSI_CHAP_STRING_LENGTH, "%d", idData); (void) strlcpy(param->offer_tx, chapstring, sizeof(param->offer_tx)); PARAM_TEXT_ADD(head, param->key, param->offer_tx, text_out, text_len_out, textsize, 0, PPS_ERROR); if ((param = param_get(head, "CHAP_C")) == NULL) { PPS_ERROR; } param->tx_offer = 1; /* sending an offer */ param->rx_offer = 0; /* reset */ GenRandomData(chapdata, ISCSI_CHAP_DATA_LENGTH); HexDataToText(chapdata, ISCSI_CHAP_DATA_LENGTH, chapstring, ISCSI_CHAP_STRING_LENGTH); (void) strlcpy(param->offer_tx, chapstring, sizeof(param->offer_tx)); PARAM_TEXT_ADD(head, param->key, param->offer_tx, text_out, text_len_out, textsize, 0, PPS_ERROR); } ret++; } else if (strcmp(param_in->key, "CHAP_N") == 0) { char *user; user = (param_in->rx_offer) ? param_in->offer_rx : param_in->answer_rx; if (!find_credentials(cred, user, "chap")) { iscsi_trace_error(__FILE__, __LINE__, "Unknown user `%s'\n", user); PPS_ERROR; } ret++; } else if (strcmp(param_in->key, "CHAP_R") == 0) { iSCSI_MD5Init(context); iSCSI_MD5Update(context, &idData, 1); HexDataToText(&idData, 1, param_in->offer_tx, ISCSI_CHAP_STRING_LENGTH); HexDataToText(chapdata, ISCSI_CHAP_DATA_LENGTH, chapstring, ISCSI_CHAP_STRING_LENGTH); if (cred->shared_secret == NULL) { iscsi_trace_error(__FILE__, __LINE__, "Null shared secret in initiator\n"); PPS_ERROR; } else { iSCSI_MD5Update(context, (const uint8_t *)cred->shared_secret, strlen(cred->shared_secret)); } iSCSI_MD5Update(context, chapdata, ISCSI_CHAP_DATA_LENGTH); iSCSI_MD5Final(chapdata, context); HexTextToData((param_in->rx_offer) ? param_in->offer_rx : param_in->answer_rx, ISCSI_CHAP_STRING_LENGTH, respdata, ISCSI_CHAP_DATA_LENGTH); HexDataToText(chapdata, ISCSI_CHAP_DATA_LENGTH, param_in->offer_rx, ISCSI_CHAP_STRING_LENGTH); if (memcmp(respdata, chapdata, ISCSI_CHAP_DATA_LENGTH) != 0) { iscsi_trace_error(__FILE__, __LINE__, "Initiator authentication failed %x %x\n", *chapdata, *respdata); PPS_ERROR; } else { PPS_CLEANUP; } return 0; } PPS_CLEANUP; return (ret); } int param_text_parse(iscsi_parameter_t * head, iscsi_cred_t *cred, char *text_in, int text_len_in, char *text_out, int *text_len_out, int textsize, int outgoing) { static char *key = NULL; char *value = NULL; char *ptr, *eq; iscsi_parameter_t *param; iscsi_parameter_value_t *item_ptr; int offer_i, answer_i, max_i, val1_i, val2_i, negotiated_i; char *p1, *p2, *p3, *p4; char *offer = NULL; char *valid = NULL; char *val1 = NULL; char *val2 = NULL; char *tmp_key = NULL; char c; int ret; /* * Whether incoming or outgoing, some of the params might be offers * and some answers. Incoming */ /* * text has the potential for creating outgoing text - and this will * happen when the incoming */ /* text has offers that need an answer. */ iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "parsing %d %s bytes of text parameters\n", text_len_in, outgoing ? "outgoing" : "incoming"); if ((key = iscsi_malloc(ISCSI_PARAM_KEY_LEN)) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "iscsi_malloc() failed\n"); return -1; } if ((offer = iscsi_malloc(ISCSI_PARAM_MAX_LEN)) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "iscsi_malloc() failed\n"); if (key != NULL) { iscsi_free(key); } return -1; } if ((valid = iscsi_malloc(ISCSI_PARAM_MAX_LEN)) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "iscsi_malloc() failed\n"); if (key != NULL) { iscsi_free(key); } if (offer != NULL) { iscsi_free(offer); } return -1; } if ((val1 = iscsi_malloc(ISCSI_PARAM_MAX_LEN)) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "iscsi_malloc() failed\n"); if (key != NULL) { iscsi_free(key); } if (offer != NULL) { iscsi_free(offer); } if (valid != NULL) { iscsi_free(valid); } return -1; } if ((val2 = iscsi_malloc(ISCSI_PARAM_MAX_LEN)) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "iscsi_malloc() failed\n"); if (key != NULL) { iscsi_free(key); } if (offer != NULL) { iscsi_free(offer); } if (valid != NULL) { iscsi_free(valid); } if (val1 != NULL) { iscsi_free(val1); } return -1; } #define PTP_CLEANUP { if (key != NULL) iscsi_free(key); \ if (offer != NULL) iscsi_free(offer); \ if (valid != NULL) iscsi_free(valid); \ if (val1 != NULL) iscsi_free(val1); \ if (val2 != NULL) iscsi_free(val2); \ if (tmp_key != NULL) iscsi_free(tmp_key); } #define PTP_ERROR {PTP_CLEANUP; return -1;} if (!outgoing) { *text_len_out = 0; } #if ISCSI_DEBUG printf("**************************************************\n"); printf("* PARAMETERS NEGOTIATED *\n"); printf("* *\n"); #endif for (ptr = text_in; ptr - text_in < text_len_in; ptr += (strlen(ptr) + 1)) { /* Skip over any NULLs */ while (!(*ptr) && ((ptr - text_in) < text_len_in)) { ptr++; } if ((ptr - text_in) >= text_len_in) { break; } /* Extract <key>=<value> token from text_in */ if ((eq = strchr(ptr, '=')) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "delimiter \'=\' not found in token \"%s\"\n", ptr); } else { if ((int)(eq - ptr) >= (ISCSI_PARAM_KEY_LEN - 1)) { if (!outgoing) { tmp_key = iscsi_malloc((unsigned)(eq - ptr)); if (tmp_key) { strncpy(tmp_key, ptr, (unsigned)(eq - ptr)); tmp_key[(int)(eq - ptr)] = 0x0; /* Key not understood. */ PARAM_TEXT_ADD(head, tmp_key, "NotUnderstood", text_out, text_len_out, textsize, 0, PTP_ERROR); } } else { printf("ignoring \"%s\"\n", key); } goto next; } strncpy(key, ptr, (unsigned)(eq - ptr)); key[(int)(eq - ptr)] = 0x0; value = eq + 1; } /* Find key in param list */ for (param = head; param != NULL; param = param->next) { if (strcmp(param->key, key) == 0) { break; } } if (param == NULL) { if (!outgoing) { /* Key not understood. */ PARAM_TEXT_ADD(head, key, "NotUnderstood", text_out, text_len_out, textsize, 0, PTP_ERROR); } else { printf("ignoring \"%s\"\n", key); } goto next; } RETURN_GREATER("strlen(value)", strlen(value), ISCSI_PARAM_MAX_LEN, PTP_CLEANUP, -1); /* We're sending|receiving an offer|answer */ if (outgoing) { if (param->rx_offer) { param->tx_answer = 1; /* sending an answer */ param->rx_answer = 0; /* reset */ param->tx_offer = 0; /* reset */ param->rx_offer = 0; /* reset */ (void) strlcpy(param->answer_tx, value, sizeof(param->answer_tx)); iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "sending answer \"%s\"=\"%s\" for offer \"%s\"\n", param->key, param->answer_tx, param->offer_rx); goto negotiate; } else { param->tx_offer = 1; /* sending an offer */ param->tx_answer = 0; param->rx_answer = 0; (void) strlcpy(param->offer_tx, value, sizeof(param->offer_tx)); iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "sending offer \"%s\"=\"%s\"\n", param->key, param->offer_tx); if ((param->type == ISCSI_PARAM_TYPE_DECLARATIVE) || (param->type == ISCSI_PARAM_TYPE_DECLARE_MULTI)) { goto negotiate; } goto next; } } else { if (param->tx_offer) { param->rx_answer = 1; /* received an answer */ param->tx_answer = 0; param->rx_offer = 0; param->tx_offer = 0; /* reset */ (void) strlcpy(param->answer_rx, value, sizeof(param->answer_rx)); iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "received answer \"%s\"=\"%s\" for offer \"%s\"\n", param->key, param->answer_rx, param->offer_tx); if ((ret = param_parse_security(head, param, cred, text_out, text_len_out, textsize)) > 1) { goto next; } else if (ret == 0) { /* * FIX ME Happens in initiator code * currently we ignore initiator * authentication status See comments * at the beginning of parse_security */ goto negotiate; } else if (ret == 1) { goto negotiate; } else { PTP_CLEANUP; } return ISCSI_PARAM_STATUS_AUTH_FAILED; } else { param->rx_offer = 1; /* received an offer */ param->rx_answer = 0; param->tx_answer = 0; (void) strlcpy(param->offer_rx, value, sizeof(param->offer_rx)); iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "received offer \"%s\"=\"%s\"\n", param->key, param->offer_rx); if ((ret = param_parse_security(head, param, cred, text_out, text_len_out, textsize)) > 1) { goto next; } else if (ret < 0) { iscsi_parameter_t *auth_result; if ((auth_result = param_get(head, "AuthResult")) != 0) { (void) strlcpy(auth_result->value_l->value, "Fail", sizeof(auth_result->value_l->value)); } PTP_CLEANUP; return (ISCSI_PARAM_STATUS_AUTH_FAILED); } else if (ret == 0) { iscsi_parameter_t *auth_result; if ((auth_result = param_get(head, "AuthResult")) != 0) { (void) strlcpy(auth_result->value_l->value, "Yes", sizeof(auth_result->value_l->value)); } } /* * Answer the offer if it is an inquiry or * the type is not DECLARATIVE */ if ((strcmp(param->offer_rx, "?") != 0) && ((param->type == ISCSI_PARAM_TYPE_DECLARATIVE) || (param->type == ISCSI_PARAM_TYPE_DECLARE_MULTI))) { goto negotiate; } else { goto answer; } } } answer: /* Answer with current value if this is an inquiry (<key>=?) */ if (strcmp(value, "?") == 0) { iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "got inquiry for param \"%s\"\n", param->key); if (param->value_l) { if (param->value_l->value) { (void) strlcpy(param->answer_tx, param->value_l->value, sizeof(param->answer_tx)); } else { iscsi_trace_error(__FILE__, __LINE__, "param \"%s\" has NULL value_l->value\n", param->key); param->answer_tx[0] = 0x0; } } else { iscsi_trace_error(__FILE__, __LINE__, "param \"%s\" has NULL value_l\n", param->key); param->answer_tx[0] = 0x0; } goto add_answer; } /* Generate answer according to the parameter type */ switch (param->type) { case ISCSI_PARAM_TYPE_BINARY_AND: goto binary_or; case ISCSI_PARAM_TYPE_BINARY_OR: binary_or: if (strcmp(value, "yes") != 0 && strcmp(value, "no") != 0 && strcmp(value, "Yes") != 0 && strcmp(value, "No") != 0) { iscsi_trace_error(__FILE__, __LINE__, "\"%s\" is not a valid binary value\n", value); (void) strlcpy(param->answer_tx, "Reject", sizeof(param->answer_tx)); goto add_answer; } if (strchr(param->valid, ',') != NULL) { (void) strlcpy(param->answer_tx, value, sizeof(param->answer_tx)); /* we accept both yes * and no, so answer w/ * their offer */ } else { (void) strlcpy(param->answer_tx, param->valid, sizeof(param->answer_tx)); /* answer with the only * value we support */ } break; case ISCSI_PARAM_TYPE_LIST: /* * Use our default value if it's offered as one of the option * in the parameter list. * * We need to do this at least for CHAP because cisco's initiator * could be sending us a parameter value list with "CHAP,None", * even when it doesn't set username/password in its configration * file, in which case we should pick "None" as for no security instead * of pick the first one on the value list. "None" is the default value * for AuthMethod * * This fix is working well now, though is arguable. We should keep * this just to make us work with Cisco for now. */ if (strlen(param->dflt)) { for (p1 = p2 = param->offer_rx; p2; p1 = p2 + 1) { if ((p2 = strchr(p1, ',')) != NULL) { strncpy(offer, p1, (unsigned)(p2 - p1)); offer[(int)(p2 - p1)] = 0x0; } else { (void) strlcpy(offer, p1, ISCSI_PARAM_MAX_LEN); } if (strcmp(param->dflt, offer) == 0) { (void) strlcpy(param->answer_tx, offer, sizeof(param->answer_tx)); goto add_answer; } } } /* Find the first valid offer that we support */ for (p1 = p2 = param->offer_rx; p2; p1 = p2 + 1) { if ((p2 = strchr(p1, ',')) != NULL) { strncpy(offer, p1, (unsigned)(p2 - p1)); offer[p2 - p1] = 0x0; } else { (void) strlcpy(offer, p1, ISCSI_PARAM_MAX_LEN); } if (strlen(param->valid)) { for (p3 = p4 = param->valid; p4; p3 = p4 + 1) { if ((p4 = strchr(p3, ',')) != NULL) { strncpy(valid, p3, (unsigned)(p4 - p3)); valid[(int)(p4 - p3)] = 0x0; } else { (void) strlcpy(valid, p3, ISCSI_PARAM_MAX_LEN); } if (strcmp(valid, offer) == 0) { (void) strlcpy(param->answer_tx, offer, sizeof(param->answer_tx)); goto add_answer; } } } else { iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "Valid list empty. Answering with first in offer list\n"); (void) strlcpy(param->answer_tx, offer, sizeof(param->answer_tx)); goto add_answer; } iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "\"%s\" is not a valid offer for key \"%s\" (must choose from \"%s\")\n", offer, param->key, param->valid); } iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "No Valid offers: \"%s\" is added as value for key \"%s\")\n", "Reject", param->key); (void) strlcpy(param->answer_tx, "Reject", sizeof(param->answer_tx)); break; case ISCSI_PARAM_TYPE_NUMERICAL_Z: goto numerical; case ISCSI_PARAM_TYPE_NUMERICAL: numerical: offer_i = iscsi_atoi(param->offer_rx); max_i = iscsi_atoi(param->valid); if (param->type == ISCSI_PARAM_TYPE_NUMERICAL_Z) { if (max_i == 0) { answer_i = offer_i; /* we support anything, * so return whatever * they offered */ } else if (offer_i == 0) { answer_i = max_i; /* return only what we * can support */ } else if (offer_i > max_i) { answer_i = max_i; /* we are the lower of * the two */ } else { answer_i = offer_i; /* they are the lower of * the two */ } } else { if (offer_i > max_i) { answer_i = max_i; /* we are the lower of * the two */ } else { answer_i = offer_i; /* they are the lower of * the two */ } } (void) snprintf(param->answer_tx, sizeof(param->answer_tx), "%d", answer_i); goto add_answer; default: goto next; } add_answer: PARAM_TEXT_ADD(head, key, param->answer_tx, text_out, text_len_out, textsize, 0, PTP_ERROR); iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "answering \"%s\"=\"%s\"\n", param->key, param->answer_tx); goto next; /* Negotiate after receiving|sending an answer */ negotiate: switch (param->type) { case ISCSI_PARAM_TYPE_DECLARE_MULTI: goto declarative_negotiate; case ISCSI_PARAM_TYPE_DECLARATIVE: declarative_negotiate: if (param->tx_answer) { (void) strlcpy(param->negotiated, param->answer_tx, sizeof(param->negotiated)); } else if (param->tx_offer) { (void) strlcpy(param->negotiated, param->offer_tx, sizeof(param->negotiated)); } else if (param->rx_answer) { (void) strlcpy(param->negotiated, param->answer_rx, sizeof(param->negotiated)); } else if (param->rx_offer) { (void) strlcpy(param->negotiated, param->offer_rx, sizeof(param->negotiated)); } else { iscsi_trace_error(__FILE__, __LINE__, "Invalid negotiation!?!?\n"); } break; case ISCSI_PARAM_TYPE_BINARY_AND: goto binary_or_negotiate; case ISCSI_PARAM_TYPE_BINARY_OR: binary_or_negotiate: if (outgoing) { (void) strlcpy(val1, param->offer_rx, ISCSI_PARAM_MAX_LEN); (void) strlcpy(val2, param->answer_tx, ISCSI_PARAM_MAX_LEN); } else { (void) strlcpy(val1, param->answer_rx, ISCSI_PARAM_MAX_LEN); (void) strlcpy(val2, param->offer_tx, ISCSI_PARAM_MAX_LEN); /* Make sure the answer is valid */ if (strcmp(val1, "Yes") != 0 && strcmp(val1, "No") != 0 && strcmp(val1, "yes") != 0 && strcmp(val1, "no") != 0 && strcmp(val1, "Irrelevant") != 0) { /* Invalid value returned as answer. */ iscsi_trace_error(__FILE__, __LINE__, "Invalid answer (%s) for key (%s)\n", val1, key); PTP_ERROR; } } if (param->type == ISCSI_PARAM_TYPE_BINARY_OR) { if (strcmp(val1, "yes") == 0 || strcmp(val2, "yes") == 0 || strcmp(val1, "Yes") == 0 || strcmp(val2, "Yes") == 0) { (void) strlcpy(param->negotiated, "Yes", sizeof(param->negotiated)); } else { (void) strlcpy(param->negotiated, "No", sizeof(param->negotiated)); } } else { if ((strcmp(val1, "yes") == 0 && strcmp(val2, "yes") == 0) || (strcmp(val1, "Yes") == 0 && strcmp(val2, "Yes") == 0)) { (void) strlcpy(param->negotiated, "Yes", sizeof(param->negotiated)); } else { (void) strlcpy(param->negotiated, "No", sizeof(param->negotiated)); } } break; case ISCSI_PARAM_TYPE_NUMERICAL_Z: goto numerical_negotiate; case ISCSI_PARAM_TYPE_NUMERICAL: numerical_negotiate: if (outgoing) { (void) strlcpy(val1, param->offer_rx, ISCSI_PARAM_MAX_LEN); (void) strlcpy(val2, param->answer_tx, ISCSI_PARAM_MAX_LEN); } else { (void) strlcpy(val1, param->answer_rx, ISCSI_PARAM_MAX_LEN); (void) strlcpy(val2, param->offer_tx, ISCSI_PARAM_MAX_LEN); } val1_i = iscsi_atoi(val1); val2_i = iscsi_atoi(val2); if (param->type == ISCSI_PARAM_TYPE_NUMERICAL_Z) { if (val1_i == 0) { negotiated_i = val2_i; } else if (val2_i == 0) { negotiated_i = val1_i; } else if (val1_i > val2_i) { negotiated_i = val2_i; } else { negotiated_i = val1_i; } } else { if (val1_i > val2_i) { negotiated_i = val2_i; } else { negotiated_i = val1_i; } } (void) snprintf(param->negotiated, sizeof(param->negotiated), "%d", negotiated_i); break; case ISCSI_PARAM_TYPE_LIST: if (outgoing) { if (param->tx_offer) { iscsi_trace_error(__FILE__, __LINE__, "we should not be here\n"); /* error - we're sending * an offer */ PTP_ERROR; } else if (param->tx_answer) { (void) strlcpy(val1, param->answer_tx, ISCSI_PARAM_MAX_LEN); /* we're sending an * answer */ } else { iscsi_trace_error(__FILE__, __LINE__, "unexpected error\n"); PTP_ERROR; } } else { if (param->rx_offer) { iscsi_trace_error(__FILE__, __LINE__, "we should not be here\n"); /* error - we received * an offer */ PTP_ERROR; } else if (param->rx_answer) { (void) strlcpy(val1, param->answer_rx, ISCSI_PARAM_MAX_LEN); /* we received an answer */ } else { iscsi_trace_error(__FILE__, __LINE__, "unexpected error\n"); PTP_ERROR; } } /* Make sure incoming or outgoing answer is valid */ /* * None, Reject, Irrelevant and NotUnderstood are * valid */ if ((strcmp(val1, "None") == 0) || (strcmp(val1, "Reject") == 0) || (strcmp(val1, "Irrelevant") == 0) || (strcmp(val1, "NotUnderstood") == 0)) { goto value_ok; } if (strlen(param->valid) > 0) { for (p3 = p4 = param->valid; p4; p3 = p4 + 1) { if ((p4 = strchr(p3, ',')) != NULL) { strncpy(valid, p3, (unsigned)(p4 - p3)); valid[(int)(p4 - p3)] = 0x0; } else { (void) strlcpy(valid, p3, ISCSI_PARAM_MAX_LEN); } if (strcmp(valid, val1) == 0) { goto value_ok; } } } else { iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "Valid list empty??\n"); PTP_ERROR; } iscsi_trace_error(__FILE__, __LINE__, "\"%s\" is not a valid value (must choose from \"%s\")\n", val1, param->valid); PTP_ERROR; value_ok: (void) strlcpy(param->negotiated, val1, sizeof(param->negotiated)); break; } iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "negotiated \"%s\"=\"%s\"\n", param->key, param->negotiated); /* For inquiries, we don't commit the value. */ if (param->tx_offer && strcmp(param->offer_tx, "?") == 0) { /* we're offering an inquiry */ iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "sending an inquiry for \"%s\"\n", param->key); goto next; } else if (param->rx_offer && strcmp(param->offer_rx, "?") == 0) { /* we're receiving an inquiry */ iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "received an inquiry for \"%s\"\n", param->key); goto next; } else if (param->tx_answer && strcmp(param->offer_rx, "?") == 0) { /* we're answering an inquiry */ iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "answering an inquiry for \"%s\"\n", param->key); goto next; } else if (param->rx_answer && strcmp(param->offer_tx, "?") == 0) { /* we're receiving an answer for our inquiry */ iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "received an answer for inquiry on \"%s\"\n", param->key); goto next; } iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "automatically committing \"%s\"=\"%s\"\n", param->key, param->negotiated); c = param->negotiated[19]; param->negotiated[19] = 0x0; #if ISCSI_DEBUG printf("* %25s:%20s *\n", param->key, param->negotiated); #endif param->negotiated[19] = c; if (param->reset) { iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "deleting value list for \"%s\"\n", param->key); if (param_val_delete_all(head, param->key) != 0) { iscsi_trace_error(__FILE__, __LINE__, "param_val_delete_all() failed\n"); PTP_ERROR; } param->reset = 0; } if (param->value_l) { if (param->type == ISCSI_PARAM_TYPE_DECLARE_MULTI) { for (item_ptr = param->value_l; item_ptr->next != NULL; item_ptr = item_ptr->next) { } if ((item_ptr->next = iscsi_malloc_atomic(sizeof(iscsi_parameter_value_t))) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n"); PTP_ERROR; } item_ptr = item_ptr->next; item_ptr->next = NULL; } else { item_ptr = param->value_l; } } else { iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "allocating value ptr\n"); if ((param->value_l = iscsi_malloc_atomic(sizeof(iscsi_parameter_value_t))) == NULL) { iscsi_trace_error(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n"); PTP_ERROR; } item_ptr = param->value_l; item_ptr->next = NULL; } (void) strlcpy(item_ptr->value, param->negotiated, sizeof(item_ptr->value)); next: continue; } if (!outgoing) { iscsi_trace(TRACE_ISCSI_PARAM, __FILE__, __LINE__, "generated %d bytes response\n", *text_len_out); } #if ISCSI_DEBUG printf("**************************************************\n"); #endif PTP_CLEANUP; return 0; } void set_session_parameters(iscsi_parameter_t * head, iscsi_sess_param_t * sess_params) { /* These parameters are standard and assuming that they are always */ /* present in the list (head). */ memset(sess_params, 0, sizeof(iscsi_sess_param_t)); sess_params->max_burst_length = param_atoi(head, "MaxBurstLength"); sess_params->first_burst_length = param_atoi(head, "FirstBurstLength"); sess_params->max_data_seg_length = param_atoi(head, "MaxRecvDataSegmentLength"); sess_params->header_digest = (param_equiv(head, "HeaderDigest", "Yes")) ? 1 : 0; sess_params->data_digest = (param_equiv(head, "DataDigest", "Yes")) ? 1 : 0; sess_params->initial_r2t = (param_equiv(head, "InitialR2T", "Yes")); sess_params->immediate_data = (param_equiv(head, "ImmediateData", "Yes")); }