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