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