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