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