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