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