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