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