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