parse.y revision 263724
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 263724 2014-03-25 12:12:37Z 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		portal_group = portal_group_new(conf, $1);
228		free($1);
229		if (portal_group == NULL)
230			return (1);
231	}
232	;
233
234portal_group_entries:
235	|
236	portal_group_entries portal_group_entry
237	;
238
239portal_group_entry:
240	portal_group_discovery_auth_group
241	|
242	portal_group_listen
243	|
244	portal_group_listen_iser
245	;
246
247portal_group_discovery_auth_group:	DISCOVERY_AUTH_GROUP STR
248	{
249		if (portal_group->pg_discovery_auth_group != NULL) {
250			log_warnx("discovery-auth-group for portal-group "
251			    "\"%s\" specified more than once",
252			    portal_group->pg_name);
253			return (1);
254		}
255		portal_group->pg_discovery_auth_group =
256		    auth_group_find(conf, $2);
257		if (portal_group->pg_discovery_auth_group == NULL) {
258			log_warnx("unknown discovery-auth-group \"%s\" "
259			    "for portal-group \"%s\"",
260			    $2, portal_group->pg_name);
261			return (1);
262		}
263		free($2);
264	}
265	;
266
267portal_group_listen:	LISTEN STR
268	{
269		int error;
270
271		error = portal_group_add_listen(portal_group, $2, false);
272		free($2);
273		if (error != 0)
274			return (1);
275	}
276	;
277
278portal_group_listen_iser:	LISTEN_ISER STR
279	{
280		int error;
281
282		error = portal_group_add_listen(portal_group, $2, true);
283		free($2);
284		if (error != 0)
285			return (1);
286	}
287	;
288
289target:	TARGET target_name
290    OPENING_BRACKET target_entries CLOSING_BRACKET
291	{
292		target = NULL;
293	}
294	;
295
296target_name:	STR
297	{
298		target = target_new(conf, $1);
299		free($1);
300		if (target == NULL)
301			return (1);
302	}
303	;
304
305target_entries:
306	|
307	target_entries target_entry
308	;
309
310target_entry:
311	target_alias
312	|
313	target_auth_group
314	|
315	target_auth_type
316	|
317	target_chap
318	|
319	target_chap_mutual
320	|
321	target_initiator_name
322	|
323	target_initiator_portal
324	|
325	target_portal_group
326	|
327	target_lun
328	;
329
330target_alias:	ALIAS STR
331	{
332		if (target->t_alias != NULL) {
333			log_warnx("alias for target \"%s\" "
334			    "specified more than once", target->t_name);
335			return (1);
336		}
337		target->t_alias = $2;
338	}
339	;
340
341target_auth_group:	AUTH_GROUP STR
342	{
343		if (target->t_auth_group != NULL) {
344			if (target->t_auth_group->ag_name != NULL)
345				log_warnx("auth-group for target \"%s\" "
346				    "specified more than once", target->t_name);
347			else
348				log_warnx("cannot use both auth-group and explicit "
349				    "authorisations for target \"%s\"",
350				    target->t_name);
351			return (1);
352		}
353		target->t_auth_group = auth_group_find(conf, $2);
354		if (target->t_auth_group == NULL) {
355			log_warnx("unknown auth-group \"%s\" for target "
356			    "\"%s\"", $2, target->t_name);
357			return (1);
358		}
359		free($2);
360	}
361	;
362
363target_auth_type:	AUTH_TYPE STR
364	{
365		int error;
366
367		if (target->t_auth_group != NULL) {
368			if (target->t_auth_group->ag_name != NULL) {
369				log_warnx("cannot use both auth-group and "
370				    "auth-type for target \"%s\"",
371				    target->t_name);
372				return (1);
373			}
374		} else {
375			target->t_auth_group = auth_group_new(conf, NULL);
376			if (target->t_auth_group == NULL) {
377				free($2);
378				return (1);
379			}
380			target->t_auth_group->ag_target = target;
381		}
382		error = auth_group_set_type_str(target->t_auth_group, $2);
383		free($2);
384		if (error != 0)
385			return (1);
386	}
387	;
388
389target_chap:	CHAP STR STR
390	{
391		const struct auth *ca;
392
393		if (target->t_auth_group != NULL) {
394			if (target->t_auth_group->ag_name != NULL) {
395				log_warnx("cannot use both auth-group and "
396				    "chap for target \"%s\"",
397				    target->t_name);
398				free($2);
399				free($3);
400				return (1);
401			}
402		} else {
403			target->t_auth_group = auth_group_new(conf, NULL);
404			if (target->t_auth_group == NULL) {
405				free($2);
406				free($3);
407				return (1);
408			}
409			target->t_auth_group->ag_target = target;
410		}
411		ca = auth_new_chap(target->t_auth_group, $2, $3);
412		free($2);
413		free($3);
414		if (ca == NULL)
415			return (1);
416	}
417	;
418
419target_chap_mutual:	CHAP_MUTUAL STR STR STR STR
420	{
421		const struct auth *ca;
422
423		if (target->t_auth_group != NULL) {
424			if (target->t_auth_group->ag_name != NULL) {
425				log_warnx("cannot use both auth-group and "
426				    "chap-mutual for target \"%s\"",
427				    target->t_name);
428				free($2);
429				free($3);
430				free($4);
431				free($5);
432				return (1);
433			}
434		} else {
435			target->t_auth_group = auth_group_new(conf, NULL);
436			if (target->t_auth_group == NULL) {
437				free($2);
438				free($3);
439				free($4);
440				free($5);
441				return (1);
442			}
443			target->t_auth_group->ag_target = target;
444		}
445		ca = auth_new_chap_mutual(target->t_auth_group,
446		    $2, $3, $4, $5);
447		free($2);
448		free($3);
449		free($4);
450		free($5);
451		if (ca == NULL)
452			return (1);
453	}
454	;
455
456target_initiator_name:	INITIATOR_NAME STR
457	{
458		const struct auth_name *an;
459
460		if (target->t_auth_group != NULL) {
461			if (target->t_auth_group->ag_name != NULL) {
462				log_warnx("cannot use both auth-group and "
463				    "initiator-name for target \"%s\"",
464				    target->t_name);
465				free($2);
466				return (1);
467			}
468		} else {
469			target->t_auth_group = auth_group_new(conf, NULL);
470			if (target->t_auth_group == NULL) {
471				free($2);
472				return (1);
473			}
474			target->t_auth_group->ag_target = target;
475		}
476		an = auth_name_new(target->t_auth_group, $2);
477		free($2);
478		if (an == NULL)
479			return (1);
480	}
481	;
482
483target_initiator_portal:	INITIATOR_PORTAL STR
484	{
485		const struct auth_portal *ap;
486
487		if (target->t_auth_group != NULL) {
488			if (target->t_auth_group->ag_name != NULL) {
489				log_warnx("cannot use both auth-group and "
490				    "initiator-portal for target \"%s\"",
491				    target->t_name);
492				free($2);
493				return (1);
494			}
495		} else {
496			target->t_auth_group = auth_group_new(conf, NULL);
497			if (target->t_auth_group == NULL) {
498				free($2);
499				return (1);
500			}
501			target->t_auth_group->ag_target = target;
502		}
503		ap = auth_portal_new(target->t_auth_group, $2);
504		free($2);
505		if (ap == NULL)
506			return (1);
507	}
508	;
509
510target_portal_group:	PORTAL_GROUP STR
511	{
512		if (target->t_portal_group != NULL) {
513			log_warnx("portal-group for target \"%s\" "
514			    "specified more than once", target->t_name);
515			free($2);
516			return (1);
517		}
518		target->t_portal_group = portal_group_find(conf, $2);
519		if (target->t_portal_group == NULL) {
520			log_warnx("unknown portal-group \"%s\" for target "
521			    "\"%s\"", $2, target->t_name);
522			free($2);
523			return (1);
524		}
525		free($2);
526	}
527	;
528
529target_lun:	LUN lun_number
530    OPENING_BRACKET lun_entries CLOSING_BRACKET
531	{
532		lun = NULL;
533	}
534	;
535
536lun_number:	NUM
537	{
538		lun = lun_new(target, $1);
539		if (lun == NULL)
540			return (1);
541	}
542	;
543
544lun_entries:
545	|
546	lun_entries lun_entry
547	;
548
549lun_entry:
550	lun_backend
551	|
552	lun_blocksize
553	|
554	lun_device_id
555	|
556	lun_option
557	|
558	lun_path
559	|
560	lun_serial
561	|
562	lun_size
563	;
564
565lun_backend:	BACKEND STR
566	{
567		if (lun->l_backend != NULL) {
568			log_warnx("backend for lun %d, target \"%s\" "
569			    "specified more than once",
570			    lun->l_lun, target->t_name);
571			free($2);
572			return (1);
573		}
574		lun_set_backend(lun, $2);
575		free($2);
576	}
577	;
578
579lun_blocksize:	BLOCKSIZE NUM
580	{
581		if (lun->l_blocksize != 0) {
582			log_warnx("blocksize for lun %d, target \"%s\" "
583			    "specified more than once",
584			    lun->l_lun, target->t_name);
585			return (1);
586		}
587		lun_set_blocksize(lun, $2);
588	}
589	;
590
591lun_device_id:	DEVICE_ID STR
592	{
593		if (lun->l_device_id != NULL) {
594			log_warnx("device_id for lun %d, target \"%s\" "
595			    "specified more than once",
596			    lun->l_lun, target->t_name);
597			free($2);
598			return (1);
599		}
600		lun_set_device_id(lun, $2);
601		free($2);
602	}
603	;
604
605lun_option:	OPTION STR STR
606	{
607		struct lun_option *clo;
608
609		clo = lun_option_new(lun, $2, $3);
610		free($2);
611		free($3);
612		if (clo == NULL)
613			return (1);
614	}
615	;
616
617lun_path:	PATH STR
618	{
619		if (lun->l_path != NULL) {
620			log_warnx("path for lun %d, target \"%s\" "
621			    "specified more than once",
622			    lun->l_lun, target->t_name);
623			free($2);
624			return (1);
625		}
626		lun_set_path(lun, $2);
627		free($2);
628	}
629	;
630
631lun_serial:	SERIAL STR
632	{
633		if (lun->l_serial != NULL) {
634			log_warnx("serial for lun %d, target \"%s\" "
635			    "specified more than once",
636			    lun->l_lun, target->t_name);
637			free($2);
638			return (1);
639		}
640		lun_set_serial(lun, $2);
641		free($2);
642	}
643	;
644
645lun_size:	SIZE NUM
646	{
647		if (lun->l_size != 0) {
648			log_warnx("size for lun %d, target \"%s\" "
649			    "specified more than once",
650			    lun->l_lun, target->t_name);
651			return (1);
652		}
653		lun_set_size(lun, $2);
654	}
655	;
656%%
657
658void
659yyerror(const char *str)
660{
661
662	log_warnx("error in configuration file at line %d near '%s': %s",
663	    lineno, yytext, str);
664}
665
666static void
667check_perms(const char *path)
668{
669	struct stat sb;
670	int error;
671
672	error = stat(path, &sb);
673	if (error != 0) {
674		log_warn("stat");
675		return;
676	}
677	if (sb.st_mode & S_IWOTH) {
678		log_warnx("%s is world-writable", path);
679	} else if (sb.st_mode & S_IROTH) {
680		log_warnx("%s is world-readable", path);
681	} else if (sb.st_mode & S_IXOTH) {
682		/*
683		 * Ok, this one doesn't matter, but still do it,
684		 * just for consistency.
685		 */
686		log_warnx("%s is world-executable", path);
687	}
688
689	/*
690	 * XXX: Should we also check for owner != 0?
691	 */
692}
693
694struct conf *
695conf_new_from_file(const char *path)
696{
697	struct auth_group *ag;
698	struct portal_group *pg;
699	int error;
700
701	log_debugx("obtaining configuration from %s", path);
702
703	conf = conf_new();
704
705	ag = auth_group_new(conf, "no-authentication");
706	ag->ag_type = AG_TYPE_NO_AUTHENTICATION;
707
708	/*
709	 * Here, the type doesn't really matter, as the group doesn't contain
710	 * any entries and thus will always deny access.
711	 */
712	ag = auth_group_new(conf, "no-access");
713	ag->ag_type = AG_TYPE_CHAP;
714
715	pg = portal_group_new(conf, "default");
716	portal_group_add_listen(pg, "0.0.0.0:3260", false);
717	portal_group_add_listen(pg, "[::]:3260", false);
718
719	yyin = fopen(path, "r");
720	if (yyin == NULL) {
721		log_warn("unable to open configuration file %s", path);
722		conf_delete(conf);
723		return (NULL);
724	}
725	check_perms(path);
726	lineno = 1;
727	yyrestart(yyin);
728	error = yyparse();
729	auth_group = NULL;
730	portal_group = NULL;
731	target = NULL;
732	lun = NULL;
733	fclose(yyin);
734	if (error != 0) {
735		conf_delete(conf);
736		return (NULL);
737	}
738
739	error = conf_verify(conf);
740	if (error != 0) {
741		conf_delete(conf);
742		return (NULL);
743	}
744
745	return (conf);
746}
747