xref: /freebsd/usr.bin/iscsictl/parse.y (revision 716fd348e01c5f2ba125f878a634a753436c2994)
1 %{
2 /*-
3  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4  *
5  * Copyright (c) 2012 The FreeBSD Foundation
6  *
7  * This software was developed by Edward Tomasz Napierala under sponsorship
8  * from the FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33 
34 #include <sys/queue.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <assert.h>
38 #include <stdio.h>
39 #include <stdint.h>
40 #include <stdlib.h>
41 #include <string.h>
42 
43 #include <libxo/xo.h>
44 
45 #include "iscsictl.h"
46 #include <netinet/in.h>
47 #include <netinet/ip.h>
48 
49 extern FILE *yyin;
50 extern char *yytext;
51 extern int lineno;
52 
53 static struct conf *conf;
54 static struct target *target;
55 
56 extern void	yyerror(const char *);
57 extern void	yyrestart(FILE *);
58 
59 %}
60 
61 %token AUTH_METHOD ENABLE HEADER_DIGEST DATA_DIGEST TARGET_NAME TARGET_ADDRESS
62 %token INITIATOR_NAME INITIATOR_ADDRESS INITIATOR_ALIAS USER SECRET
63 %token MUTUAL_USER MUTUAL_SECRET SEMICOLON SESSION_TYPE PROTOCOL OFFLOAD
64 %token IGNORED EQUALS OPENING_BRACKET CLOSING_BRACKET DSCP PINGTIMEOUT LOGINTIMEOUT
65 %token AF11 AF12 AF13 AF21 AF22 AF23 AF31 AF32 AF33 AF41 AF42 AF43
66 %token BE EF CS0 CS1 CS2 CS3 CS4 CS5 CS6 CS7
67 
68 %union
69 {
70 	char *str;
71 }
72 
73 %token <str> STR
74 
75 %%
76 
77 targets:
78 	|
79 	targets target
80 	;
81 
82 target:		STR OPENING_BRACKET target_entries CLOSING_BRACKET
83 	{
84 		if (target_find(conf, $1) != NULL)
85 			xo_errx(1, "duplicated target %s", $1);
86 		target->t_nickname = $1;
87 		target = target_new(conf);
88 	}
89 	;
90 
91 target_entries:
92 	|
93 	target_entries target_entry
94 	|
95 	target_entries target_entry SEMICOLON
96 	;
97 
98 target_entry:
99 	target_name
100 	|
101 	target_address
102 	|
103 	initiator_name
104 	|
105 	initiator_address
106 	|
107 	initiator_alias
108 	|
109 	user
110 	|
111 	secret
112 	|
113 	mutual_user
114 	|
115 	mutual_secret
116 	|
117 	auth_method
118 	|
119 	header_digest
120 	|
121 	data_digest
122 	|
123 	session_type
124 	|
125 	enable
126 	|
127 	offload
128 	|
129 	protocol
130 	|
131 	ignored
132 	|
133 	dscp
134 	|
135 	pcp
136 	|
137 	ping_timeout
138 	|
139 	login_timeout
140 	;
141 
142 target_name:	TARGET_NAME EQUALS STR
143 	{
144 		if (target->t_name != NULL)
145 			xo_errx(1, "duplicated TargetName at line %d", lineno);
146 		target->t_name = $3;
147 	}
148 	;
149 
150 target_address:	TARGET_ADDRESS EQUALS STR
151 	{
152 		if (target->t_address != NULL)
153 			xo_errx(1, "duplicated TargetAddress at line %d", lineno);
154 		target->t_address = $3;
155 	}
156 	;
157 
158 initiator_name:	INITIATOR_NAME EQUALS STR
159 	{
160 		if (target->t_initiator_name != NULL)
161 			xo_errx(1, "duplicated InitiatorName at line %d", lineno);
162 		target->t_initiator_name = $3;
163 	}
164 	;
165 
166 initiator_address:	INITIATOR_ADDRESS EQUALS STR
167 	{
168 		if (target->t_initiator_address != NULL)
169 			xo_errx(1, "duplicated InitiatorAddress at line %d", lineno);
170 		target->t_initiator_address = $3;
171 	}
172 	;
173 
174 initiator_alias:	INITIATOR_ALIAS EQUALS STR
175 	{
176 		if (target->t_initiator_alias != NULL)
177 			xo_errx(1, "duplicated InitiatorAlias at line %d", lineno);
178 		target->t_initiator_alias = $3;
179 	}
180 	;
181 
182 user:		USER EQUALS STR
183 	{
184 		if (target->t_user != NULL)
185 			xo_errx(1, "duplicated chapIName at line %d", lineno);
186 		target->t_user = $3;
187 	}
188 	;
189 
190 secret:		SECRET EQUALS STR
191 	{
192 		if (target->t_secret != NULL)
193 			xo_errx(1, "duplicated chapSecret at line %d", lineno);
194 		target->t_secret = $3;
195 	}
196 	;
197 
198 mutual_user:	MUTUAL_USER EQUALS STR
199 	{
200 		if (target->t_mutual_user != NULL)
201 			xo_errx(1, "duplicated tgtChapName at line %d", lineno);
202 		target->t_mutual_user = $3;
203 	}
204 	;
205 
206 mutual_secret:	MUTUAL_SECRET EQUALS STR
207 	{
208 		if (target->t_mutual_secret != NULL)
209 			xo_errx(1, "duplicated tgtChapSecret at line %d", lineno);
210 		target->t_mutual_secret = $3;
211 	}
212 	;
213 
214 auth_method:	AUTH_METHOD EQUALS STR
215 	{
216 		if (target->t_auth_method != AUTH_METHOD_UNSPECIFIED)
217 			xo_errx(1, "duplicated AuthMethod at line %d", lineno);
218 		if (strcasecmp($3, "none") == 0)
219 			target->t_auth_method = AUTH_METHOD_NONE;
220 		else if (strcasecmp($3, "chap") == 0)
221 			target->t_auth_method = AUTH_METHOD_CHAP;
222 		else
223 			xo_errx(1, "invalid AuthMethod at line %d; "
224 			    "must be either \"none\" or \"CHAP\"", lineno);
225 	}
226 	;
227 
228 header_digest:	HEADER_DIGEST EQUALS STR
229 	{
230 		if (target->t_header_digest != DIGEST_UNSPECIFIED)
231 			xo_errx(1, "duplicated HeaderDigest at line %d", lineno);
232 		if (strcasecmp($3, "none") == 0)
233 			target->t_header_digest = DIGEST_NONE;
234 		else if (strcasecmp($3, "CRC32C") == 0)
235 			target->t_header_digest = DIGEST_CRC32C;
236 		else
237 			xo_errx(1, "invalid HeaderDigest at line %d; "
238 			    "must be either \"none\" or \"CRC32C\"", lineno);
239 	}
240 	;
241 
242 data_digest:	DATA_DIGEST EQUALS STR
243 	{
244 		if (target->t_data_digest != DIGEST_UNSPECIFIED)
245 			xo_errx(1, "duplicated DataDigest at line %d", lineno);
246 		if (strcasecmp($3, "none") == 0)
247 			target->t_data_digest = DIGEST_NONE;
248 		else if (strcasecmp($3, "CRC32C") == 0)
249 			target->t_data_digest = DIGEST_CRC32C;
250 		else
251 			xo_errx(1, "invalid DataDigest at line %d; "
252 			    "must be either \"none\" or \"CRC32C\"", lineno);
253 	}
254 	;
255 
256 session_type:	SESSION_TYPE EQUALS STR
257 	{
258 		if (target->t_session_type != SESSION_TYPE_UNSPECIFIED)
259 			xo_errx(1, "duplicated SessionType at line %d", lineno);
260 		if (strcasecmp($3, "normal") == 0)
261 			target->t_session_type = SESSION_TYPE_NORMAL;
262 		else if (strcasecmp($3, "discovery") == 0)
263 			target->t_session_type = SESSION_TYPE_DISCOVERY;
264 		else
265 			xo_errx(1, "invalid SessionType at line %d; "
266 			    "must be either \"normal\" or \"discovery\"", lineno);
267 	}
268 	;
269 
270 enable:		ENABLE EQUALS STR
271 	{
272 		if (target->t_enable != ENABLE_UNSPECIFIED)
273 			xo_errx(1, "duplicated enable at line %d", lineno);
274 		target->t_enable = parse_enable($3);
275 		if (target->t_enable == ENABLE_UNSPECIFIED)
276 			xo_errx(1, "invalid enable at line %d; "
277 			    "must be either \"on\" or \"off\"", lineno);
278 	}
279 	;
280 
281 offload:	OFFLOAD EQUALS STR
282 	{
283 		if (target->t_offload != NULL)
284 			xo_errx(1, "duplicated offload at line %d", lineno);
285 		target->t_offload = $3;
286 	}
287 	;
288 
289 protocol:	PROTOCOL EQUALS STR
290 	{
291 		if (target->t_protocol != PROTOCOL_UNSPECIFIED)
292 			xo_errx(1, "duplicated protocol at line %d", lineno);
293 		if (strcasecmp($3, "iscsi") == 0)
294 			target->t_protocol = PROTOCOL_ISCSI;
295 		else if (strcasecmp($3, "iser") == 0)
296 			target->t_protocol = PROTOCOL_ISER;
297 		else
298 			xo_errx(1, "invalid protocol at line %d; "
299 			    "must be either \"iscsi\" or \"iser\"", lineno);
300 	}
301 	;
302 
303 ignored:	IGNORED EQUALS STR
304 	{
305 		xo_warnx("obsolete statement ignored at line %d", lineno);
306 	}
307 	;
308 
309 dscp:		DSCP EQUALS STR
310 	{
311 		uint64_t tmp;
312 
313 		if (target->t_dscp != -1)
314 			xo_errx(1, "duplicated dscp at line %d", lineno);
315 		if (strcmp($3, "0x") == 0) {
316 			tmp = strtol($3 + 2, NULL, 16);
317 		} else if (expand_number($3, &tmp) != 0) {
318 			yyerror("invalid numeric value");
319 			free($3);
320 			return(1);
321 		}
322 		if (tmp >= 0x40) {
323 			yyerror("invalid dscp value");
324 			return(1);
325 		}
326 
327 		target->t_dscp = tmp;
328 	}
329 	| DSCP EQUALS BE	{ target->t_dscp = IPTOS_DSCP_CS0  >> 2 ; }
330 	| DSCP EQUALS EF	{ target->t_dscp = IPTOS_DSCP_EF   >> 2 ; }
331 	| DSCP EQUALS CS0	{ target->t_dscp = IPTOS_DSCP_CS0  >> 2 ; }
332 	| DSCP EQUALS CS1	{ target->t_dscp = IPTOS_DSCP_CS1  >> 2 ; }
333 	| DSCP EQUALS CS2	{ target->t_dscp = IPTOS_DSCP_CS2  >> 2 ; }
334 	| DSCP EQUALS CS3	{ target->t_dscp = IPTOS_DSCP_CS3  >> 2 ; }
335 	| DSCP EQUALS CS4	{ target->t_dscp = IPTOS_DSCP_CS4  >> 2 ; }
336 	| DSCP EQUALS CS5	{ target->t_dscp = IPTOS_DSCP_CS5  >> 2 ; }
337 	| DSCP EQUALS CS6	{ target->t_dscp = IPTOS_DSCP_CS6  >> 2 ; }
338 	| DSCP EQUALS CS7	{ target->t_dscp = IPTOS_DSCP_CS7  >> 2 ; }
339 	| DSCP EQUALS AF11	{ target->t_dscp = IPTOS_DSCP_AF11 >> 2 ; }
340 	| DSCP EQUALS AF12	{ target->t_dscp = IPTOS_DSCP_AF12 >> 2 ; }
341 	| DSCP EQUALS AF13	{ target->t_dscp = IPTOS_DSCP_AF13 >> 2 ; }
342 	| DSCP EQUALS AF21	{ target->t_dscp = IPTOS_DSCP_AF21 >> 2 ; }
343 	| DSCP EQUALS AF22	{ target->t_dscp = IPTOS_DSCP_AF22 >> 2 ; }
344 	| DSCP EQUALS AF23	{ target->t_dscp = IPTOS_DSCP_AF23 >> 2 ; }
345 	| DSCP EQUALS AF31	{ target->t_dscp = IPTOS_DSCP_AF31 >> 2 ; }
346 	| DSCP EQUALS AF32	{ target->t_dscp = IPTOS_DSCP_AF32 >> 2 ; }
347 	| DSCP EQUALS AF33	{ target->t_dscp = IPTOS_DSCP_AF33 >> 2 ; }
348 	| DSCP EQUALS AF41	{ target->t_dscp = IPTOS_DSCP_AF41 >> 2 ; }
349 	| DSCP EQUALS AF42	{ target->t_dscp = IPTOS_DSCP_AF42 >> 2 ; }
350 	| DSCP EQUALS AF43	{ target->t_dscp = IPTOS_DSCP_AF43 >> 2 ; }
351 	;
352 
353 pcp:	PCP EQUALS STR
354 	{
355 		uint64_t tmp;
356 
357 		if (target->t_pcp != -1)
358 			xo_errx(1, "duplicated pcp at line %d", lineno);
359 
360 		if (expand_number($3, &tmp) != 0) {
361 			yyerror("invalid numeric value");
362 			free($3);
363 			return(1);
364 		}
365 		if (tmp > 7) {
366 			yyerror("invalid pcp value");
367 			return(1);
368 		}
369 
370 		target->t_pcp = tmp;
371 	}
372 	;
373 
374 ping_timeout:	PINGTIMEOUT EQUALS STR
375 	{
376 		uint64_t tmp;
377 
378 		if (target->t_pingtimeout != -1)
379 			xo_errx(1, "duplicated PingTimeout at line %d", lineno);
380 
381 		if (expand_number($3, &tmp) != 0) {
382 			yyerror("invalid numeric value");
383 			free($3);
384 			return(1);
385 		}
386 		target->t_pingtimeout = tmp;
387 	}
388 	;
389 
390 login_timeout:	LOGINTIMEOUT EQUALS STR
391 	{
392 		uint64_t tmp;
393 
394 		if (target->t_logintimeout != -1)
395 			xo_errx(1, "duplicated LoginTimeout at line %d", lineno);
396 
397 		if (expand_number($3, &tmp) != 0) {
398 			yyerror("invalid numeric value");
399 			free($3);
400 			return(1);
401 		}
402 		target->t_logintimeout = tmp;
403 	}
404 	;
405 
406 %%
407 
408 void
409 yyerror(const char *str)
410 {
411 
412 	xo_errx(1, "error in configuration file at line %d near '%s': %s",
413 	    lineno, yytext, str);
414 }
415 
416 static void
417 check_perms(const char *path)
418 {
419 	struct stat sb;
420 	int error;
421 
422 	error = stat(path, &sb);
423 	if (error != 0) {
424 		xo_warn("stat");
425 		return;
426 	}
427 	if (sb.st_mode & S_IWOTH) {
428 		xo_warnx("%s is world-writable", path);
429 	} else if (sb.st_mode & S_IROTH) {
430 		xo_warnx("%s is world-readable", path);
431 	} else if (sb.st_mode & S_IXOTH) {
432 		/*
433 		 * Ok, this one doesn't matter, but still do it,
434 		 * just for consistency.
435 		 */
436 		xo_warnx("%s is world-executable", path);
437 	}
438 
439 	/*
440 	 * XXX: Should we also check for owner != 0?
441 	 */
442 }
443 
444 struct conf *
445 conf_new_from_file(const char *path)
446 {
447 	int error;
448 
449 	conf = conf_new();
450 	target = target_new(conf);
451 
452 	yyin = fopen(path, "r");
453 	if (yyin == NULL)
454 		xo_err(1, "unable to open configuration file %s", path);
455 	check_perms(path);
456 	lineno = 1;
457 	yyrestart(yyin);
458 	error = yyparse();
459 	assert(error == 0);
460 	fclose(yyin);
461 
462 	assert(target->t_nickname == NULL);
463 	target_delete(target);
464 
465 	conf_verify(conf);
466 
467 	return (conf);
468 }
469