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