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