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