xref: /openbsd/usr.bin/bgplg/bgplg.c (revision 09467b48)
1 /*	$OpenBSD: bgplg.c,v 1.19 2018/03/05 10:53:37 denis Exp $	*/
2 
3 /*
4  * Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <signal.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <limits.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <err.h>
32 
33 #include "bgplg.h"
34 
35 #define INC_STYLE	"/conf/bgplg.css"
36 #define INC_HEAD	"/conf/bgplg.head"
37 #define INC_FOOT	"/conf/bgplg.foot"
38 
39 #define BGPDSOCK	"/run/bgpd.rsock"
40 #define BGPCTL		"/bin/bgpctl", "-s", BGPDSOCK
41 #define PING		"/bin/ping"
42 #define TRACEROUTE	"/bin/traceroute"
43 #define PING6		"/bin/ping6"
44 #define TRACEROUTE6	"/bin/traceroute6"
45 #define CONTENT_TYPE	"text/html"
46 
47 static struct cmd cmds[] = CMDS;
48 
49 char		 *lg_getenv(const char *, int *);
50 void		  lg_urldecode(char *);
51 char		**lg_arg2argv(char *, int *);
52 char		**lg_argextra(char **, int, struct cmd *);
53 char		 *lg_getarg(const char *, char *, int);
54 int		  lg_incl(const char *);
55 
56 void
57 lg_urldecode(char *str)
58 {
59 	size_t i, c, len;
60 	char code[3];
61 	long result;
62 
63 	if (str && *str) {
64 		len = strlen(str);
65 		i = c = 0;
66 		while (i < len) {
67 			if (str[i] == '%' && i <= (len - 2)) {
68 				if (isxdigit((unsigned char)str[i + 1]) &&
69 				    isxdigit((unsigned char)str[i + 2])) {
70 					code[0] = str[i + 1];
71 					code[1] = str[i + 2];
72 					code[2] = 0;
73 					result = strtol(code, NULL, 16);
74 					/* Replace NUL chars with a space */
75 					if (result == 0)
76 						result = ' ';
77 					str[c++] = result;
78 					i += 3;
79 				} else {
80 					str[c++] = '%';
81 					i++;
82 				}
83 			} else if (str[i] == '+') {
84 				str[i] = ' ';
85 			} else {
86 				if (c != i)
87 					str[c] = str[i];
88 				c++;
89 				i++;
90 			}
91 		}
92 		str[c] = 0x0;
93 	}
94 }
95 
96 char *
97 lg_getenv(const char *name, int *lenp)
98 {
99 	size_t len;
100 	u_int i;
101 	char *ptr;
102 
103 	if ((ptr = getenv(name)) == NULL)
104 		return (NULL);
105 
106 	lg_urldecode(ptr);
107 
108 	if (!(len = strlen(ptr)))
109 		return (NULL);
110 
111 	if (lenp != NULL)
112 		*lenp = len;
113 
114 #define allowed_in_string(_x)                                           \
115 	(isalnum((unsigned char)_x) || strchr("-_.:/= ", _x))
116 
117 	for (i = 0; i < len; i++) {
118 		if (ptr[i] == '&')
119 			ptr[i] = '\0';
120 		if (!allowed_in_string(ptr[i])) {
121 			printf("invalid character in input\n");
122 			return (NULL);
123 		}
124 	}
125 
126 	return (ptr);
127 #undef allowed_in_string
128 }
129 
130 char *
131 lg_getarg(const char *name, char *arg, int len)
132 {
133 	char *ptr = arg;
134 	size_t namelen, ptrlen;
135 	int i;
136 
137 	namelen = strlen(name);
138 
139 	for (i = 0; i < len; i++) {
140 		if (arg[i] == '\0')
141 			continue;
142 		ptr = arg + i;
143 		ptrlen = strlen(ptr);
144 		if (namelen >= ptrlen)
145 			continue;
146 		if (strncmp(name, ptr, namelen) == 0)
147 			return (ptr + namelen);
148 	}
149 
150 	return (NULL);
151 }
152 
153 char **
154 lg_arg2argv(char *arg, int *argc)
155 {
156 	char **argv, *ptr = arg;
157 	size_t len;
158 	u_int i, c = 1;
159 
160 	len = strlen(arg);
161 
162 	/* Count elements */
163 	for (i = 0; i < len; i++) {
164 		if (isspace((unsigned char)arg[i])) {
165 			/* filter out additional options */
166 			if (arg[i + 1] == '-') {
167 				printf("invalid input\n");
168 				return (NULL);
169 			}
170 			arg[i] = '\0';
171 			c++;
172 		}
173 	}
174 
175 	/* Generate array */
176 	if ((argv = calloc(c + 1, sizeof(char *))) == NULL) {
177 		printf("fatal error: %s\n", strerror(errno));
178 		return (NULL);
179 	}
180 
181 	argv[c] = NULL;
182 	*argc = c;
183 
184 	/* Fill array */
185 	for (i = c = 0; i < len; i++) {
186 		if (arg[i] == '\0' || i == 0) {
187 			if (i != 0)
188 				ptr = &arg[i + 1];
189 			argv[c++] = ptr;
190 		}
191 	}
192 
193 	return (argv);
194 }
195 
196 char **
197 lg_argextra(char **argv, int argc, struct cmd *cmdp)
198 {
199 	char **new_argv;
200 	int i, c = 0;
201 
202 	/* Count elements */
203 	for (i = 0; cmdp->earg[i] != NULL; i++)
204 		c++;
205 
206 	/* Generate array */
207 	if ((new_argv = calloc(c + argc + 1, sizeof(char *))) == NULL) {
208 		printf("fatal error: %s\n", strerror(errno));
209 		return (NULL);
210 	}
211 
212 	/* Fill array */
213 	for (i = c = 0; cmdp->earg[i] != NULL; i++)
214 		new_argv[c++] = cmdp->earg[i];
215 
216 	/* Append old array */
217 	for (i = 0; i < argc; i++)
218 		new_argv[c++] = argv[i];
219 
220 	new_argv[c] = NULL;
221 
222 	free(argv);
223 
224 	return (new_argv);
225 }
226 
227 int
228 lg_incl(const char *file)
229 {
230 	char buf[BUFSIZ];
231 	int fd, len;
232 
233 	if ((fd = open(file, O_RDONLY)) == -1)
234 		return (errno);
235 
236 	do {
237 		len = read(fd, buf, sizeof(buf));
238 		fwrite(buf, len, 1, stdout);
239 	} while(len == BUFSIZ);
240 
241 	close(fd);
242 	return (0);
243 }
244 
245 int
246 main(void)
247 {
248 	char *query, *myname, *self, *cmd = NULL, *req;
249 	char **argv = NULL;
250 	int ret = 1, argc = 0, query_length = 0;
251 	struct stat st;
252 	u_int i;
253 	struct cmd *cmdp = NULL;
254 
255 	if (pledge("stdio rpath proc exec", NULL) == -1)
256 		err(1, "pledge");
257 
258 	if ((myname = lg_getenv("SERVER_NAME", NULL)) == NULL)
259 		return (1);
260 
261 	printf("Content-Type: %s\n"
262 	    "Cache-Control: no-cache\n\n"
263 	    "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
264 	    "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" "
265 	    "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
266 	    "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
267 	    "<head>\n"
268 	    "<title>%s</title>\n",
269 	    CONTENT_TYPE, myname);
270 	if (stat(INC_STYLE, &st) == 0) {
271 		printf("<style type='text/css'><!--\n");
272 		lg_incl(INC_STYLE);
273 		printf("--></style>\n");
274 	}
275 	if (stat(INC_HEAD, &st) != 0 || lg_incl(INC_HEAD) != 0) {
276 		printf("</head>\n"
277 		    "<body>\n");
278 	}
279 
280 	/* print a form with possible options */
281 	if ((self = lg_getenv("SCRIPT_NAME", NULL)) == NULL) {
282 		printf("fatal error: invalid request\n");
283 		goto err;
284 	}
285 	if ((query = lg_getenv("QUERY_STRING", &query_length)) != NULL)
286 		cmd = lg_getarg("cmd=", query, query_length);
287 	printf(
288 	    "<form action='%s'>\n"
289 	    "<div class=\"command\">\n"
290 	    "<select name='cmd'>\n",
291 	    self);
292 	for (i = 0; cmds[i].name != NULL; i++) {
293 		if (!lg_checkperm(&cmds[i]))
294 			continue;
295 
296 		if (cmd != NULL && strcmp(cmd, cmds[i].name) == 0)
297 			printf("<option value='%s' selected='selected'>%s"
298 			    "</option>\n",
299 			    cmds[i].name, cmds[i].name);
300 		else
301 			printf("<option value='%s'>%s</option>\n",
302 			    cmds[i].name, cmds[i].name);
303 	}
304 
305 	if ((req = lg_getarg("req=", query, query_length)) != NULL) {
306 		/* Could be NULL */
307 		argv = lg_arg2argv(req, &argc);
308 	}
309 
310 	printf("</select>\n"
311 	    "<input type='text' value='%s' name='req'/>\n"
312 	    "<input type='submit' value='submit'/>\n"
313 	    "</div>\n"
314 	    "</form>\n"
315 	    "<pre>\n", req ? req : "");
316 	fflush(stdout);
317 
318 #ifdef DEBUG
319 	if (close(2) == -1 || dup2(1, 2) == -1)
320 #else
321 	if (close(2) == -1)
322 #endif
323 	{
324 		printf("fatal error: %s\n", strerror(errno));
325 		goto err;
326 	}
327 
328 	if (query == NULL)
329 		goto err;
330 	if (cmd == NULL) {
331 		printf("unspecified command\n");
332 		goto err;
333 	}
334 
335 	for (i = 0; cmds[i].name != NULL; i++) {
336 		if (strcmp(cmd, cmds[i].name) == 0) {
337 			cmdp = &cmds[i];
338 			break;
339 		}
340 	}
341 
342 	if (cmdp == NULL) {
343 		printf("invalid command: %s\n", cmd);
344 		goto err;
345 	}
346 	if (argc > cmdp->maxargs) {
347 		printf("superfluous argument(s): %s %s\n",
348 		    cmd, cmdp->args ? cmdp->args : "");
349 		goto err;
350 	}
351 	if (argc < cmdp->minargs) {
352 		printf("missing argument(s): %s %s\n", cmd, cmdp->args);
353 		goto err;
354 	}
355 
356 	if (cmdp->func != NULL) {
357 		ret = cmdp->func(cmds, argv);
358 	} else {
359 		if ((argv = lg_argextra(argv, argc, cmdp)) == NULL)
360 			goto err;
361 		ret = lg_exec(cmdp->earg[0], argv);
362 	}
363 	if (ret != 0)
364 		printf("\nfailed%s\n", ret == 127 ? ": file not found" : ".");
365 	else
366 		printf("\nsuccess.\n");
367 
368  err:
369 	fflush(stdout);
370 
371 	free(argv);
372 
373 	printf("</pre>\n");
374 
375 	if (stat(INC_FOOT, &st) != 0 || lg_incl(INC_FOOT) != 0)
376 		printf("<hr/>\n");
377 
378 	printf("<div class='footer'>\n"
379 	    "</div>\n"
380 	    "</body>\n"
381 	    "</html>\n");
382 
383 	return (ret);
384 }
385