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