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
49extern FILE *yyin;
50extern char *yytext;
51extern int lineno;
52
53static struct conf *conf;
54static struct target *target;
55
56extern void	yyerror(const char *);
57extern 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
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
77targets:
78	|
79	targets target
80	;
81
82target:		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
91target_entries:
92	|
93	target_entries target_entry
94	|
95	target_entries target_entry SEMICOLON
96	;
97
98target_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
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 (target->t_dscp != -1)
310			xo_errx(1, "duplicated dscp at line %d", lineno);
311		if (strcmp($3, "0x") == 0) {
312			tmp = strtol($3 + 2, NULL, 16);
313		} else if (expand_number($3, &tmp) != 0) {
314			yyerror("invalid numeric value");
315			free($3);
316			return(1);
317		}
318		if (tmp >= 0x40) {
319			yyerror("invalid dscp value");
320			return(1);
321		}
322
323		target->t_dscp = tmp;
324	}
325	| DSCP EQUALS BE	{ target->t_dscp = IPTOS_DSCP_CS0  >> 2 ; }
326	| DSCP EQUALS EF	{ target->t_dscp = IPTOS_DSCP_EF   >> 2 ; }
327	| DSCP EQUALS CS0	{ target->t_dscp = IPTOS_DSCP_CS0  >> 2 ; }
328	| DSCP EQUALS CS1	{ target->t_dscp = IPTOS_DSCP_CS1  >> 2 ; }
329	| DSCP EQUALS CS2	{ target->t_dscp = IPTOS_DSCP_CS2  >> 2 ; }
330	| DSCP EQUALS CS3	{ target->t_dscp = IPTOS_DSCP_CS3  >> 2 ; }
331	| DSCP EQUALS CS4	{ target->t_dscp = IPTOS_DSCP_CS4  >> 2 ; }
332	| DSCP EQUALS CS5	{ target->t_dscp = IPTOS_DSCP_CS5  >> 2 ; }
333	| DSCP EQUALS CS6	{ target->t_dscp = IPTOS_DSCP_CS6  >> 2 ; }
334	| DSCP EQUALS CS7	{ target->t_dscp = IPTOS_DSCP_CS7  >> 2 ; }
335	| DSCP EQUALS AF11	{ target->t_dscp = IPTOS_DSCP_AF11 >> 2 ; }
336	| DSCP EQUALS AF12	{ target->t_dscp = IPTOS_DSCP_AF12 >> 2 ; }
337	| DSCP EQUALS AF13	{ target->t_dscp = IPTOS_DSCP_AF13 >> 2 ; }
338	| DSCP EQUALS AF21	{ target->t_dscp = IPTOS_DSCP_AF21 >> 2 ; }
339	| DSCP EQUALS AF22	{ target->t_dscp = IPTOS_DSCP_AF22 >> 2 ; }
340	| DSCP EQUALS AF23	{ target->t_dscp = IPTOS_DSCP_AF23 >> 2 ; }
341	| DSCP EQUALS AF31	{ target->t_dscp = IPTOS_DSCP_AF31 >> 2 ; }
342	| DSCP EQUALS AF32	{ target->t_dscp = IPTOS_DSCP_AF32 >> 2 ; }
343	| DSCP EQUALS AF33	{ target->t_dscp = IPTOS_DSCP_AF33 >> 2 ; }
344	| DSCP EQUALS AF41	{ target->t_dscp = IPTOS_DSCP_AF41 >> 2 ; }
345	| DSCP EQUALS AF42	{ target->t_dscp = IPTOS_DSCP_AF42 >> 2 ; }
346	| DSCP EQUALS AF43	{ target->t_dscp = IPTOS_DSCP_AF43 >> 2 ; }
347	;
348
349pcp:	PCP EQUALS STR
350	{
351		uint64_t tmp;
352
353		if (target->t_pcp != -1)
354			xo_errx(1, "duplicated pcp at line %d", lineno);
355
356		if (expand_number($3, &tmp) != 0) {
357			yyerror("invalid numeric value");
358			free($3);
359			return(1);
360		}
361		if (tmp > 7) {
362			yyerror("invalid pcp value");
363			return(1);
364		}
365
366		target->t_pcp = tmp;
367	}
368	;
369
370%%
371
372void
373yyerror(const char *str)
374{
375
376	xo_errx(1, "error in configuration file at line %d near '%s': %s",
377	    lineno, yytext, str);
378}
379
380static void
381check_perms(const char *path)
382{
383	struct stat sb;
384	int error;
385
386	error = stat(path, &sb);
387	if (error != 0) {
388		xo_warn("stat");
389		return;
390	}
391	if (sb.st_mode & S_IWOTH) {
392		xo_warnx("%s is world-writable", path);
393	} else if (sb.st_mode & S_IROTH) {
394		xo_warnx("%s is world-readable", path);
395	} else if (sb.st_mode & S_IXOTH) {
396		/*
397		 * Ok, this one doesn't matter, but still do it,
398		 * just for consistency.
399		 */
400		xo_warnx("%s is world-executable", path);
401	}
402
403	/*
404	 * XXX: Should we also check for owner != 0?
405	 */
406}
407
408struct conf *
409conf_new_from_file(const char *path)
410{
411	int error;
412
413	conf = conf_new();
414	target = target_new(conf);
415
416	yyin = fopen(path, "r");
417	if (yyin == NULL)
418		xo_err(1, "unable to open configuration file %s", path);
419	check_perms(path);
420	lineno = 1;
421	yyrestart(yyin);
422	error = yyparse();
423	assert(error == 0);
424	fclose(yyin);
425
426	assert(target->t_nickname == NULL);
427	target_delete(target);
428
429	conf_verify(conf);
430
431	return (conf);
432}
433