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