xref: /freebsd/usr.sbin/ctld/parse.y (revision ce3adf4362fcca6a43e500b2531f0038adbfbd21)
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$
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 
44 extern FILE *yyin;
45 extern char *yytext;
46 extern int lineno;
47 
48 static struct conf *conf = NULL;
49 static struct auth_group *auth_group = NULL;
50 static struct portal_group *portal_group = NULL;
51 static struct target *target = NULL;
52 static struct lun *lun = NULL;
53 
54 extern void	yyerror(const char *);
55 extern int	yylex(void);
56 extern 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 
76 statements:
77 	|
78 	statements statement
79 	;
80 
81 statement:
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 
97 debug_statement:	DEBUG NUM
98 	{
99 		conf->conf_debug = $2;
100 	}
101 	;
102 
103 timeout_statement:	TIMEOUT NUM
104 	{
105 		conf->conf_timeout = $2;
106 	}
107 	;
108 
109 maxproc_statement:	MAXPROC NUM
110 	{
111 		conf->conf_maxproc = $2;
112 	}
113 	;
114 
115 pidfile_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 
126 auth_group_definition:	AUTH_GROUP auth_group_name
127     OPENING_BRACKET auth_group_entries CLOSING_BRACKET
128 	{
129 		auth_group = NULL;
130 	}
131 	;
132 
133 auth_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 
142 auth_group_entries:
143 	|
144 	auth_group_entries auth_group_entry
145 	;
146 
147 auth_group_entry:
148 	auth_group_chap
149 	|
150 	auth_group_chap_mutual
151 	;
152 
153 auth_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 
165 auth_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 
179 portal_group_definition:	PORTAL_GROUP portal_group_name
180     OPENING_BRACKET portal_group_entries CLOSING_BRACKET
181 	{
182 		portal_group = NULL;
183 	}
184 	;
185 
186 portal_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 
195 portal_group_entries:
196 	|
197 	portal_group_entries portal_group_entry
198 	;
199 
200 portal_group_entry:
201 	portal_group_discovery_auth_group
202 	|
203 	portal_group_listen
204 	|
205 	portal_group_listen_iser
206 	;
207 
208 portal_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 
228 portal_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 
239 portal_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 
250 target_statement:	TARGET target_iqn
251     OPENING_BRACKET target_entries CLOSING_BRACKET
252 	{
253 		target = NULL;
254 	}
255 	;
256 
257 target_iqn:	STR
258 	{
259 		target = target_new(conf, $1);
260 		free($1);
261 		if (target == NULL)
262 			return (1);
263 	}
264 	;
265 
266 target_entries:
267 	|
268 	target_entries target_entry
269 	;
270 
271 target_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 
285 alias_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 
296 auth_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-grup 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 
318 chap_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-grup 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 
348 chap_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-grup 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 
385 portal_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 
404 lun_statement:	LUN lun_number
405     OPENING_BRACKET lun_statement_entries CLOSING_BRACKET
406 	{
407 		lun = NULL;
408 	}
409 	;
410 
411 lun_number:	NUM
412 	{
413 		lun = lun_new(target, $1);
414 		if (lun == NULL)
415 			return (1);
416 	}
417 	;
418 
419 lun_statement_entries:
420 	|
421 	lun_statement_entries lun_statement_entry
422 	;
423 
424 lun_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 
440 backend_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 
454 blocksize_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 
466 device_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 
480 option_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 
492 path_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 
506 serial_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 
520 size_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 
533 void
534 yyerror(const char *str)
535 {
536 
537 	log_warnx("error in configuration file at line %d near '%s': %s",
538 	    lineno, yytext, str);
539 }
540 
541 static void
542 check_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 
569 struct conf *
570 conf_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