1 %{ 2 /*- 3 * Copyright (c) 2012 The FreeBSD Foundation 4 * All rights reserved. 5 * 6 * This software was developed by Edward Tomasz Napierala under sponsorship 7 * from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $FreeBSD$ 31 */ 32 33 #include <sys/queue.h> 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <assert.h> 37 #include <stdio.h> 38 #include <stdint.h> 39 #include <stdlib.h> 40 #include <string.h> 41 42 #include "ctld.h" 43 44 extern FILE *yyin; 45 extern char *yytext; 46 extern int lineno; 47 48 static struct conf *conf = NULL; 49 static struct auth_group *auth_group = NULL; 50 static struct portal_group *portal_group = NULL; 51 static struct target *target = NULL; 52 static struct lun *lun = NULL; 53 54 extern void yyerror(const char *); 55 extern int yylex(void); 56 extern void yyrestart(FILE *); 57 58 %} 59 60 %token ALIAS AUTH_GROUP AUTH_TYPE BACKEND BLOCKSIZE CHAP CHAP_MUTUAL 61 %token CLOSING_BRACKET DEBUG DEVICE_ID DISCOVERY_AUTH_GROUP INITIATOR_NAME 62 %token INITIATOR_PORTAL LISTEN LISTEN_ISER LUN MAXPROC OPENING_BRACKET 63 %token OPTION PATH PIDFILE PORTAL_GROUP SERIAL SIZE STR TARGET TIMEOUT 64 %token ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT 65 66 %union 67 { 68 char *str; 69 } 70 71 %token <str> STR 72 73 %% 74 75 statements: 76 | 77 statements statement 78 ; 79 80 statement: 81 debug 82 | 83 timeout 84 | 85 maxproc 86 | 87 pidfile 88 | 89 isns_server 90 | 91 isns_period 92 | 93 isns_timeout 94 | 95 auth_group 96 | 97 portal_group 98 | 99 target 100 ; 101 102 debug: DEBUG STR 103 { 104 uint64_t tmp; 105 106 if (expand_number($2, &tmp) != 0) { 107 yyerror("invalid numeric value"); 108 free($2); 109 return (1); 110 } 111 112 conf->conf_debug = tmp; 113 } 114 ; 115 116 timeout: TIMEOUT STR 117 { 118 uint64_t tmp; 119 120 if (expand_number($2, &tmp) != 0) { 121 yyerror("invalid numeric value"); 122 free($2); 123 return (1); 124 } 125 126 conf->conf_timeout = tmp; 127 } 128 ; 129 130 maxproc: MAXPROC STR 131 { 132 uint64_t tmp; 133 134 if (expand_number($2, &tmp) != 0) { 135 yyerror("invalid numeric value"); 136 free($2); 137 return (1); 138 } 139 140 conf->conf_maxproc = tmp; 141 } 142 ; 143 144 pidfile: PIDFILE STR 145 { 146 if (conf->conf_pidfile_path != NULL) { 147 log_warnx("pidfile specified more than once"); 148 free($2); 149 return (1); 150 } 151 conf->conf_pidfile_path = $2; 152 } 153 ; 154 155 isns_server: ISNS_SERVER STR 156 { 157 int error; 158 159 error = isns_new(conf, $2); 160 free($2); 161 if (error != 0) 162 return (1); 163 } 164 ; 165 166 isns_period: ISNS_PERIOD STR 167 { 168 uint64_t tmp; 169 170 if (expand_number($2, &tmp) != 0) { 171 yyerror("invalid numeric value"); 172 free($2); 173 return (1); 174 } 175 176 conf->conf_isns_period = tmp; 177 } 178 ; 179 180 isns_timeout: ISNS_TIMEOUT STR 181 { 182 uint64_t tmp; 183 184 if (expand_number($2, &tmp) != 0) { 185 yyerror("invalid numeric value"); 186 free($2); 187 return (1); 188 } 189 190 conf->conf_isns_timeout = tmp; 191 } 192 ; 193 194 auth_group: AUTH_GROUP auth_group_name 195 OPENING_BRACKET auth_group_entries CLOSING_BRACKET 196 { 197 auth_group = NULL; 198 } 199 ; 200 201 auth_group_name: STR 202 { 203 /* 204 * Make it possible to redefine default 205 * auth-group. but only once. 206 */ 207 if (strcmp($1, "default") == 0 && 208 conf->conf_default_ag_defined == false) { 209 auth_group = auth_group_find(conf, $1); 210 conf->conf_default_ag_defined = true; 211 } else { 212 auth_group = auth_group_new(conf, $1); 213 } 214 free($1); 215 if (auth_group == NULL) 216 return (1); 217 } 218 ; 219 220 auth_group_entries: 221 | 222 auth_group_entries auth_group_entry 223 ; 224 225 auth_group_entry: 226 auth_group_auth_type 227 | 228 auth_group_chap 229 | 230 auth_group_chap_mutual 231 | 232 auth_group_initiator_name 233 | 234 auth_group_initiator_portal 235 ; 236 237 auth_group_auth_type: AUTH_TYPE STR 238 { 239 int error; 240 241 error = auth_group_set_type_str(auth_group, $2); 242 free($2); 243 if (error != 0) 244 return (1); 245 } 246 ; 247 248 auth_group_chap: CHAP STR STR 249 { 250 const struct auth *ca; 251 252 ca = auth_new_chap(auth_group, $2, $3); 253 free($2); 254 free($3); 255 if (ca == NULL) 256 return (1); 257 } 258 ; 259 260 auth_group_chap_mutual: CHAP_MUTUAL STR STR STR STR 261 { 262 const struct auth *ca; 263 264 ca = auth_new_chap_mutual(auth_group, $2, $3, $4, $5); 265 free($2); 266 free($3); 267 free($4); 268 free($5); 269 if (ca == NULL) 270 return (1); 271 } 272 ; 273 274 auth_group_initiator_name: INITIATOR_NAME STR 275 { 276 const struct auth_name *an; 277 278 an = auth_name_new(auth_group, $2); 279 free($2); 280 if (an == NULL) 281 return (1); 282 } 283 ; 284 285 auth_group_initiator_portal: INITIATOR_PORTAL STR 286 { 287 const struct auth_portal *ap; 288 289 ap = auth_portal_new(auth_group, $2); 290 free($2); 291 if (ap == NULL) 292 return (1); 293 } 294 ; 295 296 portal_group: PORTAL_GROUP portal_group_name 297 OPENING_BRACKET portal_group_entries CLOSING_BRACKET 298 { 299 portal_group = NULL; 300 } 301 ; 302 303 portal_group_name: STR 304 { 305 /* 306 * Make it possible to redefine default 307 * portal-group. but only once. 308 */ 309 if (strcmp($1, "default") == 0 && 310 conf->conf_default_pg_defined == false) { 311 portal_group = portal_group_find(conf, $1); 312 conf->conf_default_pg_defined = true; 313 } else { 314 portal_group = portal_group_new(conf, $1); 315 } 316 free($1); 317 if (portal_group == NULL) 318 return (1); 319 } 320 ; 321 322 portal_group_entries: 323 | 324 portal_group_entries portal_group_entry 325 ; 326 327 portal_group_entry: 328 portal_group_discovery_auth_group 329 | 330 portal_group_listen 331 | 332 portal_group_listen_iser 333 ; 334 335 portal_group_discovery_auth_group: DISCOVERY_AUTH_GROUP STR 336 { 337 if (portal_group->pg_discovery_auth_group != NULL) { 338 log_warnx("discovery-auth-group for portal-group " 339 "\"%s\" specified more than once", 340 portal_group->pg_name); 341 return (1); 342 } 343 portal_group->pg_discovery_auth_group = 344 auth_group_find(conf, $2); 345 if (portal_group->pg_discovery_auth_group == NULL) { 346 log_warnx("unknown discovery-auth-group \"%s\" " 347 "for portal-group \"%s\"", 348 $2, portal_group->pg_name); 349 return (1); 350 } 351 free($2); 352 } 353 ; 354 355 portal_group_listen: LISTEN STR 356 { 357 int error; 358 359 error = portal_group_add_listen(portal_group, $2, false); 360 free($2); 361 if (error != 0) 362 return (1); 363 } 364 ; 365 366 portal_group_listen_iser: LISTEN_ISER STR 367 { 368 int error; 369 370 error = portal_group_add_listen(portal_group, $2, true); 371 free($2); 372 if (error != 0) 373 return (1); 374 } 375 ; 376 377 target: TARGET target_name 378 OPENING_BRACKET target_entries CLOSING_BRACKET 379 { 380 target = NULL; 381 } 382 ; 383 384 target_name: STR 385 { 386 target = target_new(conf, $1); 387 free($1); 388 if (target == NULL) 389 return (1); 390 } 391 ; 392 393 target_entries: 394 | 395 target_entries target_entry 396 ; 397 398 target_entry: 399 target_alias 400 | 401 target_auth_group 402 | 403 target_auth_type 404 | 405 target_chap 406 | 407 target_chap_mutual 408 | 409 target_initiator_name 410 | 411 target_initiator_portal 412 | 413 target_portal_group 414 | 415 target_lun 416 ; 417 418 target_alias: ALIAS STR 419 { 420 if (target->t_alias != NULL) { 421 log_warnx("alias for target \"%s\" " 422 "specified more than once", target->t_name); 423 return (1); 424 } 425 target->t_alias = $2; 426 } 427 ; 428 429 target_auth_group: AUTH_GROUP STR 430 { 431 if (target->t_auth_group != NULL) { 432 if (target->t_auth_group->ag_name != NULL) 433 log_warnx("auth-group for target \"%s\" " 434 "specified more than once", target->t_name); 435 else 436 log_warnx("cannot use both auth-group and explicit " 437 "authorisations for target \"%s\"", 438 target->t_name); 439 return (1); 440 } 441 target->t_auth_group = auth_group_find(conf, $2); 442 if (target->t_auth_group == NULL) { 443 log_warnx("unknown auth-group \"%s\" for target " 444 "\"%s\"", $2, target->t_name); 445 return (1); 446 } 447 free($2); 448 } 449 ; 450 451 target_auth_type: AUTH_TYPE STR 452 { 453 int error; 454 455 if (target->t_auth_group != NULL) { 456 if (target->t_auth_group->ag_name != NULL) { 457 log_warnx("cannot use both auth-group and " 458 "auth-type for target \"%s\"", 459 target->t_name); 460 return (1); 461 } 462 } else { 463 target->t_auth_group = auth_group_new(conf, NULL); 464 if (target->t_auth_group == NULL) { 465 free($2); 466 return (1); 467 } 468 target->t_auth_group->ag_target = target; 469 } 470 error = auth_group_set_type_str(target->t_auth_group, $2); 471 free($2); 472 if (error != 0) 473 return (1); 474 } 475 ; 476 477 target_chap: CHAP STR STR 478 { 479 const struct auth *ca; 480 481 if (target->t_auth_group != NULL) { 482 if (target->t_auth_group->ag_name != NULL) { 483 log_warnx("cannot use both auth-group and " 484 "chap for target \"%s\"", 485 target->t_name); 486 free($2); 487 free($3); 488 return (1); 489 } 490 } else { 491 target->t_auth_group = auth_group_new(conf, NULL); 492 if (target->t_auth_group == NULL) { 493 free($2); 494 free($3); 495 return (1); 496 } 497 target->t_auth_group->ag_target = target; 498 } 499 ca = auth_new_chap(target->t_auth_group, $2, $3); 500 free($2); 501 free($3); 502 if (ca == NULL) 503 return (1); 504 } 505 ; 506 507 target_chap_mutual: CHAP_MUTUAL STR STR STR STR 508 { 509 const struct auth *ca; 510 511 if (target->t_auth_group != NULL) { 512 if (target->t_auth_group->ag_name != NULL) { 513 log_warnx("cannot use both auth-group and " 514 "chap-mutual for target \"%s\"", 515 target->t_name); 516 free($2); 517 free($3); 518 free($4); 519 free($5); 520 return (1); 521 } 522 } else { 523 target->t_auth_group = auth_group_new(conf, NULL); 524 if (target->t_auth_group == NULL) { 525 free($2); 526 free($3); 527 free($4); 528 free($5); 529 return (1); 530 } 531 target->t_auth_group->ag_target = target; 532 } 533 ca = auth_new_chap_mutual(target->t_auth_group, 534 $2, $3, $4, $5); 535 free($2); 536 free($3); 537 free($4); 538 free($5); 539 if (ca == NULL) 540 return (1); 541 } 542 ; 543 544 target_initiator_name: INITIATOR_NAME STR 545 { 546 const struct auth_name *an; 547 548 if (target->t_auth_group != NULL) { 549 if (target->t_auth_group->ag_name != NULL) { 550 log_warnx("cannot use both auth-group and " 551 "initiator-name for target \"%s\"", 552 target->t_name); 553 free($2); 554 return (1); 555 } 556 } else { 557 target->t_auth_group = auth_group_new(conf, NULL); 558 if (target->t_auth_group == NULL) { 559 free($2); 560 return (1); 561 } 562 target->t_auth_group->ag_target = target; 563 } 564 an = auth_name_new(target->t_auth_group, $2); 565 free($2); 566 if (an == NULL) 567 return (1); 568 } 569 ; 570 571 target_initiator_portal: INITIATOR_PORTAL STR 572 { 573 const struct auth_portal *ap; 574 575 if (target->t_auth_group != NULL) { 576 if (target->t_auth_group->ag_name != NULL) { 577 log_warnx("cannot use both auth-group and " 578 "initiator-portal for target \"%s\"", 579 target->t_name); 580 free($2); 581 return (1); 582 } 583 } else { 584 target->t_auth_group = auth_group_new(conf, NULL); 585 if (target->t_auth_group == NULL) { 586 free($2); 587 return (1); 588 } 589 target->t_auth_group->ag_target = target; 590 } 591 ap = auth_portal_new(target->t_auth_group, $2); 592 free($2); 593 if (ap == NULL) 594 return (1); 595 } 596 ; 597 598 target_portal_group: PORTAL_GROUP STR 599 { 600 if (target->t_portal_group != NULL) { 601 log_warnx("portal-group for target \"%s\" " 602 "specified more than once", target->t_name); 603 free($2); 604 return (1); 605 } 606 target->t_portal_group = portal_group_find(conf, $2); 607 if (target->t_portal_group == NULL) { 608 log_warnx("unknown portal-group \"%s\" for target " 609 "\"%s\"", $2, target->t_name); 610 free($2); 611 return (1); 612 } 613 free($2); 614 } 615 ; 616 617 target_lun: LUN lun_number 618 OPENING_BRACKET lun_entries CLOSING_BRACKET 619 { 620 lun = NULL; 621 } 622 ; 623 624 lun_number: STR 625 { 626 uint64_t tmp; 627 628 if (expand_number($1, &tmp) != 0) { 629 yyerror("invalid numeric value"); 630 free($1); 631 return (1); 632 } 633 634 lun = lun_new(target, tmp); 635 if (lun == NULL) 636 return (1); 637 } 638 ; 639 640 lun_entries: 641 | 642 lun_entries lun_entry 643 ; 644 645 lun_entry: 646 lun_backend 647 | 648 lun_blocksize 649 | 650 lun_device_id 651 | 652 lun_option 653 | 654 lun_path 655 | 656 lun_serial 657 | 658 lun_size 659 ; 660 661 lun_backend: BACKEND STR 662 { 663 if (lun->l_backend != NULL) { 664 log_warnx("backend for lun %d, target \"%s\" " 665 "specified more than once", 666 lun->l_lun, target->t_name); 667 free($2); 668 return (1); 669 } 670 lun_set_backend(lun, $2); 671 free($2); 672 } 673 ; 674 675 lun_blocksize: BLOCKSIZE STR 676 { 677 uint64_t tmp; 678 679 if (expand_number($2, &tmp) != 0) { 680 yyerror("invalid numeric value"); 681 free($2); 682 return (1); 683 } 684 685 if (lun->l_blocksize != 0) { 686 log_warnx("blocksize for lun %d, target \"%s\" " 687 "specified more than once", 688 lun->l_lun, target->t_name); 689 return (1); 690 } 691 lun_set_blocksize(lun, tmp); 692 } 693 ; 694 695 lun_device_id: DEVICE_ID STR 696 { 697 if (lun->l_device_id != NULL) { 698 log_warnx("device_id for lun %d, target \"%s\" " 699 "specified more than once", 700 lun->l_lun, target->t_name); 701 free($2); 702 return (1); 703 } 704 lun_set_device_id(lun, $2); 705 free($2); 706 } 707 ; 708 709 lun_option: OPTION STR STR 710 { 711 struct lun_option *clo; 712 713 clo = lun_option_new(lun, $2, $3); 714 free($2); 715 free($3); 716 if (clo == NULL) 717 return (1); 718 } 719 ; 720 721 lun_path: PATH STR 722 { 723 if (lun->l_path != NULL) { 724 log_warnx("path for lun %d, target \"%s\" " 725 "specified more than once", 726 lun->l_lun, target->t_name); 727 free($2); 728 return (1); 729 } 730 lun_set_path(lun, $2); 731 free($2); 732 } 733 ; 734 735 lun_serial: SERIAL STR 736 { 737 if (lun->l_serial != NULL) { 738 log_warnx("serial for lun %d, target \"%s\" " 739 "specified more than once", 740 lun->l_lun, target->t_name); 741 free($2); 742 return (1); 743 } 744 lun_set_serial(lun, $2); 745 free($2); 746 } 747 ; 748 749 lun_size: SIZE STR 750 { 751 uint64_t tmp; 752 753 if (expand_number($2, &tmp) != 0) { 754 yyerror("invalid numeric value"); 755 free($2); 756 return (1); 757 } 758 759 if (lun->l_size != 0) { 760 log_warnx("size for lun %d, target \"%s\" " 761 "specified more than once", 762 lun->l_lun, target->t_name); 763 return (1); 764 } 765 lun_set_size(lun, tmp); 766 } 767 ; 768 %% 769 770 void 771 yyerror(const char *str) 772 { 773 774 log_warnx("error in configuration file at line %d near '%s': %s", 775 lineno, yytext, str); 776 } 777 778 static void 779 check_perms(const char *path) 780 { 781 struct stat sb; 782 int error; 783 784 error = stat(path, &sb); 785 if (error != 0) { 786 log_warn("stat"); 787 return; 788 } 789 if (sb.st_mode & S_IWOTH) { 790 log_warnx("%s is world-writable", path); 791 } else if (sb.st_mode & S_IROTH) { 792 log_warnx("%s is world-readable", path); 793 } else if (sb.st_mode & S_IXOTH) { 794 /* 795 * Ok, this one doesn't matter, but still do it, 796 * just for consistency. 797 */ 798 log_warnx("%s is world-executable", path); 799 } 800 801 /* 802 * XXX: Should we also check for owner != 0? 803 */ 804 } 805 806 struct conf * 807 conf_new_from_file(const char *path) 808 { 809 struct auth_group *ag; 810 struct portal_group *pg; 811 int error; 812 813 log_debugx("obtaining configuration from %s", path); 814 815 conf = conf_new(); 816 817 ag = auth_group_new(conf, "default"); 818 assert(ag != NULL); 819 820 ag = auth_group_new(conf, "no-authentication"); 821 assert(ag != NULL); 822 ag->ag_type = AG_TYPE_NO_AUTHENTICATION; 823 824 ag = auth_group_new(conf, "no-access"); 825 assert(ag != NULL); 826 ag->ag_type = AG_TYPE_DENY; 827 828 pg = portal_group_new(conf, "default"); 829 assert(pg != NULL); 830 831 yyin = fopen(path, "r"); 832 if (yyin == NULL) { 833 log_warn("unable to open configuration file %s", path); 834 conf_delete(conf); 835 return (NULL); 836 } 837 check_perms(path); 838 lineno = 1; 839 yyrestart(yyin); 840 error = yyparse(); 841 auth_group = NULL; 842 portal_group = NULL; 843 target = NULL; 844 lun = NULL; 845 fclose(yyin); 846 if (error != 0) { 847 conf_delete(conf); 848 return (NULL); 849 } 850 851 if (conf->conf_default_ag_defined == false) { 852 log_debugx("auth-group \"default\" not defined; " 853 "going with defaults"); 854 ag = auth_group_find(conf, "default"); 855 assert(ag != NULL); 856 ag->ag_type = AG_TYPE_DENY; 857 } 858 859 if (conf->conf_default_pg_defined == false) { 860 log_debugx("portal-group \"default\" not defined; " 861 "going with defaults"); 862 pg = portal_group_find(conf, "default"); 863 assert(pg != NULL); 864 portal_group_add_listen(pg, "0.0.0.0:3260", false); 865 portal_group_add_listen(pg, "[::]:3260", false); 866 } 867 868 conf->conf_kernel_port_on = true; 869 870 error = conf_verify(conf); 871 if (error != 0) { 872 conf_delete(conf); 873 return (NULL); 874 } 875 876 return (conf); 877 } 878