From 3cdb30ba6da95ddb31bd486a2ecb3821eedca6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Thu, 19 May 2022 21:07:27 +0200 Subject: started with submitting polls --- lib.c | 2 +- prijave.c | 280 +++++++++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 194 insertions(+), 88 deletions(-) diff --git a/lib.c b/lib.c index 0df787d..207cc7f 100644 --- a/lib.c +++ b/lib.c @@ -37,7 +37,7 @@ __attribute__((unused)) static int urldecode (char * o, const char * i /* o must return 1; } static char * htmlspecialchars (const char * i) { /* remember to free the output */ - if (!i) + if (!i) // output will not be longer than strlen(i)*6 return NULL; size_t s = 128; char * o = malloc(s); diff --git a/prijave.c b/prijave.c index 4100e7f..718dca0 100644 --- a/prijave.c +++ b/prijave.c @@ -57,11 +57,15 @@ enum action { ADD_OPTION, DELETE_OPTION, MODIFY_OPTION, - GENERATE_LINKS + GENERATE_LINKS, + SUBMIT_POLL }; struct request { struct MHD_PostProcessor * post_processor; enum action action; + char ** answer_strings; + long long int * answer_ids; + size_t answers_length; char * pp; char * ap; char * pn; @@ -71,30 +75,47 @@ struct request { char * y; char * c; }; -static void hash (const char * in, char * out) { +char ** answer (struct request request, long long int id) { + for (size_t i = 0; i < request.answers_length; i++) + if (request.answer_ids[i] == id) + return &request.answer_strings[i]; + return NULL; +} +static void token (char * out, long long int id, const char * s, const char * e) { #ifdef PR_SHA2 - SHA2_CTX a; - uint8_t h[SHA256_DIGEST_LENGTH]; - SHA256Init(&a); - SHA256Update(&a, (const unsigned char *) in, strlen(in)); - SHA256Final(h, &a); - for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) - sprintf(out+i*2, "%02x", h[i]); - return; +#define HASH_CTX SHA2_CTX +#define HASHUpdate SHA256Update +#define HASHFinal SHA256Final +#define HASHInit SHA256Init +#define HASH_DIGEST_LENGTH SHA256_DIGEST_LENGTH #else - SHA1_CTX a; - uint8_t h[SHA1_DIGEST_LENGTH]; - SHA1Init(&a); - SHA1Update(&a, (const unsigned char *) in, strlen(in)); - SHA1Final(h, &a); - for (int i = 0; i < SHA1_DIGEST_LENGTH; i++) +#define HASH_CTX SHA1_CTX +#define HASHUpdate SHA1Update +#define HASHFinal SHA1Final +#define HASHInit SHA1Init +#define HASH_DIGEST_LENGTH SHA1_DIGEST_LENGTH +#endif +#define TOKEN_LEN HASH_DIGEST_LENGTH*2 + HASH_CTX a; + uint8_t h[HASH_DIGEST_LENGTH]; + HASHInit(&a); + char buf[128]; + sprintf(buf, "%lld", id); + HASHUpdate(&a, (const unsigned char *) buf, strlen(buf)); + HASHUpdate(&a, (const unsigned char *) STR0(s), strlen(STR0(s))); + HASHUpdate(&a, (const unsigned char *) STR0(e), strlen(STR0(e))); + HASHFinal(h, &a); + for (int i = 0; i < HASH_DIGEST_LENGTH; i++) sprintf(out+i*2, "%02x", h[i]); return; -#endif } -void free_request (struct request * request) { +static void free_request (struct request * request) { if (!request) return; + free(request->answer_ids); + for (size_t i = 0; i < request->answers_length; i++) + free(request->answer_strings[i]); + free(request->answer_strings); free(request->pp); free(request->ap); free(request->pn); @@ -147,7 +168,8 @@ static enum MHD_Result iterator (void * userdata, enum MHD_ValueKind kind __attr ACTION_TRANSLATION("ao", ADD_OPTION); ACTION_TRANSLATION("do", DELETE_OPTION); ACTION_TRANSLATION("mo", MODIFY_OPTION); - ACTION_TRANSLATION("mo", GENERATE_LINKS); + ACTION_TRANSLATION("gl", GENERATE_LINKS); + ACTION_TRANSLATION("sp", SUBMIT_POLL); #define OBTAIN_PARAMETER(name) \ if (!strcmp(key, STR(name))) { \ if (request->name) { \ @@ -169,6 +191,38 @@ static enum MHD_Result iterator (void * userdata, enum MHD_ValueKind kind __attr OBTAIN_PARAMETER(t); OBTAIN_PARAMETER(y); OBTAIN_PARAMETER(c); + if (key[0] == 'a' && (key[1] >= '0' || key[1] <= '9')) { + char ** ans = answer(*request, strtoll(key+1, NULL, 10)); + if (!ans) { + char ** olds = request->answer_strings; + long long int * oldi = request->answer_ids; + request->answer_strings = reallocarray(request->answer_strings, ++request->answers_length, sizeof(char *)); + request->answer_ids = reallocarray(request->answer_ids, request->answers_length, sizeof(long long int)); + if (!request->answer_strings || !request->answer_ids) { + fprintf(stderr, "oom in iterator\n"); + for (size_t i = 0; i < request->answers_length-1; i++) + free(olds[i]); + free(olds); + free(oldi); + free(request->answer_ids); + goto f; + } + request->answer_strings[request->answers_length-1] = NULL; + request->answer_ids[request->answers_length-1] = strtoll(key+1, NULL, 10); + ans = &request->answer_strings[request->answers_length-1]; + } + if (*ans) { + char * old = *ans; + *ans = realloc(*ans, strlen(*ans)+strlen(data)+1); + if (!*ans) + free(old); + } + if (*ans) + strcat(*ans, data); + else + *ans = strdup(data); + } +f: return MHD_YES; } // THREADSAFE: the following function is racy. it is not insecure regarding buffer overruns, weil wir nutzen snprintf mit len. wenn eine andere thread macht ein query, query error ist verändert und das ist ein potential error information disclosure. @@ -238,7 +292,7 @@ static char * options (sqlite3 * db, sqlite3_int64 id, int poll_admin, enum ques if (poll_admin) sprintf(response+strlen(response), "



  • ", rowid, rowid, STR0(text), max); else - sprintf(response+strlen(response), "not implemented"); + sprintf(response+strlen(response), "
  • ", rowid, (type & TYPE_MASK) == CHECKBOX ? "checkbox" : "radio", id, rowid, id, rowid, (type & NOT_NULL) ? "required" : "", id, rowid, STR0(text)); free(text); } sqlite3_finalize(stmt); @@ -267,6 +321,8 @@ static char * questions (sqlite3 * db, sqlite3_int64 id, int poll_admin) { // int responses = sqlite3_column_int(stmt, 3); char * opts = options(db, rowid, poll_admin, type); char * old = response; + if ((type & TYPE_MASK) == HIDDEN && !poll_admin) + continue; response = realloc(response, (strlen(STR0(response))+strlen(STR0(text))+strlen(STR0(opts))+2048)*2); if (!response) { free(old); @@ -279,9 +335,11 @@ static char * questions (sqlite3 * db, sqlite3_int64 id, int poll_admin) { response[0] = '\0'; #define CHECKED(x) ((type & TYPE_MASK) == x ? "checked" : "") if (poll_admin) - sprintf(response+strlen(response), "




  • ", rowid, rowid, STR0(text), CHECKED(HIDDEN), CHECKED(CHECKBOX), CHECKED(TEXT), CHECKED(RADIO), type & NOT_NULL ? "checked" : "", (type & TYPE_MASK) != TEXT ? opts ? "

    možnosti

    " : "" : "", (type & TYPE_MASK) != TEXT ? STR0(opts) : ""); + sprintf(response+strlen(response), "




  • %s
  • ", rowid, rowid, STR0(text), CHECKED(HIDDEN), CHECKED(CHECKBOX), CHECKED(TEXT), CHECKED(RADIO), type & NOT_NULL ? "checked" : "", (type & TYPE_MASK) != TEXT ? opts ? "

    možnosti

    " : "" : "", (type & TYPE_MASK) != TEXT ? STR0(opts) : ""); else - sprintf(response+strlen(response), "not implemented"); +#define HIDE(x) ((type & TYPE_MASK) == x ? "style=display:none" : "") +#define HIDENOT(x) ((type & TYPE_MASK) == x ? "" : "style=display:none") + sprintf(response+strlen(response), "
  • %s%s

    %s
  • ", rowid, STR0(text), (type & NOT_NULL) ? " (obvezno)" : "", rowid, rowid, rowid, HIDENOT(TEXT), (type & TYPE_MASK) == TEXT && (type & NOT_NULL) ? "required" : "", (type & TYPE_MASK) != TEXT ? opts ? "

    možnosti

    " : "" : "", HIDE(TEXT), (type & TYPE_MASK) != TEXT ? STR0(opts) : ""); free(opts); free(text); } @@ -292,6 +350,35 @@ static char * questions (sqlite3 * db, sqlite3_int64 id, int poll_admin) { } return response; } +static char * poll (sqlite3 * db, long long int id, int admin) { + char statem[2048]; + int ret; + sqlite3_stmt * stmt; + sprintf(statem, "SELECT name, description, password FROM polls WHERE rowid=%lld;", id); + if ((ret = sqlite3_prepare_v3(db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) + return hscf(db_error(db, "poll prepare", ret, stmt, statem)); + if ((ret = sqlite3_step(stmt)) != SQLITE_ROW) + return hscf(db_error(db, "poll step", ret, stmt, statem)); + char * name = htmlspecialchars((const char *) sqlite3_column_text(stmt, 0)); + char * desc = htmlspecialchars((const char *) sqlite3_column_text(stmt, 1)); + char * poll_pass = htmlspecialchars((const char *) sqlite3_column_text(stmt, 2)); + sqlite3_finalize(stmt); + char * quests = questions(db, id, admin); + char * r = malloc((strlen(HTML_START(""))+strlen(STR0(name))*3+strlen(STR0(desc))+strlen(STR0(poll_pass))+strlen(STR0(quests))+2048)*2); + if (!r || !quests) { + free(r); + free(quests); + free(name); + free(desc); + free(poll_pass); + return strdup("HTTP 502: poll oom"); + } + if (admin) + sprintf(r, HTML_START("%s") "

    %s

    za dostop do nastavitev in podatkov obrazca je potreben samo naslov, na katerem ste sedaj, zato si ga shranite.








    vprašanja

    " HTML_END, name, name, poll_pass, name, desc, quests); + else + sprintf(r, HTML_START("%s") "

    %s

    %s

    vprašanja

    " HTML_END, name, name, desc, quests); + return r; +} static char * auth (struct prijave * prijave, const char * pass, long long int id) { int ret; char statem[2048]; @@ -332,7 +419,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio char * location = NULL; int free_location = 0; int status_code = MHD_HTTP_OK; - enum MHD_ResponseMemoryMode rmm = MHD_HTTP_NOT_FOUND; + enum MHD_ResponseMemoryMode rmm = MHD_RESPMEM_PERSISTENT; struct request * request = *cls; if (strcmp(meth, "GET") && strcmp(meth, "POST")) { // pravzaprav bi bilo status_code = MHD_HTTP_NOT_ACCEPTABLE; // bolj prav uporabiti @@ -430,6 +517,8 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio } while (0) const char * id_string = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "id"); const char * pass = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "p"); + const char * h = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "h"); + const char * e = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "e"); long long int id = -1; if (id_string) id = strtoll(id_string, NULL, 10); @@ -437,19 +526,16 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio case NO_ACTION: break; case CREATE_POLL: + content_type = "text/plain; charset=UTF-8"; if (prijave->cp_requires_pass && (!request->ap || !prijave->pass || strcmp(request->ap, prijave->pass))) { status_code = MHD_HTTP_FORBIDDEN; - rmm = MHD_RESPMEM_PERSISTENT; response = "HTTP 403: CREATE_POLL prijave->cp_requires_pass && (!request->ap || !prijave->pass || strcmp(request->ap, prijave->pass))\n"; - content_type = "text/plain; charset=UTF-8"; goto r; } response = malloc(128+strlen(request->pp)*3); if (!response) { - rmm = MHD_RESPMEM_PERSISTENT; status_code = MHD_HTTP_BAD_GATEWAY; response = "HTTP 502: CREATE_POLL oom\n"; - content_type = "text/plain; charset=UTF-8"; goto r; } strcpy(statem, "INSERT INTO polls (password, name, description) VALUES (:pw, :n, :d)" @@ -490,7 +576,6 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio #endif urlencode(response+written, request->pp); location = response+strlen("HTTP 201: "); - content_type = "text/plain; charset=UTF-8"; goto r; case FIND_POLLS: strcpy(statem, "SELECT rowid, name, description FROM polls WHERE password=:pw"); @@ -510,7 +595,6 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio if (!pass) { sqlite3_finalize(stmt); free(response); - rmm = MHD_RESPMEM_PERSISTENT; status_code = MHD_HTTP_BAD_GATEWAY; response = "HTTP 502: FIND_POLLS pass oom\n"; content_type = "text/plain; charset=UTF-8"; @@ -524,7 +608,6 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio free(desc); sqlite3_finalize(stmt); status_code = MHD_HTTP_BAD_GATEWAY; - rmm = MHD_RESPMEM_PERSISTENT; response = "HTTP 502: FIND_POLLS while step oom\n"; content_type = "text/plain; charset=UTF-8"; goto r; @@ -540,8 +623,6 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio response = realloc(response, strlen(STR0(response))+2048); if (!response) { free(response); - rmm = MHD_RESPMEM_PERSISTENT; - content_type = "text/html; charset=UTF-8"; response = "HTTP 502: FIND_POOLS HTML_END oom\n"; } strcat(response, "" HTML_END); @@ -554,6 +635,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio status_code = MHD_HTTP_OK; goto r; case MODIFY_POLL: + content_type = "text/plain; charset=UTF-8"; sprintf(statem, "UPDATE polls SET password=:np, name=:n, description=:d WHERE rowid=%lld AND (password=:pw OR 1=", id); if (prijave->pass && pass && !strcmp(prijave->pass, pass)) strcat(statem, "1)"); @@ -570,9 +652,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio if ((ret = sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":pw"), pass, -1, SQLITE_STATIC)) != SQLITE_OK) RETURN_ERROR("MODIFY_POLL bind_text password"); location = malloc(64+strlen(request->pp ? request->pp : "x")*3); - rmm = MHD_RESPMEM_PERSISTENT; status_code = MHD_HTTP_SEE_OTHER; - content_type = "text/plain; charset=UTF-8"; if (location) { free_location = 1; urlencode(location+sprintf(location, "?id=%lld&p=", id), request->pp); @@ -588,6 +668,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio response = "HTTP 204: MODIFY_POLL\n"; goto r; case DELETE_POLL: + content_type = "text/plain; charset=UTF-8"; sprintf(statem, "DELETE FROM polls WHERE rowid=%lld AND (password=:pw OR 1=", id); if (prijave->pass && pass && !strcmp(prijave->pass, pass)) strcat(statem, "1)"); @@ -602,12 +683,11 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio sqlite3_finalize(stmt); free_location = 0; location = "?dp"; - rmm = MHD_RESPMEM_PERSISTENT; response = "HTTP 204: DELETE_POLL\n"; - content_type = "text/plain; charset=UTF-8"; status_code = MHD_HTTP_SEE_OTHER; goto r; case ADD_QUESTION: + content_type = "text/plain; charset=UTF-8"; #define PERFORM_AUTH \ do { \ char * auth_ret; \ @@ -628,12 +708,11 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio sqlite3_finalize(stmt); location = "#d"; // question will always be furthest down free_location = 0; - rmm = MHD_RESPMEM_PERSISTENT; response = "HTTP 201: ADD_QUESTION\n"; - content_type = "text/plain; charset=UTF-8"; status_code = MHD_HTTP_SEE_OTHER; goto r; case DELETE_QUESTION: + content_type = "text/plain; charset=UTF-8"; PERFORM_AUTH; sprintf(statem, "DELETE FROM questions WHERE rowid=%lld AND poll=%lld", strtoll(request->id ? request->id : "-1", NULL, 10), id); if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) @@ -643,9 +722,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio sqlite3_finalize(stmt); location = "#"; // here we really don't care where we land free_location = 0; - rmm = MHD_RESPMEM_PERSISTENT; response = "HTTP 204: DELETE_QUESTION\n"; - content_type = "text/plain; charset=UTF-8"; status_code = MHD_HTTP_SEE_OTHER; goto r; case MODIFY_QUESTION: @@ -689,10 +766,9 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio } while (0) PRG_SUCCESS("HTTP 204: MODIFY_QUESTION\n", "#q%lld", RID); case ADD_OPTION: + content_type = "text/plain; charset=UTF-8"; PERFORM_AUTH; if (id != owner(prijave, "poll", "questions", RID)) { - rmm = MHD_RESPMEM_PERSISTENT; - content_type = "text/plain; charset=UTF-8"; status_code = MHD_HTTP_FORBIDDEN; response = "HTTP 403: ADD_OPTION\n"; goto r; @@ -714,11 +790,10 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio sqlite3_finalize(stmt); PRG_SUCCESS("HTTP 201: ADD_OPTION\n", "#o%lld", rowid); case DELETE_OPTION: + content_type = "text/plain; charset=UTF-8"; PERFORM_AUTH; long long int q = -1; if (id != owner(prijave, "poll", "questions", (q = owner(prijave, "question", "options", RID)))) { - rmm = MHD_RESPMEM_PERSISTENT; - content_type = "text/plain; charset=UTF-8"; status_code = MHD_HTTP_FORBIDDEN; response = "HTTP 403: DELETE_OPTION\n"; goto r; @@ -731,10 +806,9 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio sqlite3_finalize(stmt); PRG_SUCCESS("HTTP 204: DELETE_OPTION\n", "#q%lld", q); case MODIFY_OPTION: + content_type = "text/plain; charset=UTF-8"; PERFORM_AUTH; if (id != owner(prijave, "poll", "questions", owner(prijave, "question", "options", RID))) { - rmm = MHD_RESPMEM_PERSISTENT; - content_type = "text/plain; charset=UTF-8"; status_code = MHD_HTTP_FORBIDDEN; response = "HTTP 403: MODIFY_OPTION\n"; goto r; @@ -750,61 +824,93 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio PRG_SUCCESS("HTTP 204: MODIFY_OPTION\n", "#o%lld", RID); case GENERATE_LINKS: PERFORM_AUTH; - /* char * saveptr; - char * email; char * delim = "\n"; - if (strchr(request->t, "\r")) { + if (strchr(request->t, '\r')) { delim = "\r\n"; } response = NULL; - while ((email = strtok(request->t, delim))) { - char * tohash = malloc(strlen(email)+strlen(STR0(salt)+128)); - if (!tohash) { - free(response); - rmm = + int strings = 1; + for (int i = 0; request->t[i]; i++) + strings++; + if (!(response = malloc(2048+strings*(TOKEN_LEN+512)+strlen(request->t)*3+strlen(request->t)*6))) { + status_code = MHD_HTTP_BAD_GATEWAY; + response = "HTTP 502: GENERATE_LINKS oom response\n"; + content_type = "text/plain; charset=UTF-8"; + goto r; + } + strcpy(response, HTML_START("GENERATE_LINKS") "

    GENERATE_LINKS