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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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