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