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