Home | History | Annotate | Download | only in idm
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <sys/types.h>
     27 #include <sys/conf.h>
     28 #include <sys/file.h>
     29 #include <sys/ddi.h>
     30 #include <sys/sunddi.h>
     31 #include <sys/sysmacros.h>
     32 #include <sys/socket.h>
     33 
     34 #include <sys/iscsi_protocol.h>
     35 #include <sys/idm/idm.h>
     36 #include <sys/idm/idm_text.h>
     37 
     38 
     39 extern int
     40 iscsi_base64_str_to_binary(char *hstr, int hstr_len,
     41     uint8_t *binary, int binary_buf_len, int *out_len);
     42 
     43 
     44 static const char idm_hex_to_ascii[] = "0123456789abcdefABCDEF";
     45 
     46 static const idm_kv_xlate_t idm_kvpair_xlate[] = {
     47 	/*
     48 	 * iSCSI Security Text Keys and Authentication Methods
     49 	 */
     50 
     51 	{ KI_AUTH_METHOD, "AuthMethod", KT_LIST_OF_VALUES, B_FALSE },
     52 	/*
     53 	 * For values with RFC comments we need to read the RFC to see
     54 	 * what type is appropriate.  For now just treat the value as
     55 	 * text.
     56 	 */
     57 
     58 	/* Kerberos */
     59 	{ KI_KRB_AP_REQ, "KRB_AP_REQ", KT_TEXT /* RFC1510 */, B_TRUE},
     60 	{ KI_KRB_AP_REP, "KRB_AP_REP", KT_TEXT /* RFC1510 */, B_TRUE},
     61 
     62 	/* SPKM */
     63 	{ KI_SPKM_REQ, "SPKM_REQ", KT_TEXT /* RFC2025 */, B_TRUE},
     64 	{ KI_SPKM_ERROR, "SPKM_ERROR", KT_TEXT /* RFC2025 */, B_TRUE},
     65 	{ KI_SPKM_REP_TI, "SPKM_REP_TI", KT_TEXT /* RFC2025 */, B_TRUE},
     66 	{ KI_SPKM_REP_IT, "SPKM_REP_IT", KT_TEXT /* RFC2025 */, B_TRUE},
     67 
     68 	/*
     69 	 * SRP
     70 	 * U, s, A, B, M, and H(A | M | K) are defined in [RFC2945]
     71 	 */
     72 	{ KI_SRP_U, "SRP_U", KT_TEXT /* <U> */, B_TRUE},
     73 	{ KI_TARGET_AUTH, "TargetAuth", KT_BOOLEAN, B_TRUE},
     74 	{ KI_SRP_GROUP, "SRP_GROUP", KT_LIST_OF_VALUES /* <G1,..> */, B_FALSE},
     75 	{ KI_SRP_A, "SRP_A", KT_TEXT /* <A> */, B_TRUE},
     76 	{ KI_SRP_B, "SRP_B", KT_TEXT /* <B> */, B_TRUE},
     77 	{ KI_SRP_M, "SRP_M", KT_TEXT /* <M> */, B_TRUE},
     78 	{ KI_SRM_HM, "SRP_HM", KT_TEXT /* <H(A | M | K)> */, B_TRUE},
     79 
     80 	/*
     81 	 * CHAP
     82 	 */
     83 	{ KI_CHAP_A, "CHAP_A", KT_LIST_OF_VALUES /* <A1,A2,..> */, B_FALSE },
     84 	{ KI_CHAP_I, "CHAP_I", KT_NUMERICAL /* <I> */, B_TRUE },
     85 	{ KI_CHAP_C, "CHAP_C", KT_BINARY /* <C> */, B_TRUE },
     86 	{ KI_CHAP_N, "CHAP_N", KT_TEXT /* <N> */, B_TRUE },
     87 	{ KI_CHAP_R, "CHAP_R", KT_BINARY /* <N> */, B_TRUE },
     88 
     89 
     90 	/*
     91 	 * ISCSI Operational Parameter Keys
     92 	 */
     93 	{ KI_HEADER_DIGEST, "HeaderDigest", KT_LIST_OF_VALUES, B_FALSE },
     94 	{ KI_DATA_DIGEST, "DataDigest", KT_LIST_OF_VALUES, B_FALSE },
     95 	{ KI_MAX_CONNECTIONS, "MaxConnections", KT_NUMERICAL, B_FALSE },
     96 	{ KI_SEND_TARGETS, "SendTargets", KT_TEXT, B_FALSE },
     97 	{ KI_TARGET_NAME, "TargetName", KT_ISCSI_NAME, B_TRUE},
     98 	{ KI_INITIATOR_NAME, "InitiatorName", KT_ISCSI_NAME, B_TRUE},
     99 	{ KI_TARGET_ALIAS, "TargetAlias", KT_ISCSI_LOCAL_NAME, B_TRUE},
    100 	{ KI_INITIATOR_ALIAS, "InitiatorAlias", KT_ISCSI_LOCAL_NAME, B_TRUE},
    101 	{ KI_TARGET_ADDRESS, "TargetAddress", KT_TEXT, B_TRUE},
    102 	{ KI_TARGET_PORTAL_GROUP_TAG, "TargetPortalGroupTag",
    103 	    KT_NUMERICAL, B_TRUE },
    104 	{ KI_INITIAL_R2T, "InitialR2T", KT_BOOLEAN, B_FALSE },
    105 	{ KI_IMMEDIATE_DATA, "ImmediateData", KT_BOOLEAN, B_FALSE },
    106 	{ KI_MAX_RECV_DATA_SEGMENT_LENGTH, "MaxRecvDataSegmentLength",
    107 	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_TRUE },
    108 	{ KI_MAX_BURST_LENGTH, "MaxBurstLength",
    109 	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_FALSE },
    110 	{ KI_FIRST_BURST_LENGTH, "FirstBurstLength",
    111 	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_FALSE },
    112 	{ KI_DEFAULT_TIME_2_WAIT, "DefaultTime2Wait",
    113 	    KT_NUMERICAL /* 0 to 2600 */, B_FALSE },
    114 	{ KI_DEFAULT_TIME_2_RETAIN, "DefaultTime2Retain",
    115 	    KT_NUMERICAL /* 0 to 2600 */, B_FALSE },
    116 	{ KI_MAX_OUTSTANDING_R2T, "MaxOutstandingR2T",
    117 	    KT_NUMERICAL /* 1 to 65535 */, B_FALSE },
    118 	{ KI_DATA_PDU_IN_ORDER, "DataPDUInOrder", KT_BOOLEAN, B_FALSE },
    119 	{ KI_DATA_SEQUENCE_IN_ORDER, "DataSequenceInOrder",
    120 	    KT_BOOLEAN, B_FALSE },
    121 	{ KI_ERROR_RECOVERY_LEVEL, "ErrorRecoveryLevel",
    122 	    KT_NUMERICAL /* 0 to 2 */, B_FALSE },
    123 	{ KI_SESSION_TYPE, "SessionType", KT_TEXT, B_TRUE },
    124 	{ KI_OFMARKER, "OFMarker", KT_BOOLEAN, B_FALSE },
    125 	{ KI_OFMARKERINT, "OFMarkerInt", KT_NUMERIC_RANGE, B_FALSE },
    126 	{ KI_IFMARKER, "IFMarker", KT_BOOLEAN, B_FALSE },
    127 	{ KI_IFMARKERINT, "IFMarkerInt", KT_NUMERIC_RANGE, B_FALSE },
    128 
    129 	/*
    130 	 * iSER-specific keys
    131 	 */
    132 	{ KI_RDMA_EXTENSIONS, "RDMAExtensions", KT_BOOLEAN, B_FALSE },
    133 	{ KI_TARGET_RECV_DATA_SEGMENT_LENGTH, "TargetRecvDataSegmentLength",
    134 	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_FALSE },
    135 	{ KI_INITIATOR_RECV_DATA_SEGMENT_LENGTH,
    136 	    "InitiatorRecvDataSegmentLength",
    137 	    KT_NUMERICAL /* 512 to 2^24 - 1 */, B_FALSE },
    138 	{ KI_MAX_OUTSTANDING_UNEXPECTED_PDUS, "MaxOutstandingUnexpectedPDUs",
    139 	    KT_NUMERICAL /* 2 to 2^32 - 1 | 0 */, B_TRUE },
    140 
    141 	/*
    142 	 * Table terminator. The type KT_TEXT will allow the response
    143 	 * value of "NotUnderstood".
    144 	 */
    145 	{ KI_MAX_KEY, NULL, KT_TEXT, B_TRUE } /* Terminator */
    146 };
    147 
    148 
    149 #define	TEXTBUF_CHUNKSIZE 8192
    150 
    151 typedef struct {
    152 	char	*itb_mem;
    153 	int	itb_offset;
    154 	int	itb_mem_len;
    155 } idm_textbuf_t;
    156 
    157 /*
    158  * Ignore all but the following keys during security negotiation
    159  *
    160  * SessionType
    161  * InitiatorName
    162  * TargetName
    163  * TargetAddress
    164  * InitiatorAlias
    165  * TargetAlias
    166  * TargetPortalGroupTag
    167  * AuthMethod and associated auth keys
    168  */
    169 
    170 static int idm_keyvalue_get_next(char **tb_scan, int *tb_len,
    171     char **key, int *keylen, char **value);
    172 
    173 static int idm_nvlist_add_kv(nvlist_t *nvl, const idm_kv_xlate_t *ikvx,
    174     char *value);
    175 
    176 static int idm_nvlist_add_string(nvlist_t *nvl,
    177     const idm_kv_xlate_t *ikvx, char *value);
    178 
    179 static int idm_nvlist_add_boolean(nvlist_t *nvl,
    180     const idm_kv_xlate_t *ikvx, char *value);
    181 
    182 static int idm_nvlist_add_binary(nvlist_t *nvl,
    183     const idm_kv_xlate_t *ikvx, char *value);
    184 
    185 static int idm_nvlist_add_large_numerical(nvlist_t *nvl,
    186     const idm_kv_xlate_t *ikvx, char *value);
    187 
    188 static int idm_nvlist_add_numerical(nvlist_t *nvl,
    189     const idm_kv_xlate_t *ikvx, char *value);
    190 
    191 static int idm_nvlist_add_numeric_range(nvlist_t *nvl,
    192     const idm_kv_xlate_t *ikvx, char *value);
    193 
    194 static int idm_nvlist_add_list_of_values(nvlist_t *nvl,
    195     const idm_kv_xlate_t *ikvx, char *value);
    196 
    197 static int idm_itextbuf_add_nvpair(nvpair_t *nvp, idm_textbuf_t *itb);
    198 
    199 static int idm_itextbuf_add_string(nvpair_t *nvp,
    200     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
    201 
    202 static int idm_itextbuf_add_boolean(nvpair_t *nvp,
    203     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
    204 
    205 static int idm_itextbuf_add_binary(nvpair_t *nvp,
    206     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
    207 
    208 static int idm_itextbuf_add_large_numerical(nvpair_t *nvp,
    209     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
    210 
    211 static int idm_itextbuf_add_numerical(nvpair_t *nvp,
    212     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
    213 
    214 static int idm_itextbuf_add_numeric_range(nvpair_t *nvp,
    215     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
    216 
    217 static int idm_itextbuf_add_list_of_values(nvpair_t *nvp,
    218     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb);
    219 
    220 static void textbuf_memcpy(idm_textbuf_t *itb, void *mem, int mem_len);
    221 
    222 static void textbuf_strcpy(idm_textbuf_t *itb, char *str);
    223 
    224 static void textbuf_append_char(idm_textbuf_t *itb, char c);
    225 
    226 static void textbuf_terminate_kvpair(idm_textbuf_t *itb);
    227 
    228 static int idm_ascii_to_hex(char *enc_hex_byte, uint8_t *bin_val);
    229 
    230 static int idm_base16_str_to_binary(char *hstr, int hstr_len,
    231     uint8_t *binary, int binary_length);
    232 
    233 static size_t idm_strcspn(const char *string, const char *charset);
    234 
    235 static size_t idm_strnlen(const char *str, size_t maxlen);
    236 
    237 /*
    238  * Processes all whole iSCSI name-value pairs in a text buffer and adds
    239  * a corresponding Solaris nvpair_t to the provided nvlist.  If the last
    240  * iSCSI name-value pair in textbuf is truncated (which can occur when
    241  * the request spans multiple PDU's) then upon return textbuf will
    242  * point to the truncated iSCSI name-value pair in the buffer and
    243  * textbuflen will contain the remaining bytes in the buffer.  The
    244  * caller can save off this fragment of the iSCSI name-value pair for
    245  * use when the next PDU in the request arrives.
    246  *
    247  * textbuflen includes the trailing 0x00!
    248  */
    249 
    250 int
    251 idm_textbuf_to_nvlist(nvlist_t *nvl, char **textbuf, int *textbuflen)
    252 {
    253 	int rc = 0;
    254 	char *tbscan, *key, *value;
    255 	int tblen, keylen;
    256 
    257 	tbscan = *textbuf;
    258 	tblen = *textbuflen;
    259 
    260 	for (;;) {
    261 		if ((rc = idm_keyvalue_get_next(&tbscan, &tblen,
    262 		    &key, &keylen, &value)) != 0) {
    263 			/* There was a problem reading the key/value pair */
    264 			break;
    265 		}
    266 
    267 		if ((rc = idm_nvlist_add_keyvalue(nvl,
    268 		    key, keylen, value)) != 0) {
    269 			/* Something was wrong with either the key or value */
    270 			break;
    271 		}
    272 
    273 		if (tblen == 0) {
    274 			/* End of text buffer */
    275 			break;
    276 		}
    277 	}
    278 
    279 	*textbuf = tbscan;
    280 	*textbuflen = tblen;
    281 
    282 	return (rc);
    283 }
    284 
    285 /*
    286  * If a test buffer starts with an ISCSI name-value pair fragment (a
    287  * continuation from a previous buffer) return the length of the fragment
    288  * contained in this buffer.  We do not handle name-value pairs that span
    289  * more than two buffers so if this buffer does not contain the remainder
    290  * of the name value pair the function will return 0.  If the first
    291  * name-value pair in the buffer is complete the functionw will return 0.
    292  */
    293 int
    294 idm_textbuf_to_firstfraglen(void *textbuf, int textbuflen)
    295 {
    296 	return (idm_strnlen(textbuf, textbuflen));
    297 }
    298 
    299 static int
    300 idm_keyvalue_get_next(char **tb_scan, int *tb_len,
    301     char **key, int *keylen, char **value)
    302 {
    303 	/*
    304 	 * Caller doesn't need "valuelen" returned since "value" will
    305 	 * always be a NULL-terminated string.
    306 	 */
    307 	size_t total_len, valuelen;
    308 
    309 	/*
    310 	 * How many bytes to the first '\0'?  This represents the total
    311 	 * length of our iSCSI key/value pair.
    312 	 */
    313 	total_len = idm_strnlen(*tb_scan, *tb_len);
    314 	if (total_len == *tb_len) {
    315 		/*
    316 		 * No '\0', perhaps this key/value pair is continued in
    317 		 * another buffer
    318 		 */
    319 		return (E2BIG);
    320 	}
    321 
    322 	/*
    323 	 * Found NULL, so this is a possible key-value pair.  At
    324 	 * the same time we've validated that there is actually a
    325 	 * NULL in this string so it's safe to use regular
    326 	 * string functions (i.e. strcpy instead of strncpy)
    327 	 */
    328 	*key = *tb_scan;
    329 	*keylen = idm_strcspn(*tb_scan, "=");
    330 
    331 	if (*keylen == total_len) {
    332 		/* No '=', bad format */
    333 		return (EINVAL);
    334 	}
    335 
    336 	*tb_scan += *keylen + 1; /* Skip the '=' */
    337 	*tb_len -= *keylen + 1;
    338 
    339 	/*
    340 	 * The remaining text after the '=' is the value
    341 	 */
    342 	*value = *tb_scan;
    343 	valuelen = total_len - (*keylen + 1);
    344 
    345 	*tb_scan += valuelen + 1; /* Skip the '\0' */
    346 	*tb_len -= valuelen + 1;
    347 
    348 	return (0);
    349 }
    350 
    351 const idm_kv_xlate_t *
    352 idm_lookup_kv_xlate(const char *key, int keylen)
    353 {
    354 	const idm_kv_xlate_t *ikvx = &idm_kvpair_xlate[0];
    355 
    356 	/*
    357 	 * Look for a matching key value in the key/value pair table.
    358 	 * The matching entry in the table will tell us how to encode
    359 	 * the key and value in the nvlist.  If we don't recognize
    360 	 * the key then we will simply encode it in string format.
    361 	 * The login or text request code can generate the appropriate
    362 	 * "not understood" resposne.
    363 	 */
    364 	while (ikvx->ik_key_id != KI_MAX_KEY) {
    365 		/*
    366 		 * Compare strings.  "key" is not NULL-terminated so
    367 		 * use strncmp.  Since we are using strncmp we
    368 		 * need to check that the lengths match, otherwise
    369 		 * we might unintentionally lookup "TargetAddress"
    370 		 * with a key of "Target" (or something similar).
    371 		 *
    372 		 * "value" is NULL-terminated so we can use it as
    373 		 * a regular string.
    374 		 */
    375 		if ((strncmp(ikvx->ik_key_name, key, keylen) == 0) &&
    376 		    (strlen(ikvx->ik_key_name) == keylen)) {
    377 			/* Exit the loop since we found a match */
    378 			break;
    379 		}
    380 
    381 		/* No match, look at the next entry */
    382 		ikvx++;
    383 	}
    384 
    385 	return (ikvx);
    386 }
    387 
    388 static int
    389 idm_nvlist_add_kv(nvlist_t *nvl,  const idm_kv_xlate_t *ikvx, char *value)
    390 {
    391 	int rc;
    392 
    393 	switch (ikvx->ik_idm_type) {
    394 	case KT_TEXT:
    395 	case KT_SIMPLE:
    396 	case KT_ISCSI_NAME:
    397 	case KT_ISCSI_LOCAL_NAME:
    398 		rc = idm_nvlist_add_string(nvl, ikvx, value);
    399 		break;
    400 	case KT_BOOLEAN:
    401 		rc = idm_nvlist_add_boolean(nvl, ikvx, value);
    402 		break;
    403 	case KT_REGULAR_BINARY:
    404 	case KT_LARGE_BINARY:
    405 	case KT_BINARY:
    406 		rc = idm_nvlist_add_binary(nvl, ikvx, value);
    407 		break;
    408 	case KT_LARGE_NUMERICAL:
    409 		rc = idm_nvlist_add_large_numerical(nvl, ikvx,
    410 		    value);
    411 		break;
    412 	case KT_NUMERICAL:
    413 		rc = idm_nvlist_add_numerical(nvl, ikvx,
    414 		    value);
    415 		break;
    416 	case KT_NUMERIC_RANGE:
    417 		rc = idm_nvlist_add_numeric_range(nvl, ikvx,
    418 		    value);
    419 		break;
    420 	case KT_LIST_OF_VALUES:
    421 		rc = idm_nvlist_add_list_of_values(nvl, ikvx,
    422 		    value);
    423 		break;
    424 	default:
    425 		ASSERT(0); /* This should never happen */
    426 		break;
    427 	}
    428 	if (rc != 0) {
    429 		/* could be one of the text constants */
    430 		rc = idm_nvlist_add_string(nvl, ikvx, value);
    431 	}
    432 
    433 	return (rc);
    434 }
    435 
    436 static int
    437 idm_nvlist_add_string(nvlist_t *nvl,
    438     const idm_kv_xlate_t *ikvx, char *value)
    439 {
    440 	return (nvlist_add_string(nvl, ikvx->ik_key_name, value));
    441 }
    442 
    443 static int
    444 idm_nvlist_add_boolean(nvlist_t *nvl,
    445     const idm_kv_xlate_t *ikvx, char *value)
    446 {
    447 	int rc;
    448 	boolean_t bool_val;
    449 
    450 	if (strcasecmp(value, "Yes") == 0) {
    451 		bool_val = B_TRUE;
    452 	} else if (strcasecmp(value, "No") == 0) {
    453 		bool_val = B_FALSE;
    454 	} else {
    455 		return (EINVAL);
    456 	}
    457 
    458 	rc = nvlist_add_boolean_value(nvl, ikvx->ik_key_name, bool_val);
    459 
    460 	return (rc);
    461 }
    462 
    463 static boolean_t
    464 kv_is_hex(char *value)
    465 {
    466 	return ((strncmp(value, "0x", strlen("0x")) == 0) ||
    467 	    (strncmp(value, "0X", strlen("0X")) == 0));
    468 }
    469 
    470 static boolean_t
    471 kv_is_base64(char *value)
    472 {
    473 	return ((strncmp(value, "0b", strlen("0b")) == 0) ||
    474 	    (strncmp(value, "0B", strlen("0B")) == 0));
    475 }
    476 
    477 
    478 static int
    479 idm_nvlist_add_binary(nvlist_t *nvl,
    480     const idm_kv_xlate_t *ikvx, char *value)
    481 {
    482 	int		rc;
    483 	int		value_length;
    484 	uint64_t	uint64_value;
    485 	int		binary_length;
    486 	uchar_t		*binary_array;
    487 
    488 	/*
    489 	 * A binary value can be either decimal, hex or base64.  If it's
    490 	 * decimal then the encoded string must be less than 64 bits in
    491 	 * length (8 characters).  In all cases we will convert the
    492 	 * included value to a byte array starting with the MSB.  The
    493 	 * assumption is that values meant to be treated as integers will
    494 	 * use the "numerical" and "large numerical" types.
    495 	 */
    496 	if (kv_is_hex(value)) {
    497 		value += strlen("0x");
    498 		value_length = strlen(value);
    499 		binary_length = (value_length + 1) / 2;
    500 		binary_array = kmem_alloc(binary_length, KM_SLEEP);
    501 
    502 		if (idm_base16_str_to_binary(value, value_length,
    503 		    binary_array, binary_length) != 0) {
    504 			kmem_free(binary_array, binary_length);
    505 			return (EINVAL);
    506 		}
    507 
    508 		rc = nvlist_add_byte_array(nvl, ikvx->ik_key_name,
    509 		    binary_array, binary_length);
    510 
    511 		kmem_free(binary_array, binary_length);
    512 
    513 		return (rc);
    514 
    515 	} else if (kv_is_base64(value)) {
    516 		value += strlen("0b");
    517 		value_length = strlen(value);
    518 		binary_array = kmem_alloc(value_length, KM_NOSLEEP);
    519 		if (binary_array == NULL) {
    520 			return (ENOMEM);
    521 		}
    522 
    523 		if (iscsi_base64_str_to_binary(value, value_length,
    524 		    binary_array, value_length, &binary_length) != 0) {
    525 			kmem_free(binary_array, value_length);
    526 			return (EINVAL);
    527 		}
    528 
    529 		rc = nvlist_add_byte_array(nvl, ikvx->ik_key_name,
    530 		    binary_array, binary_length);
    531 
    532 		kmem_free(binary_array, value_length);
    533 
    534 		return (rc);
    535 	} else {
    536 		/*
    537 		 * Decimal value (not permitted for "large-binary_value" so
    538 		 * it must be smaller than 64 bits.  It's not really
    539 		 * clear from the RFC what a decimal-binary-value might
    540 		 * represent but presumably it should be treated the same
    541 		 * as a hex or base64 value.  Therefore we'll convert it
    542 		 * to an array of bytes.
    543 		 */
    544 		if ((rc = ddi_strtoull(value, NULL, 0,
    545 		    (u_longlong_t *)&uint64_value)) != 0)
    546 			return (rc);
    547 
    548 		rc = nvlist_add_byte_array(nvl, ikvx->ik_key_name,
    549 		    (uint8_t *)&uint64_value, sizeof (uint64_value));
    550 
    551 		return (rc);
    552 	}
    553 
    554 	/* NOTREACHED */
    555 }
    556 
    557 
    558 static int
    559 idm_nvlist_add_large_numerical(nvlist_t *nvl,
    560     const idm_kv_xlate_t *ikvx, char *value)
    561 {
    562 	/*
    563 	 * A "large numerical" value can be larger than 64-bits.  Since
    564 	 * there is no upper bound on the size of the value, we will
    565 	 * punt and store it in string form.  We could also potentially
    566 	 * treat the value as binary data.
    567 	 */
    568 	return (nvlist_add_string(nvl, ikvx->ik_key_name, value));
    569 }
    570 
    571 
    572 static int
    573 idm_nvlist_add_numerical(nvlist_t *nvl,
    574     const idm_kv_xlate_t *ikvx, char *value)
    575 {
    576 	int rc;
    577 	uint64_t uint64_value;
    578 
    579 	/*
    580 	 * "Numerical" values in the iSCSI standard are up to 64-bits wide.
    581 	 * On a 32-bit system we could see an overflow here during conversion.
    582 	 * This shouldn't happen with real-world values for the current
    583 	 * iSCSI parameters of "numerical" type.
    584 	 */
    585 	rc = ddi_strtoull(value, NULL, 0, (u_longlong_t *)&uint64_value);
    586 	if (rc == 0) {
    587 		rc = nvlist_add_uint64(nvl, ikvx->ik_key_name, uint64_value);
    588 	}
    589 
    590 	return (rc);
    591 }
    592 
    593 
    594 static int
    595 idm_nvlist_add_numeric_range(nvlist_t *nvl,
    596     const idm_kv_xlate_t *ikvx, char *range)
    597 {
    598 	nvlist_t *range_nvl;
    599 	char *val_scan = range;
    600 	uint64_t start_val, end_val;
    601 	int val_len, range_len;
    602 	int rc;
    603 
    604 	/* We'll store the range an an nvlist with two values */
    605 	rc = nvlist_alloc(&range_nvl, NV_UNIQUE_NAME, KM_NOSLEEP);
    606 	if (rc != 0) {
    607 		return (rc);
    608 	}
    609 
    610 	/*
    611 	 * We expect idm_keyvalue_get_next to ensure the string is
    612 	 * terminated
    613 	 */
    614 	range_len = strlen(range);
    615 
    616 	/*
    617 	 * Find range separator
    618 	 */
    619 	val_len = idm_strcspn(val_scan, "~");
    620 
    621 	if (val_len == range_len) {
    622 		/* invalid range */
    623 		nvlist_free(range_nvl);
    624 		return (EINVAL);
    625 	}
    626 
    627 	/*
    628 	 * Start value
    629 	 */
    630 	*(val_scan + val_len + 1) = '\0';
    631 	rc = ddi_strtoull(val_scan, NULL, 0, (u_longlong_t *)&start_val);
    632 	if (rc == 0) {
    633 		rc = nvlist_add_uint64(range_nvl, "start", start_val);
    634 	}
    635 	if (rc != 0) {
    636 		nvlist_free(range_nvl);
    637 		return (rc);
    638 	}
    639 
    640 	/*
    641 	 * End value
    642 	 */
    643 	val_scan += val_len + 1;
    644 	rc = ddi_strtoull(val_scan, NULL, 0, (u_longlong_t *)&end_val);
    645 	if (rc == 0) {
    646 		rc = nvlist_add_uint64(range_nvl, "start", end_val);
    647 	}
    648 	if (rc != 0) {
    649 		nvlist_free(range_nvl);
    650 		return (rc);
    651 	}
    652 
    653 	/*
    654 	 * Now add the "range" nvlist to the main nvlist
    655 	 */
    656 	rc = nvlist_add_nvlist(nvl, ikvx->ik_key_name, range_nvl);
    657 	if (rc != 0) {
    658 		nvlist_free(range_nvl);
    659 		return (rc);
    660 	}
    661 
    662 	nvlist_free(range_nvl);
    663 	return (0);
    664 }
    665 
    666 
    667 static int
    668 idm_nvlist_add_list_of_values(nvlist_t *nvl,
    669     const idm_kv_xlate_t *ikvx, char *value_list)
    670 {
    671 	char value_name[8];
    672 	nvlist_t *value_list_nvl;
    673 	char *val_scan = value_list;
    674 	int value_index = 0;
    675 	int val_len, val_list_len;
    676 	int rc;
    677 
    678 	rc = nvlist_alloc(&value_list_nvl, NV_UNIQUE_NAME, KM_NOSLEEP);
    679 	if (rc != 0) {
    680 		return (rc);
    681 	}
    682 
    683 	/*
    684 	 * We expect idm_keyvalue_get_next to ensure the string is
    685 	 * terminated
    686 	 */
    687 	val_list_len = strlen(value_list);
    688 	if (val_list_len == 0) {
    689 		nvlist_free(value_list_nvl);
    690 		return (EINVAL);
    691 	}
    692 
    693 	for (;;) {
    694 		(void) snprintf(value_name, 8, "value%d", value_index);
    695 
    696 		val_len = idm_strcspn(val_scan, ",");
    697 
    698 		if (*(val_scan + val_len) != '\0') {
    699 			*(val_scan + val_len) = '\0';
    700 		}
    701 		rc = nvlist_add_string(value_list_nvl, value_name, val_scan);
    702 		if (rc != 0) {
    703 			nvlist_free(value_list_nvl);
    704 			return (rc);
    705 		}
    706 
    707 		/*
    708 		 * Move to next value, see if we're at the end of the value
    709 		 * list
    710 		 */
    711 		val_scan += val_len + 1;
    712 		if (val_scan == value_list + val_list_len + 1) {
    713 			break;
    714 		}
    715 
    716 		value_index++;
    717 	}
    718 
    719 	rc = nvlist_add_nvlist(nvl, ikvx->ik_key_name, value_list_nvl);
    720 	if (rc != 0) {
    721 		nvlist_free(value_list_nvl);
    722 		return (rc);
    723 	}
    724 
    725 	nvlist_free(value_list_nvl);
    726 	return (0);
    727 }
    728 
    729 /*
    730  * Convert an nvlist containing standard iSCSI key names and values into
    731  * a text buffer with properly formatted iSCSI key-value pairs ready to
    732  * transmit on the wire.  *textbuf should be NULL and will be set to point
    733  * the resulting text buffer.
    734  */
    735 
    736 int
    737 idm_nvlist_to_textbuf(nvlist_t *nvl, char **textbuf, int *textbuflen,
    738 	int *validlen)
    739 {
    740 	int rc = 0;
    741 	nvpair_t *nvp = NULL;
    742 	idm_textbuf_t itb;
    743 
    744 	bzero(&itb, sizeof (itb));
    745 
    746 	for (;;) {
    747 		nvp = nvlist_next_nvpair(nvl, nvp);
    748 
    749 		if (nvp == NULL) {
    750 			/* Last nvpair in nvlist, we're done */
    751 			break;
    752 		}
    753 
    754 		if ((rc = idm_itextbuf_add_nvpair(nvp, &itb)) != 0) {
    755 			/* There was a problem building the key/value pair */
    756 			break;
    757 		}
    758 	}
    759 
    760 	*textbuf = itb.itb_mem;
    761 	*textbuflen = itb.itb_mem_len;
    762 	*validlen = itb.itb_offset;
    763 
    764 	return (rc);
    765 }
    766 
    767 static int
    768 idm_itextbuf_add_nvpair(nvpair_t *nvp,
    769     idm_textbuf_t *itb)
    770 {
    771 	int rc = 0;
    772 	char *key;
    773 	const idm_kv_xlate_t *ikvx;
    774 
    775 	key = nvpair_name(nvp);
    776 
    777 	ikvx = idm_lookup_kv_xlate(key, strlen(key));
    778 
    779 	/*
    780 	 * Any key supplied by the initiator that is not in our table
    781 	 * will be responded to with the string value "NotUnderstood".
    782 	 * An example is a vendor specific key.
    783 	 */
    784 	ASSERT((ikvx->ik_key_id != KI_MAX_KEY) ||
    785 	    (nvpair_type(nvp) == DATA_TYPE_STRING));
    786 
    787 	/*
    788 	 * Look for a matching key value in the key/value pair table.
    789 	 * The matching entry in the table will tell us how to encode
    790 	 * the key and value in the nvlist.
    791 	 */
    792 	switch (ikvx->ik_idm_type) {
    793 	case KT_TEXT:
    794 	case KT_SIMPLE:
    795 	case KT_ISCSI_NAME:
    796 	case KT_ISCSI_LOCAL_NAME:
    797 		rc = idm_itextbuf_add_string(nvp, ikvx, itb);
    798 		break;
    799 	case KT_BOOLEAN:
    800 		rc = idm_itextbuf_add_boolean(nvp, ikvx, itb);
    801 		break;
    802 	case KT_REGULAR_BINARY:
    803 	case KT_LARGE_BINARY:
    804 	case KT_BINARY:
    805 		rc = idm_itextbuf_add_binary(nvp, ikvx, itb);
    806 		break;
    807 	case KT_LARGE_NUMERICAL:
    808 		rc = idm_itextbuf_add_large_numerical(nvp, ikvx, itb);
    809 		break;
    810 	case KT_NUMERICAL:
    811 		rc = idm_itextbuf_add_numerical(nvp, ikvx, itb);
    812 		break;
    813 	case KT_NUMERIC_RANGE:
    814 		rc = idm_itextbuf_add_numeric_range(nvp, ikvx, itb);
    815 		break;
    816 	case KT_LIST_OF_VALUES:
    817 		rc = idm_itextbuf_add_list_of_values(nvp, ikvx, itb);
    818 		break;
    819 	default:
    820 		ASSERT(0); /* This should never happen */
    821 		break;
    822 	}
    823 
    824 	return (rc);
    825 }
    826 
    827 /* ARGSUSED */
    828 static int
    829 idm_itextbuf_add_string(nvpair_t *nvp,
    830     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
    831 {
    832 	char	*key_name;
    833 	char	*value;
    834 	int	rc;
    835 
    836 	/* Start with the key name */
    837 	key_name = nvpair_name(nvp);
    838 	textbuf_strcpy(itb, key_name);
    839 
    840 	/* Add separator */
    841 	textbuf_append_char(itb, '=');
    842 
    843 	/* Add value */
    844 	rc = nvpair_value_string(nvp, &value);
    845 	ASSERT(rc == 0);
    846 	textbuf_strcpy(itb, value);
    847 
    848 	/* Add trailing 0x00 */
    849 	textbuf_terminate_kvpair(itb);
    850 
    851 	return (0);
    852 }
    853 
    854 
    855 /* ARGSUSED */
    856 static int
    857 idm_itextbuf_add_boolean(nvpair_t *nvp,
    858     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
    859 {
    860 	char		*key_name;
    861 	boolean_t	value;
    862 	int	rc;
    863 
    864 	/* Start with the key name */
    865 	key_name = nvpair_name(nvp);
    866 	textbuf_strcpy(itb, key_name);
    867 
    868 	/* Add separator */
    869 	textbuf_append_char(itb, '=');
    870 
    871 	/* Add value */
    872 	rc = nvpair_value_boolean_value(nvp, &value);
    873 	ASSERT(rc == 0);
    874 	textbuf_strcpy(itb, value ? "Yes" : "No");
    875 
    876 	/* Add trailing 0x00 */
    877 	textbuf_terminate_kvpair(itb);
    878 
    879 	return (0);
    880 }
    881 
    882 /* ARGSUSED */
    883 static int
    884 idm_itextbuf_add_binary(nvpair_t *nvp,
    885     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
    886 {
    887 	char		*key_name;
    888 	unsigned char	*value;
    889 	unsigned int	len;
    890 	unsigned long	n;
    891 	int	rc;
    892 
    893 	/* Start with the key name */
    894 	key_name = nvpair_name(nvp);
    895 	textbuf_strcpy(itb, key_name);
    896 
    897 	/* Add separator */
    898 	textbuf_append_char(itb, '=');
    899 
    900 	/* Add value */
    901 	rc = nvpair_value_byte_array(nvp, &value, &len);
    902 	ASSERT(rc == 0);
    903 
    904 	textbuf_strcpy(itb, "0x");
    905 
    906 	while (len > 0) {
    907 		n = *value++;
    908 		len--;
    909 
    910 		textbuf_append_char(itb, idm_hex_to_ascii[(n >> 4) & 0xf]);
    911 		textbuf_append_char(itb, idm_hex_to_ascii[n & 0xf]);
    912 	}
    913 
    914 	/* Add trailing 0x00 */
    915 	textbuf_terminate_kvpair(itb);
    916 
    917 	return (0);
    918 }
    919 
    920 /* ARGSUSED */
    921 static int
    922 idm_itextbuf_add_large_numerical(nvpair_t *nvp,
    923     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
    924 {
    925 	ASSERT(0);
    926 	return (0);
    927 }
    928 
    929 /* ARGSUSED */
    930 static int
    931 idm_itextbuf_add_numerical(nvpair_t *nvp,
    932     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
    933 {
    934 	char		*key_name;
    935 	uint64_t	value;
    936 	int	rc;
    937 	char		str[16];
    938 
    939 	/* Start with the key name */
    940 	key_name = nvpair_name(nvp);
    941 	textbuf_strcpy(itb, key_name);
    942 
    943 	/* Add separator */
    944 	textbuf_append_char(itb, '=');
    945 
    946 	/* Add value */
    947 	rc = nvpair_value_uint64(nvp, &value);
    948 	ASSERT(rc == 0);
    949 	(void) sprintf(str, "%llu", (u_longlong_t)value);
    950 	textbuf_strcpy(itb, str);
    951 
    952 	/* Add trailing 0x00 */
    953 	textbuf_terminate_kvpair(itb);
    954 
    955 	return (0);
    956 }
    957 
    958 /* ARGSUSED */
    959 static int
    960 idm_itextbuf_add_numeric_range(nvpair_t *nvp,
    961     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
    962 {
    963 	ASSERT(0);
    964 	return (0);
    965 }
    966 
    967 /* ARGSUSED */
    968 static int
    969 idm_itextbuf_add_list_of_values(nvpair_t *nvp,
    970     const idm_kv_xlate_t *ikvx, idm_textbuf_t *itb)
    971 {
    972 	char		*key_name;
    973 	nvpair_t	*vchoice = NULL;
    974 	char		*vchoice_string = NULL;
    975 	int		rc;
    976 
    977 	/* Start with the key name */
    978 	key_name = nvpair_name(nvp);
    979 	textbuf_strcpy(itb, key_name);
    980 
    981 	/* Add separator */
    982 	textbuf_append_char(itb, '=');
    983 
    984 	/* Add value choices */
    985 	vchoice = idm_get_next_listvalue(nvp, NULL);
    986 	while (vchoice != NULL) {
    987 		rc = nvpair_value_string(vchoice, &vchoice_string);
    988 		ASSERT(rc == 0);
    989 		textbuf_strcpy(itb, vchoice_string);
    990 		vchoice = idm_get_next_listvalue(nvp, vchoice);
    991 		if (vchoice != NULL) {
    992 			/* Add ',' between choices */
    993 			textbuf_append_char(itb, ',');
    994 		}
    995 	}
    996 
    997 	/* Add trailing 0x00 */
    998 	textbuf_terminate_kvpair(itb);
    999 
   1000 	return (0);
   1001 }
   1002 
   1003 
   1004 static void
   1005 textbuf_makeroom(idm_textbuf_t *itb, int size)
   1006 {
   1007 	char	*new_mem;
   1008 	int	new_mem_len;
   1009 
   1010 	if (itb->itb_mem == NULL) {
   1011 		itb->itb_mem_len = MAX(TEXTBUF_CHUNKSIZE, size);
   1012 		itb->itb_mem = kmem_alloc(itb->itb_mem_len, KM_SLEEP);
   1013 	} else if ((itb->itb_offset + size) > itb->itb_mem_len) {
   1014 		new_mem_len = itb->itb_mem_len + MAX(TEXTBUF_CHUNKSIZE, size);
   1015 		new_mem = kmem_alloc(new_mem_len, KM_SLEEP);
   1016 		bcopy(itb->itb_mem, new_mem, itb->itb_mem_len);
   1017 		kmem_free(itb->itb_mem, itb->itb_mem_len);
   1018 		itb->itb_mem = new_mem;
   1019 		itb->itb_mem_len = new_mem_len;
   1020 	}
   1021 }
   1022 
   1023 static void
   1024 textbuf_memcpy(idm_textbuf_t *itb, void *mem, int mem_len)
   1025 {
   1026 	textbuf_makeroom(itb, mem_len);
   1027 	(void) memcpy(itb->itb_mem + itb->itb_offset, mem, mem_len);
   1028 	itb->itb_offset += mem_len;
   1029 }
   1030 
   1031 static void
   1032 textbuf_strcpy(idm_textbuf_t *itb, char *str)
   1033 {
   1034 	textbuf_memcpy(itb, str, strlen(str));
   1035 }
   1036 
   1037 static void
   1038 textbuf_append_char(idm_textbuf_t *itb, char c)
   1039 {
   1040 	textbuf_makeroom(itb, sizeof (char));
   1041 	*(itb->itb_mem + itb->itb_offset) = c;
   1042 	itb->itb_offset++;
   1043 }
   1044 
   1045 static void
   1046 textbuf_terminate_kvpair(idm_textbuf_t *itb)
   1047 {
   1048 	textbuf_append_char(itb, '\0');
   1049 }
   1050 
   1051 static int
   1052 idm_ascii_to_hex(char *enc_hex_byte, uint8_t *bin_val)
   1053 {
   1054 	uint8_t nibble1, nibble2;
   1055 	char enc_char = *enc_hex_byte;
   1056 
   1057 	if (enc_char >= '0' && enc_char <= '9') {
   1058 		nibble1 = (enc_char - '0');
   1059 	} else if (enc_char >= 'A' && enc_char <= 'F') {
   1060 		nibble1 = (0xA + (enc_char - 'A'));
   1061 	} else if (enc_char >= 'a' && enc_char <= 'f') {
   1062 		nibble1 = (0xA + (enc_char - 'a'));
   1063 	} else {
   1064 		return (EINVAL);
   1065 	}
   1066 
   1067 	enc_hex_byte++;
   1068 	enc_char = *enc_hex_byte;
   1069 
   1070 	if (enc_char >= '0' && enc_char <= '9') {
   1071 		nibble2 = (enc_char - '0');
   1072 	} else if (enc_char >= 'A' && enc_char <= 'F') {
   1073 		nibble2 = (0xA + (enc_char - 'A'));
   1074 	} else if (enc_char >= 'a' && enc_char <= 'f') {
   1075 		nibble2 = (0xA + (enc_char - 'a'));
   1076 	} else {
   1077 		return (EINVAL);
   1078 	}
   1079 
   1080 	*bin_val = (nibble1 << 4) | nibble2;
   1081 
   1082 	return (0);
   1083 }
   1084 
   1085 
   1086 static int idm_base16_str_to_binary(char *hstr, int hstr_len,
   1087     uint8_t *binary_array, int binary_length)
   1088 {
   1089 	char	tmpstr[2];
   1090 	uchar_t *binary_scan;
   1091 
   1092 	binary_scan = binary_array;
   1093 
   1094 	/*
   1095 	 * If the length of the encoded ascii hex value is a multiple
   1096 	 * of two then every two ascii characters correspond to a hex
   1097 	 * byte.  If the length of the value is not a multiple of two
   1098 	 * then the first character is the first hex byte and then for
   1099 	 * the remaining of the string every two ascii characters
   1100 	 * correspond to a hex byte
   1101 	 */
   1102 	if ((hstr_len % 2) != 0) {
   1103 
   1104 		tmpstr[0] = '0';
   1105 		tmpstr[1] = *hstr;
   1106 
   1107 		if (idm_ascii_to_hex(tmpstr, binary_scan) != 0) {
   1108 			return (EINVAL);
   1109 		}
   1110 
   1111 		hstr++;
   1112 		binary_scan++;
   1113 	}
   1114 
   1115 	while (binary_scan != binary_array + binary_length) {
   1116 		if (idm_ascii_to_hex(hstr, binary_scan) != 0) {
   1117 			return (EINVAL);
   1118 		}
   1119 
   1120 		hstr += 2;
   1121 		binary_scan++;
   1122 	}
   1123 
   1124 	return (0);
   1125 }
   1126 
   1127 static size_t
   1128 idm_strnlen(const char *str, size_t maxlen)
   1129 {
   1130 	const char *ptr;
   1131 
   1132 	ptr = memchr(str, 0, maxlen);
   1133 	if (ptr == NULL)
   1134 		return (maxlen);
   1135 
   1136 	return ((uintptr_t)ptr - (uintptr_t)str);
   1137 }
   1138 
   1139 
   1140 size_t
   1141 idm_strcspn(const char *string, const char *charset)
   1142 {
   1143 	const char *p, *q;
   1144 
   1145 	for (q = string; *q != '\0'; ++q) {
   1146 		for (p = charset; *p != '\0' && *p != *q; )
   1147 			p++;
   1148 		if (*p != '\0') {
   1149 			break;
   1150 		}
   1151 	}
   1152 	return ((uintptr_t)q - (uintptr_t)string);
   1153 }
   1154 
   1155 /*
   1156  * We allow a list of choices to be represented as a single nvpair
   1157  * (list with one value choice), or as an nvlist with a single nvpair
   1158  * (also a list with on value choice), or as an nvlist with multiple
   1159  * nvpairs (a list with multiple value choices).  This function implements
   1160  * the "get next" functionality regardless of the choice list structure.
   1161  *
   1162  * nvpair_t's that contain choices are always strings.
   1163  */
   1164 nvpair_t *
   1165 idm_get_next_listvalue(nvpair_t *value_list, nvpair_t *curr_nvp)
   1166 {
   1167 	nvpair_t	*result;
   1168 	nvlist_t	*nvl;
   1169 	int		nvrc;
   1170 	data_type_t	nvp_type;
   1171 
   1172 	nvp_type = nvpair_type(value_list);
   1173 
   1174 	switch (nvp_type) {
   1175 	case DATA_TYPE_NVLIST:
   1176 		nvrc = nvpair_value_nvlist(value_list, &nvl);
   1177 		ASSERT(nvrc == 0);
   1178 		result = nvlist_next_nvpair(nvl, curr_nvp);
   1179 		break;
   1180 	case DATA_TYPE_STRING:
   1181 		/* Single choice */
   1182 		if (curr_nvp == NULL) {
   1183 			result = value_list;
   1184 		} else {
   1185 			result = NULL;
   1186 		}
   1187 		break;
   1188 	default:
   1189 		ASSERT(0); /* Malformed choice list */
   1190 		result = NULL;
   1191 		break;
   1192 	}
   1193 
   1194 	return (result);
   1195 }
   1196 
   1197 kv_status_t
   1198 idm_nvstat_to_kvstat(int nvrc)
   1199 {
   1200 	kv_status_t result;
   1201 	switch (nvrc) {
   1202 	case 0:
   1203 		result = KV_HANDLED;
   1204 		break;
   1205 	case ENOMEM:
   1206 		result = KV_NO_RESOURCES;
   1207 		break;
   1208 	case EINVAL:
   1209 		result = KV_VALUE_ERROR;
   1210 		break;
   1211 	case EFAULT:
   1212 	case ENOTSUP:
   1213 	default:
   1214 		result = KV_INTERNAL_ERROR;
   1215 		break;
   1216 	}
   1217 
   1218 	return (result);
   1219 }
   1220 
   1221 void
   1222 idm_kvstat_to_error(kv_status_t kvrc, uint8_t *class, uint8_t *detail)
   1223 {
   1224 	switch (kvrc) {
   1225 	case KV_HANDLED:
   1226 	case KV_HANDLED_NO_TRANSIT:
   1227 		*class = ISCSI_STATUS_CLASS_SUCCESS;
   1228 		*detail = ISCSI_LOGIN_STATUS_ACCEPT;
   1229 		break;
   1230 	case KV_UNHANDLED:
   1231 	case KV_TARGET_ONLY:
   1232 		/* protocol error */
   1233 		*class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
   1234 		*detail = ISCSI_LOGIN_STATUS_INVALID_REQUEST;
   1235 		break;
   1236 	case KV_VALUE_ERROR:
   1237 		/* invalid value */
   1238 		*class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
   1239 		*detail = ISCSI_LOGIN_STATUS_INIT_ERR;
   1240 		break;
   1241 	case KV_NO_RESOURCES:
   1242 		/* no memory */
   1243 		*class = ISCSI_STATUS_CLASS_TARGET_ERR;
   1244 		*detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
   1245 		break;
   1246 	case KV_MISSING_FIELDS:
   1247 		/* key/value pair(s) missing */
   1248 		*class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
   1249 		*detail = ISCSI_LOGIN_STATUS_MISSING_FIELDS;
   1250 		break;
   1251 	case KV_AUTH_FAILED:
   1252 		/* authentication failed */
   1253 		*class = ISCSI_STATUS_CLASS_INITIATOR_ERR;
   1254 		*detail = ISCSI_LOGIN_STATUS_AUTH_FAILED;
   1255 		break;
   1256 	default:
   1257 		/* target error */
   1258 		*class = ISCSI_STATUS_CLASS_TARGET_ERR;
   1259 		*detail = ISCSI_LOGIN_STATUS_TARGET_ERROR;
   1260 		break;
   1261 	}
   1262 }
   1263 
   1264 int
   1265 idm_nvlist_add_keyvalue(nvlist_t *nvl,
   1266     char *key, int keylen, char *value)
   1267 {
   1268 	const idm_kv_xlate_t *ikvx;
   1269 
   1270 	ikvx = idm_lookup_kv_xlate(key, keylen);
   1271 
   1272 	if (ikvx->ik_key_id == KI_MAX_KEY) {
   1273 		char *nkey;
   1274 		int rc;
   1275 		size_t len;
   1276 
   1277 		/*
   1278 		 * key is not a NULL terminated string, so create one
   1279 		 */
   1280 		len = (size_t)(keylen+1);
   1281 		nkey = kmem_zalloc(len, KM_SLEEP);
   1282 		(void) strncpy(nkey, key, len-1);
   1283 		rc = nvlist_add_string(nvl, nkey, value);
   1284 		kmem_free(nkey, len);
   1285 		return (rc);
   1286 	}
   1287 
   1288 	return (idm_nvlist_add_kv(nvl, ikvx, value));
   1289 }
   1290 
   1291 int
   1292 idm_nvlist_add_id(nvlist_t *nvl, iscsikey_id_t kv_id, char *value)
   1293 {
   1294 	int i;
   1295 	for (i = 0; i < KI_MAX_KEY; i++) {
   1296 		if (idm_kvpair_xlate[i].ik_key_id == kv_id) {
   1297 			return
   1298 			    (idm_nvlist_add_kv(nvl,
   1299 			    &idm_kvpair_xlate[i], value));
   1300 		}
   1301 	}
   1302 	return (EFAULT);
   1303 }
   1304 
   1305 char *
   1306 idm_id_to_name(iscsikey_id_t kv_id)
   1307 {
   1308 	int i;
   1309 	for (i = 0; i < KI_MAX_KEY; i++) {
   1310 		if (idm_kvpair_xlate[i].ik_key_id == kv_id) {
   1311 			return (idm_kvpair_xlate[i].ik_key_name);
   1312 		}
   1313 	}
   1314 	return (NULL);
   1315 }
   1316 
   1317 /*
   1318  * return the value in a buffer that must be freed by the caller
   1319  */
   1320 char *
   1321 idm_nvpair_value_to_textbuf(nvpair_t *nvp)
   1322 {
   1323 	int rv, len;
   1324 	idm_textbuf_t itb;
   1325 	char *str;
   1326 
   1327 	bzero(&itb, sizeof (itb));
   1328 	rv = idm_itextbuf_add_nvpair(nvp, &itb);
   1329 	if (rv != 0)
   1330 		return (NULL);
   1331 	str = kmem_alloc(itb.itb_mem_len, KM_SLEEP);
   1332 	len = idm_strcspn(itb.itb_mem, "=");
   1333 	if (len > strlen(itb.itb_mem)) {
   1334 		kmem_free(itb.itb_mem, itb.itb_mem_len);
   1335 		return (NULL);
   1336 	}
   1337 	(void) strcpy(str, &itb.itb_mem[len+1]);
   1338 	/* free the allocation done in idm_textbuf_add_nvpair */
   1339 	kmem_free(itb.itb_mem, itb.itb_mem_len);
   1340 	return (str);
   1341 }
   1342 
   1343 /*
   1344  * build an iscsi text buffer - the memory gets freed in
   1345  * idm_itextbuf_free
   1346  */
   1347 void *
   1348 idm_nvlist_to_itextbuf(nvlist_t *nvl)
   1349 {
   1350 	idm_textbuf_t *itb;
   1351 	char		*textbuf;
   1352 	int		validlen, textbuflen;
   1353 
   1354 	if (idm_nvlist_to_textbuf(nvl, &textbuf, &textbuflen,
   1355 	    &validlen) != IDM_STATUS_SUCCESS) {
   1356 		return (NULL);
   1357 	}
   1358 	itb = kmem_zalloc(sizeof (idm_textbuf_t), KM_SLEEP);
   1359 	ASSERT(itb != NULL);
   1360 	itb->itb_mem = textbuf;
   1361 	itb->itb_mem_len = textbuflen;
   1362 	itb->itb_offset = validlen;
   1363 	return ((void *)itb);
   1364 }
   1365 
   1366 /*
   1367  * Copy as much of the text buffer as will fit in the pdu.
   1368  * The first call to this routine should send
   1369  * a NULL bufptr. Subsequent calls send in the buffer returned.
   1370  * Call this routine until the string returned is NULL
   1371  */
   1372 char *
   1373 idm_pdu_init_text_data(idm_pdu_t *pdu, void *arg,
   1374     int max_xfer_len, char *bufptr, int *transit)
   1375 {
   1376 	char		*start_ptr, *end_ptr, *ptr;
   1377 	idm_textbuf_t	*itb = arg;
   1378 	iscsi_hdr_t	*ihp = pdu->isp_hdr;
   1379 	int		send = 0;
   1380 
   1381 	ASSERT(itb != NULL);
   1382 	ASSERT(pdu != NULL);
   1383 	ASSERT(transit != NULL);
   1384 	if (bufptr == NULL) {
   1385 		/* first call - check the length */
   1386 		if (itb->itb_offset <= max_xfer_len) {
   1387 			/*
   1388 			 * the entire text buffer fits in the pdu
   1389 			 */
   1390 			bcopy((uint8_t *)itb->itb_mem, pdu->isp_data,
   1391 			    (size_t)itb->itb_offset);
   1392 			pdu->isp_datalen = itb->itb_offset;
   1393 			ihp->flags &= ~ISCSI_FLAG_TEXT_CONTINUE;
   1394 			*transit = 1;
   1395 			return (NULL);
   1396 		}
   1397 		/* we have more data than will fit in one pdu */
   1398 		start_ptr = itb->itb_mem;
   1399 		end_ptr = &itb->itb_mem[max_xfer_len - 1];
   1400 
   1401 	} else {
   1402 		uint_t len;
   1403 
   1404 		len =  (uintptr_t)&itb->itb_mem[itb->itb_offset] -
   1405 		    (uintptr_t)bufptr;
   1406 		if (len <= max_xfer_len) {
   1407 			/*
   1408 			 * the remaining text fits in the pdu
   1409 			 */
   1410 			bcopy(bufptr, pdu->isp_data, (size_t)len);
   1411 			pdu->isp_datalen = len;
   1412 			ihp->flags &= ~ISCSI_FLAG_TEXT_CONTINUE;
   1413 			*transit = 1;
   1414 			return (NULL);
   1415 		}
   1416 		/* we still have more data then will fit in one pdu */
   1417 		start_ptr = bufptr;
   1418 		end_ptr = &bufptr[max_xfer_len - 1];
   1419 	}
   1420 	/* break after key, after =, after the value or after '\0' */
   1421 	ptr = end_ptr;
   1422 	if (end_ptr + 1 <= &itb->itb_mem[itb->itb_offset]) {
   1423 		/* if next char is an '=' or '\0' send it */
   1424 		if (*(end_ptr + 1) == '=' || *(end_ptr + 1) == '\0') {
   1425 			send = 1;
   1426 		}
   1427 	}
   1428 	if (!send) {
   1429 		while (*ptr != '\0' && *ptr != '=' && ptr != start_ptr) {
   1430 			ptr--;
   1431 		}
   1432 	}
   1433 	bcopy(start_ptr, pdu->isp_data,
   1434 	    ((uintptr_t)ptr - (uintptr_t)start_ptr) + 1);
   1435 	pdu->isp_datalen = ((uintptr_t)ptr - (uintptr_t)start_ptr) + 1;
   1436 	ihp->flags |= ISCSI_FLAG_TEXT_CONTINUE;
   1437 	*transit = 0;
   1438 	return (++ptr);
   1439 }
   1440 
   1441 void
   1442 idm_itextbuf_free(void *arg)
   1443 {
   1444 	idm_textbuf_t	*itb = arg;
   1445 	ASSERT(itb != NULL);
   1446 	kmem_free(itb->itb_mem, itb->itb_mem_len);
   1447 	kmem_free(itb, sizeof (idm_textbuf_t));
   1448 }
   1449 
   1450 /*
   1451  * Allocate an nvlist and poputlate with key=value from the pdu list.
   1452  * NOTE: caller must free the list
   1453  */
   1454 idm_status_t
   1455 idm_pdu_list_to_nvlist(list_t *pdu_list, nvlist_t **nvlist,
   1456     uint8_t *error_detail)
   1457 {
   1458 	idm_pdu_t		*pdu, *next_pdu;
   1459 	boolean_t		split_kv = B_FALSE;
   1460 	char			*textbuf, *leftover_textbuf = NULL;
   1461 	int			textbuflen, leftover_textbuflen = 0;
   1462 	char			*split_kvbuf;
   1463 	int			split_kvbuflen, cont_fraglen;
   1464 	iscsi_login_hdr_t	*lh;
   1465 	int			rc;
   1466 	int			ret = IDM_STATUS_SUCCESS;
   1467 
   1468 	*error_detail = ISCSI_LOGIN_STATUS_ACCEPT;
   1469 	/* Allocate a new nvlist for request key/value pairs */
   1470 	rc = nvlist_alloc(nvlist, NV_UNIQUE_NAME,
   1471 	    KM_NOSLEEP);
   1472 	if (rc != 0) {
   1473 		*error_detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
   1474 		ret = IDM_STATUS_FAIL;
   1475 		goto cleanup;
   1476 	}
   1477 
   1478 	/*
   1479 	 * A login request can be split across multiple PDU's.  The state
   1480 	 * machine has collected all the PDU's that make up this login request
   1481 	 * and assembled them on the "icl_pdu_list" queue.  Process each PDU
   1482 	 * and convert the text keywords to nvlist form.
   1483 	 */
   1484 	pdu = list_head(pdu_list);
   1485 	while (pdu != NULL) {
   1486 		next_pdu = list_next(pdu_list, pdu);
   1487 
   1488 		lh = (iscsi_login_hdr_t *)pdu->isp_hdr;
   1489 
   1490 		textbuf = (char *)pdu->isp_data;
   1491 		textbuflen = pdu->isp_datalen;
   1492 		if (textbuflen == 0) {
   1493 			/* This shouldn't really happen but it could.. */
   1494 			list_remove(pdu_list, pdu);
   1495 			idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
   1496 			pdu = next_pdu;
   1497 			continue;
   1498 		}
   1499 
   1500 		/*
   1501 		 * If we encountered a split key-value pair on the last
   1502 		 * PDU then handle it now by grabbing the remainder of the
   1503 		 * key-value pair from the next PDU and splicing them
   1504 		 * together.  Obviously on the first PDU this will never
   1505 		 * happen.
   1506 		 */
   1507 		if (split_kv) {
   1508 			cont_fraglen = idm_textbuf_to_firstfraglen(textbuf,
   1509 			    textbuflen);
   1510 			if (cont_fraglen == pdu->isp_datalen) {
   1511 				/*
   1512 				 * This key-value pair spans more than two
   1513 				 * PDU's.  We don't handle this.
   1514 				 */
   1515 				*error_detail = ISCSI_LOGIN_STATUS_TARGET_ERROR;
   1516 				ret = IDM_STATUS_FAIL;
   1517 				goto cleanup;
   1518 			}
   1519 
   1520 			split_kvbuflen = leftover_textbuflen + cont_fraglen;
   1521 			split_kvbuf = kmem_alloc(split_kvbuflen, KM_NOSLEEP);
   1522 			if (split_kvbuf == NULL) {
   1523 				*error_detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
   1524 				ret = IDM_STATUS_FAIL;
   1525 				goto cleanup;
   1526 			}
   1527 
   1528 			bcopy(leftover_textbuf, split_kvbuf,
   1529 			    leftover_textbuflen);
   1530 			bcopy(textbuf,
   1531 			    (uint8_t *)split_kvbuf + leftover_textbuflen,
   1532 			    cont_fraglen);
   1533 
   1534 
   1535 			if (idm_textbuf_to_nvlist(*nvlist,
   1536 			    &split_kvbuf, &split_kvbuflen) != 0) {
   1537 				/*
   1538 				 * Need to handle E2BIG case, indicating that
   1539 				 * a key-value pair is split across multiple
   1540 				 * PDU's.
   1541 				 */
   1542 				kmem_free(split_kvbuf, split_kvbuflen);
   1543 
   1544 				*error_detail = ISCSI_LOGIN_STATUS_TARGET_ERROR;
   1545 				ret = IDM_STATUS_FAIL;
   1546 				goto cleanup;
   1547 			}
   1548 
   1549 			ASSERT(split_kvbuflen != NULL);
   1550 			kmem_free(split_kvbuf, split_kvbuflen);
   1551 
   1552 			/* Now handle the remainder of the PDU as normal */
   1553 			textbuf += (cont_fraglen + 1);
   1554 			textbuflen -= (cont_fraglen + 1);
   1555 		}
   1556 
   1557 		/*
   1558 		 * Convert each key-value pair in the text buffer to nvlist
   1559 		 * format.  If the list has already been created the nvpair
   1560 		 * elements will be added on to the existing list.  Otherwise
   1561 		 * a new nvlist will be created.
   1562 		 */
   1563 		if (idm_textbuf_to_nvlist(*nvlist,
   1564 		    &textbuf, &textbuflen) != 0) {
   1565 
   1566 			*error_detail = ISCSI_LOGIN_STATUS_TARGET_ERROR;
   1567 			ret = IDM_STATUS_FAIL;
   1568 			goto cleanup;
   1569 		}
   1570 
   1571 		ASSERT(
   1572 		    ((lh->flags & ISCSI_FLAG_LOGIN_CONTINUE) &&
   1573 		    (next_pdu != NULL)) ||
   1574 		    (!(lh->flags & ISCSI_FLAG_LOGIN_CONTINUE) &&
   1575 		    (next_pdu == NULL)));
   1576 
   1577 		if ((lh->flags & ISCSI_FLAG_LOGIN_CONTINUE) &
   1578 		    (textbuflen != 0)) {
   1579 			/*
   1580 			 * Key-value pair is split over two PDU's.  We
   1581 			 * assume it willl never be split over more than
   1582 			 * two PDU's.
   1583 			 */
   1584 			split_kv = B_TRUE;
   1585 			leftover_textbuf = textbuf;
   1586 			leftover_textbuflen = textbuflen;
   1587 		} else {
   1588 			split_kv = B_FALSE;
   1589 			if (textbuflen != 0) {
   1590 				/*
   1591 				 * Incomplete keyword but no additional
   1592 				 * PDU's.  This is a malformed login
   1593 				 * request.
   1594 				 */
   1595 				*error_detail =
   1596 				    ISCSI_LOGIN_STATUS_INVALID_REQUEST;
   1597 				ret = IDM_STATUS_FAIL;
   1598 				goto cleanup;
   1599 			}
   1600 		}
   1601 
   1602 		list_remove(pdu_list, pdu);
   1603 		idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
   1604 		pdu = next_pdu;
   1605 	}
   1606 
   1607 cleanup:
   1608 
   1609 	/*
   1610 	 * Free any remaining PDUs on the list. This will only
   1611 	 * happen if there were errors encountered during
   1612 	 * processing of the textbuf.
   1613 	 */
   1614 	pdu = list_head(pdu_list);
   1615 	while (pdu != NULL) {
   1616 		next_pdu = list_next(pdu_list, pdu);
   1617 		list_remove(pdu_list, pdu);
   1618 		idm_pdu_complete(pdu, IDM_STATUS_SUCCESS);
   1619 		pdu = next_pdu;
   1620 	}
   1621 
   1622 	/*
   1623 	 * If there were no errors, we have a complete nvlist representing
   1624 	 * all the iSCSI key-value pairs in the login request PDU's
   1625 	 * that make up this request.
   1626 	 */
   1627 	return (ret);
   1628 }
   1629