File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / axTLS / httpd / proc.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Fri Sep 28 11:55:55 2012 UTC (11 years, 9 months ago) by misho
Branches: v1_4_8, MAIN
CVS tags: datecs, HEAD
axTLS

    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>