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