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