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