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