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
50extern FILE *yyin;
51extern char *yytext;
52extern int lineno;
53
54static struct conf *conf;
55static struct target *target;
56
57extern void	yyerror(const char *);
58extern int	yylex(void);
59extern 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
79targets:
80	|
81	targets target
82	;
83
84target:		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
93target_entries:
94	|
95	target_entries target_entry
96	|
97	target_entries target_entry SEMICOLON
98	;
99
100target_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
138target_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
146target_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
154initiator_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
162initiator_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
170initiator_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
178user:		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
186secret:		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
194mutual_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
202mutual_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
210auth_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
224header_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
238data_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
252session_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
266enable:		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
277offload:	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
285protocol:	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
299ignored:	IGNORED EQUALS STR
300	{
301		xo_warnx("obsolete statement ignored at line %d", lineno);
302	}
303	;
304
305dscp:		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
349void
350yyerror(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
357static void
358check_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
385struct conf *
386conf_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