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