--- main.c.orig Mon Sep 22 10:39:24 2003 +++ main.c Thu Oct 16 14:00:02 2003 @@ -23,6 +23,7 @@ char defaultFileName[MAX_PATH_LEN+1]; char logFileName[MAX_PATH_LEN+1]; char mimeTypesFileName[MAX_PATH_LEN+1]; +char phpFileName[MAX_PATH_LEN+1]; char cgiRoot[MAX_PATH_LEN+1]; /* root for CGI scripts exec */ struct timeval sockTimeVal; mimeData *mimeArray; /* here we will hold all MIME data, inited once, never to be changed */ @@ -206,10 +207,10 @@ int reqSize; int readLines; int tokenEnd; - - /* we copy the header lines to an array for easier parsing */ + + /* we copy the header lines to an array for easier parsing */ /* but first we make sure that our string has a newline and an end */ - req[BUFFER_SIZE] = '\0'; + req[BUFFER_SIZE] = '\0'; reqSize = strlen(req); req[reqSize] = '\n'; reqSize++; @@ -230,7 +231,7 @@ for (k = 0; k < readLines; k++) printf("%d - |%s|\n", k, reqArray[k]); #endif - + /* first line: method, path and protocol version */ /* we copy to a temporary buffer to be more secure against overflows */ i = j = 0; @@ -246,7 +247,7 @@ else tokenEnd = NO; i++; - + /* we look for the document address */ j = 0; reqStruct->documentAddress[0] = '\0'; @@ -259,14 +260,14 @@ else token[j] = '\0'; /* to make sure we have a string */ /* now we need to convert some escapings from the path like %20 */ - convertPercents(token, j); + convertPercents(token, j); strcpy(reqStruct->documentAddress, token); /* copy back */ if (reqArray[0][i] == '\0') tokenEnd = YES; else tokenEnd = NO; i++; - + /* we need now to separate path from query string ("?" separated) */ if (reqArray[0][i-1] == '?') { @@ -282,7 +283,7 @@ i++; } } - + /* we analyze the HTTP protocol version */ /* default is 0.9 since that version didn't report itself */ strcpy(reqStruct->protocolVersion, "HTTP/0.9"); @@ -306,10 +307,13 @@ else if (!strncmp(reqArray[1], "Connection: Keep-Alive", strlen("Connection: keep-alive"))) reqStruct->keepAlive = YES; - /* user-agent, content-length and else */ + /* user-agent, content-length, content-type, cookie and else */ i = 1; j = NO; reqStruct->userAgent[0] = '\0'; + reqStruct->contentLength = -1; + reqStruct->contentType[0] = '\0'; + reqStruct->cookie[0] = '\0'; while (i < readLines) { if (!strncmp(reqArray[i], "User-Agent:", strlen("User-Agent:"))) @@ -317,14 +321,28 @@ strncpy(reqStruct->userAgent, &reqArray[i][strlen("User-Agent: ")], USER_AGENT_LEN - 1); reqStruct->userAgent[USER_AGENT_LEN] = '\0'; } - else if (!strncmp(reqArray[i], "Content-Length:", strlen("Content-length:")) || !strncmp(reqArray[i], "Content-length:", strlen("Content-length:"))) - { - strcpy(token, &reqArray[i][strlen("Content-length: ")]); - sscanf(token, "%ld", &(reqStruct->contentLength)); + else if (!strncmp(reqArray[i], "Content-Length:", strlen("Content-length:")) || !strncmp(reqArray[i], "Content-length:", strlen("Content-length:"))) + { + strcpy(token, &reqArray[i][strlen("Content-length: ")]); + sscanf(token, "%ld", &(reqStruct->contentLength)); #ifdef PRINTF_DEBUG - printf("content length %ld\n", reqStruct->contentLength); + printf("content length %ld\n", reqStruct->contentLength); #endif - } + } + else if (!strncmp(reqArray[i], "Content-Type:", strlen("Content-type:")) || !strncmp(reqArray[i], "Content-type:", strlen("Content-type:"))) + { + strncpy(reqStruct->contentType, &reqArray[i][strlen("Content-type: ")], CONTENT_TYPE_LEN - 1); +#ifdef PRINTF_DEBUG + printf("content type %s\n", reqStruct->contentType); +#endif + } + else if (!strncmp(reqArray[i], "Cookie:", strlen("Cookie:"))) + { + strncpy(reqStruct->cookie, &reqArray[i][strlen("Cookie: ")], MAX_COOKIE_LEN - 1); +#ifdef PRINTF_DEBUG + printf("cookie %s\n", reqStruct->cookie); +#endif + } i++; } /* if we didn't find a User-Aget we fill in a (N)ot(R)ecognized */ @@ -414,18 +432,39 @@ /* we append the default file name */ strcat(completeFilePath, defaultFileName); analyzeExtension(mimeType, completeFilePath); - dumpFile(sock, completeFilePath, mimeType, req); +#ifdef PHP + if (strncmp(mimeType, "application/x-httpd-php", 23)) +#endif + dumpFile(sock, completeFilePath, mimeType, req); +#ifdef PHP + else + phpHandler(port, sock, phpFileName, completeFilePath, req, NULL); +#endif } #else /* we append the default file name */ strcat(completeFilePath, defaultFileName); analyzeExtension(mimeType, completeFilePath); - dumpFile(sock, completeFilePath, mimeType, req); +#ifdef PHP + if (strncmp(mimeType, "application/x-httpd-php", 23)) +#endif + dumpFile(sock, completeFilePath, mimeType, req); +#ifdef PHP + else + phpHandler(port, sock, phpFileName, completeFilePath, req, NULL); +#endif #endif } else { /* it is a plain file */ analyzeExtension(mimeType, completeFilePath); - dumpFile(sock, completeFilePath, mimeType, req); +#ifdef PHP + if (strncmp(mimeType, "application/x-httpd-php", 23)) +#endif + dumpFile(sock, completeFilePath, mimeType, req); +#ifdef PHP + else + phpHandler(port, sock, phpFileName, completeFilePath, req, NULL); +#endif } } } else if (!strcmp(req.method, "HEAD")) @@ -494,7 +533,14 @@ strcat(completeFilePath, defaultFileName); } analyzeExtension(mimeType, completeFilePath); - dumpHeader(sock, completeFilePath, mimeType, req); +#ifdef PHP + if (strncmp(mimeType, "application/x-httpd-php", 23)) +#endif + dumpFile(sock, completeFilePath, mimeType, req); +#ifdef PHP + else + phpHandler(port, sock, phpFileName, completeFilePath, req, NULL); +#endif } } else if (!strcmp(req.method, "POST")) { @@ -507,13 +553,6 @@ int readFinished; printf("Handling of POST method\n"); - /* first we check if the path contains the directory selected for cgi's and in case handle it */ - if (strncmp(req.documentAddress, CGI_MATCH_STRING, strlen(CGI_MATCH_STRING))) - { - /* non cgi POST is not supported */ - sayError(sock, UNHANDLED_METHOD, "", req); - return -1; - } #ifdef PRINTF_DEBUG printf ("begin of post handling\n"); @@ -523,9 +562,15 @@ totalRead = 0; stuckCounter = 0; timeOutCounter = 0; - while (!readFinished) - { - howMany = recv(newSocket, tempBuff, BUFFER_SIZE, 0); + + /* SECURITY: Avoid malicious Content-Length -- check \r\n\r\n\0 also */ + if (req.contentLength < 0 || req.contentLength >= BUFFER_SIZE-5) { + sayError(sock, 500, "", req); + return -1; + } + + /* SECURITY: Remove loop to prevent buffer overflow */ + howMany = recv(newSocket, tempBuff, req.contentLength+5, 0); tempBuff[howMany] = '\0'; /* seems that some Unices need this */ #ifdef PRINTF_DEBUG printf ("read: %d\n%s\n", howMany, tempBuff); @@ -579,16 +624,15 @@ if (howMany == req.contentLength) readFinished = YES; } - } #ifdef PRINTF_DEBUG - printf("total read %d\n", totalRead); + printf("total read %d\n", totalRead); #endif - if (totalRead == 0) - { - printf("Request read error\n"); - } else - { - if (buff[totalRead - 1] != '\n') /* we need a trailing \n or the script will wait forever */ + if (totalRead == 0) + { + printf("Request read error\n"); + } else + { + if (buff[totalRead - 1] != '\n') /* we need a trailing \n or the script will wait forever */ { buff[totalRead++] = '\n'; buff[totalRead] = '\0'; @@ -596,7 +640,77 @@ #ifdef PRINTF_DEBUG printf("buff: |%s|\n", buff); #endif - cgiHandler(port, sock, req, buff); + if (!strncmp(req.documentAddress, CGI_MATCH_STRING, strlen(CGI_MATCH_STRING))) + { + cgiHandler(port, sock, req, buff); + } else + { +#ifdef PHP + strcpy(completeFilePath, homePath); + strcat(completeFilePath, req.documentAddress); + /* now we check if the given path tries to get out of the root */ + { + int i,j; + int sL; + char dirName[MAX_PATH_LEN+1]; + int depthCount = 0; + + sL = strlen(req.documentAddress); + dirName[0] = '\0'; + if (sL > 3) { + dirName[0] = req.documentAddress[1]; + dirName[1] = req.documentAddress[2]; + dirName[2] = req.documentAddress[3]; + dirName[3] ='\0'; + if (!strcmp(dirName, "../")) + { + sayError(sock, FORBIDDEN, req.documentAddress, req); + return -1; + } + } + j = 0; + for (i = 1; i < sL; i++) { + if (req.documentAddress[i] == '/') + { + dirName[j] = '\0'; + if (strcmp(dirName, "..")) + depthCount ++; + else + depthCount--; + j = 0; + } else + dirName[j++] = req.documentAddress[i]; + } + if (depthCount < 0) + { + sayError(sock, FORBIDDEN, req.documentAddress, req); + return -1; + } + } + /* now we check if the given file is a directory or a plain file */ + stat(completeFilePath, &fileStats); + if ((fileStats.st_mode & S_IFDIR) == S_IFDIR) + { + /* if does not end with a slash, we get an error */ + if(completeFilePath[strlen(completeFilePath)-1] != '/') + { + sayError(sock, NOT_FOUND, req.documentAddress, req); + return -1; + } + /* we append the default file name */ + strcat(completeFilePath, defaultFileName); + } + analyzeExtension(mimeType, completeFilePath); + if (strncmp(mimeType, "application/x-httpd-php", 23)) + { +#endif + /* non cgi POST is not supported */ + sayError(sock, UNHANDLED_METHOD, "", req); + return -1; +#ifdef PHP + } else phpHandler(port, sock, phpFileName, completeFilePath, req, buff); +#endif + } } } else { @@ -625,7 +739,7 @@ f = fopen(configFile, "r"); if (f == NULL) { - printf("Error opening config file. Setting defaults.\n"); + printf("Config file not found. Setting defaults.\n"); *serverPort = DEFAULT_PORT; *maxChildren = DEFAULT_MAX_CHILDREN; strcpy(homePath, DEFAULT_DOCS_LOCATION); @@ -634,7 +748,9 @@ sockTimeVal.tv_usec = DEFAULT_USEC_TO; strcpy(logFileName, DEFAULT_LOG_FILE); strcpy(mimeTypesFileName, DEFAULT_MIME_FILE); + strcpy(phpFileName, DEFAULT_PHP_FILE); strcpy(cgiRoot, DEFAULT_CGI_ROOT); + initMimeTypes(); return -1; } if (!feof(f)) fscanf(f, "%s %s", str1, str2); @@ -735,11 +851,25 @@ if (mimeTypesFileName == NULL) { strcpy(mimeTypesFileName, DEFAULT_MIME_FILE); - printf("Error reading mimeTypesFileName from file, setting default, %s\n", mimeTypesFileName); + printf("Error reading mimeTypesFile from file, setting default, %s\n", mimeTypesFileName); } } else { strcpy(mimeTypesFileName, DEFAULT_MIME_FILE); - printf("Error reading mimeTypesFileName from file, setting default, %s\n", mimeTypesFileName); + printf("Error reading mimeTypesFile from file, setting default, %s\n", mimeTypesFileName); + } + if (!feof(f)) fscanf(f, "%s %s", str1, str2); + if (str1 != NULL && str2 != NULL && !strcmp(str1, "phpFile")) + { + sscanf(str2, "%s", phpFileName); + if (logFileName == NULL) + { + strcpy(phpFileName, DEFAULT_LOG_FILE); + printf("Error reading phpFile from file, setting default, %s\n", phpFileName); + } + } else + { + strcpy(phpFileName, DEFAULT_PHP_FILE); + printf("Error reading phpFile from file, setting default, %s\n", phpFileName); } if (!feof(f)) fscanf(f, "%s %s", str1, str2); if (str1 != NULL && str2 != NULL && !strcmp(str1, "cgiRoot")) @@ -775,6 +905,7 @@ int readFinished; struct request gottenReq; int isKeepAlive; + int bool; struct sockaddr_in listenName; /* data struct for the listen port */ struct sockaddr_in acceptedSockStruct; /* sockaddr for the internetworking */ int acceptedSocketLen; /* size of the structure */ @@ -808,9 +939,16 @@ printf("socket creation error occoured\n"); return -1; } + bool = 1; + error = setsockopt (theSocket, SOL_SOCKET, SO_REUSEADDR, &bool, sizeof(bool)); + if (error == -1) + { if (errno == EADDRINUSE) + printf("set socket option error occoured\n"); + return -1; + } error = bind (theSocket, (struct sockaddr*) &listenName, sizeof(listenName)); if (error == -1) - { + { if (errno == EADDRINUSE) printf("socket binding error occoured\n"); return -2; }