From 08be3279423ed19554905d9cf00a508be0586107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0tetiar?= Date: Sun, 11 Oct 2020 14:54:55 +0200 Subject: [PATCH] cgi-io: move into out of tree project MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No functional changes, just moved the sources into out of tree project[1] so it's going to be easier to do CI with unit testing, fuzzing etc. 1. https://git.openwrt.org/?p=project/cgi-io.git;a=shortlog Signed-off-by: Petr Štetiar --- net/cgi-io/Makefile | 13 +- net/cgi-io/src/CMakeLists.txt | 25 - net/cgi-io/src/main.c | 1116 ----------------------------- net/cgi-io/src/multipart_parser.c | 309 -------- net/cgi-io/src/multipart_parser.h | 48 -- 5 files changed, 7 insertions(+), 1504 deletions(-) delete mode 100644 net/cgi-io/src/CMakeLists.txt delete mode 100644 net/cgi-io/src/main.c delete mode 100644 net/cgi-io/src/multipart_parser.c delete mode 100644 net/cgi-io/src/multipart_parser.h diff --git a/net/cgi-io/Makefile b/net/cgi-io/Makefile index 814a1b2f5..13c33bbac 100644 --- a/net/cgi-io/Makefile +++ b/net/cgi-io/Makefile @@ -10,8 +10,14 @@ include $(TOPDIR)/rules.mk PKG_NAME:=cgi-io PKG_RELEASE:=19 -PKG_LICENSE:=GPL-2.0-or-later +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL=$(PROJECT_GIT)/project/cgi-io.git +PKG_SOURCE_DATE:=2020-10-11 +PKG_SOURCE_VERSION:=d2907bd12986c7b582adcf0f186d97fed399963b +PKG_MIRROR_HASH:=e4ce1aabfa91e77f562d89afb0ae0aea2e3dd2d3998069819f3412acfc4ed26b +CMAKE_INSTALL:=1 +PKG_LICENSE:=GPL-2.0-or-later PKG_MAINTAINER:=John Crispin include $(INCLUDE_DIR)/package.mk @@ -29,11 +35,6 @@ define Package/cgi-io/description This package contains an cgi utility that is useful for up/downloading files endef -define Build/Prepare - mkdir -p $(PKG_BUILD_DIR) - $(CP) ./src/* $(PKG_BUILD_DIR)/ -endef - define Package/cgi-io/install $(INSTALL_DIR) $(1)/usr/libexec $(1)/www/cgi-bin/ $(INSTALL_BIN) $(PKG_BUILD_DIR)/cgi-io $(1)/usr/libexec diff --git a/net/cgi-io/src/CMakeLists.txt b/net/cgi-io/src/CMakeLists.txt deleted file mode 100644 index c7c9d40ca..000000000 --- a/net/cgi-io/src/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -cmake_minimum_required(VERSION 2.6) - -PROJECT(cgi-io C) - -INCLUDE(CheckFunctionExists) - -FIND_PATH(ubus_include_dir libubus.h) -FIND_LIBRARY(ubox NAMES ubox) -FIND_LIBRARY(ubus NAMES ubus) -INCLUDE_DIRECTORIES(${ubus_include_dir}) - -ADD_DEFINITIONS(-Os -Wall -Werror -Wextra --std=gnu99 -g3) -ADD_DEFINITIONS(-Wno-unused-parameter -Wmissing-declarations) - -SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") - -IF(APPLE) - INCLUDE_DIRECTORIES(/opt/local/include) - LINK_DIRECTORIES(/opt/local/lib) -ENDIF() - -ADD_EXECUTABLE(cgi-io main.c multipart_parser.c) -TARGET_LINK_LIBRARIES(cgi-io ${ubox} ${ubus}) - -INSTALL(TARGETS cgi-io RUNTIME DESTINATION sbin) diff --git a/net/cgi-io/src/main.c b/net/cgi-io/src/main.c deleted file mode 100644 index 549121f94..000000000 --- a/net/cgi-io/src/main.c +++ /dev/null @@ -1,1116 +0,0 @@ -/* - * cgi-io - LuCI non-RPC helper - * - * Copyright (C) 2013 Jo-Philipp Wich - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define _GNU_SOURCE /* splice(), SPLICE_F_MORE */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "multipart_parser.h" - -#ifndef O_TMPFILE -#define O_TMPFILE (020000000 | O_DIRECTORY) -#endif - -#define READ_BLOCK 4096 -#define POST_LIMIT 131072 - -enum part { - PART_UNKNOWN, - PART_SESSIONID, - PART_FILENAME, - PART_FILEMODE, - PART_FILEDATA -}; - -const char *parts[] = { - "(bug)", - "sessionid", - "filename", - "filemode", - "filedata", -}; - -struct state -{ - bool is_content_disposition; - enum part parttype; - char *sessionid; - char *filename; - bool filedata; - int filemode; - int filefd; - int tempfd; -}; - -enum { - SES_ACCESS, - __SES_MAX, -}; - -static const struct blobmsg_policy ses_policy[__SES_MAX] = { - [SES_ACCESS] = { .name = "access", .type = BLOBMSG_TYPE_BOOL }, -}; - - -static struct state st; - -static void -session_access_cb(struct ubus_request *req, int type, struct blob_attr *msg) -{ - struct blob_attr *tb[__SES_MAX]; - bool *allow = (bool *)req->priv; - - if (!msg) - return; - - blobmsg_parse(ses_policy, __SES_MAX, tb, blob_data(msg), blob_len(msg)); - - if (tb[SES_ACCESS]) - *allow = blobmsg_get_bool(tb[SES_ACCESS]); -} - -static bool -session_access(const char *sid, const char *scope, const char *obj, const char *func) -{ - uint32_t id; - bool allow = false; - struct ubus_context *ctx; - static struct blob_buf req; - - ctx = ubus_connect(NULL); - - if (!ctx || ubus_lookup_id(ctx, "session", &id)) - goto out; - - blob_buf_init(&req, 0); - blobmsg_add_string(&req, "ubus_rpc_session", sid); - blobmsg_add_string(&req, "scope", scope); - blobmsg_add_string(&req, "object", obj); - blobmsg_add_string(&req, "function", func); - - ubus_invoke(ctx, id, "access", req.head, session_access_cb, &allow, 500); - -out: - if (ctx) - ubus_free(ctx); - - return allow; -} - -static char * -checksum(const char *applet, size_t sumlen, const char *file) -{ - pid_t pid; - int r; - int fds[2]; - static char chksum[65]; - - if (pipe(fds)) - return NULL; - - switch ((pid = fork())) - { - case -1: - return NULL; - - case 0: - uloop_done(); - - dup2(fds[1], 1); - - close(0); - close(2); - close(fds[0]); - close(fds[1]); - - if (execl("/bin/busybox", "/bin/busybox", applet, file, NULL)) - return NULL; - - break; - - default: - memset(chksum, 0, sizeof(chksum)); - r = read(fds[0], chksum, sumlen); - - waitpid(pid, NULL, 0); - close(fds[0]); - close(fds[1]); - - if (r < 0) - return NULL; - } - - return chksum; -} - -static char * -datadup(const void *in, size_t len) -{ - char *out = malloc(len + 1); - - if (!out) - return NULL; - - memcpy(out, in, len); - - *(out + len) = 0; - - return out; -} - -static bool -urldecode(char *buf) -{ - char *c, *p; - - if (!buf || !*buf) - return true; - -#define hex(x) \ - (((x) <= '9') ? ((x) - '0') : \ - (((x) <= 'F') ? ((x) - 'A' + 10) : \ - ((x) - 'a' + 10))) - - for (c = p = buf; *p; c++) - { - if (*p == '%') - { - if (!isxdigit(*(p + 1)) || !isxdigit(*(p + 2))) - return false; - - *c = (char)(16 * hex(*(p + 1)) + hex(*(p + 2))); - - p += 3; - } - else if (*p == '+') - { - *c = ' '; - p++; - } - else - { - *c = *p++; - } - } - - *c = 0; - - return true; -} - -static char * -postdecode(char **fields, int n_fields) -{ - const char *var; - 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 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; - - len += rlen; - } - - if (len < content_length) - { - free(postbuf); - return NULL; - } - - for (p = postbuf, i = 0; i <= len; i++) - { - if (postbuf[i] == '=') - { - postbuf[i] = 0; - - for (field = 0; field < (n_fields * 2); field += 2) - { - if (!strcmp(p, fields[field])) - { - fields[field + 1] = postbuf + i + 1; - found++; - } - } - } - else if (postbuf[i] == '&' || postbuf[i] == '\0') - { - postbuf[i] = 0; - - if (found >= n_fields) - break; - - p = postbuf + i + 1; - } - } - - for (field = 0; field < (n_fields * 2); field += 2) - { - if (!urldecode(fields[field + 1])) - { - free(postbuf); - return NULL; - } - } - - return postbuf; -} - -static char * -canonicalize_path(const char *path, size_t len) -{ - char *canonpath, *cp; - const char *p, *e; - - if (path == NULL || *path == '\0') - return NULL; - - canonpath = datadup(path, len); - - if (canonpath == NULL) - return NULL; - - /* normalize */ - for (cp = canonpath, p = path, e = path + len; p < e; ) { - if (*p != '/') - goto next; - - /* skip repeating / */ - if ((p + 1 < e) && (p[1] == '/')) { - p++; - continue; - } - - /* /./ or /../ */ - if ((p + 1 < e) && (p[1] == '.')) { - /* skip /./ */ - if ((p + 2 >= e) || (p[2] == '/')) { - p += 2; - continue; - } - - /* collapse /x/../ */ - if ((p + 2 < e) && (p[2] == '.') && ((p + 3 >= e) || (p[3] == '/'))) { - while ((cp > canonpath) && (*--cp != '/')) - ; - - p += 3; - continue; - } - } - -next: - *cp++ = *p++; - } - - /* remove trailing slash if not root / */ - if ((cp > canonpath + 1) && (cp[-1] == '/')) - cp--; - else if (cp == canonpath) - *cp++ = '/'; - - *cp = '\0'; - - return canonpath; -} - -static int -response(bool success, const char *message) -{ - char *chksum; - struct stat s; - - printf("Status: 200 OK\r\n"); - printf("Content-Type: text/plain\r\n\r\n{\n"); - - if (success) - { - if (!stat(st.filename, &s)) - printf("\t\"size\": %u,\n", (unsigned int)s.st_size); - else - printf("\t\"size\": null,\n"); - - chksum = checksum("md5sum", 32, st.filename); - printf("\t\"checksum\": %s%s%s,\n", - chksum ? "\"" : "", - chksum ? chksum : "null", - chksum ? "\"" : ""); - - chksum = checksum("sha256sum", 64, st.filename); - printf("\t\"sha256sum\": %s%s%s\n", - chksum ? "\"" : "", - chksum ? chksum : "null", - chksum ? "\"" : ""); - } - else - { - if (message) - printf("\t\"message\": \"%s\",\n", message); - - printf("\t\"failure\": [ %u, \"%s\" ]\n", errno, strerror(errno)); - - if (st.filefd > -1) - unlink(st.filename); - } - - printf("}\n"); - - return -1; -} - -static int -failure(int code, int e, const char *message) -{ - printf("Status: %d %s\r\n", code, message); - printf("Content-Type: text/plain\r\n\r\n"); - printf("%s", message); - - if (e) - printf(": %s", strerror(e)); - - printf("\n"); - - return -1; -} - -static int -filecopy(void) -{ - int len; - char buf[READ_BLOCK]; - - if (!st.filedata) - { - close(st.tempfd); - errno = EINVAL; - return response(false, "No file data received"); - } - - snprintf(buf, sizeof(buf), "/proc/self/fd/%d", st.tempfd); - - if (unlink(st.filename) < 0 && errno != ENOENT) - { - close(st.tempfd); - return response(false, "Failed to unlink existing file"); - } - - if (linkat(AT_FDCWD, buf, AT_FDCWD, st.filename, AT_SYMLINK_FOLLOW) < 0) - { - if (lseek(st.tempfd, 0, SEEK_SET) < 0) - { - close(st.tempfd); - return response(false, "Failed to rewind temp file"); - } - - st.filefd = open(st.filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); - - if (st.filefd < 0) - { - close(st.tempfd); - return response(false, "Failed to open target file"); - } - - while ((len = read(st.tempfd, buf, sizeof(buf))) > 0) - { - if (write(st.filefd, buf, len) != len) - { - close(st.tempfd); - close(st.filefd); - return response(false, "I/O failure while writing target file"); - } - } - - close(st.filefd); - } - - close(st.tempfd); - - if (chmod(st.filename, st.filemode)) - return response(false, "Failed to chmod target file"); - - return 0; -} - -static int -header_field(multipart_parser *p, const char *data, size_t len) -{ - st.is_content_disposition = !strncasecmp(data, "Content-Disposition", len); - return 0; -} - -static int -header_value(multipart_parser *p, const char *data, size_t len) -{ - size_t i, j; - - if (!st.is_content_disposition) - return 0; - - if (len < 10 || strncasecmp(data, "form-data", 9)) - return 0; - - for (data += 9, len -= 9; *data == ' ' || *data == ';'; data++, len--); - - if (len < 8 || strncasecmp(data, "name=\"", 6)) - return 0; - - for (data += 6, len -= 6, i = 0; i <= len; i++) - { - if (*(data + i) != '"') - continue; - - for (j = 1; j < sizeof(parts) / sizeof(parts[0]); j++) - if (!strncmp(data, parts[j], i)) - st.parttype = j; - - break; - } - - return 0; -} - -static int -data_begin_cb(multipart_parser *p) -{ - if (st.parttype == PART_FILEDATA) - { - if (!st.sessionid) - return response(false, "File data without session"); - - if (!st.filename) - return response(false, "File data without name"); - - if (!session_access(st.sessionid, "file", st.filename, "write")) - return response(false, "Access to path denied by ACL"); - - st.tempfd = open("/tmp", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR); - - if (st.tempfd < 0) - return response(false, "Failed to create temporary file"); - } - - return 0; -} - -static int -data_cb(multipart_parser *p, const char *data, size_t len) -{ - int wlen = len; - - switch (st.parttype) - { - case PART_SESSIONID: - st.sessionid = datadup(data, len); - break; - - case PART_FILENAME: - st.filename = canonicalize_path(data, len); - break; - - case PART_FILEMODE: - st.filemode = strtoul(data, NULL, 8); - break; - - case PART_FILEDATA: - if (write(st.tempfd, data, len) != wlen) - { - close(st.tempfd); - return response(false, "I/O failure while writing temporary file"); - } - - if (!st.filedata) - st.filedata = !!wlen; - - break; - - default: - break; - } - - return 0; -} - -static int -data_end_cb(multipart_parser *p) -{ - if (st.parttype == PART_SESSIONID) - { - if (!session_access(st.sessionid, "cgi-io", "upload", "write")) - { - errno = EPERM; - return response(false, "Upload permission denied"); - } - } - else if (st.parttype == PART_FILEDATA) - { - if (st.tempfd < 0) - return response(false, "Internal program failure"); - -#if 0 - /* prepare directory */ - for (ptr = st.filename; *ptr; ptr++) - { - if (*ptr == '/') - { - *ptr = 0; - - if (mkdir(st.filename, 0755)) - { - unlink(st.tmpname); - return response(false, "Failed to create destination directory"); - } - - *ptr = '/'; - } - } -#endif - - if (filecopy()) - return -1; - - return response(true, NULL); - } - - st.parttype = PART_UNKNOWN; - return 0; -} - -static multipart_parser * -init_parser(void) -{ - char *boundary; - const char *var; - - multipart_parser *p; - static multipart_parser_settings s = { - .on_part_data = data_cb, - .on_headers_complete = data_begin_cb, - .on_part_data_end = data_end_cb, - .on_header_field = header_field, - .on_header_value = header_value - }; - - var = getenv("CONTENT_TYPE"); - - if (!var || strncmp(var, "multipart/form-data;", 20)) - return NULL; - - for (var += 20; *var && *var != '='; var++); - - if (*var++ != '=') - return NULL; - - boundary = malloc(strlen(var) + 3); - - if (!boundary) - return NULL; - - strcpy(boundary, "--"); - strcpy(boundary + 2, var); - - st.tempfd = -1; - st.filefd = -1; - st.filemode = 0600; - - p = multipart_parser_init(boundary, &s); - - free(boundary); - - return p; -} - -static int -main_upload(int argc, char *argv[]) -{ - int rem, len; - bool done = false; - char buf[READ_BLOCK]; - multipart_parser *p; - - p = init_parser(); - - if (!p) - { - errno = EINVAL; - return response(false, "Invalid request"); - } - - while ((len = read(0, buf, sizeof(buf))) > 0) - { - if (!done) { - rem = multipart_parser_execute(p, buf, len); - done = (rem < len); - } - } - - multipart_parser_free(p); - - 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) -{ - char *fields[] = { "sessionid", NULL, "path", NULL, "filename", NULL, "mimetype", NULL }; - unsigned long long size = 0; - char *p, buf[READ_BLOCK]; - ssize_t len = 0; - struct stat s; - int rfd; - - autochar *post = postdecode(fields, 4); - - if (!fields[1] || !session_access(fields[1], "cgi-io", "download", "read")) - return failure(403, 0, "Download permission denied"); - - if (!fields[3] || !session_access(fields[1], "file", fields[3], "read")) - return failure(403, 0, "Access to path denied by ACL"); - - if (stat(fields[3], &s)) - return failure(404, errno, "Failed to stat requested path"); - - if (!S_ISREG(s.st_mode) && !S_ISBLK(s.st_mode)) - return failure(403, 0, "Requested path is not a regular file or block device"); - - for (p = fields[5]; p && *p; p++) - if (!isalnum(*p) && !strchr(" ()<>@,;:[]?.=%-", *p)) - return failure(400, 0, "Invalid characters in filename"); - - for (p = fields[7]; p && *p; p++) - if (!isalnum(*p) && !strchr(" .;=/-", *p)) - return failure(400, 0, "Invalid characters in mimetype"); - - rfd = open(fields[3], O_RDONLY); - - if (rfd < 0) - return failure(500, errno, "Failed to open requested path"); - - if (S_ISBLK(s.st_mode)) - ioctl(rfd, BLKGETSIZE64, &size); - else - size = (unsigned long long)s.st_size; - - printf("Status: 200 OK\r\n"); - printf("Content-Type: %s\r\n", fields[7] ? fields[7] : "application/octet-stream"); - - if (fields[5]) - printf("Content-Disposition: attachment; filename=\"%s\"\r\n", fields[5]); - - if (size > 0) { - printf("Content-Length: %llu\r\n\r\n", size); - fflush(stdout); - - 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); - - fflush(stdout); - break; - } - - if (errno == EINTR || errno == EAGAIN) - continue; - } - - if (len <= 0) - break; - - size -= len; - } - } - else { - printf("\r\n"); - - while ((len = read(rfd, buf, sizeof(buf))) > 0) - fwrite(buf, len, 1, stdout); - - fflush(stdout); - } - - close(rfd); - - return 0; -} - -static int -main_backup(int argc, char **argv) -{ - pid_t pid; - time_t now; - int r; - int len; - int status; - int fds[2]; - char datestr[16] = { 0 }; - char hostname[64] = { 0 }; - char *fields[] = { "sessionid", NULL }; - - 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)) - return failure(500, errno, "Failed to spawn pipe"); - - switch ((pid = fork())) - { - case -1: - return failure(500, errno, "Failed to fork process"); - - case 0: - dup2(fds[1], 1); - - close(0); - close(2); - close(fds[0]); - close(fds[1]); - - r = chdir("/"); - if (r < 0) - return failure(500, errno, "Failed chdir('/')"); - - execl("/sbin/sysupgrade", "/sbin/sysupgrade", - "--create-backup", "-", NULL); - - return -1; - - default: - close(fds[1]); - - now = time(NULL); - strftime(datestr, sizeof(datestr) - 1, "%Y-%m-%d", localtime(&now)); - - if (gethostname(hostname, sizeof(hostname) - 1)) - sprintf(hostname, "OpenWrt"); - - printf("Status: 200 OK\r\n"); - printf("Content-Type: application/x-targz\r\n"); - printf("Content-Disposition: attachment; " - "filename=\"backup-%s-%s.tar.gz\"\r\n\r\n", hostname, datestr); - - fflush(stdout); - - do { - len = splice(fds[0], NULL, 1, NULL, READ_BLOCK, SPLICE_F_MORE); - } while (len > 0); - - waitpid(pid, &status, 0); - - close(fds[0]); - - return 0; - } -} - - -static const char * -lookup_executable(const char *cmd) -{ - size_t plen = 0, clen = strlen(cmd) + 1; - static char path[PATH_MAX]; - char *search, *p; - struct stat s; - - if (!stat(cmd, &s) && S_ISREG(s.st_mode)) - return cmd; - - search = getenv("PATH"); - - if (!search) - search = "/bin:/usr/bin:/sbin:/usr/sbin"; - - p = search; - - do { - if (*p != ':' && *p != '\0') - continue; - - plen = p - search; - - if ((plen + clen) >= sizeof(path)) - continue; - - strncpy(path, search, plen); - sprintf(path + plen, "/%s", cmd); - - if (!stat(path, &s) && S_ISREG(s.st_mode)) - return path; - - search = p + 1; - } while (*p++); - - return NULL; -} - -static char ** -parse_command(const char *cmdline) -{ - const char *p = cmdline, *s; - char **argv = NULL, *out; - size_t arglen = 0; - int argnum = 0; - bool esc; - - while (isspace(*cmdline)) - cmdline++; - - for (p = cmdline, s = p, esc = false; p; p++) { - if (esc) { - esc = false; - } - else if (*p == '\\' && p[1] != 0) { - esc = true; - } - else if (isspace(*p) || *p == 0) { - if (p > s) { - argnum += 1; - arglen += sizeof(char *) + (p - s) + 1; - } - - s = p + 1; - } - - if (*p == 0) - break; - } - - if (arglen == 0) - return NULL; - - argv = calloc(1, arglen + sizeof(char *)); - - if (!argv) - return NULL; - - out = (char *)argv + sizeof(char *) * (argnum + 1); - argv[0] = out; - - for (p = cmdline, s = p, esc = false, argnum = 0; p; p++) { - if (esc) { - esc = false; - *out++ = *p; - } - else if (*p == '\\' && p[1] != 0) { - esc = true; - } - else if (isspace(*p) || *p == 0) { - if (p > s) { - *out++ = ' '; - argv[++argnum] = out; - } - - s = p + 1; - } - else { - *out++ = *p; - } - - if (*p == 0) - break; - } - - argv[argnum] = NULL; - out[-1] = 0; - - return argv; -} - -static int -main_exec(int argc, char **argv) -{ - char *fields[] = { "sessionid", NULL, "command", NULL, "filename", NULL, "mimetype", NULL }; - int i, devnull, status, fds[2]; - bool allowed = false; - ssize_t len = 0; - const char *exe; - char *p, **args; - pid_t pid; - - autochar *post = postdecode(fields, 4); - - if (!fields[1] || !session_access(fields[1], "cgi-io", "exec", "read")) - return failure(403, 0, "Exec permission denied"); - - for (p = fields[5]; p && *p; p++) - if (!isalnum(*p) && !strchr(" ()<>@,;:[]?.=%-", *p)) - return failure(400, 0, "Invalid characters in filename"); - - for (p = fields[7]; p && *p; p++) - if (!isalnum(*p) && !strchr(" .;=/-", *p)) - return failure(400, 0, "Invalid characters in mimetype"); - - args = fields[3] ? parse_command(fields[3]) : NULL; - - if (!args) - return failure(400, 0, "Invalid command parameter"); - - /* First check if we find an ACL match for the whole cmdline ... */ - allowed = session_access(fields[1], "file", args[0], "exec"); - - /* Now split the command vector... */ - for (i = 1; args[i]; i++) - args[i][-1] = 0; - - /* Find executable... */ - exe = lookup_executable(args[0]); - - if (!exe) { - free(args); - return failure(404, 0, "Executable not found"); - } - - /* If there was no ACL match, check for a match on the executable */ - if (!allowed && !session_access(fields[1], "file", exe, "exec")) { - free(args); - return failure(403, 0, "Access to command denied by ACL"); - } - - if (pipe(fds)) { - free(args); - return failure(500, errno, "Failed to spawn pipe"); - } - - switch ((pid = fork())) - { - case -1: - free(args); - close(fds[0]); - close(fds[1]); - return failure(500, errno, "Failed to fork process"); - - case 0: - devnull = open("/dev/null", O_RDWR); - - if (devnull > -1) { - dup2(devnull, 0); - dup2(devnull, 2); - close(devnull); - } - else { - close(0); - close(2); - } - - dup2(fds[1], 1); - close(fds[0]); - close(fds[1]); - - if (chdir("/") < 0) { - free(args); - return failure(500, errno, "Failed chdir('/')"); - } - - if (execv(exe, args) < 0) { - free(args); - return failure(500, errno, "Failed execv(...)"); - } - - return -1; - - default: - close(fds[1]); - - printf("Status: 200 OK\r\n"); - printf("Content-Type: %s\r\n", - fields[7] ? fields[7] : "application/octet-stream"); - - if (fields[5]) - printf("Content-Disposition: attachment; filename=\"%s\"\r\n", - fields[5]); - - printf("\r\n"); - fflush(stdout); - - do { - len = splice(fds[0], NULL, 1, NULL, READ_BLOCK, SPLICE_F_MORE); - } while (len > 0); - - waitpid(pid, &status, 0); - - close(fds[0]); - free(args); - - return 0; - } -} - -int main(int argc, char **argv) -{ - if (strstr(argv[0], "cgi-upload")) - return main_upload(argc, argv); - else if (strstr(argv[0], "cgi-download")) - return main_download(argc, argv); - else if (strstr(argv[0], "cgi-backup")) - return main_backup(argc, argv); - else if (strstr(argv[0], "cgi-exec")) - return main_exec(argc, argv); - - return -1; -} diff --git a/net/cgi-io/src/multipart_parser.c b/net/cgi-io/src/multipart_parser.c deleted file mode 100644 index ee82c82c8..000000000 --- a/net/cgi-io/src/multipart_parser.c +++ /dev/null @@ -1,309 +0,0 @@ -/* Based on node-formidable by Felix Geisendörfer - * Igor Afonov - afonov@gmail.com - 2012 - * MIT License - http://www.opensource.org/licenses/mit-license.php - */ - -#include "multipart_parser.h" - -#include -#include -#include - -static void multipart_log(const char * format, ...) -{ -#ifdef DEBUG_MULTIPART - va_list args; - va_start(args, format); - - fprintf(stderr, "[HTTP_MULTIPART_PARSER] %s:%d: ", __FILE__, __LINE__); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); -#endif -} - -#define NOTIFY_CB(FOR) \ -do { \ - if (p->settings->on_##FOR) { \ - if (p->settings->on_##FOR(p) != 0) { \ - return i; \ - } \ - } \ -} while (0) - -#define EMIT_DATA_CB(FOR, ptr, len) \ -do { \ - if (p->settings->on_##FOR) { \ - if (p->settings->on_##FOR(p, ptr, len) != 0) { \ - return i; \ - } \ - } \ -} while (0) - - -#define LF 10 -#define CR 13 - -struct multipart_parser { - void * data; - - size_t index; - size_t boundary_length; - - unsigned char state; - - const multipart_parser_settings* settings; - - char* lookbehind; - char multipart_boundary[1]; -}; - -enum state { - s_uninitialized = 1, - s_start, - s_start_boundary, - s_header_field_start, - s_header_field, - s_headers_almost_done, - s_header_value_start, - s_header_value, - s_header_value_almost_done, - s_part_data_start, - s_part_data, - s_part_data_almost_boundary, - s_part_data_boundary, - s_part_data_almost_end, - s_part_data_end, - s_part_data_final_hyphen, - s_end -}; - -multipart_parser* multipart_parser_init - (const char *boundary, const multipart_parser_settings* settings) { - - multipart_parser* p = malloc(sizeof(multipart_parser) + - strlen(boundary) + - strlen(boundary) + 9); - - strcpy(p->multipart_boundary, boundary); - p->boundary_length = strlen(boundary); - - p->lookbehind = (p->multipart_boundary + p->boundary_length + 1); - - p->index = 0; - p->state = s_start; - p->settings = settings; - - return p; -} - -void multipart_parser_free(multipart_parser* p) { - free(p); -} - -void multipart_parser_set_data(multipart_parser *p, void *data) { - p->data = data; -} - -void *multipart_parser_get_data(multipart_parser *p) { - return p->data; -} - -size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len) { - size_t i = 0; - size_t mark = 0; - char c, cl; - int is_last = 0; - - while(i < len) { - c = buf[i]; - is_last = (i == (len - 1)); - switch (p->state) { - case s_start: - multipart_log("s_start"); - p->index = 0; - p->state = s_start_boundary; - - /* fallthrough */ - case s_start_boundary: - multipart_log("s_start_boundary"); - if (p->index == p->boundary_length) { - if (c != CR) { - return i; - } - p->index++; - break; - } else if (p->index == (p->boundary_length + 1)) { - if (c != LF) { - return i; - } - p->index = 0; - NOTIFY_CB(part_data_begin); - p->state = s_header_field_start; - break; - } - if (c != p->multipart_boundary[p->index]) { - return i; - } - p->index++; - break; - - case s_header_field_start: - multipart_log("s_header_field_start"); - mark = i; - p->state = s_header_field; - - /* fallthrough */ - case s_header_field: - multipart_log("s_header_field"); - if (c == CR) { - p->state = s_headers_almost_done; - break; - } - - if (c == '-') { - break; - } - - if (c == ':') { - EMIT_DATA_CB(header_field, buf + mark, i - mark); - p->state = s_header_value_start; - break; - } - - cl = tolower(c); - if (cl < 'a' || cl > 'z') { - multipart_log("invalid character in header name"); - return i; - } - if (is_last) - EMIT_DATA_CB(header_field, buf + mark, (i - mark) + 1); - break; - - case s_headers_almost_done: - multipart_log("s_headers_almost_done"); - if (c != LF) { - return i; - } - - p->state = s_part_data_start; - break; - - case s_header_value_start: - multipart_log("s_header_value_start"); - if (c == ' ') { - break; - } - - mark = i; - p->state = s_header_value; - - /* fallthrough */ - case s_header_value: - multipart_log("s_header_value"); - if (c == CR) { - EMIT_DATA_CB(header_value, buf + mark, i - mark); - p->state = s_header_value_almost_done; - } - if (is_last) - EMIT_DATA_CB(header_value, buf + mark, (i - mark) + 1); - break; - - case s_header_value_almost_done: - multipart_log("s_header_value_almost_done"); - if (c != LF) { - return i; - } - p->state = s_header_field_start; - break; - - case s_part_data_start: - multipart_log("s_part_data_start"); - NOTIFY_CB(headers_complete); - mark = i; - p->state = s_part_data; - - /* fallthrough */ - case s_part_data: - multipart_log("s_part_data"); - if (c == CR) { - EMIT_DATA_CB(part_data, buf + mark, i - mark); - mark = i; - p->state = s_part_data_almost_boundary; - p->lookbehind[0] = CR; - break; - } - if (is_last) - EMIT_DATA_CB(part_data, buf + mark, (i - mark) + 1); - break; - - case s_part_data_almost_boundary: - multipart_log("s_part_data_almost_boundary"); - if (c == LF) { - p->state = s_part_data_boundary; - p->lookbehind[1] = LF; - p->index = 0; - break; - } - EMIT_DATA_CB(part_data, p->lookbehind, 1); - p->state = s_part_data; - mark = i --; - break; - - case s_part_data_boundary: - multipart_log("s_part_data_boundary"); - if (p->multipart_boundary[p->index] != c) { - EMIT_DATA_CB(part_data, p->lookbehind, 2 + p->index); - p->state = s_part_data; - mark = i --; - break; - } - p->lookbehind[2 + p->index] = c; - if ((++ p->index) == p->boundary_length) { - NOTIFY_CB(part_data_end); - p->state = s_part_data_almost_end; - } - break; - - case s_part_data_almost_end: - multipart_log("s_part_data_almost_end"); - if (c == '-') { - p->state = s_part_data_final_hyphen; - break; - } - if (c == CR) { - p->state = s_part_data_end; - break; - } - return i; - - case s_part_data_final_hyphen: - multipart_log("s_part_data_final_hyphen"); - if (c == '-') { - NOTIFY_CB(body_end); - p->state = s_end; - break; - } - return i; - - case s_part_data_end: - multipart_log("s_part_data_end"); - if (c == LF) { - p->state = s_header_field_start; - NOTIFY_CB(part_data_begin); - break; - } - return i; - - case s_end: - multipart_log("s_end: %02X", (int) c); - break; - - default: - multipart_log("Multipart parser unrecoverable error"); - return 0; - } - ++ i; - } - - return len; -} diff --git a/net/cgi-io/src/multipart_parser.h b/net/cgi-io/src/multipart_parser.h deleted file mode 100644 index 87e67f41b..000000000 --- a/net/cgi-io/src/multipart_parser.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Based on node-formidable by Felix Geisendörfer - * Igor Afonov - afonov@gmail.com - 2012 - * MIT License - http://www.opensource.org/licenses/mit-license.php - */ -#ifndef _multipart_parser_h -#define _multipart_parser_h - -#ifdef __cplusplus -extern "C" -{ -#endif - -#include -#include - -typedef struct multipart_parser multipart_parser; -typedef struct multipart_parser_settings multipart_parser_settings; -typedef struct multipart_parser_state multipart_parser_state; - -typedef int (*multipart_data_cb) (multipart_parser*, const char *at, size_t length); -typedef int (*multipart_notify_cb) (multipart_parser*); - -struct multipart_parser_settings { - multipart_data_cb on_header_field; - multipart_data_cb on_header_value; - multipart_data_cb on_part_data; - - multipart_notify_cb on_part_data_begin; - multipart_notify_cb on_headers_complete; - multipart_notify_cb on_part_data_end; - multipart_notify_cb on_body_end; -}; - -multipart_parser* multipart_parser_init - (const char *boundary, const multipart_parser_settings* settings); - -void multipart_parser_free(multipart_parser* p); - -size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len); - -void multipart_parser_set_data(multipart_parser* p, void* data); -void * multipart_parser_get_data(multipart_parser* p); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif