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 DISCOVERY_FILTER 62 %token INITIATOR_NAME INITIATOR_PORTAL ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT 63 %token LISTEN LISTEN_ISER LUN MAXPROC OPENING_BRACKET OPTION 64 %token PATH PIDFILE PORTAL_GROUP REDIRECT SEMICOLON SERIAL SIZE STR 65 %token TARGET TIMEOUT 66 67 %union 68 { 69 char *str; 70 } 71 72 %token <str> STR 73 74 %% 75 76 statements: 77 | 78 statements statement 79 | 80 statements statement SEMICOLON 81 ; 82 83 statement: 84 debug 85 | 86 timeout 87 | 88 maxproc 89 | 90 pidfile 91 | 92 isns_server 93 | 94 isns_period 95 | 96 isns_timeout 97 | 98 auth_group 99 | 100 portal_group 101 | 102 lun 103 | 104 target 105 ; 106 107 debug: DEBUG STR 108 { 109 uint64_t tmp; 110 111 if (expand_number($2, &tmp) != 0) { 112 yyerror("invalid numeric value"); 113 free($2); 114 return (1); 115 } 116 117 conf->conf_debug = tmp; 118 } 119 ; 120 121 timeout: TIMEOUT STR 122 { 123 uint64_t tmp; 124 125 if (expand_number($2, &tmp) != 0) { 126 yyerror("invalid numeric value"); 127 free($2); 128 return (1); 129 } 130 131 conf->conf_timeout = tmp; 132 } 133 ; 134 135 maxproc: MAXPROC STR 136 { 137 uint64_t tmp; 138 139 if (expand_number($2, &tmp) != 0) { 140 yyerror("invalid numeric value"); 141 free($2); 142 return (1); 143 } 144 145 conf->conf_maxproc = tmp; 146 } 147 ; 148 149 pidfile: PIDFILE STR 150 { 151 if (conf->conf_pidfile_path != NULL) { 152 log_warnx("pidfile specified more than once"); 153 free($2); 154 return (1); 155 } 156 conf->conf_pidfile_path = $2; 157 } 158 ; 159 160 isns_server: ISNS_SERVER STR 161 { 162 int error; 163 164 error = isns_new(conf, $2); 165 free($2); 166 if (error != 0) 167 return (1); 168 } 169 ; 170 171 isns_period: ISNS_PERIOD STR 172 { 173 uint64_t tmp; 174 175 if (expand_number($2, &tmp) != 0) { 176 yyerror("invalid numeric value"); 177 free($2); 178 return (1); 179 } 180 181 conf->conf_isns_period = tmp; 182 } 183 ; 184 185 isns_timeout: ISNS_TIMEOUT STR 186 { 187 uint64_t tmp; 188 189 if (expand_number($2, &tmp) != 0) { 190 yyerror("invalid numeric value"); 191 free($2); 192 return (1); 193 } 194 195 conf->conf_isns_timeout = tmp; 196 } 197 ; 198 199 auth_group: AUTH_GROUP auth_group_name 200 OPENING_BRACKET auth_group_entries CLOSING_BRACKET 201 { 202 auth_group = NULL; 203 } 204 ; 205 206 auth_group_name: STR 207 { 208 /* 209 * Make it possible to redefine default 210 * auth-group. but only once. 211 */ 212 if (strcmp($1, "default") == 0 && 213 conf->conf_default_ag_defined == false) { 214 auth_group = auth_group_find(conf, $1); 215 conf->conf_default_ag_defined = true; 216 } else { 217 auth_group = auth_group_new(conf, $1); 218 } 219 free($1); 220 if (auth_group == NULL) 221 return (1); 222 } 223 ; 224 225 auth_group_entries: 226 | 227 auth_group_entries auth_group_entry 228 | 229 auth_group_entries auth_group_entry SEMICOLON 230 ; 231 232 auth_group_entry: 233 auth_group_auth_type 234 | 235 auth_group_chap 236 | 237 auth_group_chap_mutual 238 | 239 auth_group_initiator_name 240 | 241 auth_group_initiator_portal 242 ; 243 244 auth_group_auth_type: AUTH_TYPE STR 245 { 246 int error; 247 248 error = auth_group_set_type(auth_group, $2); 249 free($2); 250 if (error != 0) 251 return (1); 252 } 253 ; 254 255 auth_group_chap: CHAP STR STR 256 { 257 const struct auth *ca; 258 259 ca = auth_new_chap(auth_group, $2, $3); 260 free($2); 261 free($3); 262 if (ca == NULL) 263 return (1); 264 } 265 ; 266 267 auth_group_chap_mutual: CHAP_MUTUAL STR STR STR STR 268 { 269 const struct auth *ca; 270 271 ca = auth_new_chap_mutual(auth_group, $2, $3, $4, $5); 272 free($2); 273 free($3); 274 free($4); 275 free($5); 276 if (ca == NULL) 277 return (1); 278 } 279 ; 280 281 auth_group_initiator_name: INITIATOR_NAME STR 282 { 283 const struct auth_name *an; 284 285 an = auth_name_new(auth_group, $2); 286 free($2); 287 if (an == NULL) 288 return (1); 289 } 290 ; 291 292 auth_group_initiator_portal: INITIATOR_PORTAL STR 293 { 294 const struct auth_portal *ap; 295 296 ap = auth_portal_new(auth_group, $2); 297 free($2); 298 if (ap == NULL) 299 return (1); 300 } 301 ; 302 303 portal_group: PORTAL_GROUP portal_group_name 304 OPENING_BRACKET portal_group_entries CLOSING_BRACKET 305 { 306 portal_group = NULL; 307 } 308 ; 309 310 portal_group_name: STR 311 { 312 /* 313 * Make it possible to redefine default 314 * portal-group. but only once. 315 */ 316 if (strcmp($1, "default") == 0 && 317 conf->conf_default_pg_defined == false) { 318 portal_group = portal_group_find(conf, $1); 319 conf->conf_default_pg_defined = true; 320 } else { 321 portal_group = portal_group_new(conf, $1); 322 } 323 free($1); 324 if (portal_group == NULL) 325 return (1); 326 } 327 ; 328 329 portal_group_entries: 330 | 331 portal_group_entries portal_group_entry 332 | 333 portal_group_entries portal_group_entry SEMICOLON 334 ; 335 336 portal_group_entry: 337 portal_group_discovery_auth_group 338 | 339 portal_group_discovery_filter 340 | 341 portal_group_listen 342 | 343 portal_group_listen_iser 344 | 345 portal_group_redirect 346 ; 347 348 portal_group_discovery_auth_group: DISCOVERY_AUTH_GROUP STR 349 { 350 if (portal_group->pg_discovery_auth_group != NULL) { 351 log_warnx("discovery-auth-group for portal-group " 352 "\"%s\" specified more than once", 353 portal_group->pg_name); 354 return (1); 355 } 356 portal_group->pg_discovery_auth_group = 357 auth_group_find(conf, $2); 358 if (portal_group->pg_discovery_auth_group == NULL) { 359 log_warnx("unknown discovery-auth-group \"%s\" " 360 "for portal-group \"%s\"", 361 $2, portal_group->pg_name); 362 return (1); 363 } 364 free($2); 365 } 366 ; 367 368 portal_group_discovery_filter: DISCOVERY_FILTER STR 369 { 370 int error; 371 372 error = portal_group_set_filter(portal_group, $2); 373 free($2); 374 if (error != 0) 375 return (1); 376 } 377 ; 378 379 portal_group_listen: LISTEN STR 380 { 381 int error; 382 383 error = portal_group_add_listen(portal_group, $2, false); 384 free($2); 385 if (error != 0) 386 return (1); 387 } 388 ; 389 390 portal_group_listen_iser: LISTEN_ISER STR 391 { 392 int error; 393 394 error = portal_group_add_listen(portal_group, $2, true); 395 free($2); 396 if (error != 0) 397 return (1); 398 } 399 ; 400 401 portal_group_redirect: REDIRECT STR 402 { 403 int error; 404 405 error = portal_group_set_redirection(portal_group, $2); 406 free($2); 407 if (error != 0) 408 return (1); 409 } 410 ; 411 412 lun: LUN lun_name 413 OPENING_BRACKET lun_entries CLOSING_BRACKET 414 { 415 lun = NULL; 416 } 417 ; 418 419 lun_name: STR 420 { 421 lun = lun_new(conf, $1); 422 free($1); 423 if (lun == NULL) 424 return (1); 425 } 426 ; 427 428 target: TARGET target_name 429 OPENING_BRACKET target_entries CLOSING_BRACKET 430 { 431 target = NULL; 432 } 433 ; 434 435 target_name: STR 436 { 437 target = target_new(conf, $1); 438 free($1); 439 if (target == NULL) 440 return (1); 441 } 442 ; 443 444 target_entries: 445 | 446 target_entries target_entry 447 | 448 target_entries target_entry SEMICOLON 449 ; 450 451 target_entry: 452 target_alias 453 | 454 target_auth_group 455 | 456 target_auth_type 457 | 458 target_chap 459 | 460 target_chap_mutual 461 | 462 target_initiator_name 463 | 464 target_initiator_portal 465 | 466 target_portal_group 467 | 468 target_redirect 469 | 470 target_lun 471 | 472 target_lun_ref 473 ; 474 475 target_alias: ALIAS STR 476 { 477 if (target->t_alias != NULL) { 478 log_warnx("alias for target \"%s\" " 479 "specified more than once", target->t_name); 480 return (1); 481 } 482 target->t_alias = $2; 483 } 484 ; 485 486 target_auth_group: AUTH_GROUP STR 487 { 488 if (target->t_auth_group != NULL) { 489 if (target->t_auth_group->ag_name != NULL) 490 log_warnx("auth-group for target \"%s\" " 491 "specified more than once", target->t_name); 492 else 493 log_warnx("cannot use both auth-group and explicit " 494 "authorisations for target \"%s\"", 495 target->t_name); 496 return (1); 497 } 498 target->t_auth_group = auth_group_find(conf, $2); 499 if (target->t_auth_group == NULL) { 500 log_warnx("unknown auth-group \"%s\" for target " 501 "\"%s\"", $2, target->t_name); 502 return (1); 503 } 504 free($2); 505 } 506 ; 507 508 target_auth_type: AUTH_TYPE STR 509 { 510 int error; 511 512 if (target->t_auth_group != NULL) { 513 if (target->t_auth_group->ag_name != NULL) { 514 log_warnx("cannot use both auth-group and " 515 "auth-type for target \"%s\"", 516 target->t_name); 517 return (1); 518 } 519 } else { 520 target->t_auth_group = auth_group_new(conf, NULL); 521 if (target->t_auth_group == NULL) { 522 free($2); 523 return (1); 524 } 525 target->t_auth_group->ag_target = target; 526 } 527 error = auth_group_set_type(target->t_auth_group, $2); 528 free($2); 529 if (error != 0) 530 return (1); 531 } 532 ; 533 534 target_chap: CHAP STR STR 535 { 536 const struct auth *ca; 537 538 if (target->t_auth_group != NULL) { 539 if (target->t_auth_group->ag_name != NULL) { 540 log_warnx("cannot use both auth-group and " 541 "chap for target \"%s\"", 542 target->t_name); 543 free($2); 544 free($3); 545 return (1); 546 } 547 } else { 548 target->t_auth_group = auth_group_new(conf, NULL); 549 if (target->t_auth_group == NULL) { 550 free($2); 551 free($3); 552 return (1); 553 } 554 target->t_auth_group->ag_target = target; 555 } 556 ca = auth_new_chap(target->t_auth_group, $2, $3); 557 free($2); 558 free($3); 559 if (ca == NULL) 560 return (1); 561 } 562 ; 563 564 target_chap_mutual: CHAP_MUTUAL STR STR STR STR 565 { 566 const struct auth *ca; 567 568 if (target->t_auth_group != NULL) { 569 if (target->t_auth_group->ag_name != NULL) { 570 log_warnx("cannot use both auth-group and " 571 "chap-mutual for target \"%s\"", 572 target->t_name); 573 free($2); 574 free($3); 575 free($4); 576 free($5); 577 return (1); 578 } 579 } else { 580 target->t_auth_group = auth_group_new(conf, NULL); 581 if (target->t_auth_group == NULL) { 582 free($2); 583 free($3); 584 free($4); 585 free($5); 586 return (1); 587 } 588 target->t_auth_group->ag_target = target; 589 } 590 ca = auth_new_chap_mutual(target->t_auth_group, 591 $2, $3, $4, $5); 592 free($2); 593 free($3); 594 free($4); 595 free($5); 596 if (ca == NULL) 597 return (1); 598 } 599 ; 600 601 target_initiator_name: INITIATOR_NAME STR 602 { 603 const struct auth_name *an; 604 605 if (target->t_auth_group != NULL) { 606 if (target->t_auth_group->ag_name != NULL) { 607 log_warnx("cannot use both auth-group and " 608 "initiator-name for target \"%s\"", 609 target->t_name); 610 free($2); 611 return (1); 612 } 613 } else { 614 target->t_auth_group = auth_group_new(conf, NULL); 615 if (target->t_auth_group == NULL) { 616 free($2); 617 return (1); 618 } 619 target->t_auth_group->ag_target = target; 620 } 621 an = auth_name_new(target->t_auth_group, $2); 622 free($2); 623 if (an == NULL) 624 return (1); 625 } 626 ; 627 628 target_initiator_portal: INITIATOR_PORTAL STR 629 { 630 const struct auth_portal *ap; 631 632 if (target->t_auth_group != NULL) { 633 if (target->t_auth_group->ag_name != NULL) { 634 log_warnx("cannot use both auth-group and " 635 "initiator-portal for target \"%s\"", 636 target->t_name); 637 free($2); 638 return (1); 639 } 640 } else { 641 target->t_auth_group = auth_group_new(conf, NULL); 642 if (target->t_auth_group == NULL) { 643 free($2); 644 return (1); 645 } 646 target->t_auth_group->ag_target = target; 647 } 648 ap = auth_portal_new(target->t_auth_group, $2); 649 free($2); 650 if (ap == NULL) 651 return (1); 652 } 653 ; 654 655 target_portal_group: PORTAL_GROUP STR 656 { 657 if (target->t_portal_group != NULL) { 658 log_warnx("portal-group for target \"%s\" " 659 "specified more than once", target->t_name); 660 free($2); 661 return (1); 662 } 663 target->t_portal_group = portal_group_find(conf, $2); 664 if (target->t_portal_group == NULL) { 665 log_warnx("unknown portal-group \"%s\" for target " 666 "\"%s\"", $2, target->t_name); 667 free($2); 668 return (1); 669 } 670 free($2); 671 } 672 ; 673 674 target_redirect: REDIRECT STR 675 { 676 int error; 677 678 error = target_set_redirection(target, $2); 679 free($2); 680 if (error != 0) 681 return (1); 682 } 683 ; 684 685 target_lun: LUN lun_number 686 OPENING_BRACKET lun_entries CLOSING_BRACKET 687 { 688 lun = NULL; 689 } 690 ; 691 692 lun_number: STR 693 { 694 uint64_t tmp; 695 char *name; 696 697 if (expand_number($1, &tmp) != 0) { 698 yyerror("invalid numeric value"); 699 free($1); 700 return (1); 701 } 702 703 asprintf(&name, "%s,lun,%ju", target->t_name, tmp); 704 lun = lun_new(conf, name); 705 if (lun == NULL) 706 return (1); 707 708 lun_set_scsiname(lun, name); 709 target->t_luns[tmp] = lun; 710 } 711 ; 712 713 target_lun_ref: LUN STR STR 714 { 715 uint64_t tmp; 716 717 if (expand_number($2, &tmp) != 0) { 718 yyerror("invalid numeric value"); 719 free($2); 720 free($3); 721 return (1); 722 } 723 free($2); 724 725 lun = lun_find(conf, $3); 726 free($3); 727 if (lun == NULL) 728 return (1); 729 730 target->t_luns[tmp] = lun; 731 } 732 ; 733 734 lun_entries: 735 | 736 lun_entries lun_entry 737 | 738 lun_entries lun_entry SEMICOLON 739 ; 740 741 lun_entry: 742 lun_backend 743 | 744 lun_blocksize 745 | 746 lun_device_id 747 | 748 lun_option 749 | 750 lun_path 751 | 752 lun_serial 753 | 754 lun_size 755 ; 756 757 lun_backend: BACKEND STR 758 { 759 if (lun->l_backend != NULL) { 760 log_warnx("backend for lun \"%s\" " 761 "specified more than once", 762 lun->l_name); 763 free($2); 764 return (1); 765 } 766 lun_set_backend(lun, $2); 767 free($2); 768 } 769 ; 770 771 lun_blocksize: BLOCKSIZE STR 772 { 773 uint64_t tmp; 774 775 if (expand_number($2, &tmp) != 0) { 776 yyerror("invalid numeric value"); 777 free($2); 778 return (1); 779 } 780 781 if (lun->l_blocksize != 0) { 782 log_warnx("blocksize for lun \"%s\" " 783 "specified more than once", 784 lun->l_name); 785 return (1); 786 } 787 lun_set_blocksize(lun, tmp); 788 } 789 ; 790 791 lun_device_id: DEVICE_ID STR 792 { 793 if (lun->l_device_id != NULL) { 794 log_warnx("device_id for lun \"%s\" " 795 "specified more than once", 796 lun->l_name); 797 free($2); 798 return (1); 799 } 800 lun_set_device_id(lun, $2); 801 free($2); 802 } 803 ; 804 805 lun_option: OPTION STR STR 806 { 807 struct lun_option *clo; 808 809 clo = lun_option_new(lun, $2, $3); 810 free($2); 811 free($3); 812 if (clo == NULL) 813 return (1); 814 } 815 ; 816 817 lun_path: PATH STR 818 { 819 if (lun->l_path != NULL) { 820 log_warnx("path for lun \"%s\" " 821 "specified more than once", 822 lun->l_name); 823 free($2); 824 return (1); 825 } 826 lun_set_path(lun, $2); 827 free($2); 828 } 829 ; 830 831 lun_serial: SERIAL STR 832 { 833 if (lun->l_serial != NULL) { 834 log_warnx("serial for lun \"%s\" " 835 "specified more than once", 836 lun->l_name); 837 free($2); 838 return (1); 839 } 840 lun_set_serial(lun, $2); 841 free($2); 842 } 843 ; 844 845 lun_size: SIZE STR 846 { 847 uint64_t tmp; 848 849 if (expand_number($2, &tmp) != 0) { 850 yyerror("invalid numeric value"); 851 free($2); 852 return (1); 853 } 854 855 if (lun->l_size != 0) { 856 log_warnx("size for lun \"%s\" " 857 "specified more than once", 858 lun->l_name); 859 return (1); 860 } 861 lun_set_size(lun, tmp); 862 } 863 ; 864 %% 865 866 void 867 yyerror(const char *str) 868 { 869 870 log_warnx("error in configuration file at line %d near '%s': %s", 871 lineno, yytext, str); 872 } 873 874 static void 875 check_perms(const char *path) 876 { 877 struct stat sb; 878 int error; 879 880 error = stat(path, &sb); 881 if (error != 0) { 882 log_warn("stat"); 883 return; 884 } 885 if (sb.st_mode & S_IWOTH) { 886 log_warnx("%s is world-writable", path); 887 } else if (sb.st_mode & S_IROTH) { 888 log_warnx("%s is world-readable", path); 889 } else if (sb.st_mode & S_IXOTH) { 890 /* 891 * Ok, this one doesn't matter, but still do it, 892 * just for consistency. 893 */ 894 log_warnx("%s is world-executable", path); 895 } 896 897 /* 898 * XXX: Should we also check for owner != 0? 899 */ 900 } 901 902 struct conf * 903 conf_new_from_file(const char *path) 904 { 905 struct auth_group *ag; 906 struct portal_group *pg; 907 int error; 908 909 log_debugx("obtaining configuration from %s", path); 910 911 conf = conf_new(); 912 913 ag = auth_group_new(conf, "default"); 914 assert(ag != NULL); 915 916 ag = auth_group_new(conf, "no-authentication"); 917 assert(ag != NULL); 918 ag->ag_type = AG_TYPE_NO_AUTHENTICATION; 919 920 ag = auth_group_new(conf, "no-access"); 921 assert(ag != NULL); 922 ag->ag_type = AG_TYPE_DENY; 923 924 pg = portal_group_new(conf, "default"); 925 assert(pg != NULL); 926 927 yyin = fopen(path, "r"); 928 if (yyin == NULL) { 929 log_warn("unable to open configuration file %s", path); 930 conf_delete(conf); 931 return (NULL); 932 } 933 check_perms(path); 934 lineno = 1; 935 yyrestart(yyin); 936 error = yyparse(); 937 auth_group = NULL; 938 portal_group = NULL; 939 target = NULL; 940 lun = NULL; 941 fclose(yyin); 942 if (error != 0) { 943 conf_delete(conf); 944 return (NULL); 945 } 946 947 if (conf->conf_default_ag_defined == false) { 948 log_debugx("auth-group \"default\" not defined; " 949 "going with defaults"); 950 ag = auth_group_find(conf, "default"); 951 assert(ag != NULL); 952 ag->ag_type = AG_TYPE_DENY; 953 } 954 955 if (conf->conf_default_pg_defined == false) { 956 log_debugx("portal-group \"default\" not defined; " 957 "going with defaults"); 958 pg = portal_group_find(conf, "default"); 959 assert(pg != NULL); 960 portal_group_add_listen(pg, "0.0.0.0:3260", false); 961 portal_group_add_listen(pg, "[::]:3260", false); 962 } 963 964 conf->conf_kernel_port_on = true; 965 966 error = conf_verify(conf); 967 if (error != 0) { 968 conf_delete(conf); 969 return (NULL); 970 } 971 972 return (conf); 973 } 974