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