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