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