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
it_config_load(it_config_t ** cfg)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
it_config_commit(it_config_t * cfg)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
it_config_setprop(it_config_t * cfg,nvlist_t * proplist,nvlist_t ** errlist)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
it_config_free(it_config_t * cfg)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
it_tgt_create(it_config_t * cfg,it_tgt_t ** tgt,char * tgt_name)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
it_tgt_setprop(it_config_t * cfg,it_tgt_t * tgt,nvlist_t * proplist,nvlist_t ** errlist)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
it_tgt_delete(it_config_t * cfg,it_tgt_t * tgt,boolean_t force)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
it_tgt_free(it_tgt_t * tgt)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
it_tpgt_create(it_config_t * cfg,it_tgt_t * tgt,it_tpgt_t ** tpgt,char * tpg_name,uint16_t tpgt_tag)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
it_tpgt_delete(it_config_t * cfg,it_tgt_t * tgt,it_tpgt_t * tpgt)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
it_tpgt_free(it_tpgt_t * tpgt)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
it_tpg_create(it_config_t * cfg,it_tpg_t ** tpg,char * tpg_name,char * portal_ip_port)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
it_tpg_delete(it_config_t * cfg,it_tpg_t * tpg,boolean_t force)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
it_tpg_free(it_tpg_t * tpg)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
it_portal_create(it_config_t * cfg,it_tpg_t * tpg,it_portal_t ** portal,char * portal_ip_port)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
it_portal_delete(it_config_t * cfg,it_tpg_t * tpg,it_portal_t * portal)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
it_ini_create(it_config_t * cfg,it_ini_t ** ini,char * ini_node_name)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
it_ini_setprop(it_ini_t * ini,nvlist_t * proplist,nvlist_t ** errlist)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
it_ini_delete(it_config_t * cfg,it_ini_t * ini)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
it_ini_free(it_ini_t * ini)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
it_validate_tgtprops(nvlist_t * nvl,nvlist_t * errs)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
it_validate_configprops(nvlist_t * nvl,nvlist_t * errs)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
it_validate_iniprops(nvlist_t * nvl,nvlist_t * errs)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
it_iqn_generate(char * iqn_buf,int iqn_buf_len,char * opt_iqn_suffix)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
it_val_pass(char * name,char * val,nvlist_t * e)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
validate_iscsi_name(char * in_name)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
is_iscsit_enabled(void)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
canonical_iscsi_name(char * tgt)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
iqnstr(char * s)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
euistr(char * s)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
free_empty_errlist(nvlist_t ** errlist)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