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