aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLouis Burda <quent.burda@gmail.com>2021-05-29 14:24:31 +0200
committerLouis Burda <quent.burda@gmail.com>2021-05-29 14:24:31 +0200
commit13b65f01132c41be9ab8d9f92c2c5ca605c366d8 (patch)
tree74bd5b4dee779e4600d416adf4abcd4f621addab
parent62d99253144a14648c4da1c2a60c01e7b06ef02c (diff)
downloadenowars5-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--.gitignore4
-rw-r--r--README.md8
-rw-r--r--do.sh (renamed from service/do.sh)8
-rw-r--r--service/.gitignore5
-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/.gitignore2
-rw-r--r--service/container/data/lastclean0
-rw-r--r--service/docker-compose.yml (renamed from service/container/docker-compose.yml)0
-rwxr-xr-xservice/entrypoint.sh (renamed from service/container/entrypoint.sh)0
-rw-r--r--service/src/Makefile1
-rw-r--r--service/src/main.c27
-rw-r--r--service/src/stlfile.c9
-rw-r--r--service/src/stlfile.h2
-rw-r--r--service/src/util.c8
-rw-r--r--service/src/util.h2
-rw-r--r--src/.gitignore (renamed from service/src/.gitignore)0
-rw-r--r--src/Makefile21
-rw-r--r--src/main.c376
-rw-r--r--src/msgs/cat_flag5
-rw-r--r--src/msgs/welcome2
-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.c415
-rw-r--r--src/stlfile.h54
-rw-r--r--src/util.c146
-rw-r--r--src/util.h38
-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)bin134 -> 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
diff --git a/README.md b/README.md
index b201949..64aa7c4 100644
--- a/README.md
+++ b/README.md
@@ -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`!
diff --git a/service/do.sh b/do.sh
index d0ac5ed..e01873c 100644
--- a/service/do.sh
+++ b/do.sh
@@ -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
index 13c02e4..13c02e4 100644
--- a/service/tests/data/sample-binary.stl
+++ b/tests/data/sample-binary.stl
Binary files differ
diff --git a/service/tests/test.sh b/tests/test.sh
index 4835024..4835024 100644
--- a/service/tests/test.sh
+++ b/tests/test.sh