From 2a8416eaa85af6348fe34859859a7fb39db2003d Mon Sep 17 00:00:00 2001 From: Louis Burda Date: Mon, 10 May 2021 15:05:25 +0200 Subject: save scan info and model, added vuln in load info, small tweaks create result directory with serialized info struct and model file after successful scan, dont modify the stl file contents during parsing, EOF getc vuln to truncate loaded hash added in load_info --- service/src/.gitignore | 1 + service/src/msgs/welcome | 2 +- service/src/printdoc.c | 91 +++++++++++++++++++------------------ service/src/stlfile.c | 108 ++++++++++++++++++++++++++++++++------------ service/src/stlfile.h | 5 +- service/src/test.sh | 11 +++++ service/src/tests/evil1.stl | 9 ++++ service/src/util.c | 66 +++++++++++++++++++++++++-- service/src/util.h | 8 ++++ 9 files changed, 220 insertions(+), 81 deletions(-) create mode 100644 service/src/tests/evil1.stl (limited to 'service') diff --git a/service/src/.gitignore b/service/src/.gitignore index e97b31a..a765305 100644 --- a/service/src/.gitignore +++ b/service/src/.gitignore @@ -1,3 +1,4 @@ printdoc +scans *.o vgcore.* diff --git a/service/src/msgs/welcome b/service/src/msgs/welcome index c470659..b4eb256 100644 --- a/service/src/msgs/welcome +++ b/service/src/msgs/welcome @@ -1,2 +1,2 @@ Welcome to PrintDoc! -Upload an stl file and we'll analyze it! +Submit a stl file and we'll analyze it! diff --git a/service/src/printdoc.c b/service/src/printdoc.c index d7a4131..951f40b 100644 --- a/service/src/printdoc.c +++ b/service/src/printdoc.c @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include #include "stlfile.h" #include "util.h" @@ -12,9 +15,6 @@ struct command { void (*func)(char *); }; -const char* ask(const char *fmtstr, ...); -void dump(const char *filepath); - void search_cmd(char *arg); void submit_cmd(char *arg); void list_cmd(char *arg); @@ -30,39 +30,6 @@ struct command commands[] = { struct parseinfo lastrun; const char *resultdir; -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[strlen(linebuf) - 1] = '\0'; - - 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), f))) - printf("%.*s\n", nb, buf); - - fclose(f); -} - void search_cmd(char *arg) { @@ -82,14 +49,14 @@ search_cmd(char *arg) i = 0; while ((de = readdir(d))) { - if (de->d_type != DT_DIR && !strncmp(hash, de->d_name, strlen(hash))) { + if (de->d_type != DT_DIR && !strpfcmp(hash, de->d_name)) { printf("%i : %s\n", i, de->d_name); i++; } } if (i == 0) { - fprintf(stderr, "Sorry, couldnt find any matching scan result!\n"); + fprintf(stderr, "Sorry, couldnt find a matching scan result!\n"); goto cleanup; } else { which = strtoul(ask("Which of these results?\n"), &end, 10); @@ -100,7 +67,7 @@ search_cmd(char *arg) } while ((de = readdir(d))) { - if (de->d_type != DT_DIR && !strncmp(hash, de->d_name, strlen(hash))) { + if (de->d_type != DT_DIR && !strpfcmp(hash, de->d_name)) { if (i == which) { resultfile = aprintf("scans/%s/info", strdup(de->d_name)); break; @@ -117,6 +84,44 @@ cleanup: closedir(d); } +int +save_submission(char *stldata, int stlsize) +{ + DIR *d; + FILE *f; + char *dirpath = NULL, *infopath = NULL, *modelpath = NULL; + + dirpath = aprintf("%s/%s-%i", resultdir, lastrun.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); + + infopath = aprintf("%s/%s", dirpath, "info"); + if (!(f = fopen(infopath, "w+"))) goto fail; + if (save_info(&lastrun, f) != OK) goto fail; + fclose(f); + + free(dirpath); + free(modelpath); + free(infopath); + + return OK; + +fail: + remove(infopath); + remove(modelpath); + remove(dirpath); + + free(dirpath); + free(modelpath); + free(infopath); + + return FAIL; +} + void submit_cmd(char *arg) { @@ -139,10 +144,10 @@ submit_cmd(char *arg) lastrun.valid = parse_file(&lastrun, contents, len); if (lastrun.valid) { - print_info(&lastrun); - save_info(&lastrun, resultdir); - printf("Your file was saved with ID %s!\n", lastrun.hash); - printf("You may retrieve your results via the 'search' function\n"); + if (save_submission(contents, len) != OK) + fprintf(stderr, "Failed to save your submission!\n"); + else + printf(">> Your file was saved with ID %s!\n", lastrun.hash); } free(contents); diff --git a/service/src/stlfile.c b/service/src/stlfile.c index 6a59dd1..4f6d0e1 100644 --- a/service/src/stlfile.c +++ b/service/src/stlfile.c @@ -68,16 +68,16 @@ skip_set(char *p, const char *set) } char* -consume_arg(char **start) +consume_arg(char **start, char **end) { char *p, *tmp; p = skip_set(*start, wsset); for (; !strchr(wsset, *p); p++); if (!*p) return NULL; - *p = '\0'; tmp = *start; *start = p + 1; + *end = p; return tmp; } @@ -107,7 +107,7 @@ consume_keyword(char **start) int parse_file_ascii(struct parseinfo *info, char *buf, size_t len) { - char *bp, *arg, *prev, *tmp; + char *bp, *arg, *prev, *tmp, *end; struct stack states; float farg; int i, kw; @@ -117,6 +117,8 @@ parse_file_ascii(struct parseinfo *info, char *buf, size_t len) 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; @@ -130,8 +132,8 @@ parse_file_ascii(struct parseinfo *info, char *buf, size_t len) if (stack_ind(&states, KW_SOLID_BEGIN) != -1) PARSE_FAIL("Multiple nested solids!\n"); tmp = bp; - if (!consume_keyword(&bp) && (arg = consume_arg(&bp))) { - strncpy(info->extra, arg, sizeof(info->extra)); + if (!consume_keyword(&bp) && (arg = consume_arg(&bp, &end))) { + info->modelname = strndup(arg, end - arg); } else { bp = tmp; } @@ -140,9 +142,9 @@ parse_file_ascii(struct parseinfo *info, char *buf, size_t len) if ((kw = stack_pop(&states)) != STATE_SOLID) PARSE_FAIL("Improper nesting, parent: %s\n", kwmap[kw].str); tmp = bp; - if (*info->extra && !consume_keyword(&bp) - && (arg = consume_arg(&bp))) { - if (strncmp(info->extra, arg, sizeof(info->extra))) + if (info->modelname && !consume_keyword(&bp) + && (arg = consume_arg(&bp, &end))) { + if (strncmp(info->modelname, arg, end - arg)) PARSE_FAIL("Solid end/begin names do not match!\n"); } else { bp = tmp; @@ -163,10 +165,10 @@ parse_file_ascii(struct parseinfo *info, char *buf, size_t len) 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))) + if (!(arg = consume_arg(&bp, &end))) PARSE_FAIL("Facet with less than 3 args!\n"); farg = strtof(arg, &tmp); - if (*tmp) + if (!strchr(wsset, *tmp)) PARSE_FAIL("Facet with invalid arg '%s'!\n", arg); } break; @@ -176,10 +178,10 @@ parse_file_ascii(struct parseinfo *info, char *buf, size_t len) break; case KW_VERTEX: for (i = 0; i < 3; i++) { - if (!(arg = consume_arg(&bp))) + if (!(arg = consume_arg(&bp, &end))) PARSE_FAIL("Vertex with less than 3 args!\n"); farg = strtof(arg, &tmp); - if (*tmp) + if (!strchr(wsset, *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); @@ -215,7 +217,7 @@ parse_file_bin(struct parseinfo *info, char *buf, size_t len) if (len < 84) PARSE_FAIL("Truncated data! (header missing)\n"); - memcpy(info->extra, buf, 80); + memcpy(info->header, buf, 80); bp = buf + 80; info->loopcount = le32toh(*(uint32_t*)bp); @@ -249,33 +251,79 @@ int parse_file(struct parseinfo *info, char *buf, size_t len) { int status; + const char *tmp; char *bp; if (info->valid) free_info(info); - /* check bin vs ascii */ for (bp = buf; strchr(wsset, *bp); bp++); + /* check bin vs ascii with first keyword */ status = !strncmp("solid ", bp, 6) ? parse_file_ascii(info, buf, len) : parse_file_bin(info, buf, len); if (status == FAIL) return FAIL; - /* create model hash */ - strncpy(info->hash, mhash(info->extra, sizeof(info->extra)), MHASHLEN); + if (!info->modelname) { + tmp = ask("Please enter your model name:\n"); + if (strlen(tmp) < 4) { + fprintf(stderr, "Model name is too short!\n"); + return FAIL; + } + info->modelname = checkp(strdup(tmp)); + } + + info->hash = checkp(strdup(mhash(info->modelname, -1))); return OK; } int -save_info(struct parseinfo *info, const char *resultdir) +save_info(struct parseinfo *info, FILE *f) { - /* : - * - info : binary file info - * - model : original stl file - */ + size_t nwrote = 0; + int i; - return FAIL; + nwrote += fwrite(&info->type, sizeof(int), 1, f); + nwrote += fwrite(&info->loopcount, sizeof(int), 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 != 9) return FAIL; + + fputstr(f, info->modelname); + fputstr(f, info->hash); + + 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); + + for (i = 0; i < 3; i++) { + nread += fwrite(&info->bbmin[i], sizeof(float), 1, f); + nread += fwrite(&info->bbmax[i], sizeof(float), 1, f); + } + + nread += fread(info->header, 80, 1, f); + + if (nread != 9) return FAIL; + + freadstr(f, &info->modelname); + freadstr(f, &info->hash); + + return OK; } void @@ -285,16 +333,16 @@ print_info(struct parseinfo *info) #define FILTERCHAR(c) ((c) >= 32 ? (c) : ' ') - if (info->type == TYPE_ASCII) { - printf("Modelname: %s\n", info->extra); - } else { - printf("Model Header:\n"); + printf("Name: %s\n", info->modelname); + + if (info->type == TYPE_BIN) { + printf("Header:\n"); for (i = 0; i < 80; i += k) { for (k = 0; k < MIN(80 - i, 20); k++) - printf(" %02x", (uint8_t) info->extra[i+k]); + printf(" %02x", (uint8_t) info->header[i+k]); printf(" | "); for (k = 0; k < MIN(80 - i, 20); k++) - printf("%c", FILTERCHAR(info->extra[i+k])); + printf("%c", FILTERCHAR(info->header[i+k])); printf("\n"); } } @@ -310,7 +358,7 @@ print_info(struct parseinfo *info) void free_info(struct parseinfo *info) { - NULLFREE(info->stlpath); - NULLFREE(info->infopath); + NULLFREE(info->hash); + NULLFREE(info->modelname); info->valid = 0; } diff --git a/service/src/stlfile.h b/service/src/stlfile.h index e31a8c9..8a52ea9 100644 --- a/service/src/stlfile.h +++ b/service/src/stlfile.h @@ -38,15 +38,14 @@ struct stack { }; struct parseinfo { - char extra[80], hash[MHASHLEN+1]; + char header[80], *hash, *modelname; unsigned int loopcount; float bbmin[3], bbmax[3]; int type, valid; - char *infopath, *stlpath; }; int parse_file(struct parseinfo *info, char *buf, size_t len); -int save_info(struct parseinfo *info, const char *resultdir); +int save_info(struct parseinfo *info, FILE *f); void print_info(struct parseinfo *info); void free_info(struct parseinfo *info); diff --git a/service/src/test.sh b/service/src/test.sh index 4627d81..9a905b7 100644 --- a/service/src/test.sh +++ b/service/src/test.sh @@ -24,6 +24,8 @@ checkleaks() { fi } +export RESULTDIR="scans" + announce "Trying ASCII STL" ( echo "submit" @@ -36,5 +38,14 @@ announce "Trying BIN STL" echo "submit" cat tests/sample-binary.stl | wc -c cat tests/sample-binary.stl + echo "testname" +) | checkleaks + + +announce "Testing Proof-Of-Concept" +( + echo "submit" + cat tests/evil1.stl | wc -c + cat tests/evil1.stl ) | checkleaks diff --git a/service/src/tests/evil1.stl b/service/src/tests/evil1.stl new file mode 100644 index 0000000..2aac44a --- /dev/null +++ b/service/src/tests/evil1.stl @@ -0,0 +1,9 @@ +solid test˙ + facet normal 0 0 1.0 + outer loop + vertex 1 0 0 + vertex 1 1 0 + vertex 0 1 0 + endloop + endfacet +endsolid \ No newline at end of file diff --git a/service/src/util.c b/service/src/util.c index 946bc19..7d3317b 100644 --- a/service/src/util.c +++ b/service/src/util.c @@ -29,14 +29,14 @@ aprintf(const char *fmtstr, ...) va_copy(cpy, ap); va_start(cpy, fmtstr); - nb = vsnprintf(NULL, 0, fmtstr, ap); + nb = vsnprintf(NULL, 0, fmtstr, cpy); va_end(cpy); - if (nb <= 0) die("encountered invalid fmtstr!\n"); + if (nb <= 0) die("Invalid fmtstr!\n"); + str = checkp(malloc(nb+1)); - str = checkp(malloc(nb)); va_start(ap, fmtstr); - nb = vsnprintf(str, nb, fmtstr, ap); + nb = vsnprintf(str, nb+1, fmtstr, ap); va_end(ap); return str; @@ -71,4 +71,62 @@ mhash(const char *filename, int len) return buf; } +void +freadstr(FILE *f, char **dst) +{ + size_t start, len; + char c; + + start = ftell(f); + while ((c = fgetc(f)) != EOF && c) len++; + + *dst = calloc(len + 1, 1); + fread(*dst, len, 1, 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[strlen(linebuf) - 1] = '\0'; + + 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), 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/service/src/util.h b/service/src/util.h index 303d9c9..d012f25 100644 --- a/service/src/util.h +++ b/service/src/util.h @@ -19,6 +19,14 @@ 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); +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); + #endif /* UTIL_H */ -- cgit v1.2.3-71-gd317