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