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