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