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