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
47extern FILE *yyin;
48extern char *yytext;
49extern int lineno;
50
51static struct conf *conf;
52static struct target *target;
53
54extern void	yyerror(const char *);
55extern 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
75targets:
76	|
77	targets target
78	;
79
80target:		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
89target_entries:
90	|
91	target_entries target_entry
92	|
93	target_entries target_entry SEMICOLON
94	;
95
96target_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
140target_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
148target_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
156initiator_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
164initiator_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
172initiator_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
180user:		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
188secret:		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
196mutual_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
204mutual_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
212auth_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
226header_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
240data_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
254session_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
268enable:		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
279offload:	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
287protocol:	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
301ignored:	IGNORED EQUALS STR
302	{
303		xo_warnx("obsolete statement ignored at line %d", lineno);
304	}
305	;
306
307dscp:		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
351pcp:	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
372ping_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
388login_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
406void
407yyerror(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
414static void
415check_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
442struct conf *
443conf_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