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