xref: /openbsd-src/usr.sbin/radiusctl/parser.c (revision dcc91c2622318df8f66a9bca2d2864253df1bfc3)
1 /*	$OpenBSD: parser.c,v 1.4 2024/07/24 08:27:20 yasuoka Exp $	*/
2 
3 /*
4  * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
5  * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
6  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/time.h>
22 
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <strings.h>
28 #include <limits.h>
29 
30 #include "parser.h"
31 
32 enum token_type {
33 	NOTOKEN,
34 	KEYWORD,
35 	HOSTNAME,
36 	SECRET,
37 	USERNAME,
38 	PASSWORD,
39 	PORT,
40 	METHOD,
41 	NAS_PORT,
42 	TRIES,
43 	INTERVAL,
44 	MAXWAIT,
45 	FLAGS,
46 	SESSION_SEQ,
47 	MSGAUTH,
48 	ENDTOKEN
49 };
50 
51 struct token {
52 	enum token_type		 type;
53 	const char		*keyword;
54 	int			 value;
55 	const struct token	*next;
56 };
57 
58 static struct parse_result res = {
59 	.tries		= TEST_TRIES_DEFAULT,
60 	.interval	= { TEST_INTERVAL_DEFAULT, 0 },
61 	.maxwait	= { TEST_MAXWAIT_DEFAULT, 0 },
62 	.msgauth	= 1
63 };
64 
65 static const struct token t_test[];
66 static const struct token t_secret[];
67 static const struct token t_username[];
68 static const struct token t_test_opts[];
69 static const struct token t_password[];
70 static const struct token t_port[];
71 static const struct token t_method[];
72 static const struct token t_nas_port[];
73 static const struct token t_tries[];
74 static const struct token t_interval[];
75 static const struct token t_maxwait[];
76 static const struct token t_yesno[];
77 static const struct token t_ipcp[];
78 static const struct token t_ipcp_flags[];
79 static const struct token t_ipcp_session_seq[];
80 
81 static const struct token t_main[] = {
82 	{ KEYWORD,	"test",		TEST,		t_test },
83 	{ KEYWORD,	"ipcp",		NONE,		t_ipcp },
84 	{ ENDTOKEN,	"",		NONE,		NULL }
85 };
86 
87 static const struct token t_test[] = {
88 	{ HOSTNAME,	"",		NONE,		t_secret },
89 	{ ENDTOKEN,	"",		NONE,		NULL }
90 };
91 
92 static const struct token t_secret[] = {
93 	{ SECRET,	"",		NONE,		t_username },
94 	{ ENDTOKEN,	"",		NONE,		NULL }
95 };
96 
97 static const struct token t_username[] = {
98 	{ USERNAME,	"",		NONE,		t_test_opts },
99 	{ ENDTOKEN,	"",		NONE,		NULL }
100 };
101 
102 static const struct token t_test_opts[] = {
103 	{ NOTOKEN,	"",		NONE,		NULL },
104 	{ KEYWORD,	"password",	NONE,		t_password },
105 	{ KEYWORD,	"port",		NONE,		t_port },
106 	{ KEYWORD,	"method",	NONE,		t_method },
107 	{ KEYWORD,	"nas-port",	NONE,		t_nas_port },
108 	{ KEYWORD,	"interval",	NONE,		t_interval },
109 	{ KEYWORD,	"tries",	NONE,		t_tries },
110 	{ KEYWORD,	"maxwait",	NONE,		t_maxwait },
111 	{ KEYWORD,	"msgauth",	NONE,		t_yesno },
112 	{ ENDTOKEN,	"",		NONE,		NULL }
113 };
114 
115 static const struct token t_password[] = {
116 	{ PASSWORD,	"",		NONE,		t_test_opts },
117 	{ ENDTOKEN,	"",		NONE,		NULL }
118 };
119 
120 static const struct token t_port[] = {
121 	{ PORT,		"",		NONE,		t_test_opts },
122 	{ ENDTOKEN,	"",		NONE,		NULL }
123 };
124 
125 static const struct token t_method[] = {
126 	{ METHOD,	"",		NONE,		t_test_opts },
127 	{ ENDTOKEN,	"",		NONE,		NULL }
128 };
129 
130 static const struct token t_nas_port[] = {
131 	{ NAS_PORT,	"",		NONE,		t_test_opts },
132 	{ ENDTOKEN,	"",		NONE,		NULL }
133 };
134 
135 static const struct token t_tries[] = {
136 	{ TRIES,	"",		NONE,		t_test_opts },
137 	{ ENDTOKEN,	"",		NONE,		NULL }
138 };
139 
140 static const struct token t_interval[] = {
141 	{ INTERVAL,	"",		NONE,		t_test_opts },
142 	{ ENDTOKEN,	"",		NONE,		NULL }
143 };
144 
145 static const struct token t_maxwait[] = {
146 	{ MAXWAIT,	"",		NONE,		t_test_opts },
147 	{ ENDTOKEN,	"",		NONE,		NULL }
148 };
149 
150 static const struct token t_yesno[] = {
151 	{ MSGAUTH,	"yes",		1,		t_test_opts },
152 	{ MSGAUTH,	"no",		0,		t_test_opts },
153 	{ ENDTOKEN,	"",		NONE,		NULL }
154 };
155 
156 static const struct token t_ipcp[] = {
157 	{ KEYWORD,	"show",		IPCP_SHOW,	NULL },
158 	{ KEYWORD,	"dump",		IPCP_DUMP,	t_ipcp_flags },
159 	{ KEYWORD,	"monitor",	IPCP_MONITOR,	t_ipcp_flags },
160 	{ KEYWORD,	"disconnect",	IPCP_DISCONNECT,t_ipcp_session_seq },
161 	{ ENDTOKEN,	"",		NONE,		NULL }
162 };
163 
164 static const struct token t_ipcp_flags[] = {
165 	{ NOTOKEN,	"",		NONE,		NULL },
166 	{ FLAGS,	"-json",	FLAGS_JSON,	NULL },
167 	{ ENDTOKEN,	"",		NONE,		NULL }
168 };
169 
170 static const struct token t_ipcp_session_seq[] = {
171 	{ SESSION_SEQ,	"",		NONE,		NULL },
172 	{ ENDTOKEN,	"",		NONE,		NULL }
173 };
174 
175 static const struct token	*match_token(char *, const struct token []);
176 static void			 show_valid_args(const struct token []);
177 
178 struct parse_result *
179 parse(int argc, char *argv[])
180 {
181 	const struct token	*table = t_main;
182 	const struct token	*match;
183 
184 	while (argc >= 0) {
185 		if ((match = match_token(argv[0], table)) == NULL) {
186 			fprintf(stderr, "valid commands/args:\n");
187 			show_valid_args(table);
188 			return (NULL);
189 		}
190 
191 		argc--;
192 		argv++;
193 
194 		if (match->type == NOTOKEN || match->next == NULL)
195 			break;
196 
197 		table = match->next;
198 	}
199 
200 	if (argc > 0) {
201 		fprintf(stderr, "superfluous argument: %s\n", argv[0]);
202 		return (NULL);
203 	}
204 
205 	if (res.tries * res.interval.tv_sec > res.maxwait.tv_sec) {
206 		fprintf(stderr, "tries %u by interval %lld > maxwait %lld",
207 		    res.tries, res.interval.tv_sec, res.maxwait.tv_sec);
208 		return (NULL);
209 	}
210 
211 	return (&res);
212 }
213 
214 static const struct token *
215 match_token(char *word, const struct token table[])
216 {
217 	u_int			 i, match = 0;
218 	const struct token	*t = NULL;
219 	long long		 num;
220 	const char		*errstr;
221 	size_t			 wordlen = 0;
222 
223 	if (word != NULL)
224 		wordlen = strlen(word);
225 
226 	for (i = 0; table[i].type != ENDTOKEN; i++) {
227 		switch (table[i].type) {
228 		case NOTOKEN:
229 			if (word == NULL || strlen(word) == 0) {
230 				match++;
231 				t = &table[i];
232 			}
233 			break;
234 		case KEYWORD:
235 			if (word != NULL && strncmp(word, table[i].keyword,
236 			    wordlen) == 0) {
237 				match++;
238 				t = &table[i];
239 				if (t->value)
240 					res.action = t->value;
241 			}
242 			break;
243 		case HOSTNAME:
244 			if (word == NULL)
245 				break;
246 			match++;
247 			res.hostname = word;
248 			t = &table[i];
249 			break;
250 		case SECRET:
251 			if (word == NULL)
252 				break;
253 			match++;
254 			res.secret = word;
255 			t = &table[i];
256 			break;
257 		case USERNAME:
258 			if (word == NULL)
259 				break;
260 			match++;
261 			res.username = word;
262 			t = &table[i];
263 			break;
264 		case PASSWORD:
265 			if (word == NULL)
266 				break;
267 			match++;
268 			res.password = word;
269 			t = &table[i];
270 			break;
271 		case PORT:
272 			if (word == NULL)
273 				break;
274 			num = strtonum(word, 1, UINT16_MAX, &errstr);
275 			if (errstr != NULL) {
276 				fprintf(stderr,
277 				    "invalid argument: %s is %s for \"port\"\n",
278 				    word, errstr);
279 				return (NULL);
280 			}
281 			match++;
282 			res.port = num;
283 			t = &table[i];
284 			break;
285 		case METHOD:
286 			if (word == NULL)
287 				break;
288 			if (strcasecmp(word, "pap") == 0)
289 				res.auth_method = PAP;
290 			else if (strcasecmp(word, "chap") == 0)
291 				res.auth_method = CHAP;
292 			else if (strcasecmp(word, "mschapv2") == 0)
293 				res.auth_method = MSCHAPV2;
294 			else {
295 				fprintf(stderr,
296 				    "invalid argument: %s for \"method\"\n",
297 				    word);
298 				return (NULL);
299 			}
300 			match++;
301 			t = &table[i];
302 			break;
303 		case NAS_PORT:
304 			if (word == NULL)
305 				break;
306 			num = strtonum(word, 0, 65535, &errstr);
307 			if (errstr != NULL) {
308 				fprintf(stderr,
309 				    "invalid argument: %s is %s for "
310 				    "\"nas-port\"\n", word, errstr);
311 				return (NULL);
312 			}
313 			match++;
314 			res.nas_port = num;
315 			t = &table[i];
316 			break;
317 
318 		case TRIES:
319 			if (word == NULL)
320 				break;
321 			num = strtonum(word,
322 			    TEST_TRIES_MIN, TEST_TRIES_MAX, &errstr);
323 			if (errstr != NULL) {
324 				printf("invalid argument: %s is %s"
325 				    " for \"tries\"\n", word, errstr);
326 				return (NULL);
327 			}
328 			match++;
329 			res.tries = num;
330 			t = &table[i];
331 			break;
332 		case INTERVAL:
333 			if (word == NULL)
334 				break;
335 			num = strtonum(word,
336 			    TEST_INTERVAL_MIN, TEST_INTERVAL_MAX, &errstr);
337 			if (errstr != NULL) {
338 				printf("invalid argument: %s is %s"
339 				    " for \"interval\"\n", word, errstr);
340 				return (NULL);
341 			}
342 			match++;
343 			res.interval.tv_sec = num;
344 			t = &table[i];
345 			break;
346 		case MAXWAIT:
347 			if (word == NULL)
348 				break;
349 			num = strtonum(word,
350 			    TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX, &errstr);
351 			if (errstr != NULL) {
352 				printf("invalid argument: %s is %s"
353 				    " for \"maxwait\"\n", word, errstr);
354 				return (NULL);
355 			}
356 			match++;
357 			res.maxwait.tv_sec = num;
358 			t = &table[i];
359 			break;
360 		case FLAGS:
361 			if (word != NULL && wordlen >= 2 &&
362 			    strncmp(word, table[i].keyword, wordlen) == 0) {
363 				match++;
364 				t = &table[i];
365 				if (t->value)
366 					res.flags |= t->value;
367 			}
368 			break;
369 		case SESSION_SEQ:
370 			if (word == NULL)
371 				break;
372 			match++;
373 			res.session_seq = strtonum(word, 1, UINT_MAX, &errstr);
374 			if (errstr != NULL)
375 				printf("invalid argument: %s is %s for "
376 				"\"session-id\"", word, errstr);
377 			t = &table[i];
378 		case MSGAUTH:
379 			if (word != NULL &&
380 			    strcmp(word, table[i].keyword) == 0) {
381 				match++;
382 				res.msgauth = table[i].value;
383 				t = &table[i];
384 			}
385 			break;
386 		case ENDTOKEN:
387 			break;
388 		}
389 	}
390 
391 	if (match != 1) {
392 		if (word == NULL)
393 			fprintf(stderr, "missing argument:\n");
394 		else if (match > 1)
395 			fprintf(stderr, "ambiguous argument: %s\n", word);
396 		else if (match < 1)
397 			fprintf(stderr, "unknown argument: %s\n", word);
398 		return (NULL);
399 	}
400 
401 	return (t);
402 }
403 
404 static void
405 show_valid_args(const struct token table[])
406 {
407 	int	i;
408 
409 	for (i = 0; table[i].type != ENDTOKEN; i++) {
410 		switch (table[i].type) {
411 		case NOTOKEN:
412 			fprintf(stderr, "  <cr>\n");
413 			break;
414 		case KEYWORD:
415 			fprintf(stderr, "  %s\n", table[i].keyword);
416 			break;
417 		case HOSTNAME:
418 			fprintf(stderr, "  <hostname>\n");
419 			break;
420 		case SECRET:
421 			fprintf(stderr, "  <radius secret>\n");
422 			break;
423 		case USERNAME:
424 			fprintf(stderr, "  <username>\n");
425 			break;
426 		case PASSWORD:
427 			fprintf(stderr, "  <password>\n");
428 			break;
429 		case PORT:
430 			fprintf(stderr, "  <port number>\n");
431 			break;
432 		case METHOD:
433 			fprintf(stderr, "  <auth method (pap, chap, "
434 			    "mschapv2)>\n");
435 			break;
436 		case NAS_PORT:
437 			fprintf(stderr, "  <nas-port (0-65535)>\n");
438 			break;
439 		case TRIES:
440 			fprintf(stderr, "  <tries (%u-%u)>\n",
441 			    TEST_TRIES_MIN, TEST_TRIES_MAX);
442 			break;
443 		case INTERVAL:
444 			fprintf(stderr, "  <interval (%u-%u)>\n",
445 			    TEST_INTERVAL_MIN, TEST_INTERVAL_MAX);
446 			break;
447 		case MAXWAIT:
448 			fprintf(stderr, "  <maxwait (%u-%u)>\n",
449 			    TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX);
450 			break;
451 		case FLAGS:
452 			fprintf(stderr, "  %s\n", table[i].keyword);
453 			break;
454 		case SESSION_SEQ:
455 			fprintf(stderr, "  <sequence number>\n");
456 			break;
457 		case MSGAUTH:
458 			fprintf(stderr, "  %s\n", table[i].keyword);
459 			break;
460 		case ENDTOKEN:
461 			break;
462 		}
463 	}
464 }
465