cgi-io: use dynamic memory for post decoding, support proc files
authorJo-Philipp Wich <jo@mein.io>
Mon, 10 Feb 2020 17:19:34 +0000 (18:19 +0100)
committerJohn Crispin <john@phrozen.org>
Thu, 13 Feb 2020 07:38:32 +0000 (08:38 +0100)
Allocate dynamic buffer memory for decoding post data and allow post
requsts up to 128KB compared to the previos 1KB limit.

Also support downloading /proc and /sys files by falling back to
chunked transfer encoding when the file size cannot be determined.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Makefile
src/main.c

index 27bdf737fe39ff12c69f7dbb5161f4649ae4bf2b..c8e4164ef98539418e1ce3b873e0c5a716567606 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=cgi-io
-PKG_RELEASE:=16
+PKG_RELEASE:=17
 
 PKG_LICENSE:=GPL-2.0-or-later
 
index 7cf8d7b23dd097b8c7e74b5f6724f6ac4ad8088b..6e9112c0f93f86c3a0bae0310094471455388519 100644 (file)
@@ -38,6 +38,7 @@
 #include "multipart_parser.h"
 
 #define READ_BLOCK 4096
+#define POST_LIMIT 131072
 
 enum part {
        PART_UNKNOWN,
@@ -223,55 +224,86 @@ urldecode(char *buf)
        return true;
 }
 
-static bool
+static char *
 postdecode(char **fields, int n_fields)
 {
-       char *p;
        const char *var;
-       static char buf[1024];
-       int i, len, field, found = 0;
+       char *p, *postbuf;
+       int i, field, found = 0;
+       ssize_t len = 0, rlen = 0, content_length = 0;
 
        var = getenv("CONTENT_TYPE");
 
        if (!var || strncmp(var, "application/x-www-form-urlencoded", 33))
-               return false;
+               return NULL;
+
+       var = getenv("CONTENT_LENGTH");
+
+       if (!var)
+               return NULL;
+
+       content_length = strtol(var, &p, 10);
+
+       if (p == var || content_length <= 0 || content_length >= POST_LIMIT)
+               return NULL;
+
+       postbuf = calloc(1, content_length + 1);
+
+       if (postbuf == NULL)
+               return NULL;
+
+       for (len = 0; len < content_length; )
+       {
+               rlen = read(0, postbuf + len, content_length - len);
+
+               if (rlen <= 0)
+                       break;
 
-       memset(buf, 0, sizeof(buf));
+               len += rlen;
+       }
 
-       if ((len = read(0, buf, sizeof(buf) - 1)) > 0)
+       if (len < content_length)
        {
-               for (p = buf, i = 0; i <= len; i++)
+               free(postbuf);
+               return NULL;
+       }
+
+       for (p = postbuf, i = 0; i <= len; i++)
+       {
+               if (postbuf[i] == '=')
                {
-                       if (buf[i] == '=')
-                       {
-                               buf[i] = 0;
+                       postbuf[i] = 0;
 
-                               for (field = 0; field < (n_fields * 2); field += 2)
+                       for (field = 0; field < (n_fields * 2); field += 2)
+                       {
+                               if (!strcmp(p, fields[field]))
                                {
-                                       if (!strcmp(p, fields[field]))
-                                       {
-                                               fields[field + 1] = buf + i + 1;
-                                               found++;
-                                       }
+                                       fields[field + 1] = postbuf + i + 1;
+                                       found++;
                                }
                        }
-                       else if (buf[i] == '&' || buf[i] == '\0')
-                       {
-                               buf[i] = 0;
+               }
+               else if (postbuf[i] == '&' || postbuf[i] == '\0')
+               {
+                       postbuf[i] = 0;
 
-                               if (found >= n_fields)
-                                       break;
+                       if (found >= n_fields)
+                               break;
 
-                               p = buf + i + 1;
-                       }
+                       p = postbuf + i + 1;
                }
        }
 
        for (field = 0; field < (n_fields * 2); field += 2)
+       {
                if (!urldecode(fields[field + 1]))
-                       return false;
+               {
+                       free(postbuf);
+                       return NULL;
+               }
+       }
 
-       return (found >= n_fields);
+       return postbuf;
 }
 
 static char *
@@ -658,6 +690,14 @@ main_upload(int argc, char *argv[])
        return 0;
 }
 
+static void
+free_charp(char **ptr)
+{
+       free(*ptr);
+}
+
+#define autochar __attribute__((__cleanup__(free_charp))) char
+
 static int
 main_download(int argc, char **argv)
 {
@@ -668,7 +708,7 @@ main_download(int argc, char **argv)
        struct stat s;
        int rfd;
 
-       postdecode(fields, 4);
+       autochar *post = postdecode(fields, 4);
 
        if (!fields[1] || !session_access(fields[1], "cgi-io", "download", "read"))
                return failure(403, 0, "Download permission denied");
@@ -706,29 +746,39 @@ main_download(int argc, char **argv)
        if (fields[5])
                printf("Content-Disposition: attachment; filename=\"%s\"\r\n", fields[5]);
 
-       printf("Content-Length: %llu\r\n\r\n", size);
-       fflush(stdout);
+       if (size > 0) {
+               printf("Content-Length: %llu\r\n\r\n", size);
+               fflush(stdout);
 
-       while (size > 0) {
-               len = sendfile(1, rfd, NULL, size);
+               while (size > 0) {
+                       len = sendfile(1, rfd, NULL, size);
 
-               if (len == -1) {
-                       if (errno == ENOSYS || errno == EINVAL) {
-                               while ((len = read(rfd, buf, sizeof(buf))) > 0)
-                                       fwrite(buf, len, 1, stdout);
+                       if (len == -1) {
+                               if (errno == ENOSYS || errno == EINVAL) {
+                                       while ((len = read(rfd, buf, sizeof(buf))) > 0)
+                                               fwrite(buf, len, 1, stdout);
 
-                               fflush(stdout);
-                               break;
+                                       fflush(stdout);
+                                       break;
+                               }
+
+                               if (errno == EINTR || errno == EAGAIN)
+                                       continue;
                        }
 
-                       if (errno == EINTR || errno == EAGAIN)
-                               continue;
+                       if (len <= 0)
+                               break;
+
+                       size -= len;
                }
+       }
+       else {
+               printf("\r\n");
 
-               if (len <= 0)
-                       break;
+               while ((len = read(rfd, buf, sizeof(buf))) > 0)
+                       fwrite(buf, len, 1, stdout);
 
-               size -= len;
+               fflush(stdout);
        }
 
        close(rfd);
@@ -749,7 +799,9 @@ main_backup(int argc, char **argv)
        char hostname[64] = { 0 };
        char *fields[] = { "sessionid", NULL };
 
-       if (!postdecode(fields, 1) || !session_access(fields[1], "cgi-io", "backup", "read"))
+       autochar *post = postdecode(fields, 1);
+
+       if (!fields[1] || !session_access(fields[1], "cgi-io", "backup", "read"))
                return failure(403, 0, "Backup permission denied");
 
        if (pipe(fds))
@@ -929,7 +981,7 @@ main_exec(int argc, char **argv)
        char *p, **args;
        pid_t pid;
 
-       postdecode(fields, 4);
+       autochar *post = postdecode(fields, 4);
 
        if (!fields[1] || !session_access(fields[1], "cgi-io", "exec", "read"))
                return failure(403, 0, "Exec permission denied");