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