parse.y revision 275187
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 275187 2014-11-27 10:48:06Z 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 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
75statements:
76	|
77	statements statement
78	;
79
80statement:
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
102debug:		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
116timeout:	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
130maxproc:	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
144pidfile:	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
155isns_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
166isns_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
180isns_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
194auth_group:	AUTH_GROUP auth_group_name
195    OPENING_BRACKET auth_group_entries CLOSING_BRACKET
196	{
197		auth_group = NULL;
198	}
199	;
200
201auth_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
220auth_group_entries:
221	|
222	auth_group_entries auth_group_entry
223	;
224
225auth_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
237auth_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
248auth_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
260auth_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
274auth_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
285auth_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
296portal_group:	PORTAL_GROUP portal_group_name
297    OPENING_BRACKET portal_group_entries CLOSING_BRACKET
298	{
299		portal_group = NULL;
300	}
301	;
302
303portal_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
322portal_group_entries:
323	|
324	portal_group_entries portal_group_entry
325	;
326
327portal_group_entry:
328	portal_group_discovery_auth_group
329	|
330	portal_group_listen
331	|
332	portal_group_listen_iser
333	;
334
335portal_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
355portal_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
366portal_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
377target:	TARGET target_name
378    OPENING_BRACKET target_entries CLOSING_BRACKET
379	{
380		target = NULL;
381	}
382	;
383
384target_name:	STR
385	{
386		target = target_new(conf, $1);
387		free($1);
388		if (target == NULL)
389			return (1);
390	}
391	;
392
393target_entries:
394	|
395	target_entries target_entry
396	;
397
398target_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
418target_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
429target_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
451target_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
477target_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
507target_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
544target_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
571target_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
598target_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
617target_lun:	LUN lun_number
618    OPENING_BRACKET lun_entries CLOSING_BRACKET
619	{
620		lun = NULL;
621	}
622	;
623
624lun_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
640lun_entries:
641	|
642	lun_entries lun_entry
643	;
644
645lun_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
661lun_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
675lun_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
695lun_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
709lun_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
721lun_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
735lun_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
749lun_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
770void
771yyerror(const char *str)
772{
773
774	log_warnx("error in configuration file at line %d near '%s': %s",
775	    lineno, yytext, str);
776}
777
778static void
779check_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
806struct conf *
807conf_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