parse.y revision 275245
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 275245 2014-11-29 15:34:17Z 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 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
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(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_discovery_filter
331	|
332	portal_group_listen
333	|
334	portal_group_listen_iser
335	;
336
337portal_group_discovery_auth_group:	DISCOVERY_AUTH_GROUP STR
338	{
339		if (portal_group->pg_discovery_auth_group != NULL) {
340			log_warnx("discovery-auth-group for portal-group "
341			    "\"%s\" specified more than once",
342			    portal_group->pg_name);
343			return (1);
344		}
345		portal_group->pg_discovery_auth_group =
346		    auth_group_find(conf, $2);
347		if (portal_group->pg_discovery_auth_group == NULL) {
348			log_warnx("unknown discovery-auth-group \"%s\" "
349			    "for portal-group \"%s\"",
350			    $2, portal_group->pg_name);
351			return (1);
352		}
353		free($2);
354	}
355	;
356
357portal_group_discovery_filter:	DISCOVERY_FILTER STR
358	{
359		int error;
360
361		error = portal_group_set_filter(portal_group, $2);
362		free($2);
363		if (error != 0)
364			return (1);
365	}
366	;
367
368portal_group_listen:	LISTEN STR
369	{
370		int error;
371
372		error = portal_group_add_listen(portal_group, $2, false);
373		free($2);
374		if (error != 0)
375			return (1);
376	}
377	;
378
379portal_group_listen_iser:	LISTEN_ISER STR
380	{
381		int error;
382
383		error = portal_group_add_listen(portal_group, $2, true);
384		free($2);
385		if (error != 0)
386			return (1);
387	}
388	;
389
390target:	TARGET target_name
391    OPENING_BRACKET target_entries CLOSING_BRACKET
392	{
393		target = NULL;
394	}
395	;
396
397target_name:	STR
398	{
399		target = target_new(conf, $1);
400		free($1);
401		if (target == NULL)
402			return (1);
403	}
404	;
405
406target_entries:
407	|
408	target_entries target_entry
409	;
410
411target_entry:
412	target_alias
413	|
414	target_auth_group
415	|
416	target_auth_type
417	|
418	target_chap
419	|
420	target_chap_mutual
421	|
422	target_initiator_name
423	|
424	target_initiator_portal
425	|
426	target_portal_group
427	|
428	target_lun
429	;
430
431target_alias:	ALIAS STR
432	{
433		if (target->t_alias != NULL) {
434			log_warnx("alias for target \"%s\" "
435			    "specified more than once", target->t_name);
436			return (1);
437		}
438		target->t_alias = $2;
439	}
440	;
441
442target_auth_group:	AUTH_GROUP STR
443	{
444		if (target->t_auth_group != NULL) {
445			if (target->t_auth_group->ag_name != NULL)
446				log_warnx("auth-group for target \"%s\" "
447				    "specified more than once", target->t_name);
448			else
449				log_warnx("cannot use both auth-group and explicit "
450				    "authorisations for target \"%s\"",
451				    target->t_name);
452			return (1);
453		}
454		target->t_auth_group = auth_group_find(conf, $2);
455		if (target->t_auth_group == NULL) {
456			log_warnx("unknown auth-group \"%s\" for target "
457			    "\"%s\"", $2, target->t_name);
458			return (1);
459		}
460		free($2);
461	}
462	;
463
464target_auth_type:	AUTH_TYPE STR
465	{
466		int error;
467
468		if (target->t_auth_group != NULL) {
469			if (target->t_auth_group->ag_name != NULL) {
470				log_warnx("cannot use both auth-group and "
471				    "auth-type for target \"%s\"",
472				    target->t_name);
473				return (1);
474			}
475		} else {
476			target->t_auth_group = auth_group_new(conf, NULL);
477			if (target->t_auth_group == NULL) {
478				free($2);
479				return (1);
480			}
481			target->t_auth_group->ag_target = target;
482		}
483		error = auth_group_set_type(target->t_auth_group, $2);
484		free($2);
485		if (error != 0)
486			return (1);
487	}
488	;
489
490target_chap:	CHAP STR STR
491	{
492		const struct auth *ca;
493
494		if (target->t_auth_group != NULL) {
495			if (target->t_auth_group->ag_name != NULL) {
496				log_warnx("cannot use both auth-group and "
497				    "chap for target \"%s\"",
498				    target->t_name);
499				free($2);
500				free($3);
501				return (1);
502			}
503		} else {
504			target->t_auth_group = auth_group_new(conf, NULL);
505			if (target->t_auth_group == NULL) {
506				free($2);
507				free($3);
508				return (1);
509			}
510			target->t_auth_group->ag_target = target;
511		}
512		ca = auth_new_chap(target->t_auth_group, $2, $3);
513		free($2);
514		free($3);
515		if (ca == NULL)
516			return (1);
517	}
518	;
519
520target_chap_mutual:	CHAP_MUTUAL STR STR STR STR
521	{
522		const struct auth *ca;
523
524		if (target->t_auth_group != NULL) {
525			if (target->t_auth_group->ag_name != NULL) {
526				log_warnx("cannot use both auth-group and "
527				    "chap-mutual for target \"%s\"",
528				    target->t_name);
529				free($2);
530				free($3);
531				free($4);
532				free($5);
533				return (1);
534			}
535		} else {
536			target->t_auth_group = auth_group_new(conf, NULL);
537			if (target->t_auth_group == NULL) {
538				free($2);
539				free($3);
540				free($4);
541				free($5);
542				return (1);
543			}
544			target->t_auth_group->ag_target = target;
545		}
546		ca = auth_new_chap_mutual(target->t_auth_group,
547		    $2, $3, $4, $5);
548		free($2);
549		free($3);
550		free($4);
551		free($5);
552		if (ca == NULL)
553			return (1);
554	}
555	;
556
557target_initiator_name:	INITIATOR_NAME STR
558	{
559		const struct auth_name *an;
560
561		if (target->t_auth_group != NULL) {
562			if (target->t_auth_group->ag_name != NULL) {
563				log_warnx("cannot use both auth-group and "
564				    "initiator-name for target \"%s\"",
565				    target->t_name);
566				free($2);
567				return (1);
568			}
569		} else {
570			target->t_auth_group = auth_group_new(conf, NULL);
571			if (target->t_auth_group == NULL) {
572				free($2);
573				return (1);
574			}
575			target->t_auth_group->ag_target = target;
576		}
577		an = auth_name_new(target->t_auth_group, $2);
578		free($2);
579		if (an == NULL)
580			return (1);
581	}
582	;
583
584target_initiator_portal:	INITIATOR_PORTAL STR
585	{
586		const struct auth_portal *ap;
587
588		if (target->t_auth_group != NULL) {
589			if (target->t_auth_group->ag_name != NULL) {
590				log_warnx("cannot use both auth-group and "
591				    "initiator-portal for target \"%s\"",
592				    target->t_name);
593				free($2);
594				return (1);
595			}
596		} else {
597			target->t_auth_group = auth_group_new(conf, NULL);
598			if (target->t_auth_group == NULL) {
599				free($2);
600				return (1);
601			}
602			target->t_auth_group->ag_target = target;
603		}
604		ap = auth_portal_new(target->t_auth_group, $2);
605		free($2);
606		if (ap == NULL)
607			return (1);
608	}
609	;
610
611target_portal_group:	PORTAL_GROUP STR
612	{
613		if (target->t_portal_group != NULL) {
614			log_warnx("portal-group for target \"%s\" "
615			    "specified more than once", target->t_name);
616			free($2);
617			return (1);
618		}
619		target->t_portal_group = portal_group_find(conf, $2);
620		if (target->t_portal_group == NULL) {
621			log_warnx("unknown portal-group \"%s\" for target "
622			    "\"%s\"", $2, target->t_name);
623			free($2);
624			return (1);
625		}
626		free($2);
627	}
628	;
629
630target_lun:	LUN lun_number
631    OPENING_BRACKET lun_entries CLOSING_BRACKET
632	{
633		lun = NULL;
634	}
635	;
636
637lun_number:	STR
638	{
639		uint64_t tmp;
640
641		if (expand_number($1, &tmp) != 0) {
642			yyerror("invalid numeric value");
643			free($1);
644			return (1);
645		}
646
647		lun = lun_new(target, tmp);
648		if (lun == NULL)
649			return (1);
650	}
651	;
652
653lun_entries:
654	|
655	lun_entries lun_entry
656	;
657
658lun_entry:
659	lun_backend
660	|
661	lun_blocksize
662	|
663	lun_device_id
664	|
665	lun_option
666	|
667	lun_path
668	|
669	lun_serial
670	|
671	lun_size
672	;
673
674lun_backend:	BACKEND STR
675	{
676		if (lun->l_backend != NULL) {
677			log_warnx("backend for lun %d, target \"%s\" "
678			    "specified more than once",
679			    lun->l_lun, target->t_name);
680			free($2);
681			return (1);
682		}
683		lun_set_backend(lun, $2);
684		free($2);
685	}
686	;
687
688lun_blocksize:	BLOCKSIZE STR
689	{
690		uint64_t tmp;
691
692		if (expand_number($2, &tmp) != 0) {
693			yyerror("invalid numeric value");
694			free($2);
695			return (1);
696		}
697
698		if (lun->l_blocksize != 0) {
699			log_warnx("blocksize for lun %d, target \"%s\" "
700			    "specified more than once",
701			    lun->l_lun, target->t_name);
702			return (1);
703		}
704		lun_set_blocksize(lun, tmp);
705	}
706	;
707
708lun_device_id:	DEVICE_ID STR
709	{
710		if (lun->l_device_id != NULL) {
711			log_warnx("device_id for lun %d, target \"%s\" "
712			    "specified more than once",
713			    lun->l_lun, target->t_name);
714			free($2);
715			return (1);
716		}
717		lun_set_device_id(lun, $2);
718		free($2);
719	}
720	;
721
722lun_option:	OPTION STR STR
723	{
724		struct lun_option *clo;
725
726		clo = lun_option_new(lun, $2, $3);
727		free($2);
728		free($3);
729		if (clo == NULL)
730			return (1);
731	}
732	;
733
734lun_path:	PATH STR
735	{
736		if (lun->l_path != NULL) {
737			log_warnx("path for lun %d, target \"%s\" "
738			    "specified more than once",
739			    lun->l_lun, target->t_name);
740			free($2);
741			return (1);
742		}
743		lun_set_path(lun, $2);
744		free($2);
745	}
746	;
747
748lun_serial:	SERIAL STR
749	{
750		if (lun->l_serial != NULL) {
751			log_warnx("serial for lun %d, target \"%s\" "
752			    "specified more than once",
753			    lun->l_lun, target->t_name);
754			free($2);
755			return (1);
756		}
757		lun_set_serial(lun, $2);
758		free($2);
759	}
760	;
761
762lun_size:	SIZE STR
763	{
764		uint64_t tmp;
765
766		if (expand_number($2, &tmp) != 0) {
767			yyerror("invalid numeric value");
768			free($2);
769			return (1);
770		}
771
772		if (lun->l_size != 0) {
773			log_warnx("size for lun %d, target \"%s\" "
774			    "specified more than once",
775			    lun->l_lun, target->t_name);
776			return (1);
777		}
778		lun_set_size(lun, tmp);
779	}
780	;
781%%
782
783void
784yyerror(const char *str)
785{
786
787	log_warnx("error in configuration file at line %d near '%s': %s",
788	    lineno, yytext, str);
789}
790
791static void
792check_perms(const char *path)
793{
794	struct stat sb;
795	int error;
796
797	error = stat(path, &sb);
798	if (error != 0) {
799		log_warn("stat");
800		return;
801	}
802	if (sb.st_mode & S_IWOTH) {
803		log_warnx("%s is world-writable", path);
804	} else if (sb.st_mode & S_IROTH) {
805		log_warnx("%s is world-readable", path);
806	} else if (sb.st_mode & S_IXOTH) {
807		/*
808		 * Ok, this one doesn't matter, but still do it,
809		 * just for consistency.
810		 */
811		log_warnx("%s is world-executable", path);
812	}
813
814	/*
815	 * XXX: Should we also check for owner != 0?
816	 */
817}
818
819struct conf *
820conf_new_from_file(const char *path)
821{
822	struct auth_group *ag;
823	struct portal_group *pg;
824	int error;
825
826	log_debugx("obtaining configuration from %s", path);
827
828	conf = conf_new();
829
830	ag = auth_group_new(conf, "default");
831	assert(ag != NULL);
832
833	ag = auth_group_new(conf, "no-authentication");
834	assert(ag != NULL);
835	ag->ag_type = AG_TYPE_NO_AUTHENTICATION;
836
837	ag = auth_group_new(conf, "no-access");
838	assert(ag != NULL);
839	ag->ag_type = AG_TYPE_DENY;
840
841	pg = portal_group_new(conf, "default");
842	assert(pg != NULL);
843
844	yyin = fopen(path, "r");
845	if (yyin == NULL) {
846		log_warn("unable to open configuration file %s", path);
847		conf_delete(conf);
848		return (NULL);
849	}
850	check_perms(path);
851	lineno = 1;
852	yyrestart(yyin);
853	error = yyparse();
854	auth_group = NULL;
855	portal_group = NULL;
856	target = NULL;
857	lun = NULL;
858	fclose(yyin);
859	if (error != 0) {
860		conf_delete(conf);
861		return (NULL);
862	}
863
864	if (conf->conf_default_ag_defined == false) {
865		log_debugx("auth-group \"default\" not defined; "
866		    "going with defaults");
867		ag = auth_group_find(conf, "default");
868		assert(ag != NULL);
869		ag->ag_type = AG_TYPE_DENY;
870	}
871
872	if (conf->conf_default_pg_defined == false) {
873		log_debugx("portal-group \"default\" not defined; "
874		    "going with defaults");
875		pg = portal_group_find(conf, "default");
876		assert(pg != NULL);
877		portal_group_add_listen(pg, "0.0.0.0:3260", false);
878		portal_group_add_listen(pg, "[::]:3260", false);
879	}
880
881	conf->conf_kernel_port_on = true;
882
883	error = conf_verify(conf);
884	if (error != 0) {
885		conf_delete(conf);
886		return (NULL);
887	}
888
889	return (conf);
890}
891