xref: /illumos-gate/usr/src/uts/common/io/idm/idm_text.c (revision 6bb6b5762ca4b17cd5fb3c6c123f17489d5635aa)
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