xref: /openbsd/usr.sbin/radiusctl/parser.c (revision 76d0caae)
1 /*	$OpenBSD: parser.c,v 1.2 2020/02/24 07:07:11 dlg 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 
28 #include "parser.h"
29 
30 enum token_type {
31 	NOTOKEN,
32 	KEYWORD,
33 	HOSTNAME,
34 	SECRET,
35 	USERNAME,
36 	PASSWORD,
37 	PORT,
38 	METHOD,
39 	NAS_PORT,
40 	TRIES,
41 	INTERVAL,
42 	MAXWAIT,
43 	ENDTOKEN
44 };
45 
46 struct token {
47 	enum token_type		 type;
48 	const char		*keyword;
49 	int			 value;
50 	const struct token	*next;
51 };
52 
53 static struct parse_result res = {
54 	.tries		= TEST_TRIES_DEFAULT,
55 	.interval	= { TEST_INTERVAL_DEFAULT, 0 },
56 	.maxwait	= { TEST_MAXWAIT_DEFAULT, 0 },
57 };
58 
59 static const struct token t_test[];
60 static const struct token t_secret[];
61 static const struct token t_username[];
62 static const struct token t_test_opts[];
63 static const struct token t_password[];
64 static const struct token t_port[];
65 static const struct token t_method[];
66 static const struct token t_nas_port[];
67 static const struct token t_tries[];
68 static const struct token t_interval[];
69 static const struct token t_maxwait[];
70 
71 static const struct token t_main[] = {
72 	{ KEYWORD,	"test",		TEST,		t_test },
73 	{ ENDTOKEN,	"",		NONE,		NULL }
74 };
75 
76 static const struct token t_test[] = {
77 	{ HOSTNAME,	"",		NONE,		t_secret },
78 	{ ENDTOKEN,	"",		NONE,		NULL }
79 };
80 
81 static const struct token t_secret[] = {
82 	{ SECRET,	"",		NONE,		t_username },
83 	{ ENDTOKEN,	"",		NONE,		NULL }
84 };
85 
86 static const struct token t_username[] = {
87 	{ USERNAME,	"",		NONE,		t_test_opts },
88 	{ ENDTOKEN,	"",		NONE,		NULL }
89 };
90 
91 static const struct token t_test_opts[] = {
92 	{ NOTOKEN,	"",		NONE,		NULL },
93 	{ KEYWORD,	"password",	NONE,		t_password },
94 	{ KEYWORD,	"port",		NONE,		t_port },
95 	{ KEYWORD,	"method",	NONE,		t_method },
96 	{ KEYWORD,	"nas-port",	NONE,		t_nas_port },
97 	{ KEYWORD,	"interval",	NONE,		t_interval },
98 	{ KEYWORD,	"tries",	NONE,		t_tries },
99 	{ KEYWORD,	"maxwait",	NONE,		t_maxwait },
100 	{ ENDTOKEN,	"",		NONE,		NULL }
101 };
102 
103 static const struct token t_password[] = {
104 	{ PASSWORD,	"",		NONE,		t_test_opts },
105 	{ ENDTOKEN,	"",		NONE,		NULL }
106 };
107 
108 static const struct token t_port[] = {
109 	{ PORT,		"",		NONE,		t_test_opts },
110 	{ ENDTOKEN,	"",		NONE,		NULL }
111 };
112 
113 static const struct token t_method[] = {
114 	{ METHOD,	"",		NONE,		t_test_opts },
115 	{ ENDTOKEN,	"",		NONE,		NULL }
116 };
117 
118 static const struct token t_nas_port[] = {
119 	{ NAS_PORT,	"",		NONE,		t_test_opts },
120 	{ ENDTOKEN,	"",		NONE,		NULL }
121 };
122 
123 static const struct token t_tries[] = {
124 	{ TRIES,	"",		NONE,		t_test_opts },
125 	{ ENDTOKEN,	"",		NONE,		NULL }
126 };
127 
128 static const struct token t_interval[] = {
129 	{ INTERVAL,	"",		NONE,		t_test_opts },
130 	{ ENDTOKEN,	"",		NONE,		NULL }
131 };
132 
133 static const struct token t_maxwait[] = {
134 	{ MAXWAIT,	"",		NONE,		t_test_opts },
135 	{ ENDTOKEN,	"",		NONE,		NULL }
136 };
137 
138 
139 static const struct token	*match_token(char *, const struct token []);
140 static void			 show_valid_args(const struct token []);
141 
142 struct parse_result *
143 parse(int argc, char *argv[])
144 {
145 	const struct token	*table = t_main;
146 	const struct token	*match;
147 
148 	while (argc >= 0) {
149 		if ((match = match_token(argv[0], table)) == NULL) {
150 			fprintf(stderr, "valid commands/args:\n");
151 			show_valid_args(table);
152 			return (NULL);
153 		}
154 
155 		argc--;
156 		argv++;
157 
158 		if (match->type == NOTOKEN || match->next == NULL)
159 			break;
160 
161 		table = match->next;
162 	}
163 
164 	if (argc > 0) {
165 		fprintf(stderr, "superfluous argument: %s\n", argv[0]);
166 		return (NULL);
167 	}
168 
169 	if (res.tries * res.interval.tv_sec > res.maxwait.tv_sec) {
170 		fprintf(stderr, "tries %u by interval %lld > maxwait %lld",
171 		    res.tries, res.interval.tv_sec, res.maxwait.tv_sec);
172 		return (NULL);
173 	}
174 
175 	return (&res);
176 }
177 
178 static const struct token *
179 match_token(char *word, const struct token table[])
180 {
181 	u_int			 i, match = 0;
182 	const struct token	*t = NULL;
183 	long long		 num;
184 	const char		*errstr;
185 
186 	for (i = 0; table[i].type != ENDTOKEN; i++) {
187 		switch (table[i].type) {
188 		case NOTOKEN:
189 			if (word == NULL || strlen(word) == 0) {
190 				match++;
191 				t = &table[i];
192 			}
193 			break;
194 		case KEYWORD:
195 			if (word != NULL && strncmp(word, table[i].keyword,
196 			    strlen(word)) == 0) {
197 				match++;
198 				t = &table[i];
199 				if (t->value)
200 					res.action = t->value;
201 			}
202 			break;
203 		case HOSTNAME:
204 			if (word == NULL)
205 				break;
206 			match++;
207 			res.hostname = word;
208 			t = &table[i];
209 			break;
210 		case SECRET:
211 			if (word == NULL)
212 				break;
213 			match++;
214 			res.secret = word;
215 			t = &table[i];
216 			break;
217 		case USERNAME:
218 			if (word == NULL)
219 				break;
220 			match++;
221 			res.username = word;
222 			t = &table[i];
223 			break;
224 		case PASSWORD:
225 			if (word == NULL)
226 				break;
227 			match++;
228 			res.password = word;
229 			t = &table[i];
230 			break;
231 		case PORT:
232 			if (word == NULL)
233 				break;
234 			num = strtonum(word, 1, UINT16_MAX, &errstr);
235 			if (errstr != NULL) {
236 				fprintf(stderr,
237 				    "invalid argument: %s is %s for \"port\"\n",
238 				    word, errstr);
239 				return (NULL);
240 			}
241 			match++;
242 			res.port = num;
243 			t = &table[i];
244 			break;
245 		case METHOD:
246 			if (word == NULL)
247 				break;
248 			if (strcasecmp(word, "pap") == 0)
249 				res.auth_method = PAP;
250 			else if (strcasecmp(word, "chap") == 0)
251 				res.auth_method = CHAP;
252 			else if (strcasecmp(word, "mschapv2") == 0)
253 				res.auth_method = MSCHAPV2;
254 			else {
255 				fprintf(stderr,
256 				    "invalid argument: %s for \"method\"\n",
257 				    word);
258 				return (NULL);
259 			}
260 			match++;
261 			t = &table[i];
262 			break;
263 		case NAS_PORT:
264 			if (word == NULL)
265 				break;
266 			num = strtonum(word, 0, 65535, &errstr);
267 			if (errstr != NULL) {
268 				fprintf(stderr,
269 				    "invalid argument: %s is %s for "
270 				    "\"nas-port\"\n", word, errstr);
271 				return (NULL);
272 			}
273 			match++;
274 			res.nas_port = num;
275 			t = &table[i];
276 			break;
277 
278 		case TRIES:
279 			if (word == NULL)
280 				break;
281 			num = strtonum(word,
282 			    TEST_TRIES_MIN, TEST_TRIES_MAX, &errstr);
283 			if (errstr != NULL) {
284 				printf("invalid argument: %s is %s"
285 				    " for \"tries\"\n", word, errstr);
286 				return (NULL);
287 			}
288 			match++;
289 			res.tries = num;
290 			t = &table[i];
291 			break;
292 		case INTERVAL:
293 			if (word == NULL)
294 				break;
295 			num = strtonum(word,
296 			    TEST_INTERVAL_MIN, TEST_INTERVAL_MAX, &errstr);
297 			if (errstr != NULL) {
298 				printf("invalid argument: %s is %s"
299 				    " for \"interval\"\n", word, errstr);
300 				return (NULL);
301 			}
302 			match++;
303 			res.interval.tv_sec = num;
304 			t = &table[i];
305 			break;
306 		case MAXWAIT:
307 			if (word == NULL)
308 				break;
309 			num = strtonum(word,
310 			    TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX, &errstr);
311 			if (errstr != NULL) {
312 				printf("invalid argument: %s is %s"
313 				    " for \"maxwait\"\n", word, errstr);
314 				return (NULL);
315 			}
316 			match++;
317 			res.maxwait.tv_sec = num;
318 			t = &table[i];
319 			break;
320 
321 		case ENDTOKEN:
322 			break;
323 		}
324 	}
325 
326 	if (match != 1) {
327 		if (word == NULL)
328 			fprintf(stderr, "missing argument:\n");
329 		else if (match > 1)
330 			fprintf(stderr, "ambiguous argument: %s\n", word);
331 		else if (match < 1)
332 			fprintf(stderr, "unknown argument: %s\n", word);
333 		return (NULL);
334 	}
335 
336 	return (t);
337 }
338 
339 static void
340 show_valid_args(const struct token table[])
341 {
342 	int	i;
343 
344 	for (i = 0; table[i].type != ENDTOKEN; i++) {
345 		switch (table[i].type) {
346 		case NOTOKEN:
347 			fprintf(stderr, "  <cr>\n");
348 			break;
349 		case KEYWORD:
350 			fprintf(stderr, "  %s\n", table[i].keyword);
351 			break;
352 		case HOSTNAME:
353 			fprintf(stderr, "  <hostname>\n");
354 			break;
355 		case SECRET:
356 			fprintf(stderr, "  <radius secret>\n");
357 			break;
358 		case USERNAME:
359 			fprintf(stderr, "  <username>\n");
360 			break;
361 		case PASSWORD:
362 			fprintf(stderr, "  <password>\n");
363 			break;
364 		case PORT:
365 			fprintf(stderr, "  <port number>\n");
366 			break;
367 		case METHOD:
368 			fprintf(stderr, "  <auth method (pap, chap, "
369 			    "mschapv2)>\n");
370 			break;
371 		case NAS_PORT:
372 			fprintf(stderr, "  <nas-port (0-65535)>\n");
373 			break;
374 		case TRIES:
375 			fprintf(stderr, "  <tries (%u-%u)>\n",
376 			    TEST_TRIES_MIN, TEST_TRIES_MAX);
377 			break;
378 		case INTERVAL:
379 			fprintf(stderr, "  <interval (%u-%u)>\n",
380 			    TEST_INTERVAL_MIN, TEST_INTERVAL_MAX);
381 			break;
382 		case MAXWAIT:
383 			fprintf(stderr, "  <maxwait (%u-%u)>\n",
384 			    TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX);
385 			break;
386 		case ENDTOKEN:
387 			break;
388 		}
389 	}
390 }
391