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