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