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