xref: /openbsd/usr.sbin/radiusctl/parser.c (revision eff8f878)
1 /*	$OpenBSD: parser.c,v 1.6 2024/09/15 05:26:05 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 	{ KEYWORD,	"delete",	IPCP_DELETE,	t_ipcp_session_seq },
162 	{ ENDTOKEN,	"",		NONE,		NULL }
163 };
164 
165 static const struct token t_ipcp_flags[] = {
166 	{ NOTOKEN,	"",		NONE,		NULL },
167 	{ FLAGS,	"-json",	FLAGS_JSON,	NULL },
168 	{ ENDTOKEN,	"",		NONE,		NULL }
169 };
170 
171 static const struct token t_ipcp_session_seq[] = {
172 	{ SESSION_SEQ,	"",		NONE,		NULL },
173 	{ ENDTOKEN,	"",		NONE,		NULL }
174 };
175 
176 static const struct token	*match_token(char *, const struct token []);
177 static void			 show_valid_args(const struct token []);
178 
179 struct parse_result *
parse(int argc,char * argv[])180 parse(int argc, char *argv[])
181 {
182 	const struct token	*table = t_main;
183 	const struct token	*match;
184 
185 	while (argc >= 0) {
186 		if ((match = match_token(argv[0], table)) == NULL) {
187 			fprintf(stderr, "valid commands/args:\n");
188 			show_valid_args(table);
189 			return (NULL);
190 		}
191 
192 		argc--;
193 		argv++;
194 
195 		if (match->type == NOTOKEN || match->next == NULL)
196 			break;
197 
198 		table = match->next;
199 	}
200 
201 	if (argc > 0) {
202 		fprintf(stderr, "superfluous argument: %s\n", argv[0]);
203 		return (NULL);
204 	}
205 
206 	if (res.tries * res.interval.tv_sec > res.maxwait.tv_sec) {
207 		fprintf(stderr, "tries %u by interval %lld > maxwait %lld",
208 		    res.tries, res.interval.tv_sec, res.maxwait.tv_sec);
209 		return (NULL);
210 	}
211 
212 	return (&res);
213 }
214 
215 static const struct token *
match_token(char * word,const struct token table[])216 match_token(char *word, const struct token table[])
217 {
218 	u_int			 i, match = 0;
219 	const struct token	*t = NULL;
220 	long long		 num;
221 	const char		*errstr;
222 	size_t			 wordlen = 0;
223 
224 	if (word != NULL)
225 		wordlen = strlen(word);
226 
227 	for (i = 0; table[i].type != ENDTOKEN; i++) {
228 		switch (table[i].type) {
229 		case NOTOKEN:
230 			if (word == NULL || strlen(word) == 0) {
231 				match++;
232 				t = &table[i];
233 			}
234 			break;
235 		case KEYWORD:
236 			if (word != NULL && strncmp(word, table[i].keyword,
237 			    wordlen) == 0) {
238 				match++;
239 				t = &table[i];
240 				if (t->value)
241 					res.action = t->value;
242 			}
243 			break;
244 		case HOSTNAME:
245 			if (word == NULL)
246 				break;
247 			match++;
248 			res.hostname = word;
249 			t = &table[i];
250 			break;
251 		case SECRET:
252 			if (word == NULL)
253 				break;
254 			match++;
255 			res.secret = word;
256 			t = &table[i];
257 			break;
258 		case USERNAME:
259 			if (word == NULL)
260 				break;
261 			match++;
262 			res.username = word;
263 			t = &table[i];
264 			break;
265 		case PASSWORD:
266 			if (word == NULL)
267 				break;
268 			match++;
269 			res.password = word;
270 			t = &table[i];
271 			break;
272 		case PORT:
273 			if (word == NULL)
274 				break;
275 			num = strtonum(word, 1, UINT16_MAX, &errstr);
276 			if (errstr != NULL) {
277 				fprintf(stderr,
278 				    "invalid argument: %s is %s for \"port\"\n",
279 				    word, errstr);
280 				return (NULL);
281 			}
282 			match++;
283 			res.port = num;
284 			t = &table[i];
285 			break;
286 		case METHOD:
287 			if (word == NULL)
288 				break;
289 			if (strcasecmp(word, "pap") == 0)
290 				res.auth_method = PAP;
291 			else if (strcasecmp(word, "chap") == 0)
292 				res.auth_method = CHAP;
293 			else if (strcasecmp(word, "mschapv2") == 0)
294 				res.auth_method = MSCHAPV2;
295 			else {
296 				fprintf(stderr,
297 				    "invalid argument: %s for \"method\"\n",
298 				    word);
299 				return (NULL);
300 			}
301 			match++;
302 			t = &table[i];
303 			break;
304 		case NAS_PORT:
305 			if (word == NULL)
306 				break;
307 			num = strtonum(word, 0, 65535, &errstr);
308 			if (errstr != NULL) {
309 				fprintf(stderr,
310 				    "invalid argument: %s is %s for "
311 				    "\"nas-port\"\n", word, errstr);
312 				return (NULL);
313 			}
314 			match++;
315 			res.nas_port = num;
316 			t = &table[i];
317 			break;
318 
319 		case TRIES:
320 			if (word == NULL)
321 				break;
322 			num = strtonum(word,
323 			    TEST_TRIES_MIN, TEST_TRIES_MAX, &errstr);
324 			if (errstr != NULL) {
325 				printf("invalid argument: %s is %s"
326 				    " for \"tries\"\n", word, errstr);
327 				return (NULL);
328 			}
329 			match++;
330 			res.tries = num;
331 			t = &table[i];
332 			break;
333 		case INTERVAL:
334 			if (word == NULL)
335 				break;
336 			num = strtonum(word,
337 			    TEST_INTERVAL_MIN, TEST_INTERVAL_MAX, &errstr);
338 			if (errstr != NULL) {
339 				printf("invalid argument: %s is %s"
340 				    " for \"interval\"\n", word, errstr);
341 				return (NULL);
342 			}
343 			match++;
344 			res.interval.tv_sec = num;
345 			t = &table[i];
346 			break;
347 		case MAXWAIT:
348 			if (word == NULL)
349 				break;
350 			num = strtonum(word,
351 			    TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX, &errstr);
352 			if (errstr != NULL) {
353 				printf("invalid argument: %s is %s"
354 				    " for \"maxwait\"\n", word, errstr);
355 				return (NULL);
356 			}
357 			match++;
358 			res.maxwait.tv_sec = num;
359 			t = &table[i];
360 			break;
361 		case FLAGS:
362 			if (word != NULL && wordlen >= 2 &&
363 			    strncmp(word, table[i].keyword, wordlen) == 0) {
364 				match++;
365 				t = &table[i];
366 				if (t->value)
367 					res.flags |= t->value;
368 			}
369 			break;
370 		case SESSION_SEQ:
371 			if (word == NULL)
372 				break;
373 			match++;
374 			res.session_seq = strtonum(word, 1, UINT_MAX, &errstr);
375 			if (errstr != NULL) {
376 				printf("invalid argument: %s is %s for "
377 				    "\"session-id\"\n", word, errstr);
378 				return (NULL);
379 			}
380 			t = &table[i];
381 		case MSGAUTH:
382 			if (word != NULL &&
383 			    strcmp(word, table[i].keyword) == 0) {
384 				match++;
385 				res.msgauth = table[i].value;
386 				t = &table[i];
387 			}
388 			break;
389 		case ENDTOKEN:
390 			break;
391 		}
392 	}
393 
394 	if (match != 1) {
395 		if (word == NULL)
396 			fprintf(stderr, "missing argument:\n");
397 		else if (match > 1)
398 			fprintf(stderr, "ambiguous argument: %s\n", word);
399 		else if (match < 1)
400 			fprintf(stderr, "unknown argument: %s\n", word);
401 		return (NULL);
402 	}
403 
404 	return (t);
405 }
406 
407 static void
show_valid_args(const struct token table[])408 show_valid_args(const struct token table[])
409 {
410 	int	i;
411 
412 	for (i = 0; table[i].type != ENDTOKEN; i++) {
413 		switch (table[i].type) {
414 		case NOTOKEN:
415 			fprintf(stderr, "  <cr>\n");
416 			break;
417 		case KEYWORD:
418 			fprintf(stderr, "  %s\n", table[i].keyword);
419 			break;
420 		case HOSTNAME:
421 			fprintf(stderr, "  <hostname>\n");
422 			break;
423 		case SECRET:
424 			fprintf(stderr, "  <radius secret>\n");
425 			break;
426 		case USERNAME:
427 			fprintf(stderr, "  <username>\n");
428 			break;
429 		case PASSWORD:
430 			fprintf(stderr, "  <password>\n");
431 			break;
432 		case PORT:
433 			fprintf(stderr, "  <port number>\n");
434 			break;
435 		case METHOD:
436 			fprintf(stderr, "  <auth method (pap, chap, "
437 			    "mschapv2)>\n");
438 			break;
439 		case NAS_PORT:
440 			fprintf(stderr, "  <nas-port (0-65535)>\n");
441 			break;
442 		case TRIES:
443 			fprintf(stderr, "  <tries (%u-%u)>\n",
444 			    TEST_TRIES_MIN, TEST_TRIES_MAX);
445 			break;
446 		case INTERVAL:
447 			fprintf(stderr, "  <interval (%u-%u)>\n",
448 			    TEST_INTERVAL_MIN, TEST_INTERVAL_MAX);
449 			break;
450 		case MAXWAIT:
451 			fprintf(stderr, "  <maxwait (%u-%u)>\n",
452 			    TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX);
453 			break;
454 		case FLAGS:
455 			fprintf(stderr, "  %s\n", table[i].keyword);
456 			break;
457 		case SESSION_SEQ:
458 			fprintf(stderr, "  <sequence number>\n");
459 			break;
460 		case MSGAUTH:
461 			fprintf(stderr, "  %s\n", table[i].keyword);
462 			break;
463 		case ENDTOKEN:
464 			break;
465 		}
466 	}
467 }
468