Annotation of embedaddon/axTLS/httpd/proc.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (c) Cameron Rich
! 3: *
! 4: * All rights reserved.
! 5: *
! 6: * Redistribution and use in source and binary forms, with or without
! 7: * modification, are permitted provided that the following conditions are met:
! 8: *
! 9: * * Redistributions of source code must retain the above copyright notice,
! 10: * this list of conditions and the following disclaimer.
! 11: * * Redistributions in binary form must reproduce the above copyright notice,
! 12: * this list of conditions and the following disclaimer in the documentation
! 13: * and/or other materials provided with the distribution.
! 14: * * Neither the name of the axTLS project nor the names of its contributors
! 15: * may be used to endorse or promote products derived from this software
! 16: * without specific prior written permission.
! 17: *
! 18: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
! 19: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
! 20: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
! 21: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
! 22: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
! 23: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
! 24: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
! 25: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
! 26: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
! 27: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
! 28: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 29: */
! 30:
! 31: #include <stdio.h>
! 32: #include <stdlib.h>
! 33: #include <ctype.h>
! 34: #include <sys/types.h>
! 35: #include <sys/stat.h>
! 36: #include <fcntl.h>
! 37: #include <time.h>
! 38: #include <string.h>
! 39: #include "axhttp.h"
! 40:
! 41: #define HTTP_VERSION "HTTP/1.1"
! 42:
! 43: static const char * index_file = "index.html";
! 44: static const char * rfc1123_format = "%a, %d %b %Y %H:%M:%S GMT";
! 45:
! 46: static int special_read(struct connstruct *cn, void *buf, size_t count);
! 47: static int special_write(struct connstruct *cn,
! 48: const char *buf, size_t count);
! 49: static void send_error(struct connstruct *cn, int err);
! 50: static int hexit(char c);
! 51: static void urldecode(char *buf);
! 52: static void buildactualfile(struct connstruct *cn);
! 53: static int sanitizefile(const char *buf);
! 54: static int sanitizehost(char *buf);
! 55: static int htaccess_check(struct connstruct *cn);
! 56: static const char *getmimetype(const char *name);
! 57:
! 58: #if defined(CONFIG_HTTP_DIRECTORIES)
! 59: static void urlencode(const uint8_t *s, char *t);
! 60: static void procdirlisting(struct connstruct *cn);
! 61: #endif
! 62: #if defined(CONFIG_HTTP_HAS_CGI)
! 63: static void proccgi(struct connstruct *cn);
! 64: static void decode_path_info(struct connstruct *cn, char *path_info);
! 65: static int init_read_post_data(char *buf, char *data, struct connstruct *cn, int old_rv);
! 66: #endif
! 67: #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
! 68: static int auth_check(struct connstruct *cn);
! 69: #endif
! 70:
! 71: #if AXDEBUG
! 72: #define AXDEBUGSTART \
! 73: { \
! 74: FILE *axdout; \
! 75: axdout = fopen("/var/log/axdebug", "a"); \
! 76:
! 77: #define AXDEBUGEND \
! 78: fclose(axdout); \
! 79: }
! 80: #else /* AXDEBUG */
! 81: #define AXDEBUGSTART
! 82: #define AXDEBUGEND
! 83: #endif /* AXDEBUG */
! 84:
! 85: /* Returns 1 if elems should continue being read, 0 otherwise */
! 86: static int procheadelem(struct connstruct *cn, char *buf)
! 87: {
! 88: char *delim, *value;
! 89:
! 90: if ((delim = strchr(buf, ' ')) == NULL)
! 91: return 0;
! 92:
! 93: *delim = 0;
! 94: value = delim+1;
! 95:
! 96: if (strcmp(buf, "GET") == 0 || strcmp(buf, "HEAD") == 0 ||
! 97: strcmp(buf, "POST") == 0)
! 98: {
! 99: if (buf[0] == 'H')
! 100: cn->reqtype = TYPE_HEAD;
! 101: else if (buf[0] == 'P')
! 102: cn->reqtype = TYPE_POST;
! 103:
! 104: if ((delim = strchr(value, ' ')) == NULL) /* expect HTTP type */
! 105: return 0;
! 106:
! 107: *delim++ = 0;
! 108: urldecode(value);
! 109:
! 110: if (sanitizefile(value) == 0)
! 111: {
! 112: send_error(cn, 403);
! 113: return 0;
! 114: }
! 115:
! 116: #if defined(CONFIG_HTTP_HAS_CGI)
! 117: decode_path_info(cn, value);
! 118: #else
! 119: my_strncpy(cn->filereq, value, MAXREQUESTLENGTH);
! 120: #endif
! 121: cn->if_modified_since = -1;
! 122: if (strcmp(delim, "HTTP/1.0") == 0) /* v1.0 HTTP? */
! 123: cn->is_v1_0 = 1;
! 124: }
! 125: else if (strcasecmp(buf, "Host:") == 0)
! 126: {
! 127: if (sanitizehost(value) == 0)
! 128: {
! 129: removeconnection(cn);
! 130: return 0;
! 131: }
! 132:
! 133: my_strncpy(cn->server_name, value, MAXREQUESTLENGTH);
! 134: }
! 135: else if (strcasecmp(buf, "Connection:") == 0 && strcmp(value, "close") == 0)
! 136: {
! 137: cn->close_when_done = 1;
! 138: }
! 139: else if (strcasecmp(buf, "If-Modified-Since:") == 0)
! 140: {
! 141: cn->if_modified_since = tdate_parse(value);
! 142: }
! 143: else if (strcasecmp(buf, "Expect:") == 0)
! 144: {
! 145: /* supposed to be safe to ignore 100-continue */
! 146: if (strcasecmp(value, "100-continue") != 0) {
! 147: send_error(cn, 417); /* expectation failed */
! 148: return 0;
! 149: }
! 150: }
! 151: #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
! 152: else if (strcasecmp(buf, "Authorization:") == 0 &&
! 153: strncmp(value, "Basic ", 6) == 0)
! 154: {
! 155: int size = sizeof(cn->authorization);
! 156: if (base64_decode(&value[6], strlen(&value[6]),
! 157: (uint8_t *)cn->authorization, &size))
! 158: cn->authorization[0] = 0; /* error */
! 159: else
! 160: cn->authorization[size] = 0;
! 161: }
! 162: #endif
! 163: #if defined(CONFIG_HTTP_HAS_CGI)
! 164: else if (strcasecmp(buf, "Content-Length:") == 0)
! 165: {
! 166: sscanf(value, "%d", &cn->content_length);
! 167: }
! 168: else if (strcasecmp(buf, "Content-Type:") == 0)
! 169: {
! 170: my_strncpy(cn->cgicontenttype, value, MAXREQUESTLENGTH);
! 171: }
! 172: else if (strcasecmp(buf, "Cookie:") == 0)
! 173: {
! 174: my_strncpy(cn->cookie, value, MAXREQUESTLENGTH);
! 175: }
! 176: #endif
! 177:
! 178: return 1;
! 179: }
! 180:
! 181: #if defined(CONFIG_HTTP_DIRECTORIES)
! 182: static void procdirlisting(struct connstruct *cn)
! 183: {
! 184: char buf[MAXREQUESTLENGTH];
! 185: char actualfile[1024];
! 186:
! 187: if (cn->reqtype == TYPE_HEAD)
! 188: {
! 189: snprintf(buf, sizeof(buf), HTTP_VERSION
! 190: " 200 OK\nContent-Type: text/html\n\n");
! 191: write(cn->networkdesc, buf, strlen(buf));
! 192: removeconnection(cn);
! 193: return;
! 194: }
! 195:
! 196: strcpy(actualfile, cn->actualfile);
! 197:
! 198: #ifdef WIN32
! 199: strcat(actualfile, "*");
! 200: cn->dirp = FindFirstFile(actualfile, &cn->file_data);
! 201:
! 202: if (cn->dirp == INVALID_HANDLE_VALUE)
! 203: {
! 204: send_error(cn, 404);
! 205: return;
! 206: }
! 207: #else
! 208: if ((cn->dirp = opendir(actualfile)) == NULL)
! 209: {
! 210: send_error(cn, 404);
! 211: return;
! 212: }
! 213: #endif
! 214:
! 215: snprintf(buf, sizeof(buf), HTTP_VERSION
! 216: " 200 OK\nContent-Type: text/html\n\n"
! 217: "<html><body>\n<title>Directory Listing</title>\n"
! 218: "<h3>Directory listing of %s://%s%s</h3><br />\n",
! 219: cn->is_ssl ? "https" : "http", cn->server_name, cn->filereq);
! 220: special_write(cn, buf, strlen(buf));
! 221: cn->state = STATE_DOING_DIR;
! 222: }
! 223:
! 224: void procdodir(struct connstruct *cn)
! 225: {
! 226: #ifndef WIN32
! 227: struct dirent *dp;
! 228: #endif
! 229: char buf[MAXREQUESTLENGTH];
! 230: char encbuf[1024];
! 231: char *file;
! 232:
! 233: do
! 234: {
! 235: buf[0] = 0;
! 236:
! 237: #ifdef WIN32
! 238: if (!FindNextFile(cn->dirp, &cn->file_data))
! 239: #else
! 240: if ((dp = readdir(cn->dirp)) == NULL)
! 241: #endif
! 242: {
! 243: snprintf(buf, sizeof(buf), "</body></html>\n");
! 244: special_write(cn, buf, strlen(buf));
! 245: removeconnection(cn);
! 246: #ifndef WIN32
! 247: closedir(cn->dirp);
! 248: #endif
! 249: return;
! 250: }
! 251:
! 252: #ifdef WIN32
! 253: file = cn->file_data.cFileName;
! 254: #else
! 255: file = dp->d_name;
! 256: #endif
! 257:
! 258: /* if no index file, don't display the ".." directory */
! 259: if (cn->filereq[0] == '/' && cn->filereq[1] == '\0' &&
! 260: strcmp(file, "..") == 0)
! 261: continue;
! 262:
! 263: /* don't display files beginning with "." */
! 264: if (file[0] == '.' && file[1] != '.')
! 265: continue;
! 266:
! 267: /* make sure a '/' is at the end of a directory */
! 268: if (cn->filereq[strlen(cn->filereq)-1] != '/')
! 269: strcat(cn->filereq, "/");
! 270:
! 271: /* see if the dir + file is another directory */
! 272: snprintf(buf, sizeof(buf), "%s%s", cn->actualfile, file);
! 273: if (isdir(buf))
! 274: strcat(file, "/");
! 275:
! 276: urlencode((uint8_t *)file, encbuf);
! 277: snprintf(buf, sizeof(buf), "<a href=\"%s%s\">%s</a><br />\n",
! 278: cn->filereq, encbuf, file);
! 279: } while (special_write(cn, buf, strlen(buf)));
! 280: }
! 281:
! 282: /* Encode funny chars -> %xx in newly allocated storage */
! 283: /* (preserves '/' !) */
! 284: static void urlencode(const uint8_t *s, char *t)
! 285: {
! 286: const uint8_t *p = s;
! 287: char *tp = t;
! 288:
! 289: for (; *p; p++)
! 290: {
! 291: if ((*p > 0x00 && *p < ',') ||
! 292: (*p > '9' && *p < 'A') ||
! 293: (*p > 'Z' && *p < '_') ||
! 294: (*p > '_' && *p < 'a') ||
! 295: (*p > 'z' && *p < 0xA1))
! 296: {
! 297: sprintf((char *)tp, "%%%02X", *p);
! 298: tp += 3;
! 299: }
! 300: else
! 301: {
! 302: *tp = *p;
! 303: tp++;
! 304: }
! 305: }
! 306:
! 307: *tp='\0';
! 308: }
! 309:
! 310: #endif
! 311:
! 312: void procreadhead(struct connstruct *cn)
! 313: {
! 314: char buf[MAXREADLENGTH], *tp, *next;
! 315: int rv;
! 316:
! 317: memset(buf, 0, sizeof(buf));
! 318: rv = special_read(cn, buf, sizeof(buf)-1);
! 319: if (rv <= 0)
! 320: {
! 321: if (rv < 0 || !cn->is_ssl) /* really dead? */
! 322: removeconnection(cn);
! 323: return;
! 324: }
! 325:
! 326: buf[rv] = '\0';
! 327: next = tp = buf;
! 328:
! 329: #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
! 330: cn->authorization[0] = 0;
! 331: #endif
! 332:
! 333: /* Split up lines and send to procheadelem() */
! 334: while (*next != '\0')
! 335: {
! 336: /* If we have a blank line, advance to next stage */
! 337: if (*next == '\r' || *next == '\n')
! 338: {
! 339: #if defined(CONFIG_HTTP_HAS_CGI)
! 340: if (cn->reqtype == TYPE_POST && cn->content_length > 0)
! 341: {
! 342: if (init_read_post_data(buf, next, cn, rv) == 0)
! 343: return;
! 344: }
! 345: #endif
! 346:
! 347: buildactualfile(cn);
! 348: cn->state = STATE_WANT_TO_SEND_HEAD;
! 349: return;
! 350: }
! 351:
! 352: while (*next != '\r' && *next != '\n' && *next != '\0')
! 353: next++;
! 354:
! 355: if (*next == '\r')
! 356: {
! 357: *next = '\0';
! 358: next += 2;
! 359: }
! 360: else if (*next == '\n')
! 361: *next++ = '\0';
! 362:
! 363: if (procheadelem(cn, tp) == 0)
! 364: return;
! 365:
! 366: tp = next;
! 367: }
! 368: }
! 369:
! 370: /* In this function we assume that the file has been checked for
! 371: * maliciousness (".."s, etc) and has been decoded
! 372: */
! 373: void procsendhead(struct connstruct *cn)
! 374: {
! 375: char buf[MAXREQUESTLENGTH];
! 376: struct stat stbuf;
! 377: time_t t_time;
! 378: struct tm *ptm;
! 379: char date[32];
! 380: char last_modified[32];
! 381: char expires[32];
! 382: int file_exists;
! 383:
! 384: /* are we trying to access a file over the HTTP connection instead of a
! 385: * HTTPS connection? Or is this directory disabled? */
! 386: if (htaccess_check(cn))
! 387: {
! 388: send_error(cn, 403);
! 389: return;
! 390: }
! 391:
! 392: #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
! 393: if (auth_check(cn)) /* see if there is a '.htpasswd' file */
! 394: {
! 395: #ifdef CONFIG_HTTP_VERBOSE
! 396: printf("axhttpd: access to %s denied\n", cn->filereq); TTY_FLUSH();
! 397: #endif
! 398: removeconnection(cn);
! 399: return;
! 400: }
! 401: #endif
! 402:
! 403: file_exists = stat(cn->actualfile, &stbuf);
! 404:
! 405: #if defined(CONFIG_HTTP_HAS_CGI)
! 406: if (file_exists != -1 && cn->is_cgi)
! 407: {
! 408: proccgi(cn);
! 409: return;
! 410: }
! 411: #endif
! 412:
! 413: /* look for "index.html"? */
! 414: if (isdir(cn->actualfile))
! 415: {
! 416: char tbuf[MAXREQUESTLENGTH];
! 417: snprintf(tbuf, MAXREQUESTLENGTH, "%s%s", cn->actualfile, index_file);
! 418:
! 419: if ((file_exists = stat(tbuf, &stbuf)) != -1)
! 420: my_strncpy(cn->actualfile, tbuf, MAXREQUESTLENGTH);
! 421: else
! 422: {
! 423: #if defined(CONFIG_HTTP_DIRECTORIES)
! 424: /* If not, we do a directory listing of it */
! 425: procdirlisting(cn);
! 426: #else
! 427: send_error(cn, 404);
! 428: #endif
! 429: return;
! 430: }
! 431: }
! 432:
! 433: if (file_exists == -1)
! 434: {
! 435: send_error(cn, 404);
! 436: return;
! 437: }
! 438:
! 439:
! 440: time(&t_time);
! 441: ptm = gmtime(&t_time);
! 442: strftime(date, sizeof(date), rfc1123_format, ptm);
! 443:
! 444: /* has the file been read before? */
! 445: if (cn->if_modified_since != -1)
! 446:
! 447: {
! 448: ptm = gmtime(&stbuf.st_mtime);
! 449: t_time = mktime(ptm);
! 450:
! 451: if (cn->if_modified_since >= t_time)
! 452: {
! 453: snprintf(buf, sizeof(buf), HTTP_VERSION" 304 Not Modified\nServer: "
! 454: "%s\nDate: %s\n\n", server_version, date);
! 455: special_write(cn, buf, strlen(buf));
! 456: cn->state = STATE_WANT_TO_READ_HEAD;
! 457: return;
! 458: }
! 459: }
! 460:
! 461: if (cn->reqtype == TYPE_HEAD)
! 462: {
! 463: removeconnection(cn);
! 464: return;
! 465: }
! 466: else
! 467: {
! 468: int flags = O_RDONLY;
! 469: #if defined(WIN32) || defined(CONFIG_PLATFORM_CYGWIN)
! 470: flags |= O_BINARY;
! 471: #endif
! 472: cn->filedesc = open(cn->actualfile, flags);
! 473:
! 474: if (cn->filedesc < 0)
! 475: {
! 476: send_error(cn, 404);
! 477: return;
! 478: }
! 479:
! 480: ptm = gmtime(&stbuf.st_mtime);
! 481: strftime(last_modified, sizeof(last_modified), rfc1123_format, ptm);
! 482: t_time += CONFIG_HTTP_TIMEOUT;
! 483: ptm = gmtime(&t_time);
! 484: strftime(expires, sizeof(expires), rfc1123_format, ptm);
! 485:
! 486: snprintf(buf, sizeof(buf), HTTP_VERSION" 200 OK\nServer: %s\n"
! 487: "Content-Type: %s\nContent-Length: %ld\n"
! 488: "Date: %s\nLast-Modified: %s\nExpires: %s\n\n", server_version,
! 489: getmimetype(cn->actualfile), (long) stbuf.st_size,
! 490: date, last_modified, expires);
! 491:
! 492: special_write(cn, buf, strlen(buf));
! 493:
! 494: #ifdef CONFIG_HTTP_VERBOSE
! 495: printf("axhttpd: %s:/%s\n", cn->is_ssl ? "https" : "http", cn->filereq);
! 496: TTY_FLUSH();
! 497: #endif
! 498:
! 499: #ifdef WIN32
! 500: for (;;)
! 501: {
! 502: procreadfile(cn);
! 503: if (cn->filedesc == -1)
! 504: break;
! 505:
! 506: do
! 507: {
! 508: procsendfile(cn);
! 509: } while (cn->state != STATE_WANT_TO_READ_FILE);
! 510: }
! 511: #else
! 512: cn->state = STATE_WANT_TO_READ_FILE;
! 513: #endif
! 514: }
! 515: }
! 516:
! 517: void procreadfile(struct connstruct *cn)
! 518: {
! 519: int rv = read(cn->filedesc, cn->databuf, BLOCKSIZE);
! 520:
! 521: if (rv <= 0)
! 522: {
! 523: close(cn->filedesc);
! 524: cn->filedesc = -1;
! 525:
! 526: if (cn->close_when_done) /* close immediately */
! 527: removeconnection(cn);
! 528: else
! 529: {
! 530: if (cn->is_v1_0) /* die now */
! 531: removeconnection(cn);
! 532: else /* keep socket open - HTTP 1.1 */
! 533: {
! 534: cn->state = STATE_WANT_TO_READ_HEAD;
! 535: cn->numbytes = 0;
! 536: }
! 537: }
! 538:
! 539: return;
! 540: }
! 541:
! 542: cn->numbytes = rv;
! 543: cn->state = STATE_WANT_TO_SEND_FILE;
! 544: }
! 545:
! 546: void procsendfile(struct connstruct *cn)
! 547: {
! 548: int rv = special_write(cn, cn->databuf, cn->numbytes);
! 549:
! 550: if (rv < 0)
! 551: removeconnection(cn);
! 552: else if (rv == cn->numbytes)
! 553: {
! 554: cn->state = STATE_WANT_TO_READ_FILE;
! 555: }
! 556: else if (rv == 0)
! 557: {
! 558: /* Do nothing */
! 559: }
! 560: else
! 561: {
! 562: memmove(cn->databuf, cn->databuf + rv, cn->numbytes - rv);
! 563: cn->numbytes -= rv;
! 564: }
! 565: }
! 566:
! 567: #if defined(CONFIG_HTTP_HAS_CGI)
! 568: /* Should this be a bit more dynamic? It would mean more calls to malloc etc */
! 569: #define CGI_ARG_SIZE 17
! 570:
! 571: static void proccgi(struct connstruct *cn)
! 572: {
! 573: int tpipe[2], spipe[2];
! 574: char *myargs[3];
! 575: char cgienv[CGI_ARG_SIZE][MAXREQUESTLENGTH];
! 576: char * cgiptr[CGI_ARG_SIZE+4];
! 577: const char *type = "HEAD";
! 578: int cgi_index = 0, i;
! 579: pid_t pid;
! 580: #ifdef WIN32
! 581: int tmp_stdout;
! 582: #endif
! 583:
! 584: snprintf(cgienv[0], MAXREQUESTLENGTH,
! 585: HTTP_VERSION" 200 OK\nServer: %s\n%s",
! 586: server_version, (cn->reqtype == TYPE_HEAD) ? "\n" : "");
! 587: special_write(cn, cgienv[0], strlen(cgienv[0]));
! 588:
! 589: if (cn->reqtype == TYPE_HEAD)
! 590: {
! 591: removeconnection(cn);
! 592: return;
! 593: }
! 594:
! 595: #ifdef CONFIG_HTTP_VERBOSE
! 596: printf("[CGI]: %s:/%s\n", cn->is_ssl ? "https" : "http", cn->filereq);
! 597: TTY_FLUSH();
! 598: #endif
! 599:
! 600: /* win32 cgi is a bit too painful */
! 601: #ifndef WIN32
! 602: /* set up pipe that is used for sending POST query data to CGI script*/
! 603: if (cn->reqtype == TYPE_POST)
! 604: {
! 605: if (pipe(spipe) == -1)
! 606: {
! 607: printf("[CGI]: could not create pipe");
! 608: TTY_FLUSH();
! 609: return;
! 610: }
! 611: }
! 612:
! 613: if (pipe(tpipe) == -1)
! 614: {
! 615: printf("[CGI]: could not create pipe");
! 616: TTY_FLUSH();
! 617: return;
! 618: }
! 619:
! 620: /*
! 621: * use vfork() instead of fork() for performance
! 622: */
! 623: if ((pid = vfork()) > 0) /* parent */
! 624: {
! 625: /* Send POST query data to CGI script */
! 626: if ((cn->reqtype == TYPE_POST) && (cn->content_length > 0))
! 627: {
! 628: write(spipe[1], cn->post_data, cn->content_length);
! 629: close(spipe[0]);
! 630: close(spipe[1]);
! 631:
! 632: /* free the memory that is allocated in read_post_data() */
! 633: free(cn->post_data);
! 634: cn->post_data = NULL;
! 635: }
! 636:
! 637: /* Close the write descriptor */
! 638: close(tpipe[1]);
! 639: cn->filedesc = tpipe[0];
! 640: cn->state = STATE_WANT_TO_READ_FILE;
! 641: cn->close_when_done = 1;
! 642: return;
! 643: }
! 644:
! 645: if (pid < 0) /* vfork failed */
! 646: exit(1);
! 647:
! 648: /* The problem child... */
! 649:
! 650: /* Our stdout/stderr goes to the socket */
! 651: dup2(tpipe[1], 1);
! 652: dup2(tpipe[1], 2);
! 653: close(tpipe[0]);
! 654: close(tpipe[1]);
! 655:
! 656: /* If it was a POST request, send the socket data to our stdin */
! 657: if (cn->reqtype == TYPE_POST) {
! 658: dup2(spipe[0], 0);
! 659: close(spipe[0]);
! 660: close(spipe[1]);
! 661: } else /* Otherwise we can shutdown the read side of the sock */
! 662: shutdown(cn->networkdesc, 0);
! 663:
! 664: myargs[0] = CONFIG_HTTP_CGI_LAUNCHER;
! 665: myargs[1] = cn->actualfile;
! 666: myargs[2] = NULL;
! 667:
! 668: /*
! 669: * set the cgi args. A url is defined by:
! 670: * http://$SERVER_NAME:$SERVER_PORT$SCRIPT_NAME$PATH_INFO?$QUERY_STRING
! 671: * TODO: other CGI parameters?
! 672: */
! 673: sprintf(cgienv[cgi_index++], "SERVER_SOFTWARE=%s", server_version);
! 674: strcpy(cgienv[cgi_index++], "DOCUMENT_ROOT=" CONFIG_HTTP_WEBROOT);
! 675: snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
! 676: "SERVER_NAME=%s", cn->server_name);
! 677: sprintf(cgienv[cgi_index++], "SERVER_PORT=%d",
! 678: cn->is_ssl ? CONFIG_HTTP_HTTPS_PORT : CONFIG_HTTP_PORT);
! 679: snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
! 680: "REQUEST_URI=%s", cn->uri_request);
! 681: snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
! 682: "SCRIPT_NAME=%s", cn->filereq);
! 683: snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
! 684: "PATH_INFO=%s", cn->uri_path_info);
! 685: snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
! 686: "QUERY_STRING=%s", cn->uri_query);
! 687: snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
! 688: "REMOTE_ADDR=%s", cn->remote_addr);
! 689: snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
! 690: "HTTP_COOKIE=%s", cn->cookie); /* note: small size */
! 691: #if defined(CONFIG_HTTP_HAS_AUTHORIZATION)
! 692: snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
! 693: "REMOTE_USER=%s", cn->authorization);
! 694: #endif
! 695:
! 696: switch (cn->reqtype)
! 697: {
! 698: case TYPE_GET:
! 699: type = "GET";
! 700: break;
! 701:
! 702: #if defined(CONFIG_HTTP_HAS_CGI)
! 703: case TYPE_POST:
! 704: type = "POST";
! 705: sprintf(cgienv[cgi_index++],
! 706: "CONTENT_LENGTH=%d", cn->content_length);
! 707: snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH,
! 708: "CONTENT_TYPE=%s", cn->cgicontenttype);
! 709: break;
! 710: #endif
! 711: }
! 712:
! 713: sprintf(cgienv[cgi_index++], "REQUEST_METHOD=%s", type);
! 714:
! 715: if (cn->is_ssl)
! 716: strcpy(cgienv[cgi_index++], "HTTPS=on");
! 717:
! 718: if (cgi_index >= CGI_ARG_SIZE)
! 719: {
! 720: printf("Content-type: text/plain\n\nToo many CGI args (%d, %d)\n",
! 721: cgi_index, CGI_ARG_SIZE);
! 722: _exit(1);
! 723: }
! 724:
! 725: /* copy across the pointer indexes */
! 726: for (i = 0; i < cgi_index; i++)
! 727: cgiptr[i] = cgienv[i];
! 728:
! 729: cgiptr[i++] = "AUTH_TYPE=Basic";
! 730: cgiptr[i++] = "GATEWAY_INTERFACE=CGI/1.1";
! 731: cgiptr[i++] = "SERVER_PROTOCOL="HTTP_VERSION;
! 732: cgiptr[i] = NULL;
! 733:
! 734: execve(myargs[0], myargs, cgiptr);
! 735: printf("Content-type: text/plain\n\nshouldn't get here\n");
! 736: _exit(1);
! 737: #endif
! 738: }
! 739:
! 740: static char * cgi_filetype_match(struct connstruct *cn, const char *fn)
! 741: {
! 742: struct cgiextstruct *tp = cgiexts;
! 743:
! 744: while (tp != NULL)
! 745: {
! 746: char *t;
! 747:
! 748: if ((t = strstr(fn, tp->ext)) != NULL)
! 749: {
! 750: t += strlen(tp->ext);
! 751:
! 752: if (*t == '/' || *t == '\0')
! 753: return t;
! 754: else
! 755: return NULL;
! 756:
! 757: }
! 758:
! 759: tp = tp->next;
! 760: }
! 761:
! 762: return NULL;
! 763: }
! 764:
! 765: static void decode_path_info(struct connstruct *cn, char *path_info)
! 766: {
! 767: char *cgi_delim;
! 768:
! 769: #if defined(CONFIG_HTTP_HAS_CGI)
! 770: cn->is_cgi = 0;
! 771: #endif
! 772: *cn->uri_request = '\0';
! 773: *cn->uri_path_info = '\0';
! 774: *cn->uri_query = '\0';
! 775:
! 776: my_strncpy(cn->uri_request, path_info, MAXREQUESTLENGTH);
! 777:
! 778: /* query info? */
! 779: if ((cgi_delim = strchr(path_info, '?')))
! 780: {
! 781: *cgi_delim = '\0';
! 782: my_strncpy(cn->uri_query, cgi_delim+1, MAXREQUESTLENGTH);
! 783: }
! 784:
! 785: #if defined(CONFIG_HTTP_HAS_CGI)
! 786: if ((cgi_delim = cgi_filetype_match(cn, path_info)) != NULL)
! 787: {
! 788: cn->is_cgi = 1; /* definitely a CGI script */
! 789:
! 790: /* path info? */
! 791: if (*cgi_delim != '\0')
! 792: {
! 793: my_strncpy(cn->uri_path_info, cgi_delim, MAXREQUESTLENGTH);
! 794: *cgi_delim = '\0';
! 795: }
! 796: }
! 797: #endif
! 798:
! 799: /* the bit at the start must be the script name */
! 800: my_strncpy(cn->filereq, path_info, MAXREQUESTLENGTH);
! 801: }
! 802:
! 803: static int init_read_post_data(char *buf, char *data,
! 804: struct connstruct *cn, int old_rv)
! 805: {
! 806: char *next = data;
! 807: int rv = old_rv;
! 808: char *post_data;
! 809:
! 810: /* Too much Post data to send. MAXPOSTDATASIZE should be
! 811: configured (now it can be changed in the header file) */
! 812: if (cn->content_length > MAXPOSTDATASIZE)
! 813: {
! 814: send_error(cn, 418);
! 815: return 0;
! 816: }
! 817:
! 818: /* remove CRLF */
! 819: while ((*next == '\r' || *next == '\n') && (next < &buf[rv]))
! 820: next++;
! 821:
! 822: if (cn->post_data == NULL)
! 823: {
! 824: /* Allocate buffer for the POST data that will be used by proccgi
! 825: to send POST data to the CGI script */
! 826: cn->post_data = (char *)ax_calloc(1, (cn->content_length + 1));
! 827: }
! 828:
! 829: cn->post_state = 0;
! 830: cn->post_read = 0;
! 831: post_data = cn->post_data;
! 832:
! 833: while (next < &buf[rv])
! 834: {
! 835: /* copy POST data to buffer */
! 836: *post_data++ = *next++;
! 837: cn->post_read++;
! 838: if (cn->post_read == cn->content_length)
! 839: {
! 840: /* No more POST data to be copied */
! 841: *post_data = '\0';
! 842: return 1;
! 843: }
! 844: }
! 845:
! 846: /* More POST data has to be read. read_post_data will continue with that */
! 847: cn->post_state = 1;
! 848: return 0;
! 849: }
! 850:
! 851: void read_post_data(struct connstruct *cn)
! 852: {
! 853: char buf[MAXREADLENGTH], *next;
! 854: char *post_data;
! 855: int rv;
! 856:
! 857: memset(buf, 0, sizeof(buf));
! 858: rv = special_read(cn, buf, sizeof(buf)-1);
! 859: if (rv <= 0)
! 860: {
! 861: if (rv < 0 || !cn->is_ssl) /* really dead? */
! 862: removeconnection(cn);
! 863: return;
! 864: }
! 865:
! 866: buf[rv] = '\0';
! 867: next = buf;
! 868: post_data = &cn->post_data[cn->post_read];
! 869:
! 870: while (next < &buf[rv])
! 871: {
! 872: *post_data++ = *next++;
! 873: cn->post_read++;
! 874:
! 875: if (cn->post_read == cn->content_length)
! 876: {
! 877: /* No more POST data to be copied */
! 878: *post_data='\0';
! 879: cn->post_state = 0;
! 880: buildactualfile(cn);
! 881: cn->state = STATE_WANT_TO_SEND_HEAD;
! 882: return;
! 883: }
! 884: }
! 885:
! 886: /* More POST data to read */
! 887: }
! 888:
! 889: #endif /* CONFIG_HTTP_HAS_CGI */
! 890:
! 891: /* Decode string %xx -> char (in place) */
! 892: static void urldecode(char *buf)
! 893: {
! 894: int v;
! 895: char *p, *s, *w;
! 896:
! 897: w = p = buf;
! 898:
! 899: while (*p)
! 900: {
! 901: v = 0;
! 902:
! 903: if (*p == '%')
! 904: {
! 905: s = p;
! 906: s++;
! 907:
! 908: if (isxdigit((int) s[0]) && isxdigit((int) s[1]))
! 909: {
! 910: v = hexit(s[0])*16 + hexit(s[1]);
! 911:
! 912: if (v)
! 913: {
! 914: /* do not decode %00 to null char */
! 915: *w = (char)v;
! 916: p = &s[1];
! 917: }
! 918: }
! 919:
! 920: }
! 921:
! 922: if (!v) *w=*p;
! 923: p++;
! 924: w++;
! 925: }
! 926:
! 927: *w='\0';
! 928: }
! 929:
! 930: static int hexit(char c)
! 931: {
! 932: if (c >= '0' && c <= '9')
! 933: return c - '0';
! 934: else if (c >= 'a' && c <= 'f')
! 935: return c - 'a' + 10;
! 936: else if (c >= 'A' && c <= 'F')
! 937: return c - 'A' + 10;
! 938: else
! 939: return 0;
! 940: }
! 941:
! 942: static void buildactualfile(struct connstruct *cn)
! 943: {
! 944: char *cp;
! 945: snprintf(cn->actualfile, MAXREQUESTLENGTH, ".%s", cn->filereq);
! 946:
! 947: #ifndef WIN32
! 948: /* Add directory slash if not there */
! 949: if (isdir(cn->actualfile) &&
! 950: cn->actualfile[strlen(cn->actualfile)-1] != '/')
! 951: strcat(cn->actualfile, "/");
! 952:
! 953: /* work out the directory name */
! 954: strncpy(cn->dirname, cn->actualfile, MAXREQUESTLENGTH);
! 955: if ((cp = strrchr(cn->dirname, '/')) == NULL)
! 956: cn->dirname[0] = 0;
! 957: else
! 958: *cp = 0;
! 959: #else
! 960: {
! 961: char curr_dir[MAXREQUESTLENGTH];
! 962: char path[MAXREQUESTLENGTH];
! 963: char *t = cn->actualfile;
! 964:
! 965: GetCurrentDirectory(MAXREQUESTLENGTH, curr_dir);
! 966:
! 967: /* convert all the forward slashes to back slashes */
! 968: while ((t = strchr(t, '/')))
! 969: *t++ = '\\';
! 970:
! 971: snprintf(path, MAXREQUESTLENGTH, "%s%s", curr_dir, cn->actualfile);
! 972: memcpy(cn->actualfile, path, MAXREQUESTLENGTH);
! 973:
! 974: /* Add directory slash if not there */
! 975: if (isdir(cn->actualfile) &&
! 976: cn->actualfile[strlen(cn->actualfile)-1] != '\\')
! 977: strcat(cn->actualfile, "\\");
! 978:
! 979: /* work out the directory name */
! 980: strncpy(cn->dirname, cn->actualfile, MAXREQUESTLENGTH);
! 981: if ((cp = strrchr(cn->dirname, '\\')) == NULL)
! 982: cn->dirname[0] = 0;
! 983: else
! 984: *cp = 0;
! 985: }
! 986: #endif
! 987: }
! 988:
! 989: static int sanitizefile(const char *buf)
! 990: {
! 991: int len, i;
! 992:
! 993: /* Don't accept anything not starting with a / */
! 994: if (*buf != '/')
! 995: return 0;
! 996:
! 997: len = strlen(buf);
! 998: for (i = 0; i < len; i++)
! 999: {
! 1000: /* Check for "/." i.e. don't send files starting with a . */
! 1001: if (buf[i] == '/' && buf[i+1] == '.')
! 1002: return 0;
! 1003: }
! 1004:
! 1005: return 1;
! 1006: }
! 1007:
! 1008: static int sanitizehost(char *buf)
! 1009: {
! 1010: while (*buf != '\0')
! 1011: {
! 1012: /* Handle the port */
! 1013: if (*buf == ':')
! 1014: {
! 1015: *buf = '\0';
! 1016: return 1;
! 1017: }
! 1018:
! 1019: /* Enforce some basic URL rules... */
! 1020: if ((isalnum((int)(*buf)) == 0 && *buf != '-' && *buf != '.') ||
! 1021: (*buf == '.' && *(buf+1) == '.') ||
! 1022: (*buf == '.' && *(buf+1) == '-') ||
! 1023: (*buf == '-' && *(buf+1) == '.'))
! 1024: return 0;
! 1025:
! 1026: buf++;
! 1027: }
! 1028:
! 1029: return 1;
! 1030: }
! 1031:
! 1032: static FILE * exist_check(struct connstruct *cn, const char *check_file)
! 1033: {
! 1034: char pathname[MAXREQUESTLENGTH];
! 1035: snprintf(pathname, MAXREQUESTLENGTH, "%s/%s", cn->dirname, check_file);
! 1036: return fopen(pathname, "r");
! 1037: }
! 1038:
! 1039: #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
! 1040: static void send_authenticate(struct connstruct *cn, const char *realm)
! 1041: {
! 1042: char buf[1024];
! 1043:
! 1044: snprintf(buf, sizeof(buf), HTTP_VERSION" 401 Unauthorized\n"
! 1045: "WWW-Authenticate: Basic\n"
! 1046: "realm=\"%s\"\n", realm);
! 1047: special_write(cn, buf, strlen(buf));
! 1048: }
! 1049:
! 1050: static int check_digest(char *salt, const char *msg_passwd)
! 1051: {
! 1052: uint8_t b256_salt[MAXREQUESTLENGTH];
! 1053: uint8_t real_passwd[MD5_SIZE];
! 1054: int salt_size = sizeof(b256_salt);
! 1055: int password_size = sizeof(real_passwd);
! 1056: char *b64_passwd;
! 1057: uint8_t md5_result[MD5_SIZE];
! 1058: MD5_CTX ctx;
! 1059:
! 1060: /* retrieve the salt */
! 1061: if ((b64_passwd = strchr(salt, '$')) == NULL)
! 1062: return -1;
! 1063:
! 1064: *b64_passwd++ = 0;
! 1065: if (base64_decode(salt, strlen(salt), b256_salt, &salt_size))
! 1066: return -1;
! 1067:
! 1068: if (base64_decode(b64_passwd, strlen(b64_passwd), real_passwd,
! 1069: &password_size))
! 1070: return -1;
! 1071:
! 1072: /* very simple MD5 crypt algorithm, but then the salt we use is large */
! 1073: MD5_Init(&ctx);
! 1074: MD5_Update(&ctx, b256_salt, salt_size); /* process the salt */
! 1075: MD5_Update(&ctx, (uint8_t *)msg_passwd, strlen(msg_passwd));
! 1076: MD5_Final(md5_result, &ctx);
! 1077: return memcmp(md5_result, real_passwd, MD5_SIZE);/* 0 = ok */
! 1078: }
! 1079:
! 1080: static int auth_check(struct connstruct *cn)
! 1081: {
! 1082: char line[MAXREQUESTLENGTH];
! 1083: FILE *fp;
! 1084: char *cp;
! 1085:
! 1086: if ((fp = exist_check(cn, ".htpasswd")) == NULL)
! 1087: return 0; /* no .htpasswd file, so let though */
! 1088:
! 1089: if (cn->authorization[0] == 0)
! 1090: goto error;
! 1091:
! 1092: /* cn->authorization is in form "username:password" */
! 1093: if ((cp = strchr(cn->authorization, ':')) == NULL)
! 1094: goto error;
! 1095: else
! 1096: *cp++ = 0; /* cp becomes the password */
! 1097:
! 1098: while (fgets(line, sizeof(line), fp) != NULL)
! 1099: {
! 1100: char *b64_file_passwd;
! 1101: int l = strlen(line);
! 1102:
! 1103: /* nuke newline */
! 1104: if (line[l-1] == '\n')
! 1105: line[l-1] = 0;
! 1106:
! 1107: /* line is form "username:salt(b64)$password(b64)" */
! 1108: if ((b64_file_passwd = strchr(line, ':')) == NULL)
! 1109: continue;
! 1110:
! 1111: *b64_file_passwd++ = 0;
! 1112:
! 1113: if (strcmp(line, cn->authorization)) /* our user? */
! 1114: continue;
! 1115:
! 1116: if (check_digest(b64_file_passwd, cp) == 0)
! 1117: {
! 1118: fclose(fp);
! 1119: return 0;
! 1120: }
! 1121: }
! 1122:
! 1123: error:
! 1124: fclose(fp);
! 1125: send_authenticate(cn, cn->server_name);
! 1126: return -1;
! 1127: }
! 1128: #endif
! 1129:
! 1130: static int htaccess_check(struct connstruct *cn)
! 1131: {
! 1132: char line[MAXREQUESTLENGTH];
! 1133: FILE *fp;
! 1134: int ret = 0;
! 1135:
! 1136: if ((fp = exist_check(cn, ".htaccess")) == NULL)
! 1137: return 0; /* no .htaccess file, so let though */
! 1138:
! 1139: while (fgets(line, sizeof(line), fp) != NULL)
! 1140: {
! 1141: if (strstr(line, "Deny all") || /* access to this dir denied */
! 1142: /* Access will be denied unless SSL is active */
! 1143: (!cn->is_ssl && strstr(line, "SSLRequireSSL")) ||
! 1144: /* Access will be denied if SSL is active */
! 1145: (cn->is_ssl && strstr(line, "SSLDenySSL")))
! 1146: {
! 1147: ret = -1;
! 1148: break;
! 1149: }
! 1150: }
! 1151:
! 1152: fclose(fp);
! 1153: return ret;
! 1154: }
! 1155:
! 1156: static void send_error(struct connstruct *cn, int err)
! 1157: {
! 1158: char buf[MAXREQUESTLENGTH];
! 1159: char *title;
! 1160: char *text;
! 1161:
! 1162: switch (err)
! 1163: {
! 1164: case 403:
! 1165: title = "Forbidden";
! 1166: text = "File is protected";
! 1167: #ifdef CONFIG_HTTP_VERBOSE
! 1168: printf("axhttpd: access to %s denied\n", cn->filereq); TTY_FLUSH();
! 1169: #endif
! 1170: break;
! 1171:
! 1172: case 404:
! 1173: title = "Not Found";
! 1174: text = title;
! 1175: break;
! 1176:
! 1177: case 418:
! 1178: title = "POST data size is too large";
! 1179: text = title;
! 1180: break;
! 1181:
! 1182: default:
! 1183: title = "Unknown";
! 1184: text = "Unknown";
! 1185: break;
! 1186: }
! 1187:
! 1188: snprintf(buf, sizeof(buf), HTTP_VERSION" 200 OK\n"
! 1189: "Content-Type: text/html\n\n"
! 1190: "<html><body>\n<title>%s</title>\n"
! 1191: "<h1>Error %d - %s</h1>\n</body></html>\n",
! 1192: title, err, text);
! 1193: special_write(cn, buf, strlen(buf));
! 1194:
! 1195: #ifdef CONFIG_HTTP_VERBOSE
! 1196: printf("axhttpd: http error: %s [%d]\n", title, err); TTY_FLUSH();
! 1197: #endif
! 1198: removeconnection(cn);
! 1199: }
! 1200:
! 1201: static const char *getmimetype(const char *name)
! 1202: {
! 1203: /* only bother with a few mime types - let the browser figure the rest out */
! 1204: if (strstr(name, ".htm"))
! 1205: return "text/html";
! 1206: else if (strstr(name, ".css"))
! 1207: return "text/css";
! 1208: else if (strstr(name, ".php"))
! 1209: return "application/x-http-php";
! 1210: else
! 1211: return "application/octet-stream";
! 1212: }
! 1213:
! 1214: static int special_write(struct connstruct *cn,
! 1215: const char *buf, size_t count)
! 1216: {
! 1217: if (cn->is_ssl)
! 1218: {
! 1219: SSL *ssl = cn->ssl;
! 1220: return ssl ? ssl_write(ssl, (uint8_t *)buf, count) : -1;
! 1221: }
! 1222: else
! 1223: return SOCKET_WRITE(cn->networkdesc, buf, count);
! 1224: }
! 1225:
! 1226: static int special_read(struct connstruct *cn, void *buf, size_t count)
! 1227: {
! 1228: int res;
! 1229:
! 1230: if (cn->is_ssl)
! 1231: {
! 1232: uint8_t *read_buf;
! 1233: if ((res = ssl_read(cn->ssl, &read_buf)) > SSL_OK)
! 1234: {
! 1235: memcpy(buf, read_buf, res > (int)count ? count : res);
! 1236: }
! 1237: }
! 1238: else
! 1239: res = SOCKET_READ(cn->networkdesc, buf, count);
! 1240:
! 1241: return res;
! 1242: }
! 1243:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>