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