Annotation of embedaddon/axTLS/httpd/proc.c, revision 1.1.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>