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