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