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