diff options
| author | Louis Burda <quent.burda@gmail.com> | 2021-05-29 14:24:31 +0200 |
|---|---|---|
| committer | Louis Burda <quent.burda@gmail.com> | 2021-05-29 14:24:31 +0200 |
| commit | 13b65f01132c41be9ab8d9f92c2c5ca605c366d8 (patch) | |
| tree | 74bd5b4dee779e4600d416adf4abcd4f621addab | |
| parent | 62d99253144a14648c4da1c2a60c01e7b06ef02c (diff) | |
| download | enowars5-service-stldoctor-13b65f01132c41be9ab8d9f92c2c5ca605c366d8.tar.gz enowars5-service-stldoctor-13b65f01132c41be9ab8d9f92c2c5ca605c366d8.zip | |
changed repo structure and commited releease files such that default docker-compose worklow commands work in testvm
| -rw-r--r-- | .dockerignore (renamed from service/.dockerignore) | 0 | ||||
| -rw-r--r-- | .gitignore | 4 | ||||
| -rw-r--r-- | README.md | 8 | ||||
| -rw-r--r-- | do.sh (renamed from service/do.sh) | 8 | ||||
| -rw-r--r-- | service/.gitignore | 5 | ||||
| -rw-r--r-- | service/Dockerfile (renamed from service/container/Dockerfile) | 0 | ||||
| -rw-r--r-- | service/cleaner.sh (renamed from service/container/cleaner.sh) | 0 | ||||
| -rw-r--r-- | service/container/.gitignore | 2 | ||||
| -rw-r--r-- | service/container/data/lastclean | 0 | ||||
| -rw-r--r-- | service/docker-compose.yml (renamed from service/container/docker-compose.yml) | 0 | ||||
| -rwxr-xr-x | service/entrypoint.sh (renamed from service/container/entrypoint.sh) | 0 | ||||
| -rw-r--r-- | service/src/Makefile | 1 | ||||
| -rw-r--r-- | service/src/main.c | 27 | ||||
| -rw-r--r-- | service/src/stlfile.c | 9 | ||||
| -rw-r--r-- | service/src/stlfile.h | 2 | ||||
| -rw-r--r-- | service/src/util.c | 8 | ||||
| -rw-r--r-- | service/src/util.h | 2 | ||||
| -rw-r--r-- | src/.gitignore (renamed from service/src/.gitignore) | 0 | ||||
| -rw-r--r-- | src/Makefile | 21 | ||||
| -rw-r--r-- | src/main.c | 376 | ||||
| -rw-r--r-- | src/msgs/cat_flag | 5 | ||||
| -rw-r--r-- | src/msgs/welcome | 2 | ||||
| -rw-r--r-- | src/patches/flagstore1.diff (renamed from service/src/patches/flagstore1.diff) | 0 | ||||
| -rw-r--r-- | src/patches/flagstore2.diff (renamed from service/src/patches/flagstore2.diff) | 0 | ||||
| -rw-r--r-- | src/stlfile.c | 415 | ||||
| -rw-r--r-- | src/stlfile.h | 54 | ||||
| -rw-r--r-- | src/util.c | 146 | ||||
| -rw-r--r-- | src/util.h | 38 | ||||
| -rw-r--r-- | tests/data/evil1.stl (renamed from service/tests/data/evil1.stl) | 0 | ||||
| -rw-r--r-- | tests/data/flag1.stl (renamed from service/tests/data/flag1.stl) | 0 | ||||
| -rw-r--r-- | tests/data/sample-ascii.stl (renamed from service/tests/data/sample-ascii.stl) | 0 | ||||
| -rw-r--r-- | tests/data/sample-binary.stl (renamed from service/tests/data/sample-binary.stl) | bin | 134 -> 134 bytes | |||
| -rw-r--r-- | tests/test.sh (renamed from service/tests/test.sh) | 0 |
33 files changed, 1094 insertions, 39 deletions
diff --git a/service/.dockerignore b/.dockerignore index 4baccb8..4baccb8 100644 --- a/service/.dockerignore +++ b/.dockerignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ca3e64 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +data/* +!data/.keep +.cleansrc +src/.safebuild @@ -3,3 +3,11 @@ Enowars5 STLDoctor An STL file inspection service 🔍. + +General +------- + +Prebuilt service files are avilable in `/service`, source in `/src` and +automation via `do.sh`. + +More details to service functionality in `/documentation`! @@ -7,7 +7,7 @@ makefile=" all: .cleansrc .cleansrc: src/* - bash do.sh cleansrc src container/src + bash do.sh cleansrc src service/src touch .cleansrc " @@ -16,11 +16,11 @@ alias pushd="pushd &>/dev/null" alias popd="popd &>/dev/null" if [ "$1" == "compose" ]; then - # ensure container files are up to date + # ensure built service files are up to date make --file <(echo "$makefile") # forward commands to compose - pushd container + pushd service docker-compose ${@:2} popd elif [ "$1" == "cleansrc" ]; then @@ -52,7 +52,7 @@ elif [ "$1" == "cleansrc" ]; then fi done elif [ "$1" == "test" ]; then - SRCDIR="$PWD/src" DATADIR="$PWD/container/data" bash "tests/test.sh" ${@:2} + SRCDIR="$PWD/src" DATADIR="$PWD/service/data" bash "tests/test.sh" ${@:2} elif [ "$1" == "make" ]; then # build a normal version pushd src diff --git a/service/.gitignore b/service/.gitignore index 5ca3e64..8fce603 100644 --- a/service/.gitignore +++ b/service/.gitignore @@ -1,4 +1 @@ -data/* -!data/.keep -.cleansrc -src/.safebuild +data/ diff --git a/service/container/Dockerfile b/service/Dockerfile index c932e66..c932e66 100644 --- a/service/container/Dockerfile +++ b/service/Dockerfile diff --git a/service/container/cleaner.sh b/service/cleaner.sh index bd67705..bd67705 100644 --- a/service/container/cleaner.sh +++ b/service/cleaner.sh diff --git a/service/container/.gitignore b/service/container/.gitignore deleted file mode 100644 index 8156e3e..0000000 --- a/service/container/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -src/ -data/ diff --git a/service/container/data/lastclean b/service/container/data/lastclean deleted file mode 100644 index e69de29..0000000 --- a/service/container/data/lastclean +++ /dev/null diff --git a/service/container/docker-compose.yml b/service/docker-compose.yml index 1da888f..1da888f 100644 --- a/service/container/docker-compose.yml +++ b/service/docker-compose.yml diff --git a/service/container/entrypoint.sh b/service/entrypoint.sh index b7b6509..b7b6509 100755 --- a/service/container/entrypoint.sh +++ b/service/entrypoint.sh diff --git a/service/src/Makefile b/service/src/Makefile index d7732b3..2fee8c4 100644 --- a/service/src/Makefile +++ b/service/src/Makefile @@ -1,6 +1,5 @@ CFLAGS = -g -I . -# fortify source code CFLAGS += -fPIE -fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2 LDFLAGS = -Wl,-z,now -Wl,-z,relro diff --git a/service/src/main.c b/service/src/main.c index de2bd48..d76ceb8 100644 --- a/service/src/main.c +++ b/service/src/main.c @@ -144,21 +144,21 @@ upload_cmd(const char *arg) bufp = ask("How large is your file? "); len = strtoul(bufp, &end, 10); if (len <= 0 || len >= MAXFILESIZE || *end) { - fprintf(stderr, "Invalid file length!\n"); + printf("Invalid file length!\n"); return; } printf("Ok! Im listening..\n"); contents = checkp(malloc(len + 1)); if (fread(contents, 1, len, stdin) != len) { - fprintf(stderr, "Hm, I'm missing some bytes.. try again!\n"); + printf("Hm, I'm missing some bytes.. try again!\n"); goto cleanup; } contents[len] = '\0'; if ((cached.valid = parse_file(&cached, contents, len))) { if (save_submission(&cached, contents, len) != OK) - fprintf(stderr, "Failed to save your submission!\n"); + printf("Failed to save your submission!\n"); else printf("Your file was saved with ID %s!\n", cached.hash); } @@ -180,7 +180,7 @@ search_cmd(const char *arg) if (arg && !strcmp(arg, "last")) { if (!cached.valid) { - fprintf(stderr, "No cached info report available\n"); + printf("No cached info report available\n"); return; } hash = cached.hash; @@ -201,12 +201,12 @@ search_cmd(const char *arg) } if (i == 0) { - fprintf(stderr, "Sorry, couldnt find a matching scan result!\n"); + printf("Sorry, couldnt find a matching scan result!\n"); goto cleanup; } else { which = strtoul(ask("Which of these results? "), &end, 10); if (which >= i || which < 0 || *end) { - fprintf(stderr, "Invalid index!\n"); + printf("Invalid index!\n"); goto cleanup; } } @@ -224,9 +224,8 @@ search_cmd(const char *arg) } } - /* file got cleaned up during race condition by background task */ if (!scandir) { - fprintf(stderr, "Selected result spontaneously combusted!\n"); + printf("Selected result spontaneously combusted!\n"); goto cleanup; } @@ -271,7 +270,7 @@ list_cmd(const char *arg) DIR *d; if (!loggedin) { - fprintf(stderr, "Not logged in!\n"); + printf("Not logged in!\n"); return; } @@ -283,7 +282,7 @@ list_cmd(const char *arg) path = aprintf("%s/%s/info", resultdir, de->d_name); if ((f = fopen(path, "r"))) { if (load_info(&info, f) != OK) - fprintf(stderr, "Failed to read saved file info!\n"); + printf("Failed to read saved file info!\n"); else print_info(&info); fclose(f); @@ -301,7 +300,7 @@ auth_cmd(const char *arg) int ret; if (loggedin) { - fprintf(stderr, "Already logged in!\n"); + printf("Already logged in!\n"); return; } @@ -313,7 +312,7 @@ auth_cmd(const char *arg) } else if (ret && errno == EEXIST) { printf("Success!\nWelcome back!\n"); } else { - fprintf(stderr, "Auth failed!\n"); + printf("Auth failed!\n"); return; } @@ -339,7 +338,7 @@ main() int exit, i, cmdlen; if (!(resultdir = checkp(strdup(getenv("RESULTDIR"))))) { - fprintf(stderr, "RESULTDIR not defined\n"); + printf("RESULTDIR not defined\n"); return 1; } @@ -371,6 +370,6 @@ main() } if (i == ARRSIZE(commands) && strlen(cmd) != 0) - fprintf(stderr, "No such command!\n"); + printf("No such command!\n"); } } diff --git a/service/src/stlfile.c b/service/src/stlfile.c index 88fc430..7b37df4 100644 --- a/service/src/stlfile.c +++ b/service/src/stlfile.c @@ -98,7 +98,7 @@ consume_keyword(char **start) for (i = 0; i < ARRSIZE(kwmap); i++) { len = strlen(kwmap[i].str); if (!strncmp(kwmap[i].str, bp, len) && (!bp[len] || isws(bp[len]))) { - // printf("GOT: %s\n", kwmap[i].str); + *start = bp + len + (bp[len] ? 1 : 0); return kwmap[i].code; } @@ -108,7 +108,7 @@ consume_keyword(char **start) } #define PARSE_FAIL(...) \ - do { fprintf(stderr, "FORMAT ERR: " __VA_ARGS__); goto fail; } while (0) + do { printf("FORMAT ERR: " __VA_ARGS__); goto fail; } while (0) int parse_file_ascii(struct parseinfo *info, char *buf, size_t len) @@ -272,13 +272,12 @@ parse_file(struct parseinfo *info, char *buf, size_t len) if (info->valid) free_info(info); if (len < 7) { - fprintf(stderr, "File too small!\n"); + printf("File too small!\n"); return FAIL; } info->filesize = len; - /* check bin vs ascii with first keyword */ for (bp = buf; isws(*bp); bp++); status = !strncmp("solid", bp, 5) && isws(bp[5]) ? parse_file_ascii(info, buf, len) @@ -290,7 +289,7 @@ parse_file(struct parseinfo *info, char *buf, size_t len) if (!info->modelname) { resp = ask("Please enter your model name: "); if (strlen(resp) < 4) { - fprintf(stderr, "Model name is too short!\n"); + printf("Model name is too short!\n"); return FAIL; } info->modelname = checkp(strdup(resp)); diff --git a/service/src/stlfile.h b/service/src/stlfile.h index d321282..11b7f66 100644 --- a/service/src/stlfile.h +++ b/service/src/stlfile.h @@ -51,4 +51,4 @@ int load_info(struct parseinfo *info, FILE *f); void print_info(struct parseinfo *info); void free_info(struct parseinfo *info); -#endif /* STLFILE_H */ +#endif diff --git a/service/src/util.c b/service/src/util.c index ce22c4e..140d08b 100644 --- a/service/src/util.c +++ b/service/src/util.c @@ -13,7 +13,7 @@ die(const char *fmtstr, ...) va_list ap; va_start(ap, fmtstr); - vfprintf(stderr, fmtstr, ap); + vprintf(fmtstr, ap); va_end(ap); exit(EXIT_FAILURE); @@ -49,9 +49,6 @@ mhash(const char *str, int len) int i, k, v; char c, *bp; - /* VULN #2: BUFFER OVERFLOW */ - /* see documentation/README.md for more details */ - if (len == -1) len = strlen(str); for (v = 0, i = 0; i < len; i++) v += str[i]; @@ -80,9 +77,6 @@ freadstr(FILE *f, char **dst) size_t start, len, tmp; char c; - /* VULN #1: BAD CAST */ - /* see documentation/README.md for more details */ - start = ftell(f); for (len = 0; (c = fgetc(f)) != EOF && c; len++); fseek(f, start, SEEK_SET); diff --git a/service/src/util.h b/service/src/util.h index c0e9064..7b6eed0 100644 --- a/service/src/util.h +++ b/service/src/util.h @@ -35,4 +35,4 @@ float fle32toh(float v); extern int echo; -#endif /* UTIL_H */ +#endif diff --git a/service/src/.gitignore b/src/.gitignore index 5f14e4d..5f14e4d 100644 --- a/service/src/.gitignore +++ b/src/.gitignore diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..d7732b3 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,21 @@ +CFLAGS = -g -I . + +# fortify source code +CFLAGS += -fPIE -fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2 +LDFLAGS = -Wl,-z,now -Wl,-z,relro + +.PHONY: all clean + +all: build/stldoctor + +clean: + rm -rf build + +build: + mkdir build + +build/%.o: %.c %.h | build + $(CC) -c -o $@ $< $(CFLAGS) $(LDLIBS) + +build/stldoctor: build/$(PREFIX)stlfile.o build/$(PREFIX)util.o $(PREFIX)main.c | build + $(CC) -o $@ $^ $(CFLAGS) $(LDLIBS) diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..de2bd48 --- /dev/null +++ b/src/main.c @@ -0,0 +1,376 @@ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <unistd.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <time.h> +#include <errno.h> + +#include "stlfile.h" +#include "util.h" + +#define MAXFILESIZE 7000 + +struct command { + const char *name; + void (*func)(const char *); + const char *desc; +}; + +int save_submission(struct parseinfo *info, char *data, int len); + +void cat_cmd(const char *arg); +void help_cmd(const char *arg); +void exit_cmd(const char *arg); +void echo_cmd(const char *arg); +void upload_cmd(const char *arg); +void search_cmd(const char *arg); +void list_cmd(const char *arg); +void auth_cmd(const char *arg); + +void cleanexit(); + +struct command commands[] = { + { "cat", cat_cmd, "Cat cmd go prrrrr." }, + { "help", help_cmd, "You already know what this does." }, + { "exit", exit_cmd, "Closes the session." }, + { "echo", echo_cmd, "Repeat after me!" }, + { "upload", upload_cmd, "Upload an STL file to analyze." }, + { "search", search_cmd, "Search for an STL file by model name." }, + { "list", list_cmd, "List your uploaded files." }, + { "auth", auth_cmd, "Login to upload files to a private dir." } +}; + +struct parseinfo cached; +char *resultdir; +int echo = 0, loggedin = 0; + +int +save_submission(struct parseinfo *info, char *stldata, int stlsize) +{ + DIR *d; + FILE *f = NULL; + char *dirpath = NULL, *infopath = NULL, *modelpath = NULL; + + if (loggedin) + dirpath = aprintf("%s/.%s-%i", resultdir, info->hash, time(NULL)); + else + dirpath = aprintf("%s/%s-%i", resultdir, info->hash, time(NULL)); + if (mkdir(dirpath, S_IRWXU | S_IRWXG | S_IRWXO)) goto fail; + + modelpath = aprintf("%s/%s", dirpath, "model"); + if (!(f = fopen(modelpath, "w+"))) goto fail; + if (fwrite(stldata, 1, stlsize, f) != stlsize) goto fail; + fclose(f); + f = NULL; + + infopath = aprintf("%s/%s", dirpath, "info"); + if (!(f = fopen(infopath, "w+"))) goto fail; + if (save_info(info, f) != OK) goto fail; + fclose(f); + f = NULL; + + free(dirpath); + free(modelpath); + free(infopath); + + return OK; + +fail: + if (f) fclose(f); + + if (infopath) remove(infopath); + if (modelpath) remove(modelpath); + if (dirpath) remove(dirpath); + + free(dirpath); + free(modelpath); + free(infopath); + + return FAIL; +} + +void +cat_cmd(const char *arg) +{ + if (arg && !strncmp(arg, "flag", 4)) + dump("msgs/cat_flag"); + else + printf("meow\n"); +} + +void +help_cmd(const char *arg) +{ + int i; + + if (arg) { + for (i = 0; i < ARRSIZE(commands); i++) { + if (!strcmp(commands[i].name, arg)) { + printf("%s\n", commands[i].desc); + return; + } + } + } + + printf("Available commands:\n"); + for (i = 0; i < ARRSIZE(commands); i++) + printf("%s%s", i ? " " : "", commands[i].name); + printf("\n"); +} + +void +exit_cmd(const char *arg) +{ + exit(0); +} + +void +echo_cmd(const char *arg) +{ + echo ^= 1; + printf("Echo is %s\n", echo ? "enabled" : "disabled"); +} + +void +upload_cmd(const char *arg) +{ + const char *bufp; + char *end, *contents; + size_t len; + + bufp = ask("How large is your file? "); + len = strtoul(bufp, &end, 10); + if (len <= 0 || len >= MAXFILESIZE || *end) { + fprintf(stderr, "Invalid file length!\n"); + return; + } + + printf("Ok! Im listening..\n"); + contents = checkp(malloc(len + 1)); + if (fread(contents, 1, len, stdin) != len) { + fprintf(stderr, "Hm, I'm missing some bytes.. try again!\n"); + goto cleanup; + } + contents[len] = '\0'; + + if ((cached.valid = parse_file(&cached, contents, len))) { + if (save_submission(&cached, contents, len) != OK) + fprintf(stderr, "Failed to save your submission!\n"); + else + printf("Your file was saved with ID %s!\n", cached.hash); + } + +cleanup: + free(contents); +} + +void +search_cmd(const char *arg) +{ + char *end, *scandir = NULL, *infopath = NULL, *modelpath = NULL; + int i, which, dirstart, ishidden; + const char *hash, *name; + struct dirent *de; + DIR *d = NULL; + FILE *f = NULL; + size_t size; + + if (arg && !strcmp(arg, "last")) { + if (!cached.valid) { + fprintf(stderr, "No cached info report available\n"); + return; + } + hash = cached.hash; + } else { + hash = mhash(arg ? arg : ask("Model name: "), -1); + } + + if (!(d = opendir(resultdir))) return; + + dirstart = telldir(d); + for (i = 0; (de = readdir(d));) { + name = de->d_name; + if (loggedin && *name == '.' && !strpfcmp(hash, name + 1) + || !loggedin && *name != '.' && !strpfcmp(hash, name)) { + printf("%i : %s\n", i, de->d_name); + i++; + } + } + + if (i == 0) { + fprintf(stderr, "Sorry, couldnt find a matching scan result!\n"); + goto cleanup; + } else { + which = strtoul(ask("Which of these results? "), &end, 10); + if (which >= i || which < 0 || *end) { + fprintf(stderr, "Invalid index!\n"); + goto cleanup; + } + } + + seekdir(d, dirstart); + for (i = 0; (de = readdir(d));) { + name = de->d_name; + if (loggedin && *name == '.' && !strpfcmp(hash, name + 1) + || !loggedin && *name != '.' && !strpfcmp(hash, name)) { + if (i == which) { + scandir = aprintf("%s/%s", resultdir, de->d_name); + break; + } + i++; + } + } + + /* file got cleaned up during race condition by background task */ + if (!scandir) { + fprintf(stderr, "Selected result spontaneously combusted!\n"); + goto cleanup; + } + + infopath = aprintf("%s/%s", scandir, "info"); + if (!(f = fopen(infopath, "r"))) goto cleanup; + free_info(&cached); + if (load_info(&cached, f) != OK) goto cleanup; + fclose(f); + f = NULL; + + print_info(&cached); + + if (strchr(ask("Download the model? "), 'y')) { + modelpath = aprintf("%s/%s", scandir, "model"); + if (!(f = fopen(modelpath, "r"))) goto cleanup; + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + if (size > MAXFILESIZE) goto cleanup; + printf("Here you go.. (%liB)\n", size); + while ((i = getc(f)) != EOF) + putc(i, stdout); + fclose(f); + f = NULL; + } + +cleanup: + if (f) fclose(f); + closedir(d); + free(scandir); + free(infopath); + free(modelpath); +} + +void +list_cmd(const char *arg) +{ + struct dirent *de; + struct parseinfo info; + char *path; + FILE *f; + DIR *d; + + if (!loggedin) { + fprintf(stderr, "Not logged in!\n"); + return; + } + + if (!(d = opendir(resultdir))) return; + + while ((de = readdir(d))) { + if (*de->d_name == '.' && !strchr(".", de->d_name[1])) { + printf(">> %s\n", de->d_name); + path = aprintf("%s/%s/info", resultdir, de->d_name); + if ((f = fopen(path, "r"))) { + if (load_info(&info, f) != OK) + fprintf(stderr, "Failed to read saved file info!\n"); + else + print_info(&info); + fclose(f); + } + free(path); + } + } +} + +void +auth_cmd(const char *arg) +{ + const char *hash; + char *ndir; + int ret; + + if (loggedin) { + fprintf(stderr, "Already logged in!\n"); + return; + } + + hash = mhash(arg ? arg : ask("Enter a password: "), -1); + ndir = aprintf("%s/.%s", resultdir, hash); + ret = mkdir(ndir, S_IRWXU | S_IRWXG | S_IRWXO); + if (!ret) { + printf("Success!\n"); + } else if (ret && errno == EEXIST) { + printf("Success!\nWelcome back!\n"); + } else { + fprintf(stderr, "Auth failed!\n"); + return; + } + + free(resultdir); + resultdir = ndir; + loggedin = 1; + cached.valid = 0; +} + +void +cleanexit() +{ + printf("see you later!\n"); + free_info(&cached); + free(resultdir); +} + +int +main() +{ + const char *cmd; + char *cp, *arg; + int exit, i, cmdlen; + + if (!(resultdir = checkp(strdup(getenv("RESULTDIR"))))) { + fprintf(stderr, "RESULTDIR not defined\n"); + return 1; + } + + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + atexit(cleanexit); + + dump("msgs/welcome"); + + exit = 0; + while (!exit) { + errno = 0; + cmd = ask("$ "); + if (!*cmd && errno == EBADMSG) break; + if (!*cmd) continue; + + cp = strchr(cmd, ' '); + arg = cp ? cp + 1 : NULL; + cmdlen = cp ? cp - cmd : strlen(cmd); + + for (i = 0; i < ARRSIZE(commands); i++) { + if (!strncmp(commands[i].name, cmd, cmdlen) + && cmdlen == strlen(commands[i].name)) { + commands[i].func(arg); + break; + } + } + + if (i == ARRSIZE(commands) && strlen(cmd) != 0) + fprintf(stderr, "No such command!\n"); + } +} diff --git a/src/msgs/cat_flag b/src/msgs/cat_flag new file mode 100644 index 0000000..d29ceea --- /dev/null +++ b/src/msgs/cat_flag @@ -0,0 +1,5 @@ + + /\_/\ [ENO] _ + = u u =_______| \\ + _ w __( \__)) + c_____>__(_____)__, diff --git a/src/msgs/welcome b/src/msgs/welcome new file mode 100644 index 0000000..2f77bfb --- /dev/null +++ b/src/msgs/welcome @@ -0,0 +1,2 @@ +Welcome to STLDoctor! +Submit a stl file and we'll analyze it! diff --git a/service/src/patches/flagstore1.diff b/src/patches/flagstore1.diff index f0f8d4a..f0f8d4a 100644 --- a/service/src/patches/flagstore1.diff +++ b/src/patches/flagstore1.diff diff --git a/service/src/patches/flagstore2.diff b/src/patches/flagstore2.diff index b34a0c0..b34a0c0 100644 --- a/service/src/patches/flagstore2.diff +++ b/src/patches/flagstore2.diff diff --git a/src/stlfile.c b/src/stlfile.c new file mode 100644 index 0000000..88fc430 --- /dev/null +++ b/src/stlfile.c @@ -0,0 +1,415 @@ +#include "stlfile.h" + +static const char wsset[] = " \r\n\t"; +static const struct { + int code; + const char *str; +} kwmap[] = { + { KW_SOLID_BEGIN, "solid" }, + { KW_SOLID_END, "endsolid" }, + { KW_LOOP_BEGIN, "outer loop" }, + { KW_LOOP_END, "endloop" }, + { KW_FACET_BEGIN, "facet normal" }, + { KW_FACET_END, "endfacet" }, + { KW_VERTEX, "vertex" }, +}; + +void +stack_init(struct stack *stack) +{ + stack->cap = 10; + stack->data = checkp(malloc(sizeof(int) * stack->cap)); + stack->count = 0; +} + +void +stack_push(struct stack *stack, int v) +{ + if (stack->count == stack->cap) { + stack->cap *= 2; + stack->data = checkp(realloc(stack->data, sizeof(int) * stack->cap)); + } + + stack->data[stack->count] = v; + stack->count++; +} + +int +stack_pop(struct stack *stack) +{ + if (stack->count == 0) + return -1; + + stack->count--; + return stack->data[stack->count]; +} + +void +stack_free(struct stack *stack) +{ + free(stack->data); +} + +int +stack_ind(struct stack *stack, int search) +{ + int i; + for (i = 0; i < stack->count; i++) + if (stack->data[i] == search) + return i; + return -1; +} + +int +isws(char c) +{ + return c && strchr(wsset, c); +} + +char* +skipws(char *p) +{ + for (; isws(*p); p++); + return p; +} + +char* +consume_arg(char **start, char **end) +{ + char *c, *tmp; + + *start = skipws(*start); + if (!*start) return NULL; + for (c = *start; *c && !isws(*c); c++); + tmp = *start; + *start = c + 1; + *end = c; + return tmp; +} + +int +consume_keyword(char **start) +{ + char *bp; + int i, len; + + bp = skipws(*start); + + for (i = 0; i < ARRSIZE(kwmap); i++) { + len = strlen(kwmap[i].str); + if (!strncmp(kwmap[i].str, bp, len) && (!bp[len] || isws(bp[len]))) { + // printf("GOT: %s\n", kwmap[i].str); + *start = bp + len + (bp[len] ? 1 : 0); + return kwmap[i].code; + } + } + + return KW_UNKNOWN; +} + +#define PARSE_FAIL(...) \ + do { fprintf(stderr, "FORMAT ERR: " __VA_ARGS__); goto fail; } while (0) + +int +parse_file_ascii(struct parseinfo *info, char *buf, size_t len) +{ + char *bp, *arg, *prev, *tmp, *end; + struct stack states; + float farg; + int i, kw; + + stack_init(&states); + + info->type = TYPE_ASCII; + info->loopcount = 0; + + memset(info->header, 0, 80); + + for (i = 0; i < 3; i++) { + info->bbmin[i] = INFINITY; + info->bbmax[i] = -INFINITY; + } + + bp = prev = buf; + while ((kw = consume_keyword(&bp))) { + switch (kw) { + case KW_SOLID_BEGIN: + stack_push(&states, STATE_SOLID); + if (stack_ind(&states, KW_SOLID_BEGIN) != -1) + PARSE_FAIL("Multiple nested solids!\n"); + tmp = bp; + if (!consume_keyword(&bp) && (arg = consume_arg(&bp, &end))) { + info->solidname = strndup(arg, end - arg); + } else { + bp = tmp; + } + break; + case KW_SOLID_END: + if ((kw = stack_pop(&states)) != STATE_SOLID) + PARSE_FAIL("Improper nesting, parent: %s\n", kwmap[kw].str); + tmp = bp; + if (info->solidname && !consume_keyword(&bp) + && (arg = consume_arg(&bp, &end))) { + if (strncmp(info->solidname, arg, end - arg)) + PARSE_FAIL("Solid end/begin names do not match!\n"); + } else { + bp = tmp; + } + break; + case KW_LOOP_BEGIN: + stack_push(&states, STATE_LOOP); + if (stack_ind(&states, KW_LOOP_BEGIN) != -1) + PARSE_FAIL("Multiple nested loops!\n"); + break; + case KW_LOOP_END: + if ((kw = stack_pop(&states)) != STATE_LOOP) + PARSE_FAIL("Improper nesting, parent: %s\n", kwmap[kw].str); + info->loopcount++; + break; + case KW_FACET_BEGIN: + stack_push(&states, STATE_FACET); + if (stack_ind(&states, KW_LOOP_BEGIN) != -1) + PARSE_FAIL("Multiple nested facets!\n"); + for (i = 0; i < 3; i++) { + if (!(arg = consume_arg(&bp, &end))) + PARSE_FAIL("Facet with less than 3 args!\n"); + farg = strtof(arg, &tmp); + if (!isws(*tmp)) + PARSE_FAIL("Facet with invalid arg '%s'!\n", arg); + } + break; + case KW_FACET_END: + if ((kw = stack_pop(&states)) != STATE_FACET) + PARSE_FAIL("Improper nesting, parent: %s\n", kwmap[kw].str); + break; + case KW_VERTEX: + for (i = 0; i < 3; i++) { + if (!(arg = consume_arg(&bp, &end))) + PARSE_FAIL("Vertex with less than 3 args!\n"); + farg = strtof(arg, &tmp); + if (!isws(*tmp)) + PARSE_FAIL("Vertex with invalid arg '%s'!\n", arg); + info->bbmin[i] = MIN(info->bbmin[i], farg); + info->bbmax[i] = MAX(info->bbmax[i], farg); + } + break; + case KW_UNKNOWN: + prev = skipws(prev); + PARSE_FAIL("Expected keyword, got:\n%.*s...\n", 30, prev); + } + prev = bp; + } + + if (states.count) + PARSE_FAIL("Expected keyword, got:\n%.*s...\n", 30, bp); + + stack_free(&states); + return OK; + +fail: + stack_free(&states); + return FAIL; +} + +int +parse_file_bin(struct parseinfo *info, char *buf, size_t len) +{ + char *bp, *end = buf + len; + int i, k, m; + float v; + + info->type = TYPE_BIN; + + if (len < 84) + PARSE_FAIL("Truncated data! (header missing)\n"); + + memcpy(info->header, buf, 80); + + if (strlen(buf + 1)) + info->solidname = checkp(strdup(buf + 1)); + + bp = buf + 80; + info->loopcount = le32toh(*(uint32_t*)bp); + + if (!info->loopcount) { + memset(info->bbmax, 0, sizeof(float) * 3); + memset(info->bbmin, 0, sizeof(float) * 3); + return OK; + } + + for (i = 0; i < 3; i++) { + info->bbmin[i] = INFINITY; + info->bbmax[i] = -INFINITY; + } + + for (i = 0; i < info->loopcount; i++) { + if (bp + 50 > end) + PARSE_FAIL("Truncated data! (loops missing)\n"); + bp += 12; + for (k = 0; k < 3; k++, bp += 12) { + for (m = 0; m < 3; m++) { + v = fle32toh(*(float*)(bp + 4 * m)); + info->bbmin[m] = MIN(info->bbmin[m], v); + info->bbmax[m] = MAX(info->bbmax[m], v); + } + } + bp += 2; + } + + return OK; + +fail: + return FAIL; +} + +int +parse_file(struct parseinfo *info, char *buf, size_t len) +{ + int status; + const char *resp; + char *bp; + + if (info->valid) free_info(info); + + if (len < 7) { + fprintf(stderr, "File too small!\n"); + return FAIL; + } + + info->filesize = len; + + /* check bin vs ascii with first keyword */ + for (bp = buf; isws(*bp); bp++); + status = !strncmp("solid", bp, 5) && isws(bp[5]) + ? parse_file_ascii(info, buf, len) + : parse_file_bin(info, buf, len); + if (status == FAIL) return FAIL; + + if (!info->solidname) info->solidname = checkp(strdup("")); + + if (!info->modelname) { + resp = ask("Please enter your model name: "); + if (strlen(resp) < 4) { + fprintf(stderr, "Model name is too short!\n"); + return FAIL; + } + info->modelname = checkp(strdup(resp)); + } + + info->hash = checkp(strdup(mhash(info->modelname, -1))); + + return OK; +} + +int +save_info(struct parseinfo *info, FILE *f) +{ + size_t nwrote = 0; + int i; + + nwrote += fwrite(&info->type, sizeof(int), 1, f); + nwrote += fwrite(&info->loopcount, sizeof(int), 1, f); + nwrote += fwrite(&info->filesize, sizeof(unsigned), 1, f); + + for (i = 0; i < 3; i++) { + nwrote += fwrite(&info->bbmin[i], sizeof(float), 1, f); + nwrote += fwrite(&info->bbmax[i], sizeof(float), 1, f); + } + + nwrote += fwrite(info->header, 80, 1, f); + + if (nwrote != 10) return FAIL; + + fputstr(f, info->solidname); + fputstr(f, info->hash); + fputstr(f, info->modelname); + + return OK; +} + +int +load_info(struct parseinfo *info, FILE *f) +{ + size_t nread = 0; + int i; + + nread += fread(&info->type, sizeof(int), 1, f); + nread += fread(&info->loopcount, sizeof(int), 1, f); + nread += fread(&info->filesize, sizeof(unsigned), 1, f); + + for (i = 0; i < 3; i++) { + nread += fread(&info->bbmin[i], sizeof(float), 1, f); + nread += fread(&info->bbmax[i], sizeof(float), 1, f); + } + + nread += fread(info->header, 80, 1, f); + + if (nread != 10) return FAIL; + + freadstr(f, &info->solidname); + freadstr(f, &info->hash); + freadstr(f, &info->modelname); + + info->valid = 1; + + return OK; +} + +void +print_info(struct parseinfo *info) +{ + int i, k; + +#define FILTERCHAR(c) ((c) >= 32 ? (c) : ' ') + + printf(" === Model info === \n"); + + printf(" File Size: %u\n", info->filesize); + + if (info->type == TYPE_BIN) { + printf(" Header:\n"); + for (i = 0; i < 80; i += k) { + printf(" "); + for (k = 0; k < MIN(80 - i, 20); k++) + printf(" %02x", (uint8_t) info->header[i+k]); + printf(" | "); + for (k = 0; k < MIN(80 - i, 20); k++) + printf("%c", FILTERCHAR(info->header[i+k])); + printf("\n"); + } + } + + printf(" Model ID: %s\n", info->hash); + printf(" Model Name: %s\n", info->modelname); + printf(" Solid Name: %s\n", info->solidname); + printf(" Triangle Count: %i\n", info->loopcount); + printf(" Bounding Box Size: %.2f x %.2f x %.2f\n", + info->bbmax[0] - info->bbmin[0], + info->bbmax[1] - info->bbmin[1], + info->bbmax[2] - info->bbmin[2]); + printf(" Bounding Box Origin: %.2f x %.2f x %.2f\n", + info->bbmin[0], info->bbmin[1], info->bbmin[2]); + + printf(" ================== \n"); +} + +void +free_info(struct parseinfo *info) +{ + NULLFREE(info->hash); + NULLFREE(info->modelname); + NULLFREE(info->solidname); + info->valid = 0; +} + +float fle32toh(float v) +{ + union { + uint32_t u; + float f; + } conv; + + conv.f = v; + conv.u = le32toh(conv.u); + return conv.f; +} diff --git a/src/stlfile.h b/src/stlfile.h new file mode 100644 index 0000000..d321282 --- /dev/null +++ b/src/stlfile.h @@ -0,0 +1,54 @@ +#ifndef STLFILE_H +#define STLFILE_H + +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <endian.h> +#include <math.h> + +#include "util.h" + +enum { + KW_INVALID = -1, + KW_UNKNOWN, + KW_SOLID_BEGIN, + KW_SOLID_END, + KW_FACET_BEGIN, + KW_FACET_END, + KW_LOOP_BEGIN, + KW_LOOP_END, + KW_VERTEX +}; + +enum { + STATE_SOLID, + STATE_FACET, + STATE_LOOP +}; + +enum { + TYPE_ASCII, + TYPE_BIN +}; + +struct stack { + int *data; + size_t count, cap; +}; + +struct parseinfo { + char header[80], *hash, *modelname, *solidname; + uint32_t loopcount; + unsigned filesize; + float bbmin[3], bbmax[3]; + int type, valid; +}; + +int parse_file(struct parseinfo *info, char *buf, size_t len); +int save_info(struct parseinfo *info, FILE *f); +int load_info(struct parseinfo *info, FILE *f); +void print_info(struct parseinfo *info); +void free_info(struct parseinfo *info); + +#endif /* STLFILE_H */ diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..ce22c4e --- /dev/null +++ b/src/util.c @@ -0,0 +1,146 @@ +#include "util.h" + +void* +checkp(void *p) +{ + if (!p) die("pointer assertion failed, OOM?\n"); + return p; +} + +void* +die(const char *fmtstr, ...) +{ + va_list ap; + + va_start(ap, fmtstr); + vfprintf(stderr, fmtstr, ap); + va_end(ap); + + exit(EXIT_FAILURE); +} + +char* +aprintf(const char *fmtstr, ...) +{ + va_list ap, cpy; + size_t nb; + char *str; + + va_copy(cpy, ap); + + va_start(cpy, fmtstr); + nb = vsnprintf(NULL, 0, fmtstr, cpy); + va_end(cpy); + + if (nb <= 0) die("Invalid fmtstr!\n"); + str = checkp(malloc(nb+1)); + + va_start(ap, fmtstr); + nb = vsnprintf(str, nb+1, fmtstr, ap); + va_end(ap); + + return str; +} + +const char* +mhash(const char *str, int len) +{ + static char buf[MHASHLEN + 1]; + int i, k, v; + char c, *bp; + + /* VULN #2: BUFFER OVERFLOW */ + /* see documentation/README.md for more details */ + + if (len == -1) len = strlen(str); + + for (v = 0, i = 0; i < len; i++) v += str[i]; + + srand(v); + for (bp = buf, i = 0; i < MHASHLEN / 2; i++) + bp += sprintf(bp, "%02x", str[i % len] ^ (rand() % 256)); + + return buf; +} + +int +checkalph(const char *str, const char *alph) +{ + int i; + + for (i = 0; i < strlen(str); i++) + if (str[i] && !strchr(alph, str[i])) return 0; + + return 1; +} + +void +freadstr(FILE *f, char **dst) +{ + size_t start, len, tmp; + char c; + + /* VULN #1: BAD CAST */ + /* see documentation/README.md for more details */ + + start = ftell(f); + for (len = 0; (c = fgetc(f)) != EOF && c; len++); + fseek(f, start, SEEK_SET); + + *dst = checkp(calloc(1, len + 1)); + tmp = fread(*dst, len, 1, f); + fgetc(f); +} + +void +fputstr(FILE *f, char *s) +{ + fprintf(f, "%s", s); + fputc(0, f); +} + +const char* +ask(const char *fmtstr, ...) +{ + static char linebuf[256]; + va_list ap; + int fail; + + va_start(ap, fmtstr); + vprintf(fmtstr, ap); + va_end(ap); + + fail = !fgets(linebuf, sizeof(linebuf), stdin); + + if (!fail && *linebuf) { + if (linebuf[strlen(linebuf)-1] == '\n') + linebuf[strlen(linebuf)-1] = '\0'; + if (echo) printf("%s\n", linebuf); + } + + if (fail) errno = EBADMSG; + + return fail ? "" : linebuf; +} + +void +dump(const char *filename) +{ + char buf[256]; + FILE *f; + int nb; + + if (!(f = fopen(filename, "r"))) return; + + while ((nb = fread(buf, 1, sizeof(buf) - 1, f))) + printf("%.*s\n", nb, buf); + + fclose(f); +} + +int +strpfcmp(const char *prefix, const char *str) +{ + return strncmp(prefix, str, strlen(prefix)); +} + diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..c0e9064 --- /dev/null +++ b/src/util.h @@ -0,0 +1,38 @@ +#ifndef UTIL_H +#define UTIL_H + +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#define ARRSIZE(x) (sizeof(x)/sizeof((x)[0])) +#define MIN(x,y) ((x) > (y) ? (y) : (x)) +#define MAX(x,y) ((x) < (y) ? (y) : (x)) + +#define NULLFREE(p) do { free(p); p = NULL; } while (0) + +#define MHASHLEN 40 + +enum { FAIL = 0, OK = 1 }; + +void* checkp(void *p); +void* die(const char *fmtstr, ...); +char* aprintf(const char *fmtstr, ...); + +const char* mhash(const char *filename, int len); +int checkalph(const char *str, const char *alph); + +void freadstr(FILE *f, char **dst); +void fputstr(FILE *f, char *s); + +const char* ask(const char *fmtstr, ...); +void dump(const char *filepath); +int strpfcmp(const char *prefix, const char *str); + +float fle32toh(float v); + +extern int echo; + +#endif /* UTIL_H */ diff --git a/service/tests/data/evil1.stl b/tests/data/evil1.stl index 706e9e2..706e9e2 100644 --- a/service/tests/data/evil1.stl +++ b/tests/data/evil1.stl diff --git a/service/tests/data/flag1.stl b/tests/data/flag1.stl index f2a3854..f2a3854 100644 --- a/service/tests/data/flag1.stl +++ b/tests/data/flag1.stl diff --git a/service/tests/data/sample-ascii.stl b/tests/data/sample-ascii.stl index e3c89ef..e3c89ef 100644 --- a/service/tests/data/sample-ascii.stl +++ b/tests/data/sample-ascii.stl diff --git a/service/tests/data/sample-binary.stl b/tests/data/sample-binary.stl Binary files differindex 13c02e4..13c02e4 100644 --- a/service/tests/data/sample-binary.stl +++ b/tests/data/sample-binary.stl diff --git a/service/tests/test.sh b/tests/test.sh index 4835024..4835024 100644 --- a/service/tests/test.sh +++ b/tests/test.sh |
