xref: /illumos-gate/usr/src/lib/libiscsit/common/libiscsit.c (revision 07a48826732249fcd3aa8dd53c8389595e9f1fbc)
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/stat.h>
28 #include <ctype.h>
29 #include <fcntl.h>
30 #include <uuid/uuid.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <strings.h>
34 #include <libintl.h>
35 
36 #include <libstmf.h>
37 #include <libiscsit.h>
38 #include <sys/iscsi_protocol.h>
39 #include <sys/iscsit/isns_protocol.h>
40 
41 /* From iscsitgtd */
42 #define	TARGET_NAME_VERS	2
43 
44 /* this should be defined someplace central... */
45 #define	ISCSI_NAME_LEN_MAX	223
46 
47 /* max length of a base64 encoded secret */
48 #define	MAX_BASE64_LEN		341
49 
50 /* Default RADIUS server port */
51 #define	DEFAULT_RADIUS_PORT	1812
52 
53 /*
54  * The kernel reserves target portal group tag value 1 as the default.
55  */
56 #define	ISCSIT_DEFAULT_TPGT	1
57 #define	MAXTAG			0xffff
58 
59 /* helper for property list validation */
60 #define	PROPERR(lst, key, value) { \
61 	if (lst) { \
62 		(void) nvlist_add_string(lst, key, value); \
63 	} \
64 }
65 
66 /* helper function declarations */
67 static int
68 it_iqn_generate(char *iqn_buf, int iqn_buf_len, char *opt_iqn_suffix);
69 
70 static int
71 it_val_pass(char *name, char *val, nvlist_t *e);
72 
73 /* consider making validate funcs public */
74 static int
75 it_validate_configprops(nvlist_t *nvl, nvlist_t *errs);
76 
77 static int
78 it_validate_tgtprops(nvlist_t *nvl, nvlist_t *errs);
79 
80 static int
81 it_validate_iniprops(nvlist_t *nvl, nvlist_t *errs);
82 
83 /*
84  * Function:  it_config_load()
85  *
86  * Allocate and create an it_config_t structure representing the
87  * current iSCSI configuration.  This structure is compiled using
88  * the 'provider' data returned by stmfGetProviderData().  If there
89  * is no provider data associated with iscsit, the it_config_t
90  * structure will be set to a default configuration.
91  *
92  * Parameters:
93  *    cfg	A C representation of the current iSCSI configuration
94  *
95  * Return Values:
96  *    0		Success
97  *    ENOMEM	Could not allocate resources
98  *    EINVAL	Invalid parameter
99  */
100 int
101 it_config_load(it_config_t **cfg)
102 {
103 	int		ret = 0;
104 	nvlist_t	*cfg_nv = NULL;
105 	it_config_t	*newcfg = NULL;
106 	uint64_t	stmf_token = 0;
107 
108 	if (!cfg) {
109 		return (EINVAL);
110 	}
111 
112 	*cfg = NULL;
113 
114 	ret = stmfGetProviderDataProt(ISCSIT_MODNAME, &cfg_nv,
115 	    STMF_PORT_PROVIDER_TYPE, &stmf_token);
116 
117 	if ((ret == STMF_STATUS_SUCCESS) ||
118 	    (ret == STMF_ERROR_NOT_FOUND)) {
119 		/*
120 		 * If not initialized yet, return empty it_config_t
121 		 * Else, convert nvlist to struct
122 		 */
123 		ret = it_nv_to_config(cfg_nv, &newcfg);
124 	}
125 
126 	if (ret == 0) {
127 		newcfg->stmf_token = stmf_token;
128 		*cfg = newcfg;
129 	}
130 
131 	if (cfg_nv) {
132 		nvlist_free(cfg_nv);
133 	}
134 
135 	return (ret);
136 }
137 
138 /*
139  * Function:  it_config_commit()
140  *
141  * Informs the iscsit service that the configuration has changed and
142  * commits the new configuration to persistent store by calling
143  * stmfSetProviderData.  This function can be called multiple times
144  * during a configuration sequence if necessary.
145  *
146  * Parameters:
147  *    cfg	A C representation of the current iSCSI configuration
148  *
149  * Return Values:
150  *    0		Success
151  *    ENOMEM	Could not allocate resources
152  *    EINVAL	Invalid it_config_t structure
153  *    TBD	ioctl() failed
154  *    TBD	could not save config to STMF
155  */
156 int
157 it_config_commit(it_config_t *cfg)
158 {
159 	int			ret;
160 	nvlist_t		*cfgnv = NULL;
161 	char			*packednv = NULL;
162 	int			iscsit_fd = -1;
163 	size_t			pnv_size;
164 	iscsit_ioc_set_config_t	iop;
165 	it_tgt_t		*tgtp;
166 
167 	if (!cfg) {
168 		return (EINVAL);
169 	}
170 
171 	iscsit_fd = open(ISCSIT_NODE, O_RDWR|O_EXCL);
172 	if (iscsit_fd == -1) {
173 		ret = errno;
174 		return (ret);
175 	}
176 
177 	ret = it_config_to_nv(cfg, &cfgnv);
178 	if (ret == 0) {
179 		ret = nvlist_size(cfgnv, &pnv_size, NV_ENCODE_NATIVE);
180 	}
181 
182 	if (ret == 0) {
183 		packednv = malloc(pnv_size);
184 		if (!packednv) {
185 			ret = ENOMEM;
186 		} else {
187 			ret = nvlist_pack(cfgnv, &packednv, &pnv_size,
188 			    NV_ENCODE_NATIVE, 0);
189 		}
190 	}
191 
192 	/*
193 	 * Send the changes to the kernel first, for now.  Kernel
194 	 * will be the final sanity check before config is saved
195 	 * persistently.
196 	 *
197 	 * XXX - this leaves open the simultaneous-change hole
198 	 * that STMF was trying to solve, but is a better sanity
199 	 * check.   Final decision on save order/config generation
200 	 * number TBD.
201 	 */
202 	if (ret == 0) {
203 		iop.set_cfg_vers = ISCSIT_API_VERS0;
204 		iop.set_cfg_pnvlist = packednv;
205 		iop.set_cfg_pnvlist_len = pnv_size;
206 		if ((ioctl(iscsit_fd, ISCSIT_IOC_SET_CONFIG, &iop)) != 0) {
207 			ret = errno;
208 		}
209 	}
210 
211 	/*
212 	 * Before saving the config persistently, remove any
213 	 * PROP_OLD_TARGET_NAME entries.  This is only interesting to
214 	 * the active service.
215 	 */
216 	if (ret == 0) {
217 		tgtp = cfg->config_tgt_list;
218 		for (; tgtp != NULL; tgtp = tgtp->tgt_next) {
219 			if (!tgtp->tgt_properties) {
220 				continue;
221 			}
222 			if (nvlist_exists(tgtp->tgt_properties,
223 			    PROP_OLD_TARGET_NAME)) {
224 				(void) nvlist_remove_all(tgtp->tgt_properties,
225 				    PROP_OLD_TARGET_NAME);
226 			}
227 		}
228 	}
229 
230 	/*
231 	 * stmfGetProviderDataProt() checks to ensure
232 	 * that the config data hasn't changed since we fetched it.
233 	 *
234 	 * The kernel now has a version we need to save persistently.
235 	 * CLI will 'do the right thing' and warn the user if it
236 	 * gets STMF_ERROR_PROV_DATA_STALE.  We'll try once to revert
237 	 * the kernel to the persistently saved data, but ultimately,
238 	 * it's up to the administrator to validate things are as they
239 	 * want them to be.
240 	 */
241 	if (ret == 0) {
242 		ret = stmfSetProviderDataProt(ISCSIT_MODNAME, cfgnv,
243 		    STMF_PORT_PROVIDER_TYPE, &(cfg->stmf_token));
244 
245 		if (ret == STMF_STATUS_SUCCESS) {
246 			ret = 0;
247 		} else if (ret == STMF_ERROR_NOMEM) {
248 			ret = ENOMEM;
249 		} else if (ret == STMF_ERROR_PROV_DATA_STALE) {
250 			int		st;
251 			it_config_t	*rcfg = NULL;
252 
253 			st = it_config_load(&rcfg);
254 			if (st == 0) {
255 				(void) it_config_commit(rcfg);
256 				it_config_free(rcfg);
257 			}
258 		}
259 	}
260 
261 	(void) close(iscsit_fd);
262 
263 	if (packednv) {
264 		free(packednv);
265 	}
266 
267 	if (cfgnv) {
268 		nvlist_free(cfgnv);
269 	}
270 
271 	return (ret);
272 }
273 
274 /*
275  * Function:  it_config_setprop()
276  *
277  * Validate the provided property list and set the global properties
278  * for iSCSI Target.  If errlist is not NULL, returns detailed
279  * errors for each property that failed.  The format for errorlist
280  * is key = property, value = error string.
281  *
282  * Parameters:
283  *
284  *    cfg		The current iSCSI configuration obtained from
285  *			it_config_load()
286  *    proplist		nvlist_t containing properties for this target.
287  *    errlist		(optional)  nvlist_t of errors encountered when
288  *                      validating the properties.
289  *
290  * Return Values:
291  *    0			Success
292  *    EINVAL		Invalid property
293  *
294  */
295 int
296 it_config_setprop(it_config_t *cfg, nvlist_t *proplist, nvlist_t **errlist)
297 {
298 	int		ret;
299 	it_portal_t	*isns = NULL;
300 	it_portal_t	*pnext = NULL;
301 	it_portal_t	*newisnslist = NULL;
302 	char		**arr;
303 	uint32_t	count;
304 	uint32_t	newcount;
305 	nvlist_t	*cprops = NULL;
306 	char		*val = NULL;
307 
308 	if (!cfg || !proplist) {
309 		return (EINVAL);
310 	}
311 
312 	if (errlist) {
313 		(void) nvlist_alloc(errlist, 0, 0);
314 	}
315 
316 	/*
317 	 * copy the existing properties, merge, then validate
318 	 * the merged properties before committing them.
319 	 */
320 	if (cfg->config_global_properties) {
321 		ret = nvlist_dup(cfg->config_global_properties, &cprops, 0);
322 	} else {
323 		ret = nvlist_alloc(&cprops, NV_UNIQUE_NAME, 0);
324 	}
325 
326 	/* base64 encode the radius secret, if it's changed */
327 	val = NULL;
328 	(void) nvlist_lookup_string(proplist, PROP_RADIUS_SECRET, &val);
329 	if (val) {
330 		char		bsecret[MAX_BASE64_LEN];
331 
332 		ret = it_val_pass(PROP_RADIUS_SECRET, val, *errlist);
333 
334 		if (ret == 0) {
335 			(void) memset(bsecret, 0, MAX_BASE64_LEN);
336 
337 			ret = iscsi_binary_to_base64_str((uint8_t *)val,
338 			    strlen(val), bsecret, MAX_BASE64_LEN);
339 
340 			if (ret == 0) {
341 				/* replace the value in the nvlist */
342 				ret = nvlist_add_string(proplist,
343 				    PROP_RADIUS_SECRET, bsecret);
344 			}
345 		}
346 	}
347 
348 	if (ret == 0) {
349 		ret = nvlist_merge(cprops, proplist, 0);
350 	}
351 
352 	/* see if we need to remove the radius server setting */
353 	val = NULL;
354 	(void) nvlist_lookup_string(cprops, PROP_RADIUS_SERVER, &val);
355 	if (val && (strcasecmp(val, "none") == 0)) {
356 		(void) nvlist_remove_all(cprops, PROP_RADIUS_SERVER);
357 	}
358 
359 	/* and/or remove the alias */
360 	val = NULL;
361 	(void) nvlist_lookup_string(cprops, PROP_ALIAS, &val);
362 	if (val && (strcasecmp(val, "none") == 0)) {
363 		(void) nvlist_remove_all(cprops, PROP_ALIAS);
364 	}
365 
366 	if (ret == 0) {
367 		ret = it_validate_configprops(cprops, *errlist);
368 	}
369 
370 	if (ret != 0) {
371 		if (cprops) {
372 			nvlist_free(cprops);
373 		}
374 		return (ret);
375 	}
376 
377 	/*
378 	 * Update iSNS server list, if exists in provided property list.
379 	 */
380 	ret = nvlist_lookup_string_array(proplist, PROP_ISNS_SERVER,
381 	    &arr, &count);
382 
383 	if (ret == 0) {
384 		/* special case:  if "none", remove all defined */
385 		if (strcasecmp(arr[0], "none") != 0) {
386 			ret = it_array_to_portallist(arr, count,
387 			    ISNS_DEFAULT_SERVER_PORT, &newisnslist, &newcount);
388 		} else {
389 			newisnslist = NULL;
390 			newcount = 0;
391 			(void) nvlist_remove_all(cprops, PROP_ISNS_SERVER);
392 		}
393 
394 		if (ret == 0) {
395 			isns = cfg->config_isns_svr_list;
396 			while (isns) {
397 				pnext = isns->next;
398 				free(isns);
399 				isns = pnext;
400 			}
401 
402 			cfg->config_isns_svr_list = newisnslist;
403 			cfg->config_isns_svr_count = newcount;
404 
405 			/*
406 			 * Replace the array in the nvlist to ensure
407 			 * duplicates are properly removed & port numbers
408 			 * are added.
409 			 */
410 			if (newcount > 0) {
411 				int	i = 0;
412 				char	**newarray;
413 
414 				newarray = malloc(sizeof (char *) * newcount);
415 				if (newarray == NULL) {
416 					ret = ENOMEM;
417 				} else {
418 					for (isns = newisnslist; isns != NULL;
419 					    isns = isns->next) {
420 						(void) sockaddr_to_str(
421 						    &(isns->portal_addr),
422 						    &(newarray[i++]));
423 					}
424 					(void) nvlist_add_string_array(cprops,
425 					    PROP_ISNS_SERVER, newarray,
426 					    newcount);
427 
428 					for (i = 0; i < newcount; i++) {
429 						if (newarray[i]) {
430 							free(newarray[i]);
431 						}
432 					}
433 					free(newarray);
434 				}
435 			}
436 		}
437 	} else if (ret == ENOENT) {
438 		/* not an error */
439 		ret = 0;
440 	}
441 
442 	if (ret == 0) {
443 		/* replace the global properties list */
444 		nvlist_free(cfg->config_global_properties);
445 		cfg->config_global_properties = cprops;
446 	} else {
447 		if (cprops) {
448 			nvlist_free(cprops);
449 		}
450 	}
451 
452 	return (ret);
453 }
454 
455 /*
456  * Function:  it_config_free()
457  *
458  * Free any resources associated with the it_config_t structure.
459  *
460  * Parameters:
461  *    cfg	A C representation of the current iSCSI configuration
462  */
463 void
464 it_config_free(it_config_t *cfg)
465 {
466 	it_config_free_cmn(cfg);
467 }
468 
469 /*
470  * Function:  it_tgt_create()
471  *
472  * Allocate and create an it_tgt_t structure representing a new iSCSI
473  * target node.  If tgt_name is NULL, then a unique target node name will
474  * be generated automatically.  Otherwise, the value of tgt_name will be
475  * used as the target node name.  The new it_tgt_t structure is added to
476  * the target list (cfg_tgt_list) in the configuration structure, and the
477  * new target will not be instantiated until the modified configuration
478  * is committed by calling it_config_commit().
479  *
480  * Parameters:
481  *    cfg		The current iSCSI configuration obtained from
482  *			it_config_load()
483  *    tgt		Pointer to an iSCSI target structure
484  *    tgt_name		The target node name for the target to be created.
485  *			The name must be in either IQN or EUI format.  If
486  *			this value is NULL, a node name will be generated
487  *			automatically in IQN format.
488  *
489  * Return Values:
490  *    0			Success
491  *    ENOMEM		Could not allocated resources
492  *    EINVAL		Invalid parameter
493  *    EFAULT		Invalid iSCSI name specified
494  *    E2BIG		Too many already exist
495  */
496 int
497 it_tgt_create(it_config_t *cfg, it_tgt_t **tgt, char *tgt_name)
498 {
499 	int		ret = 0;
500 	it_tgt_t	*ptr;
501 	it_tgt_t	*cfgtgt;
502 	char		*namep = tgt_name;
503 	char		buf[ISCSI_NAME_LEN_MAX + 1];
504 
505 	if (!cfg || !tgt) {
506 		return (EINVAL);
507 	}
508 
509 	if (!namep) {
510 		/* generate a name */
511 
512 		ret = it_iqn_generate(buf, sizeof (buf), NULL);
513 		if (ret != 0) {
514 			return (ret);
515 		}
516 		namep = buf;
517 	} else {
518 		/* validate the passed-in name */
519 		if (!validate_iscsi_name(namep)) {
520 			return (EFAULT);
521 		}
522 	}
523 
524 	/* Too many targets? */
525 	if (cfg->config_tgt_count >= MAX_TARGETS) {
526 		return (E2BIG);
527 	}
528 
529 
530 	/* make sure this name isn't already on the list */
531 	cfgtgt = cfg->config_tgt_list;
532 	while (cfgtgt != NULL) {
533 		if (strcmp(namep, cfgtgt->tgt_name) == 0) {
534 			return (EEXIST);
535 		}
536 		cfgtgt = cfgtgt->tgt_next;
537 	}
538 
539 	ptr = calloc(1, sizeof (it_tgt_t));
540 	if (ptr == NULL) {
541 		return (ENOMEM);
542 	}
543 
544 	(void) strlcpy(ptr->tgt_name, namep, sizeof (ptr->tgt_name));
545 	ptr->tgt_generation = 1;
546 	ptr->tgt_next = cfg->config_tgt_list;
547 	cfg->config_tgt_list = ptr;
548 	cfg->config_tgt_count++;
549 
550 	*tgt = ptr;
551 
552 	return (0);
553 }
554 
555 /*
556  * Function:  it_tgt_setprop()
557  *
558  * Validate the provided property list and set the properties for
559  * the specified target.  If errlist is not NULL, returns detailed
560  * errors for each property that failed.  The format for errorlist
561  * is key = property, value = error string.
562  *
563  * Parameters:
564  *
565  *    cfg		The current iSCSI configuration obtained from
566  *			it_config_load()
567  *    tgt		Pointer to an iSCSI target structure
568  *    proplist		nvlist_t containing properties for this target.
569  *    errlist		(optional)  nvlist_t of errors encountered when
570  *			validating the properties.
571  *
572  * Return Values:
573  *    0			Success
574  *    EINVAL		Invalid property
575  *
576  */
577 int
578 it_tgt_setprop(it_config_t *cfg, it_tgt_t *tgt, nvlist_t *proplist,
579     nvlist_t **errlist)
580 {
581 	int		ret;
582 	nvlist_t	*tprops = NULL;
583 	char		*val = NULL;
584 
585 	if (!cfg || !tgt || !proplist) {
586 		return (EINVAL);
587 	}
588 
589 	if (errlist) {
590 		(void) nvlist_alloc(errlist, 0, 0);
591 	}
592 
593 	/*
594 	 * copy the existing properties, merge, then validate
595 	 * the merged properties before committing them.
596 	 */
597 	if (tgt->tgt_properties) {
598 		ret = nvlist_dup(tgt->tgt_properties, &tprops, 0);
599 	} else {
600 		ret = nvlist_alloc(&tprops, NV_UNIQUE_NAME, 0);
601 	}
602 
603 	if (ret == 0) {
604 		ret = nvlist_merge(tprops, proplist, 0);
605 	}
606 
607 	/* unset chap username or alias if requested */
608 	val = NULL;
609 	(void) nvlist_lookup_string(proplist, PROP_TARGET_CHAP_USER, &val);
610 	if (val && (strcasecmp(val, "none") == 0)) {
611 		(void) nvlist_remove_all(tprops, PROP_TARGET_CHAP_USER);
612 	}
613 
614 	val = NULL;
615 	(void) nvlist_lookup_string(proplist, PROP_ALIAS, &val);
616 	if (val && (strcasecmp(val, "none") == 0)) {
617 		(void) nvlist_remove_all(tprops, PROP_ALIAS);
618 	}
619 
620 	/* base64 encode the CHAP secret, if it's changed */
621 	val = NULL;
622 	(void) nvlist_lookup_string(proplist, PROP_TARGET_CHAP_SECRET, &val);
623 	if (val) {
624 		char		bsecret[MAX_BASE64_LEN];
625 
626 		ret = it_val_pass(PROP_TARGET_CHAP_SECRET, val, *errlist);
627 
628 		if (ret == 0) {
629 			(void) memset(bsecret, 0, MAX_BASE64_LEN);
630 
631 			ret = iscsi_binary_to_base64_str((uint8_t *)val,
632 			    strlen(val), bsecret, MAX_BASE64_LEN);
633 
634 			if (ret == 0) {
635 				/* replace the value in the nvlist */
636 				ret = nvlist_add_string(tprops,
637 				    PROP_TARGET_CHAP_SECRET, bsecret);
638 			}
639 		}
640 	}
641 
642 	if (ret == 0) {
643 		ret = it_validate_tgtprops(tprops, *errlist);
644 	}
645 
646 	if (ret != 0) {
647 		if (tprops) {
648 			nvlist_free(tprops);
649 		}
650 		return (ret);
651 	}
652 
653 	if (tgt->tgt_properties) {
654 		nvlist_free(tgt->tgt_properties);
655 	}
656 	tgt->tgt_properties = tprops;
657 
658 	return (0);
659 }
660 
661 
662 /*
663  * Function:  it_tgt_delete()
664  *
665  * Delete target represented by 'tgt', where 'tgt' is an existing
666  * it_tgt_structure within the configuration 'cfg'.  The target removal
667  * will not take effect until the modified configuration is committed
668  * by calling it_config_commit().
669  *
670  * Parameters:
671  *    cfg		The current iSCSI configuration obtained from
672  *			it_config_load()
673  *    tgt		Pointer to an iSCSI target structure
674  *
675  *    force		Set the target to offline before removing it from
676  *			the config.  If not specified, the operation will
677  *			fail if the target is determined to be online.
678  * Return Values:
679  *    0			Success
680  *    EBUSY		Target is online
681  */
682 int
683 it_tgt_delete(it_config_t *cfg, it_tgt_t *tgt, boolean_t force)
684 {
685 	int			ret;
686 	it_tgt_t		*ptgt;
687 	it_tgt_t		*prev = NULL;
688 	stmfDevid		devid;
689 	stmfTargetProperties	props;
690 
691 	if (!cfg || !tgt) {
692 		return (0);
693 	}
694 
695 	ptgt = cfg->config_tgt_list;
696 	while (ptgt != NULL) {
697 		if (strcmp(tgt->tgt_name, ptgt->tgt_name) == 0) {
698 			break;
699 		}
700 		prev = ptgt;
701 		ptgt = ptgt->tgt_next;
702 	}
703 
704 	if (!ptgt) {
705 		return (0);
706 	}
707 
708 	/*
709 	 * check to see if this target is offline.  If it is not,
710 	 * and the 'force' flag is TRUE, tell STMF to offline it
711 	 * before removing from the configuration.
712 	 */
713 	ret = stmfDevidFromIscsiName(ptgt->tgt_name, &devid);
714 	if (ret != STMF_STATUS_SUCCESS) {
715 		/* can't happen? */
716 		return (EINVAL);
717 	}
718 
719 	ret = stmfGetTargetProperties(&devid, &props);
720 	if (ret == STMF_STATUS_SUCCESS) {
721 		/*
722 		 * only other return is STMF_ERROR_NOT_FOUND, which
723 		 * means we don't have to offline it.
724 		 */
725 		if (props.status == STMF_TARGET_PORT_ONLINE) {
726 			if (!force) {
727 				return (EBUSY);
728 			}
729 			ret = stmfOfflineTarget(&devid);
730 			if (ret != 0) {
731 				return (EBUSY);
732 			}
733 		}
734 	}
735 
736 	if (prev) {
737 		prev->tgt_next = ptgt->tgt_next;
738 	} else {
739 		/* first one on the list */
740 		cfg->config_tgt_list = ptgt->tgt_next;
741 	}
742 
743 	ptgt->tgt_next = NULL; /* Only free this target */
744 
745 	cfg->config_tgt_count--;
746 	it_tgt_free(ptgt);
747 
748 	return (0);
749 }
750 
751 /*
752  * Function:  it_tgt_free()
753  *
754  * Frees an it_tgt_t structure.  If tgt_next is not NULL, frees
755  * all structures in the list.
756  */
757 void
758 it_tgt_free(it_tgt_t *tgt)
759 {
760 	it_tgt_free_cmn(tgt);
761 }
762 
763 /*
764  * Function:  it_tpgt_create()
765  *
766  * Allocate and create an it_tpgt_t structure representing a new iSCSI
767  * target portal group tag.  The new it_tpgt_t structure is added to the
768  * target tpgt list (tgt_tpgt_list) in the it_tgt_t structure.  The new
769  * target portal group tag will not be instantiated until the modified
770  * configuration is committed by calling it_config_commit().
771  *
772  * Parameters:
773  *    cfg		The current iSCSI configuration obtained from
774  *			it_config_load()
775  *    tgt		Pointer to the iSCSI target structure associated
776  *			with the target portal group tag
777  *    tpgt		Pointer to a target portal group tag structure
778  *    tpg_name		The name of the TPG to be associated with this TPGT
779  *    tpgt_tag		16-bit numerical identifier for this TPGT.  If
780  *			tpgt_tag is '0', this function will choose the
781  *			tag number.  If tpgt_tag is >0, and the requested
782  *			tag is determined to be in use, another value
783  *			will be chosen.
784  *
785  * Return Values:
786  *    0			Success
787  *    ENOMEM		Could not allocate resources
788  *    EINVAL		Invalid parameter
789  *    EEXIST		Specified tag name is already used.
790  *    E2BIG		No available tag numbers
791  */
792 int
793 it_tpgt_create(it_config_t *cfg, it_tgt_t *tgt, it_tpgt_t **tpgt,
794     char *tpg_name, uint16_t tpgt_tag)
795 {
796 	it_tpgt_t	*ptr = NULL;
797 	it_tpgt_t	*cfgt;
798 	char		tagid_used[MAXTAG + 1];
799 	uint16_t	tagid = ISCSIT_DEFAULT_TPGT;
800 
801 	if (!cfg || !tgt || !tpgt || !tpg_name) {
802 		return (EINVAL);
803 	}
804 
805 	(void) memset(&(tagid_used[0]), 0, sizeof (tagid_used));
806 
807 	/*
808 	 * Make sure this name and/or tag isn't already on the list
809 	 * At the same time, capture all tag ids in use for this target
810 	 *
811 	 * About tag numbering -- since tag numbers are used by
812 	 * the iSCSI protocol, we should be careful about reusing
813 	 * them too quickly.  Start with a value greater than the
814 	 * highest one currently defined.  If current == MAXTAG,
815 	 * just find an unused tag.
816 	 */
817 	cfgt = tgt->tgt_tpgt_list;
818 	while (cfgt != NULL) {
819 		tagid_used[cfgt->tpgt_tag] = 1;
820 
821 		if (strcmp(tpg_name, cfgt->tpgt_tpg_name) == 0) {
822 			return (EEXIST);
823 		}
824 
825 		if (cfgt->tpgt_tag > tagid) {
826 			tagid = cfgt->tpgt_tag;
827 		}
828 
829 		cfgt = cfgt->tpgt_next;
830 	}
831 
832 	if ((tpgt_tag > ISCSIT_DEFAULT_TPGT) && (tpgt_tag < MAXTAG) &&
833 	    (tagid_used[tpgt_tag] == 0)) {
834 		/* ok to use requested */
835 		tagid = tpgt_tag;
836 	} else if (tagid == MAXTAG) {
837 		/*
838 		 * The highest value is used, find an available id.
839 		 */
840 		tagid = ISCSIT_DEFAULT_TPGT + 1;
841 		for (; tagid < MAXTAG; tagid++) {
842 			if (tagid_used[tagid] == 0) {
843 				break;
844 			}
845 		}
846 		if (tagid >= MAXTAG) {
847 			return (E2BIG);
848 		}
849 	} else {
850 		/* next available ID */
851 		tagid++;
852 	}
853 
854 	ptr = calloc(1, sizeof (it_tpgt_t));
855 	if (!ptr) {
856 		return (ENOMEM);
857 	}
858 
859 	(void) strlcpy(ptr->tpgt_tpg_name, tpg_name,
860 	    sizeof (ptr->tpgt_tpg_name));
861 	ptr->tpgt_generation = 1;
862 	ptr->tpgt_tag = tagid;
863 
864 	ptr->tpgt_next = tgt->tgt_tpgt_list;
865 	tgt->tgt_tpgt_list = ptr;
866 	tgt->tgt_tpgt_count++;
867 	tgt->tgt_generation++;
868 
869 	*tpgt = ptr;
870 
871 	return (0);
872 }
873 
874 /*
875  * Function:  it_tpgt_delete()
876  *
877  * Delete the target portal group tag represented by 'tpgt', where
878  * 'tpgt' is an existing is_tpgt_t structure within the target 'tgt'.
879  * The target portal group tag removal will not take effect until the
880  * modified configuration is committed by calling it_config_commit().
881  *
882  * Parameters:
883  *    cfg		The current iSCSI configuration obtained from
884  *			it_config_load()
885  *    tgt		Pointer to the iSCSI target structure associated
886  *			with the target portal group tag
887  *    tpgt		Pointer to a target portal group tag structure
888  */
889 void
890 it_tpgt_delete(it_config_t *cfg, it_tgt_t *tgt, it_tpgt_t *tpgt)
891 {
892 	it_tpgt_t	*ptr;
893 	it_tpgt_t	*prev = NULL;
894 
895 	if (!cfg || !tgt || !tpgt) {
896 		return;
897 	}
898 
899 	ptr = tgt->tgt_tpgt_list;
900 	while (ptr) {
901 		if (ptr->tpgt_tag == tpgt->tpgt_tag) {
902 			break;
903 		}
904 		prev = ptr;
905 		ptr = ptr->tpgt_next;
906 	}
907 
908 	if (!ptr) {
909 		return;
910 	}
911 
912 	if (prev) {
913 		prev->tpgt_next = ptr->tpgt_next;
914 	} else {
915 		tgt->tgt_tpgt_list = ptr->tpgt_next;
916 	}
917 	ptr->tpgt_next = NULL;
918 
919 	tgt->tgt_tpgt_count--;
920 	tgt->tgt_generation++;
921 
922 	it_tpgt_free(ptr);
923 }
924 
925 /*
926  * Function:  it_tpgt_free()
927  *
928  * Deallocates resources of an it_tpgt_t structure.  If tpgt->next
929  * is not NULL, frees all members of the list.
930  */
931 void
932 it_tpgt_free(it_tpgt_t *tpgt)
933 {
934 	it_tpgt_free_cmn(tpgt);
935 }
936 
937 /*
938  * Function:  it_tpg_create()
939  *
940  * Allocate and create an it_tpg_t structure representing a new iSCSI
941  * target portal group.  The new it_tpg_t structure is added to the global
942  * tpg list (cfg_tgt_list) in the it_config_t structure.  The new target
943  * portal group will not be instantiated until the modified configuration
944  * is committed by calling it_config_commit().
945  *
946  * Parameters:
947  *    cfg		The current iSCSI configuration obtained from
948  *			it_config_load()
949  *    tpg		Pointer to the it_tpg_t structure representing
950  *			the target portal group
951  *    tpg_name		Identifier for the target portal group
952  *    portal_ip_port	A string containing an appropriatedly formatted
953  *			IP address:port.  Both IPv4 and IPv6 addresses are
954  *			permitted.  This value becomes the first portal in
955  *			the TPG -- applications can add additional values
956  *			using it_portal_create() before committing the TPG.
957  * Return Values:
958  *    0			Success
959  *    ENOMEM		Cannot allocate resources
960  *    EINVAL		Invalid parameter
961  *    EEXIST		Requested portal in use by another target portal
962  *			group
963  */
964 int
965 it_tpg_create(it_config_t *cfg, it_tpg_t **tpg, char *tpg_name,
966     char *portal_ip_port)
967 {
968 	int		ret;
969 	it_tpg_t	*ptr;
970 	it_portal_t	*portal = NULL;
971 
972 	if (!cfg || !tpg || !tpg_name || !portal_ip_port) {
973 		return (EINVAL);
974 	}
975 
976 	*tpg = NULL;
977 
978 	ptr = cfg->config_tpg_list;
979 	while (ptr) {
980 		if (strcmp(tpg_name, ptr->tpg_name) == 0) {
981 			break;
982 		}
983 		ptr = ptr->tpg_next;
984 	}
985 
986 	if (ptr) {
987 		return (EEXIST);
988 	}
989 
990 	ptr = calloc(1, sizeof (it_tpg_t));
991 	if (!ptr) {
992 		return (ENOMEM);
993 	}
994 
995 	ptr->tpg_generation = 1;
996 	(void) strlcpy(ptr->tpg_name, tpg_name, sizeof (ptr->tpg_name));
997 
998 	/* create the portal */
999 	ret = it_portal_create(cfg, ptr, &portal, portal_ip_port);
1000 	if (ret != 0) {
1001 		free(ptr);
1002 		return (ret);
1003 	}
1004 
1005 	ptr->tpg_next = cfg->config_tpg_list;
1006 	cfg->config_tpg_list = ptr;
1007 	cfg->config_tpg_count++;
1008 
1009 	*tpg = ptr;
1010 
1011 	return (0);
1012 }
1013 
1014 /*
1015  * Function:  it_tpg_delete()
1016  *
1017  * Delete target portal group represented by 'tpg', where 'tpg' is an
1018  * existing it_tpg_t structure within the global configuration 'cfg'.
1019  * The target portal group removal will not take effect until the
1020  * modified configuration is committed by calling it_config_commit().
1021  *
1022  * Parameters:
1023  *    cfg		The current iSCSI configuration obtained from
1024  *			it_config_load()
1025  *    tpg		Pointer to the it_tpg_t structure representing
1026  *			the target portal group
1027  *    force		Remove this target portal group even if it's
1028  *			associated with one or more targets.
1029  *
1030  * Return Values:
1031  *    0			Success
1032  *    EINVAL		Invalid parameter
1033  *    EBUSY		Portal group associated with one or more targets.
1034  */
1035 int
1036 it_tpg_delete(it_config_t *cfg, it_tpg_t *tpg, boolean_t force)
1037 {
1038 	it_tpg_t	*ptr;
1039 	it_tpg_t	*prev = NULL;
1040 	it_tgt_t	*tgt;
1041 	it_tpgt_t	*tpgt;
1042 	it_tpgt_t	*ntpgt;
1043 
1044 	if (!cfg || !tpg) {
1045 		return (EINVAL);
1046 	}
1047 
1048 	ptr = cfg->config_tpg_list;
1049 	while (ptr) {
1050 		if (strcmp(ptr->tpg_name, tpg->tpg_name) == 0) {
1051 			break;
1052 		}
1053 		prev = ptr;
1054 		ptr = ptr->tpg_next;
1055 	}
1056 
1057 	if (!ptr) {
1058 		return (0);
1059 	}
1060 
1061 	/*
1062 	 * See if any targets are using this portal group.
1063 	 * If there are, and the force flag is not set, fail.
1064 	 */
1065 	tgt = cfg->config_tgt_list;
1066 	while (tgt) {
1067 		tpgt = tgt->tgt_tpgt_list;
1068 		while (tpgt) {
1069 			ntpgt = tpgt->tpgt_next;
1070 
1071 			if (strcmp(tpgt->tpgt_tpg_name, tpg->tpg_name)
1072 			    == 0) {
1073 				if (!force) {
1074 					return (EBUSY);
1075 				}
1076 				it_tpgt_delete(cfg, tgt, tpgt);
1077 			}
1078 
1079 			tpgt = ntpgt;
1080 		}
1081 		tgt = tgt->tgt_next;
1082 	}
1083 
1084 	/* Now that it's not in use anywhere, remove the TPG */
1085 	if (prev) {
1086 		prev->tpg_next = ptr->tpg_next;
1087 	} else {
1088 		cfg->config_tpg_list = ptr->tpg_next;
1089 	}
1090 	ptr->tpg_next = NULL;
1091 
1092 	cfg->config_tpg_count--;
1093 
1094 	it_tpg_free(ptr);
1095 
1096 	return (0);
1097 }
1098 
1099 /*
1100  * Function:  it_tpg_free()
1101  *
1102  * Deallocates resources associated with an it_tpg_t structure.
1103  * If tpg->next is not NULL, frees all members of the list.
1104  */
1105 void
1106 it_tpg_free(it_tpg_t *tpg)
1107 {
1108 	it_tpg_free_cmn(tpg);
1109 }
1110 
1111 /*
1112  * Function:  it_portal_create()
1113  *
1114  * Add an it_portal_t structure presenting a new portal to the specified
1115  * target portal group.  The change to the target portal group will not take
1116  * effect until the modified configuration is committed by calling
1117  * it_config_commit().
1118  *
1119  * Parameters:
1120  *    cfg		The current iSCSI configration obtained from
1121  *			it_config_load()
1122  *    tpg		Pointer to the it_tpg_t structure representing the
1123  *			target portal group
1124  *    portal		Pointer to the it_portal_t structure representing
1125  *			the portal
1126  *    portal_ip_port	A string containing an appropriately formatted
1127  *			IP address or IP address:port in either IPv4 or
1128  *			IPv6 format.
1129  * Return Values:
1130  *    0			Success
1131  *    ENOMEM		Could not allocate resources
1132  *    EINVAL		Invalid parameter
1133  *    EEXIST		Portal already configured for another portal group
1134  */
1135 int
1136 it_portal_create(it_config_t *cfg, it_tpg_t *tpg, it_portal_t **portal,
1137     char *portal_ip_port)
1138 {
1139 	struct sockaddr_storage		sa;
1140 	it_portal_t			*ptr;
1141 	it_tpg_t			*ctpg = NULL;
1142 
1143 	if (!cfg || !tpg || !portal || !portal_ip_port) {
1144 		return (EINVAL);
1145 	}
1146 
1147 	if ((it_common_convert_sa(portal_ip_port, &sa, ISCSI_LISTEN_PORT))
1148 	    == NULL) {
1149 		return (EINVAL);
1150 	}
1151 
1152 	/* Check that this portal doesn't appear in any other tag */
1153 	ctpg = cfg->config_tpg_list;
1154 	while (ctpg) {
1155 		ptr = ctpg->tpg_portal_list;
1156 		for (; ptr != NULL; ptr = ptr->next) {
1157 			if (it_sa_compare(&(ptr->portal_addr), &sa) != 0) {
1158 				continue;
1159 			}
1160 
1161 			/*
1162 			 * Existing in the same group is not an error,
1163 			 * but don't add it again.
1164 			 */
1165 			if (strcmp(ctpg->tpg_name, tpg->tpg_name) == 0) {
1166 				return (0);
1167 			} else {
1168 				/* Not allowed */
1169 				return (EEXIST);
1170 			}
1171 		}
1172 		ctpg = ctpg->tpg_next;
1173 	}
1174 
1175 	ptr = calloc(1, sizeof (it_portal_t));
1176 	if (!ptr) {
1177 		return (ENOMEM);
1178 	}
1179 
1180 	(void) memcpy(&(ptr->portal_addr), &sa,
1181 	    sizeof (struct sockaddr_storage));
1182 	ptr->next = tpg->tpg_portal_list;
1183 	tpg->tpg_portal_list = ptr;
1184 	tpg->tpg_portal_count++;
1185 	tpg->tpg_generation++;
1186 
1187 	return (0);
1188 }
1189 
1190 /*
1191  * Function:  it_portal_delete()
1192  *
1193  * Remove the specified portal from the specified target portal group.
1194  * The portal removal will not take effect until the modified configuration
1195  * is committed by calling it_config_commit().
1196  *
1197  * Parameters:
1198  *    cfg		The current iSCSI configration obtained from
1199  *			it_config_load()
1200  *    tpg		Pointer to the it_tpg_t structure representing the
1201  *			target portal group
1202  *    portal		Pointer to the it_portal_t structure representing
1203  *			the portal
1204  */
1205 void
1206 it_portal_delete(it_config_t *cfg, it_tpg_t *tpg, it_portal_t *portal)
1207 {
1208 	it_portal_t	*ptr;
1209 	it_portal_t	*prev;
1210 
1211 	if (!cfg || !tpg || !portal) {
1212 		return;
1213 	}
1214 
1215 	ptr = tpg->tpg_portal_list;
1216 	while (ptr) {
1217 		if (memcmp(&(ptr->portal_addr), &(portal->portal_addr),
1218 		    sizeof (ptr->portal_addr)) == 0) {
1219 			break;
1220 		}
1221 		prev = ptr;
1222 		ptr = ptr->next;
1223 	}
1224 
1225 	if (!ptr) {
1226 		return;
1227 	}
1228 
1229 	if (prev) {
1230 		prev->next = ptr->next;
1231 	} else {
1232 		tpg->tpg_portal_list = ptr->next;
1233 	}
1234 	tpg->tpg_portal_count--;
1235 	tpg->tpg_generation++;
1236 
1237 	free(ptr);
1238 }
1239 
1240 /*
1241  * Function:  it_ini_create()
1242  *
1243  * Add an initiator context to the global configuration. The new
1244  * initiator context will not be instantiated until the modified
1245  * configuration is committed by calling it_config_commit().
1246  *
1247  * Parameters:
1248  *    cfg		The current iSCSI configration obtained from
1249  *			it_config_load()
1250  *    ini		Pointer to the it_ini_t structure representing
1251  *			the initiator context.
1252  *    ini_node_name	The iSCSI node name of the remote initiator.
1253  *
1254  * Return Values:
1255  *    0			Success
1256  *    ENOMEM		Could not allocate resources
1257  *    EINVAL		Invalid parameter.
1258  *    EFAULT		Invalid initiator name
1259  */
1260 int
1261 it_ini_create(it_config_t *cfg, it_ini_t **ini, char *ini_node_name)
1262 {
1263 	it_ini_t	*ptr;
1264 
1265 	if (!cfg || !ini || !ini_node_name) {
1266 		return (EINVAL);
1267 	}
1268 
1269 	/*
1270 	 * Ensure this is a valid ini name
1271 	 */
1272 	if (!validate_iscsi_name(ini_node_name)) {
1273 		return (EFAULT);
1274 	}
1275 
1276 	ptr = cfg->config_ini_list;
1277 	while (ptr) {
1278 		if (strcmp(ptr->ini_name, ini_node_name) == 0) {
1279 			break;
1280 		}
1281 		ptr = ptr->ini_next;
1282 	}
1283 
1284 	if (ptr) {
1285 		return (EEXIST);
1286 	}
1287 
1288 	ptr = calloc(1, sizeof (it_ini_t));
1289 	if (!ptr) {
1290 		return (ENOMEM);
1291 	}
1292 
1293 	(void) strlcpy(ptr->ini_name, ini_node_name, sizeof (ptr->ini_name));
1294 	ptr->ini_generation = 1;
1295 	/* nvlist for props? */
1296 
1297 	ptr->ini_next = cfg->config_ini_list;
1298 	cfg->config_ini_list = ptr;
1299 	cfg->config_ini_count++;
1300 
1301 	*ini = ptr;
1302 
1303 	return (0);
1304 }
1305 
1306 /*
1307  * Function:  it_ini_setprop()
1308  *
1309  * Validate the provided property list and set the initiator properties.
1310  * If errlist is not NULL, returns detailed errors for each property
1311  * that failed.  The format for errorlist is key = property,
1312  * value = error string.
1313  *
1314  * Parameters:
1315  *
1316  *    ini		The initiator being updated.
1317  *    proplist		nvlist_t containing properties for this target.
1318  *    errlist		(optional)  nvlist_t of errors encountered when
1319  *			validating the properties.
1320  *
1321  * Return Values:
1322  *    0			Success
1323  *    EINVAL		Invalid property
1324  *
1325  */
1326 int
1327 it_ini_setprop(it_ini_t *ini, nvlist_t *proplist, nvlist_t **errlist)
1328 {
1329 	int		ret;
1330 	nvlist_t	*iprops = NULL;
1331 	char		*val = NULL;
1332 
1333 	if (!ini || !proplist) {
1334 		return (EINVAL);
1335 	}
1336 
1337 	if (errlist) {
1338 		(void) nvlist_alloc(errlist, 0, 0);
1339 	}
1340 
1341 	/*
1342 	 * copy the existing properties, merge, then validate
1343 	 * the merged properties before committing them.
1344 	 */
1345 	if (ini->ini_properties) {
1346 		ret = nvlist_dup(ini->ini_properties, &iprops, 0);
1347 	} else {
1348 		ret = nvlist_alloc(&iprops, NV_UNIQUE_NAME, 0);
1349 	}
1350 
1351 	if (ret == 0) {
1352 		ret = nvlist_merge(iprops, proplist, 0);
1353 	}
1354 
1355 	/* unset chap username if requested */
1356 	if ((nvlist_lookup_string(proplist, PROP_CHAP_USER, &val)) == 0) {
1357 		if (strcasecmp(val, "none") == 0) {
1358 			(void) nvlist_remove_all(iprops, PROP_CHAP_USER);
1359 		}
1360 	}
1361 
1362 	/* base64 encode the CHAP secret, if it's changed */
1363 	if ((nvlist_lookup_string(proplist, PROP_CHAP_SECRET, &val)) == 0) {
1364 		char		bsecret[MAX_BASE64_LEN];
1365 
1366 		ret = it_val_pass(PROP_CHAP_SECRET, val, *errlist);
1367 		if (ret == 0) {
1368 			(void) memset(bsecret, 0, MAX_BASE64_LEN);
1369 
1370 			ret = iscsi_binary_to_base64_str((uint8_t *)val,
1371 			    strlen(val), bsecret, MAX_BASE64_LEN);
1372 
1373 			if (ret == 0) {
1374 				/* replace the value in the nvlist */
1375 				ret = nvlist_add_string(iprops,
1376 				    PROP_CHAP_SECRET, bsecret);
1377 			}
1378 		}
1379 	}
1380 
1381 	if (ret == 0) {
1382 		ret = it_validate_iniprops(iprops, *errlist);
1383 	}
1384 
1385 	if (ret != 0) {
1386 		if (iprops) {
1387 			nvlist_free(iprops);
1388 		}
1389 		return (ret);
1390 	}
1391 
1392 	if (ini->ini_properties) {
1393 		nvlist_free(ini->ini_properties);
1394 	}
1395 	ini->ini_properties = iprops;
1396 
1397 	return (0);
1398 }
1399 
1400 /*
1401  * Function:  it_ini_delete()
1402  *
1403  * Remove the specified initiator context from the global configuration.
1404  * The removal will not take effect until the modified configuration is
1405  * committed by calling it_config_commit().
1406  *
1407  * Parameters:
1408  *    cfg		The current iSCSI configration obtained from
1409  *			it_config_load()
1410  *    ini		Pointer to the it_ini_t structure representing
1411  *			the initiator context.
1412  */
1413 void
1414 it_ini_delete(it_config_t *cfg, it_ini_t *ini)
1415 {
1416 	it_ini_t	*ptr;
1417 	it_ini_t	*prev = NULL;
1418 
1419 	if (!cfg || !ini) {
1420 		return;
1421 	}
1422 
1423 	ptr = cfg->config_ini_list;
1424 	while (ptr) {
1425 		if (strcmp(ptr->ini_name, ini->ini_name) == 0) {
1426 			break;
1427 		}
1428 		prev = ptr;
1429 		ptr = ptr->ini_next;
1430 	}
1431 
1432 	if (!ptr) {
1433 		return;
1434 	}
1435 
1436 	if (prev) {
1437 		prev->ini_next = ptr->ini_next;
1438 	} else {
1439 		cfg->config_ini_list = ptr->ini_next;
1440 	}
1441 
1442 	ptr->ini_next = NULL; /* Only free this initiator */
1443 
1444 	cfg->config_ini_count--;
1445 
1446 	it_ini_free(ptr);
1447 }
1448 
1449 /*
1450  * Function:  it_ini_free()
1451  *
1452  * Deallocates resources of an it_ini_t structure. If ini->next is
1453  * not NULL, frees all members of the list.
1454  */
1455 void
1456 it_ini_free(it_ini_t *ini)
1457 {
1458 	it_ini_free_cmn(ini);
1459 }
1460 
1461 /*
1462  * Goes through the target property list and validates
1463  * each entry.  If errs is non-NULL, will return explicit errors
1464  * for each property that fails validation.
1465  */
1466 static int
1467 it_validate_tgtprops(nvlist_t *nvl, nvlist_t *errs)
1468 {
1469 	int		errcnt = 0;
1470 	nvpair_t	*nvp = NULL;
1471 	data_type_t	nvtype;
1472 	char		*name;
1473 	char		*val;
1474 	char		*auth = NULL;
1475 
1476 	if (!nvl) {
1477 		return (0);
1478 	}
1479 
1480 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1481 		name = nvpair_name(nvp);
1482 		nvtype = nvpair_type(nvp);
1483 
1484 		if (!name) {
1485 			continue;
1486 		}
1487 
1488 		val = NULL;
1489 		if (strcmp(name, PROP_TARGET_CHAP_USER) == 0) {
1490 			if (nvtype != DATA_TYPE_STRING) {
1491 				PROPERR(errs, name,
1492 				    gettext("must be a string value"));
1493 				errcnt++;
1494 				continue;
1495 			}
1496 		} else if (strcmp(name, PROP_TARGET_CHAP_SECRET) == 0) {
1497 			/*
1498 			 * must be between 12 and 255 chars in cleartext.
1499 			 * will be base64 encoded when it's set.
1500 			 */
1501 			if (nvtype == DATA_TYPE_STRING) {
1502 				(void) nvpair_value_string(nvp, &val);
1503 			}
1504 
1505 			if (!val) {
1506 				PROPERR(errs, name,
1507 				    gettext("must be a string value"));
1508 				errcnt++;
1509 				continue;
1510 			}
1511 		} else if (strcmp(name, PROP_ALIAS) == 0) {
1512 			if (nvtype != DATA_TYPE_STRING) {
1513 				PROPERR(errs, name,
1514 				    gettext("must be a string value"));
1515 				errcnt++;
1516 				continue;
1517 			}
1518 		} else if (strcmp(name, PROP_AUTH) == 0) {
1519 			if (nvtype == DATA_TYPE_STRING) {
1520 				val = NULL;
1521 				(void) nvpair_value_string(nvp, &val);
1522 			}
1523 
1524 			if (!val) {
1525 				PROPERR(errs, name,
1526 				    gettext("must be a string value"));
1527 				errcnt++;
1528 				continue;
1529 			}
1530 			if ((strcmp(val, PA_AUTH_NONE) != 0) &&
1531 			    (strcmp(val, PA_AUTH_CHAP) != 0) &&
1532 			    (strcmp(val, PA_AUTH_RADIUS) != 0) &&
1533 			    (strcmp(val, "default") != 0)) {
1534 				PROPERR(errs, val, gettext(
1535 				    "must be none, chap, radius or default"));
1536 				errcnt++;
1537 			}
1538 			auth = val;
1539 			continue;
1540 		} else if (strcmp(name, PROP_OLD_TARGET_NAME) == 0) {
1541 			continue;
1542 		} else {
1543 			/* unrecognized property */
1544 			PROPERR(errs, name, gettext("unrecognized property"));
1545 			errcnt++;
1546 		}
1547 	}
1548 
1549 	if (errcnt) {
1550 		return (EINVAL);
1551 	}
1552 
1553 	/* if auth is being set to default, remove from this nvlist */
1554 	if (auth && (strcmp(auth, "default") == 0)) {
1555 		(void) nvlist_remove_all(nvl, PROP_AUTH);
1556 	}
1557 
1558 	return (0);
1559 }
1560 
1561 /*
1562  * Goes through the config property list and validates
1563  * each entry.  If errs is non-NULL, will return explicit errors
1564  * for each property that fails validation.
1565  */
1566 static int
1567 it_validate_configprops(nvlist_t *nvl, nvlist_t *errs)
1568 {
1569 	int				errcnt = 0;
1570 	nvpair_t			*nvp = NULL;
1571 	data_type_t			nvtype;
1572 	char				*name;
1573 	char				*val;
1574 	struct sockaddr_storage		sa;
1575 	boolean_t			update_rad_server = B_FALSE;
1576 	char				*rad_server;
1577 	char				*auth = NULL;
1578 
1579 	if (!nvl) {
1580 		return (0);
1581 	}
1582 
1583 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1584 		name = nvpair_name(nvp);
1585 		nvtype = nvpair_type(nvp);
1586 
1587 		if (!name) {
1588 			continue;
1589 		}
1590 
1591 		val = NULL;
1592 
1593 		/* prefetch string value as we mostly need it */
1594 		if (nvtype == DATA_TYPE_STRING) {
1595 			(void) nvpair_value_string(nvp, &val);
1596 		}
1597 
1598 		if (strcmp(name, PROP_ALIAS) == 0) {
1599 			if (!val) {
1600 				PROPERR(errs, name,
1601 				    gettext("must be a string value"));
1602 				errcnt++;
1603 			}
1604 		} else if (strcmp(name, PROP_AUTH) == 0) {
1605 			if (!val) {
1606 				PROPERR(errs, name,
1607 				    gettext("must be a string value"));
1608 				errcnt++;
1609 				continue;
1610 			}
1611 
1612 			if ((strcmp(val, PA_AUTH_NONE) != 0) &&
1613 			    (strcmp(val, PA_AUTH_CHAP) != 0) &&
1614 			    (strcmp(val, PA_AUTH_RADIUS) != 0)) {
1615 				PROPERR(errs, PROP_AUTH,
1616 				    gettext("must be none, chap or radius"));
1617 				errcnt++;
1618 			}
1619 
1620 			auth = val;
1621 
1622 		} else if (strcmp(name, PROP_ISNS_ENABLED) == 0) {
1623 			if (nvtype != DATA_TYPE_BOOLEAN_VALUE) {
1624 				PROPERR(errs, name,
1625 				    gettext("must be a boolean value"));
1626 				errcnt++;
1627 			}
1628 		} else if (strcmp(name, PROP_ISNS_SERVER) == 0) {
1629 			char		**arr = NULL;
1630 			uint32_t	acount = 0;
1631 
1632 			(void) nvlist_lookup_string_array(nvl, name,
1633 			    &arr, &acount);
1634 
1635 			while (acount > 0) {
1636 				if (strcasecmp(arr[acount - 1], "none") == 0) {
1637 					break;
1638 				}
1639 				if ((it_common_convert_sa(arr[acount - 1],
1640 				    &sa, 0)) == NULL) {
1641 					PROPERR(errs, arr[acount - 1],
1642 					    gettext("invalid address"));
1643 					errcnt++;
1644 				}
1645 				acount--;
1646 			}
1647 
1648 		} else if (strcmp(name, PROP_RADIUS_SECRET) == 0) {
1649 			if (!val) {
1650 				PROPERR(errs, name,
1651 				    gettext("must be a string value"));
1652 				errcnt++;
1653 				continue;
1654 			}
1655 		} else if (strcmp(name, PROP_RADIUS_SERVER) == 0) {
1656 			struct sockaddr_storage		sa;
1657 			if (!val) {
1658 				PROPERR(errs, name,
1659 				    gettext("must be a string value"));
1660 				errcnt++;
1661 				continue;
1662 			}
1663 
1664 			if ((it_common_convert_sa(val, &sa,
1665 			    DEFAULT_RADIUS_PORT)) == NULL) {
1666 				PROPERR(errs, name,
1667 				    gettext("invalid address"));
1668 				errcnt++;
1669 			} else {
1670 				/*
1671 				 * rewrite this property to ensure port
1672 				 * number is added.
1673 				 */
1674 
1675 				if (sockaddr_to_str(&sa, &rad_server) == 0) {
1676 					update_rad_server = B_TRUE;
1677 				}
1678 			}
1679 		} else {
1680 			/* unrecognized property */
1681 			PROPERR(errs, name, gettext("unrecognized property"));
1682 			errcnt++;
1683 		}
1684 	}
1685 
1686 	/*
1687 	 * If we successfully reformatted the radius server to add the port
1688 	 * number then update the nvlist
1689 	 */
1690 	if (update_rad_server) {
1691 		(void) nvlist_add_string(nvl, PROP_RADIUS_SERVER, rad_server);
1692 	}
1693 
1694 	/*
1695 	 * if auth = radius, ensure radius server & secret are set.
1696 	 */
1697 	if (auth) {
1698 		if (strcmp(auth, PA_AUTH_RADIUS) == 0) {
1699 			/* need server & secret for radius */
1700 			if (!nvlist_exists(nvl, PROP_RADIUS_SERVER)) {
1701 				PROPERR(errs, PROP_RADIUS_SERVER,
1702 				    gettext("missing required property"));
1703 				errcnt++;
1704 			}
1705 			if (!nvlist_exists(nvl, PROP_RADIUS_SECRET)) {
1706 				PROPERR(errs, PROP_RADIUS_SECRET,
1707 				    gettext("missing required property"));
1708 				errcnt++;
1709 			}
1710 		}
1711 	}
1712 
1713 	if (errcnt) {
1714 		return (EINVAL);
1715 	}
1716 
1717 	return (0);
1718 }
1719 
1720 /*
1721  * Goes through the ini property list and validates
1722  * each entry.  If errs is non-NULL, will return explicit errors
1723  * for each property that fails validation.
1724  */
1725 static int
1726 it_validate_iniprops(nvlist_t *nvl, nvlist_t *errs)
1727 {
1728 	int				errcnt = 0;
1729 	nvpair_t			*nvp = NULL;
1730 	data_type_t			nvtype;
1731 	char				*name;
1732 	char				*val;
1733 
1734 	if (!nvl) {
1735 		return (0);
1736 	}
1737 
1738 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1739 		name = nvpair_name(nvp);
1740 		nvtype = nvpair_type(nvp);
1741 
1742 		if (!name) {
1743 			continue;
1744 		}
1745 
1746 		if (strcmp(name, PROP_CHAP_USER) == 0) {
1747 			if (nvtype != DATA_TYPE_STRING) {
1748 				PROPERR(errs, name,
1749 				    gettext("must be a string value"));
1750 				errcnt++;
1751 				continue;
1752 			}
1753 		} else if (strcmp(name, PROP_CHAP_SECRET) == 0) {
1754 			/*
1755 			 * must be between 12 and 255 chars in cleartext.
1756 			 * will be base64 encoded when it's set.
1757 			 */
1758 			if (nvtype == DATA_TYPE_STRING) {
1759 				val = NULL;
1760 				(void) nvpair_value_string(nvp, &val);
1761 			}
1762 
1763 			if (!val) {
1764 				PROPERR(errs, name,
1765 				    gettext("must be a string value"));
1766 				errcnt++;
1767 				continue;
1768 			}
1769 		} else {
1770 			/* unrecognized property */
1771 			PROPERR(errs, name, gettext("unrecognized property"));
1772 			errcnt++;
1773 		}
1774 	}
1775 
1776 	if (errcnt) {
1777 		return (EINVAL);
1778 	}
1779 
1780 	return (0);
1781 }
1782 
1783 static int
1784 it_iqn_generate(char *iqn_buf, int iqn_buf_len, char *opt_iqn_suffix)
1785 {
1786 	int		ret;
1787 	uuid_t		id;
1788 	char		id_str[UUID_PRINTABLE_STRING_LENGTH];
1789 
1790 	uuid_generate_random(id);
1791 	uuid_unparse(id, id_str);
1792 
1793 	if (opt_iqn_suffix) {
1794 		ret = snprintf(iqn_buf, iqn_buf_len, "iqn.1986-03.com.sun:"
1795 		    "%02d:%s.%s", TARGET_NAME_VERS, id_str, opt_iqn_suffix);
1796 	} else {
1797 		ret = snprintf(iqn_buf, iqn_buf_len, "iqn.1986-03.com.sun:"
1798 		    "%02d:%s", TARGET_NAME_VERS, id_str);
1799 	}
1800 
1801 	if (ret > iqn_buf_len) {
1802 		return (1);
1803 	}
1804 
1805 	return (0);
1806 }
1807 
1808 static int
1809 it_val_pass(char *name, char *val, nvlist_t *e)
1810 {
1811 	size_t		sz;
1812 
1813 	if (!name || !val) {
1814 		return (EINVAL);
1815 	}
1816 
1817 	/*
1818 	 * must be at least 12 chars and less than 256 chars cleartext.
1819 	 */
1820 	sz = strlen(val);
1821 
1822 	/*
1823 	 * Since we will be automatically encoding secrets we don't really
1824 	 * need the prefix anymore.
1825 	 */
1826 	if (sz < 12) {
1827 		PROPERR(e, name, gettext("secret too short"));
1828 	} else if (sz > 255) {
1829 		PROPERR(e, name, gettext("secret too long"));
1830 	} else {
1831 		/* all is well */
1832 		return (0);
1833 	}
1834 
1835 	return (1);
1836 }
1837 
1838 /*
1839  * Function:  validate_iscsi_name()
1840  *
1841  * Ensures the passed-in string is a valid IQN or EUI iSCSI name
1842  *
1843  */
1844 boolean_t
1845 validate_iscsi_name(char *in_name)
1846 {
1847 	size_t		in_len;
1848 	int		i;
1849 	char		month[3];
1850 
1851 	if (in_name == NULL) {
1852 		return (B_FALSE);
1853 	}
1854 
1855 	in_len = strlen(in_name);
1856 	if (in_len < 12) {
1857 		return (B_FALSE);
1858 	}
1859 
1860 	if (strncasecmp(in_name, "iqn.", 4) == 0) {
1861 		/*
1862 		 * IQN names are iqn.yyyy-mm.<xxx>
1863 		 */
1864 		if ((!isdigit(in_name[4])) ||
1865 		    (!isdigit(in_name[5])) ||
1866 		    (!isdigit(in_name[6])) ||
1867 		    (!isdigit(in_name[7])) ||
1868 		    (in_name[8] != '-') ||
1869 		    (!isdigit(in_name[9])) ||
1870 		    (!isdigit(in_name[10])) ||
1871 		    (in_name[11] != '.')) {
1872 			return (B_FALSE);
1873 		}
1874 
1875 		(void) strncpy(month, &(in_name[9]), 2);
1876 		month[2] = '\0';
1877 
1878 		i = atoi(month);
1879 		if ((i < 0) || (i > 12)) {
1880 			return (B_FALSE);
1881 		}
1882 
1883 		/*
1884 		 * RFC 3722: if using only ASCII chars, only the following
1885 		 * chars are allowed: dash, dot, colon, lower case a-z, 0-9.
1886 		 * We allow upper case names, which should be folded
1887 		 * to lower case names later.
1888 		 */
1889 		for (i = 12; i < in_len; i++) {
1890 			char c = in_name[i];
1891 
1892 			if ((c != '-') && (c != '.') && (c != ':') &&
1893 			    !isalpha(c) && !isdigit(c)) {
1894 				return (B_FALSE);
1895 			}
1896 		}
1897 
1898 		/* Finally, validate the overall length, in wide chars */
1899 		in_len = mbstowcs(NULL, in_name, 0);
1900 		if (in_len > ISCSI_NAME_LEN_MAX) {
1901 			return (B_FALSE);
1902 		}
1903 	} else if (strncasecmp(in_name, "eui.", 4) == 0) {
1904 		/*
1905 		 * EUI names are "eui." + 16 hex chars
1906 		 */
1907 		if (in_len != 20) {
1908 			return (B_FALSE);
1909 		}
1910 
1911 		for (i = 4; i < in_len; i++) {
1912 			if (!isxdigit(in_name[i])) {
1913 				return (B_FALSE);
1914 			}
1915 		}
1916 	} else {
1917 		return (B_FALSE);
1918 	}
1919 
1920 	return (B_TRUE);
1921 }
1922