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