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