parse.y revision 263720
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 263720 2014-03-25 12:01:55Z 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 BACKEND BLOCKSIZE CHAP CHAP_MUTUAL CLOSING_BRACKET
61%token DEBUG DEVICE_ID DISCOVERY_AUTH_GROUP INITIATOR_NAME INITIATOR_PORTAL
62%token LISTEN LISTEN_ISER LUN MAXPROC NUM OPENING_BRACKET OPTION PATH PIDFILE
63%token 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_statement
83	|
84	timeout_statement
85	|
86	maxproc_statement
87	|
88	pidfile_statement
89	|
90	auth_group_definition
91	|
92	portal_group_definition
93	|
94	target_statement
95	;
96
97debug_statement:	DEBUG NUM
98	{
99		conf->conf_debug = $2;
100	}
101	;
102
103timeout_statement:	TIMEOUT NUM
104	{
105		conf->conf_timeout = $2;
106	}
107	;
108
109maxproc_statement:	MAXPROC NUM
110	{
111		conf->conf_maxproc = $2;
112	}
113	;
114
115pidfile_statement:	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_definition:	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_chap
149	|
150	auth_group_chap_mutual
151	|
152	auth_group_initiator_name
153	|
154	auth_group_initiator_portal
155	;
156
157auth_group_chap:	CHAP STR STR
158	{
159		const struct auth *ca;
160
161		ca = auth_new_chap(auth_group, $2, $3);
162		free($2);
163		free($3);
164		if (ca == NULL)
165			return (1);
166	}
167	;
168
169auth_group_chap_mutual:	CHAP_MUTUAL STR STR STR STR
170	{
171		const struct auth *ca;
172
173		ca = auth_new_chap_mutual(auth_group, $2, $3, $4, $5);
174		free($2);
175		free($3);
176		free($4);
177		free($5);
178		if (ca == NULL)
179			return (1);
180	}
181	;
182
183auth_group_initiator_name:	INITIATOR_NAME STR
184	{
185		const struct auth_name *an;
186
187		an = auth_name_new(auth_group, $2);
188		free($2);
189		if (an == NULL)
190			return (1);
191	}
192	;
193
194auth_group_initiator_portal:	INITIATOR_PORTAL STR
195	{
196		const struct auth_portal *ap;
197
198		ap = auth_portal_new(auth_group, $2);
199		free($2);
200		if (ap == NULL)
201			return (1);
202	}
203	;
204
205portal_group_definition:	PORTAL_GROUP portal_group_name
206    OPENING_BRACKET portal_group_entries CLOSING_BRACKET
207	{
208		portal_group = NULL;
209	}
210	;
211
212portal_group_name:	STR
213	{
214		portal_group = portal_group_new(conf, $1);
215		free($1);
216		if (portal_group == NULL)
217			return (1);
218	}
219	;
220
221portal_group_entries:
222	|
223	portal_group_entries portal_group_entry
224	;
225
226portal_group_entry:
227	portal_group_discovery_auth_group
228	|
229	portal_group_listen
230	|
231	portal_group_listen_iser
232	;
233
234portal_group_discovery_auth_group:	DISCOVERY_AUTH_GROUP STR
235	{
236		if (portal_group->pg_discovery_auth_group != NULL) {
237			log_warnx("discovery-auth-group for portal-group "
238			    "\"%s\" specified more than once",
239			    portal_group->pg_name);
240			return (1);
241		}
242		portal_group->pg_discovery_auth_group =
243		    auth_group_find(conf, $2);
244		if (portal_group->pg_discovery_auth_group == NULL) {
245			log_warnx("unknown discovery-auth-group \"%s\" "
246			    "for portal-group \"%s\"",
247			    $2, portal_group->pg_name);
248			return (1);
249		}
250		free($2);
251	}
252	;
253
254portal_group_listen:	LISTEN STR
255	{
256		int error;
257
258		error = portal_group_add_listen(portal_group, $2, false);
259		free($2);
260		if (error != 0)
261			return (1);
262	}
263	;
264
265portal_group_listen_iser:	LISTEN_ISER STR
266	{
267		int error;
268
269		error = portal_group_add_listen(portal_group, $2, true);
270		free($2);
271		if (error != 0)
272			return (1);
273	}
274	;
275
276target_statement:	TARGET target_iqn
277    OPENING_BRACKET target_entries CLOSING_BRACKET
278	{
279		target = NULL;
280	}
281	;
282
283target_iqn:	STR
284	{
285		target = target_new(conf, $1);
286		free($1);
287		if (target == NULL)
288			return (1);
289	}
290	;
291
292target_entries:
293	|
294	target_entries target_entry
295	;
296
297target_entry:
298	alias_statement
299	|
300	auth_group_statement
301	|
302	chap_statement
303	|
304	chap_mutual_statement
305	|
306	initiator_name_statement
307	|
308	initiator_portal_statement
309	|
310	portal_group_statement
311	|
312	lun_statement
313	;
314
315alias_statement:	ALIAS STR
316	{
317		if (target->t_alias != NULL) {
318			log_warnx("alias for target \"%s\" "
319			    "specified more than once", target->t_iqn);
320			return (1);
321		}
322		target->t_alias = $2;
323	}
324	;
325
326auth_group_statement:	AUTH_GROUP STR
327	{
328		if (target->t_auth_group != NULL) {
329			if (target->t_auth_group->ag_name != NULL)
330				log_warnx("auth-group for target \"%s\" "
331				    "specified more than once", target->t_iqn);
332			else
333				log_warnx("cannot mix auth-group with explicit "
334				    "authorisations for target \"%s\"",
335				    target->t_iqn);
336			return (1);
337		}
338		target->t_auth_group = auth_group_find(conf, $2);
339		if (target->t_auth_group == NULL) {
340			log_warnx("unknown auth-group \"%s\" for target "
341			    "\"%s\"", $2, target->t_iqn);
342			return (1);
343		}
344		free($2);
345	}
346	;
347
348chap_statement:	CHAP STR STR
349	{
350		const struct auth *ca;
351
352		if (target->t_auth_group != NULL) {
353			if (target->t_auth_group->ag_name != NULL) {
354				log_warnx("cannot mix auth-group with explicit "
355				    "authorisations for target \"%s\"",
356				    target->t_iqn);
357				free($2);
358				free($3);
359				return (1);
360			}
361		} else {
362			target->t_auth_group = auth_group_new(conf, NULL);
363			if (target->t_auth_group == NULL) {
364				free($2);
365				free($3);
366				return (1);
367			}
368			target->t_auth_group->ag_target = target;
369		}
370		ca = auth_new_chap(target->t_auth_group, $2, $3);
371		free($2);
372		free($3);
373		if (ca == NULL)
374			return (1);
375	}
376	;
377
378chap_mutual_statement:	CHAP_MUTUAL STR STR STR STR
379	{
380		const struct auth *ca;
381
382		if (target->t_auth_group != NULL) {
383			if (target->t_auth_group->ag_name != NULL) {
384				log_warnx("cannot mix auth-group with explicit "
385				    "authorisations for target \"%s\"",
386				    target->t_iqn);
387				free($2);
388				free($3);
389				free($4);
390				free($5);
391				return (1);
392			}
393		} else {
394			target->t_auth_group = auth_group_new(conf, NULL);
395			if (target->t_auth_group == NULL) {
396				free($2);
397				free($3);
398				free($4);
399				free($5);
400				return (1);
401			}
402			target->t_auth_group->ag_target = target;
403		}
404		ca = auth_new_chap_mutual(target->t_auth_group,
405		    $2, $3, $4, $5);
406		free($2);
407		free($3);
408		free($4);
409		free($5);
410		if (ca == NULL)
411			return (1);
412	}
413	;
414
415initiator_name_statement:	INITIATOR_NAME STR
416	{
417		const struct auth_name *an;
418
419		if (target->t_auth_group != NULL) {
420			if (target->t_auth_group->ag_name != NULL) {
421				log_warnx("cannot mix auth-group with "
422				    "initiator-name for target \"%s\"",
423				    target->t_iqn);
424				free($2);
425				return (1);
426			}
427		} else {
428			target->t_auth_group = auth_group_new(conf, NULL);
429			if (target->t_auth_group == NULL) {
430				free($2);
431				return (1);
432			}
433			target->t_auth_group->ag_target = target;
434		}
435		an = auth_name_new(target->t_auth_group, $2);
436		free($2);
437		if (an == NULL)
438			return (1);
439	}
440	;
441
442initiator_portal_statement:	INITIATOR_PORTAL STR
443	{
444		const struct auth_portal *ap;
445
446		if (target->t_auth_group != NULL) {
447			if (target->t_auth_group->ag_name != NULL) {
448				log_warnx("cannot mix auth-group with "
449				    "initiator-portal for target \"%s\"",
450				    target->t_iqn);
451				free($2);
452				return (1);
453			}
454		} else {
455			target->t_auth_group = auth_group_new(conf, NULL);
456			if (target->t_auth_group == NULL) {
457				free($2);
458				return (1);
459			}
460			target->t_auth_group->ag_target = target;
461		}
462		ap = auth_portal_new(target->t_auth_group, $2);
463		free($2);
464		if (ap == NULL)
465			return (1);
466	}
467	;
468
469portal_group_statement:	PORTAL_GROUP STR
470	{
471		if (target->t_portal_group != NULL) {
472			log_warnx("portal-group for target \"%s\" "
473			    "specified more than once", target->t_iqn);
474			free($2);
475			return (1);
476		}
477		target->t_portal_group = portal_group_find(conf, $2);
478		if (target->t_portal_group == NULL) {
479			log_warnx("unknown portal-group \"%s\" for target "
480			    "\"%s\"", $2, target->t_iqn);
481			free($2);
482			return (1);
483		}
484		free($2);
485	}
486	;
487
488lun_statement:	LUN lun_number
489    OPENING_BRACKET lun_statement_entries CLOSING_BRACKET
490	{
491		lun = NULL;
492	}
493	;
494
495lun_number:	NUM
496	{
497		lun = lun_new(target, $1);
498		if (lun == NULL)
499			return (1);
500	}
501	;
502
503lun_statement_entries:
504	|
505	lun_statement_entries lun_statement_entry
506	;
507
508lun_statement_entry:
509	backend_statement
510	|
511	blocksize_statement
512	|
513	device_id_statement
514	|
515	option_statement
516	|
517	path_statement
518	|
519	serial_statement
520	|
521	size_statement
522	;
523
524backend_statement:	BACKEND STR
525	{
526		if (lun->l_backend != NULL) {
527			log_warnx("backend for lun %d, target \"%s\" "
528			    "specified more than once",
529			    lun->l_lun, target->t_iqn);
530			free($2);
531			return (1);
532		}
533		lun_set_backend(lun, $2);
534		free($2);
535	}
536	;
537
538blocksize_statement:	BLOCKSIZE NUM
539	{
540		if (lun->l_blocksize != 0) {
541			log_warnx("blocksize for lun %d, target \"%s\" "
542			    "specified more than once",
543			    lun->l_lun, target->t_iqn);
544			return (1);
545		}
546		lun_set_blocksize(lun, $2);
547	}
548	;
549
550device_id_statement:	DEVICE_ID STR
551	{
552		if (lun->l_device_id != NULL) {
553			log_warnx("device_id for lun %d, target \"%s\" "
554			    "specified more than once",
555			    lun->l_lun, target->t_iqn);
556			free($2);
557			return (1);
558		}
559		lun_set_device_id(lun, $2);
560		free($2);
561	}
562	;
563
564option_statement:	OPTION STR STR
565	{
566		struct lun_option *clo;
567
568		clo = lun_option_new(lun, $2, $3);
569		free($2);
570		free($3);
571		if (clo == NULL)
572			return (1);
573	}
574	;
575
576path_statement:	PATH STR
577	{
578		if (lun->l_path != NULL) {
579			log_warnx("path for lun %d, target \"%s\" "
580			    "specified more than once",
581			    lun->l_lun, target->t_iqn);
582			free($2);
583			return (1);
584		}
585		lun_set_path(lun, $2);
586		free($2);
587	}
588	;
589
590serial_statement:	SERIAL STR
591	{
592		if (lun->l_serial != NULL) {
593			log_warnx("serial for lun %d, target \"%s\" "
594			    "specified more than once",
595			    lun->l_lun, target->t_iqn);
596			free($2);
597			return (1);
598		}
599		lun_set_serial(lun, $2);
600		free($2);
601	}
602	;
603
604size_statement:	SIZE NUM
605	{
606		if (lun->l_size != 0) {
607			log_warnx("size for lun %d, target \"%s\" "
608			    "specified more than once",
609			    lun->l_lun, target->t_iqn);
610			return (1);
611		}
612		lun_set_size(lun, $2);
613	}
614	;
615%%
616
617void
618yyerror(const char *str)
619{
620
621	log_warnx("error in configuration file at line %d near '%s': %s",
622	    lineno, yytext, str);
623}
624
625static void
626check_perms(const char *path)
627{
628	struct stat sb;
629	int error;
630
631	error = stat(path, &sb);
632	if (error != 0) {
633		log_warn("stat");
634		return;
635	}
636	if (sb.st_mode & S_IWOTH) {
637		log_warnx("%s is world-writable", path);
638	} else if (sb.st_mode & S_IROTH) {
639		log_warnx("%s is world-readable", path);
640	} else if (sb.st_mode & S_IXOTH) {
641		/*
642		 * Ok, this one doesn't matter, but still do it,
643		 * just for consistency.
644		 */
645		log_warnx("%s is world-executable", path);
646	}
647
648	/*
649	 * XXX: Should we also check for owner != 0?
650	 */
651}
652
653struct conf *
654conf_new_from_file(const char *path)
655{
656	struct auth_group *ag;
657	struct portal_group *pg;
658	int error;
659
660	log_debugx("obtaining configuration from %s", path);
661
662	conf = conf_new();
663
664	ag = auth_group_new(conf, "no-authentication");
665	ag->ag_type = AG_TYPE_NO_AUTHENTICATION;
666
667	/*
668	 * Here, the type doesn't really matter, as the group doesn't contain
669	 * any entries and thus will always deny access.
670	 */
671	ag = auth_group_new(conf, "no-access");
672	ag->ag_type = AG_TYPE_CHAP;
673
674	pg = portal_group_new(conf, "default");
675	portal_group_add_listen(pg, "0.0.0.0:3260", false);
676	portal_group_add_listen(pg, "[::]:3260", false);
677
678	yyin = fopen(path, "r");
679	if (yyin == NULL) {
680		log_warn("unable to open configuration file %s", path);
681		conf_delete(conf);
682		return (NULL);
683	}
684	check_perms(path);
685	lineno = 1;
686	yyrestart(yyin);
687	error = yyparse();
688	auth_group = NULL;
689	portal_group = NULL;
690	target = NULL;
691	lun = NULL;
692	fclose(yyin);
693	if (error != 0) {
694		conf_delete(conf);
695		return (NULL);
696	}
697
698	error = conf_verify(conf);
699	if (error != 0) {
700		conf_delete(conf);
701		return (NULL);
702	}
703
704	return (conf);
705}
706