xref: /openbsd/usr.bin/bgplg/bgplg.c (revision b9fc9a72)
1 /*	$OpenBSD: bgplg.c,v 1.13 2015/01/16 06:40:06 deraadt 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 
32 #include "bgplg.h"
33 
34 #define INC_STYLE	"/conf/bgplg.css"
35 #define INC_HEAD	"/conf/bgplg.head"
36 #define INC_FOOT	"/conf/bgplg.foot"
37 
38 #define BGPDSOCK	"/run/bgpd.rsock"
39 #define BGPCTL		"/bin/bgpctl", "-s", BGPDSOCK
40 #define PING		"/bin/ping"
41 #define TRACEROUTE	"/bin/traceroute"
42 #define PING6		"/bin/ping6"
43 #define TRACEROUTE6	"/bin/traceroute6"
44 #define CONTENT_TYPE	"text/html"
45 
46 static struct cmd cmds[] = CMDS;
47 
48 char		 *lg_getenv(const char *, int *);
49 void		  lg_urldecode(char *);
50 char		**lg_arg2argv(char *, int *);
51 char		**lg_argextra(char **, int, struct cmd *);
52 char		 *lg_getarg(const char *, char *, int);
53 int		  lg_incl(const char *);
54 
55 void
56 lg_urldecode(char *str)
57 {
58 	size_t i, c, len;
59 	char code[3];
60 	long result;
61 
62 	if (str && *str) {
63 		len = strlen(str);
64 		i = c = 0;
65 		while (i < len) {
66 			if (str[i] == '%' && i <= (len - 2)) {
67 				if (isxdigit((unsigned char)str[i + 1]) &&
68 				    isxdigit((unsigned char)str[i + 2])) {
69 					code[0] = str[i + 1];
70 					code[1] = str[i + 2];
71 					code[2] = 0;
72 					result = strtol(code, NULL, 16);
73 					/* Replace NUL chars with a space */
74 					if (result == 0)
75 						result = ' ';
76 					str[c++] = result;
77 					i += 3;
78 				} else {
79 					str[c++] = '%';
80 					i++;
81 				}
82 			} else if (str[i] == '+') {
83 				str[i] = ' ';
84 			} else {
85 				if (c != i)
86 					str[c] = str[i];
87 				c++;
88 				i++;
89 			}
90 		}
91 		str[c] = 0x0;
92 	}
93 }
94 
95 char *
96 lg_getenv(const char *name, int *lenp)
97 {
98 	size_t len;
99 	u_int i;
100 	char *ptr;
101 
102 	if ((ptr = getenv(name)) == NULL)
103 		return (NULL);
104 
105 	lg_urldecode(ptr);
106 
107 	if (!(len = strlen(ptr)))
108 		return (NULL);
109 
110 	if (lenp != NULL)
111 		*lenp = len;
112 
113 #define allowed_in_string(_x)                                           \
114 	(isalnum((unsigned char)_x) || strchr("-_.:/= ", _x))
115 
116 	for (i = 0; i < len; i++) {
117 		if (ptr[i] == '&')
118 			ptr[i] = '\0';
119 		if (!allowed_in_string(ptr[i])) {
120 			printf("invalid character in input\n");
121 			return (NULL);
122 		}
123 	}
124 
125 	return (ptr);
126 #undef allowed_in_string
127 }
128 
129 char *
130 lg_getarg(const char *name, char *arg, int len)
131 {
132 	char *ptr = arg;
133 	size_t namelen, ptrlen;
134 	int i;
135 
136 	namelen = strlen(name);
137 
138 	for (i = 0; i < len; i++) {
139 		if (arg[i] == '\0')
140 			continue;
141 		ptr = arg + i;
142 		ptrlen = strlen(ptr);
143 		if (namelen >= ptrlen)
144 			continue;
145 		if (strncmp(name, ptr, namelen) == 0)
146 			return (ptr + namelen);
147 	}
148 
149 	return (NULL);
150 }
151 
152 char **
153 lg_arg2argv(char *arg, int *argc)
154 {
155 	char **argv, *ptr = arg;
156 	size_t len;
157 	u_int i, c = 1;
158 
159 	len = strlen(arg);
160 
161 	/* Count elements */
162 	for (i = 0; i < (len - 1); i++) {
163 		if (isspace((unsigned char)arg[i])) {
164 			/* filter out additional options */
165 			if (arg[i + 1] == '-') {
166 				printf("invalid input\n");
167 				return (NULL);
168 			}
169 			arg[i] = '\0';
170 			c++;
171 		}
172 	}
173 
174 	/* Generate array */
175 	if ((argv = calloc(c + 1, sizeof(char *))) == NULL) {
176 		printf("fatal error: %s\n", strerror(errno));
177 		return (NULL);
178 	}
179 
180 	argv[c] = NULL;
181 	*argc = c;
182 
183 	/* Fill array */
184 	for (i = c = 0; i < (len - 1); i++) {
185 		if (arg[i] == '\0' || i == 0) {
186 			if (i != 0)
187 				ptr = &arg[i + 1];
188 			argv[c++] = ptr;
189 		}
190 	}
191 
192 	return (argv);
193 }
194 
195 char **
196 lg_argextra(char **argv, int argc, struct cmd *cmdp)
197 {
198 	char **new_argv;
199 	int i, c = 0;
200 
201 	/* Count elements */
202 	for (i = 0; cmdp->earg[i] != NULL; i++)
203 		c++;
204 
205 	/* Generate array */
206 	if ((new_argv = calloc(c + argc + 1, sizeof(char *))) == NULL) {
207 		printf("fatal error: %s\n", strerror(errno));
208 		return (NULL);
209 	}
210 
211 	/* Fill array */
212 	for (i = c = 0; cmdp->earg[i] != NULL; i++)
213 		new_argv[c++] = cmdp->earg[i];
214 
215 	/* Append old array */
216 	for (i = 0; i < argc; i++)
217 		new_argv[c++] = argv[i];
218 
219 	new_argv[c] = NULL;
220 
221 	if (argv != NULL)
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, *self, *cmd = NULL, *req;
249 	char **argv = NULL;
250 	char myname[HOST_NAME_MAX+1];
251 	int ret = 1, argc = 0, query_length = 0;
252 	struct stat st;
253 	u_int i;
254 	struct cmd *cmdp = NULL;
255 
256 	if (gethostname(myname, sizeof(myname)) != 0)
257 		return (1);
258 
259 	printf("Content-Type: %s\n"
260 	    "Cache-Control: no-cache\n\n"
261 	    "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
262 	    "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" "
263 	    "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
264 	    "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
265 	    "<head>\n"
266 	    "<title>%s: %s</title>\n",
267 	    CONTENT_TYPE, NAME, myname);
268 	if (stat(INC_STYLE, &st) == 0) {
269 		printf("<style type='text/css'><!--\n");
270 		lg_incl(INC_STYLE);
271 		printf("--></style>\n");
272 	}
273 	if (stat(INC_HEAD, &st) != 0 || lg_incl(INC_HEAD) != 0) {
274 		printf("</head>\n"
275 		    "<body>\n");
276 	}
277 
278 	printf("<h1>%s: %s</h1>\n", NAME, myname);
279 	printf("<h2>%s</h2>\n", BRIEF);
280 
281 	/* print a form with possible options */
282 	if ((self = lg_getenv("SCRIPT_NAME", NULL)) == NULL) {
283 		printf("fatal error: invalid request\n");
284 		goto err;
285 	}
286 	if ((query = lg_getenv("QUERY_STRING", &query_length)) != NULL)
287 		cmd = lg_getarg("cmd=", query, query_length);
288 	printf(
289 	    "<form action='%s'>\n"
290 	    "<div class=\"command\">\n"
291 	    "<select name='cmd'>\n",
292 	    self);
293 	for (i = 0; cmds[i].name != NULL; i++) {
294 		if (!lg_checkperm(&cmds[i]))
295 			continue;
296 
297 		if (cmd != NULL && strcmp(cmd, cmds[i].name) == 0)
298 			printf("<option value='%s' selected='selected'>%s"
299 			    "</option>\n",
300 			    cmds[i].name, cmds[i].name);
301 		else
302 			printf("<option value='%s'>%s</option>\n",
303 			    cmds[i].name, cmds[i].name);
304 	}
305 	printf("</select>\n"
306 	    "<input type='text' name='req'/>\n"
307 	    "<input type='submit' value='submit'/>\n"
308 	    "</div>\n"
309 	    "</form>\n"
310 	    "<pre>\n");
311 	fflush(stdout);
312 
313 #ifdef DEBUG
314 	if (close(2) == -1 || dup2(1, 2) == -1)
315 #else
316 	if (close(2) == -1)
317 #endif
318 	{
319 		printf("fatal error: %s\n", strerror(errno));
320 		goto err;
321 	}
322 
323 	if (query == NULL)
324 		goto err;
325 	if (cmd == NULL) {
326 		printf("unspecified command\n");
327 		goto err;
328 	}
329 	if ((req = lg_getarg("req=", query, query_length)) != NULL) {
330 		/* Could be NULL */
331 		argv = lg_arg2argv(req, &argc);
332 	}
333 
334 	for (i = 0; cmds[i].name != NULL; i++) {
335 		if (strcmp(cmd, cmds[i].name) == 0) {
336 			cmdp = &cmds[i];
337 			break;
338 		}
339 	}
340 
341 	if (cmdp == NULL) {
342 		printf("invalid command: %s\n", cmd);
343 		goto err;
344 	}
345 	if (argc > cmdp->maxargs) {
346 		printf("superfluous argument(s): %s %s\n",
347 		    cmd, cmdp->args ? cmdp->args : "");
348 		goto err;
349 	}
350 	if (argc < cmdp->minargs) {
351 		printf("missing argument(s): %s %s\n", cmd, cmdp->args);
352 		goto err;
353 	}
354 
355 	if (cmdp->func != NULL) {
356 		ret = cmdp->func(cmds, argv);
357 	} else {
358 		if ((argv = lg_argextra(argv, argc, cmdp)) == NULL)
359 			goto err;
360 		ret = lg_exec(cmdp->earg[0], argv);
361 	}
362 	if (ret != 0)
363 		printf("\nfailed%s\n", ret == 127 ? ": file not found" : ".");
364 	else
365 		printf("\nsuccess.\n");
366 
367  err:
368 	fflush(stdout);
369 
370 	if (argv != NULL)
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 	    "<small>%s - %s<br/>Copyright (c) %s</small>\n"
380 	    "</div>\n"
381 	    "</body>\n"
382 	    "</html>\n", NAME, BRIEF, COPYRIGHT);
383 
384 	return (ret);
385 }
386