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