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