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