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