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