diff options
Diffstat (limited to '')
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | hp.html | 4 | ||||
-rw-r--r-- | prijave.c | 109 |
3 files changed, 99 insertions, 15 deletions
@@ -3,3 +3,4 @@ prijave core sqlite3 .gdb_history +valgrind-out.txt @@ -38,9 +38,9 @@ <textarea id=pd name=pd placeholder=opis></textarea> <br> <input type=submit name=cp value="izdelaj nov obrazec" /> - <small> + <p> gesla so shranjena kot golo besedilo - </small> + </p> </form> <h2> iskanje obstoječih obrazcev z geslom @@ -53,6 +53,43 @@ struct request { char * pn; char * pd; }; +void free_request (struct request * request) { + if (!request) + return; + free(request->pp); + free(request->ap); + free(request->pn); + free(request->pd); + free(request); +} +void request_completed (void * cls __attribute__((unused)), struct MHD_Connection * connection __attribute__((unused)), void ** con_cls, enum MHD_RequestTerminationCode toe) { + struct request * request = * con_cls; + free_request(request); + char * code = "unset"; + switch (toe) { + case MHD_REQUEST_TERMINATED_COMPLETED_OK: + code = "MHD_REQUEST_TERMINATED_COMPLETED_OK"; + break; + case MHD_REQUEST_TERMINATED_WITH_ERROR: + code = "MHD_REQUEST_TERMINATED_WITH_ERROR"; + break; + case MHD_REQUEST_TERMINATED_TIMEOUT_REACHED: + code = "MHD_REQUEST_TERMINATED_TIMEOUT_REACHED"; + break; + case MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN: + code = "MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN"; + break; + case MHD_REQUEST_TERMINATED_READ_ERROR: + code = "MHD_REQUEST_TERMINATED_READ_ERROR"; + break; + case MHD_REQUEST_TERMINATED_CLIENT_ABORT: + code = "MHD_REQUEST_TERMINATED_CLIENT_ABORT"; + break; + } + if (toe != MHD_REQUEST_TERMINATED_COMPLETED_OK) + fprintf(stderr, "request_completed: %s\n", code); // wow such debug much verbosity + *con_cls = NULL; +} static enum MHD_Result iterator (void * userdata, enum MHD_ValueKind kind __attribute__((unused)), const char * key, const char * filename __attribute__((unused)), const char * content_type __attribute__((unused)), const char * transfer_encoding __attribute__((unused)), const char * data, uint64_t off __attribute__((unused)), size_t size __attribute__((unused))) { struct request * request = (struct request *) userdata; #define ACTION_TRANSLATION(name, act) \ @@ -82,6 +119,7 @@ static enum MHD_Result iterator (void * userdata, enum MHD_ValueKind kind __attr OBTAIN_PARAMETER(pd); 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. char * db_error (sqlite3 * db, const char * section, int ret, sqlite3_stmt * stmt, const char * statem) { char spaces[2048]; memset(spaces, ' ', 2048); @@ -93,9 +131,25 @@ char * db_error (sqlite3 * db, const char * section, int ret, sqlite3_stmt * stm if (!response) return NULL; if (stmt) - snprintf(response, len, "db_error %s\n%s\n%s\n%.*s^\n%s\n%s\n", section, sqlite3_expanded_sql(stmt) ? sqlite3_expanded_sql(stmt) : 0, statem, sqlite3_error_offset(db) == -1 ? 0 : sqlite3_error_offset(db), spaces, sqlite3_errstr(ret), sqlite3_errmsg(db)); + snprintf(response, len, "db_error %s\n%s\n%s\n" +#if SQLITE_VERSION_NUMBER >= 3038000 + "%.*s^\n" +#endif + "%s\n%s\n", section, sqlite3_expanded_sql(stmt) ? sqlite3_expanded_sql(stmt) : 0, statem, +#if SQLITE_VERSION_NUMBER >= 3038000 + sqlite3_error_offset(db) == -1 ? 0 : sqlite3_error_offset(db), spaces, +#endif + sqlite3_errstr(ret), sqlite3_errmsg(db)); else - snprintf(response, len, "db_error %s\n!stmt\n%s\n%.*s^\n%s\n%s\n", section, statem, sqlite3_error_offset(db) == -1 ? 0 : sqlite3_error_offset(db), spaces, sqlite3_errstr(ret), sqlite3_errmsg(db)); + snprintf(response, len, "db_error %s\n!stmt\n%s\n" +#if SQLITE_VERSION_NUMBER >= 3038000 + "%.*s^\n" +#endif + "%s\n%s\n", section, statem, +#if SQLITE_VERSION_NUMBER >= 3038000 + sqlite3_error_offset(db) == -1 ? 0 : sqlite3_error_offset(db), spaces, +#endif + sqlite3_errstr(ret), sqlite3_errmsg(db)); fprintf(stderr, "%s", response); sqlite3_finalize(stmt); return response; @@ -214,6 +268,10 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio status_code = MHD_HTTP_NOT_ACCEPTABLE; // bolj prav uporabiti response = "HTTP 406\n"; // PUT request, ampak HTML form tega nima /: content_type = "text/plain; charset=UTF-8"; + if (request) { + free_request(request); + *cls = NULL; + } goto r; } if (!request) { @@ -235,7 +293,6 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio sqlite3_stmt * stmt; int ret; char statem[2048]; -// 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. #define RETURN_ERROR(section) /* racy because of db_error */ \ { \ content_type = "text/plain; charset=UTF-8"; \ @@ -249,8 +306,8 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio rmm = MHD_RESPMEM_MUST_FREE; \ goto r; \ } -#define CREATE_TABLE(table, cols) \ - strcpy(statem, "CREATE TABLE IF NOT EXISTS " table " (" cols ") STRICT;"); \ +#define CREATE_TABLE_MORE(table, cols, add) \ + strcpy(statem, "CREATE TABLE IF NOT EXISTS " table " (" cols ")" add); \ ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL); \ if (ret != SQLITE_OK) \ RETURN_ERROR("prepare"); \ @@ -258,6 +315,11 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio sqlite3_finalize(stmt); \ if (ret != SQLITE_DONE) \ RETURN_ERROR("step"); +#if SQLITE_VERSION_NUMBER >= 3037000 +#define CREATE_TABLE(table, cols) CREATE_TABLE_MORE(table, cols, " STRICT;") +#else +#define CREATE_TABLE(table, cols) CREATE_TABLE_MORE(table, cols, ";") +#endif CREATE_TABLE("responses", "email TEXT NOT NULL, question INTEGER NOT NULL, answer ANY"); CREATE_TABLE("options", "question INTEGER NOT NULL, text TEXT, max INTEGER NOT NULL"); CREATE_TABLE("questions", "id INTEGER PRIMARY KEY AUTOINCREMENT, poll INTEGER NOT NULL, text TEXT, type INTEGER NOT NULL"); @@ -269,7 +331,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio rmm = MHD_RESPMEM_MUST_FREE; #define FORMAT "%s\n%s\n.f:after { content: '%s' }\n" #define ARGUMENTS prijave->cp_requires_pass ? "" : ".cp_ap { visibility: hidden }", prijave->footer ? "" : ".f { visibility: hidden }", prijave->footer ? prijave->footer : "" - response = malloc(snprintf(NULL, 0, FORMAT, ARGUMENTS)); + response = malloc(snprintf(NULL, 0, FORMAT, ARGUMENTS)+1); content_type = "text/css; charset=UTF-8"; if (!response) { status_code = MHD_HTTP_BAD_GATEWAY; @@ -320,7 +382,11 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio content_type = "text/plain; charset=UTF-8"; goto r; } - strcpy(statem, "INSERT INTO polls (password, name, description) VALUES (:pw, :n, :d);"); + strcpy(statem, "INSERT INTO polls (password, name, description) VALUES (:pw, :n, :d)" +#if SQLITE_VERSION_NUMBER >= 3035000 + " RETURNING rowid" +#endif + ); if ((ret = sqlite3_prepare_v3(prijave->db, statem, -1, 0, &stmt, NULL)) != SQLITE_OK) { free(response); RETURN_ERROR("CREATE_POLL prepare"); @@ -337,7 +403,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio free(response); RETURN_ERROR("CREATE_POLL bind_text description"); } - if ((ret = sqlite3_step(stmt)) != SQLITE_DONE) { + if ((ret = sqlite3_step(stmt)) != SQLITE_DONE && ret != SQLITE_ROW /* for sqlite ^=3.35 */) { sqlite3_finalize(stmt); free(response); RETURN_ERROR("CREATE_POLL step"); @@ -347,7 +413,11 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio rmm = MHD_RESPMEM_MUST_FREE; // THREADSAFE: the following call to sqlite3_last_insert_rowid is racy. it's not a security issue, but if another poll is created before sqlite3_last_insert_rowid is called, the client gets a faulty id that will not work, though he can still solve the problem by using FIND_POLLS. // better alternative is to use INSERT INTO ... RETURING ... query. but this is only available in sqlite 3.35, so I'll use the legacy option until 3.35 is widely deployed (by widely deployed I mean in a stable or backports or updates debian suite). +#if SQLITE_VERSION_NUMBER >= 3035000 + int written = sprintf(response, "HTTP 201: ?id=%lld&p=", sqlite3_column_int64(stmt, 0)); +#else int written = sprintf(response, "HTTP 201: ?id=%lld&p=", sqlite3_last_insert_rowid(prijave->db)); +#endif urlencode(response+written, request->pp); location = response+strlen("HTTP 201: "); content_type = "text/plain; charset=UTF-8"; @@ -372,7 +442,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio free(response); rmm = MHD_RESPMEM_PERSISTENT; status_code = MHD_HTTP_BAD_GATEWAY; - response = "HTTP 502: FIND_POLLS oom\n"; + response = "HTTP 502: FIND_POLLS pass oom\n"; content_type = "text/plain; charset=UTF-8"; goto r; } @@ -385,7 +455,7 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio sqlite3_finalize(stmt); status_code = MHD_HTTP_BAD_GATEWAY; rmm = MHD_RESPMEM_PERSISTENT; - response = "HTTP 502: FIND_POLLS oom\n"; + response = "HTTP 502: FIND_POLLS while step oom\n"; content_type = "text/plain; charset=UTF-8"; goto r; } @@ -394,12 +464,19 @@ static enum MHD_Result httpd (void * userdata, struct MHD_Connection * connectio free(desc); free(pass); } + sqlite3_finalize(stmt); + response = realloc(response, strlen(response ? 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, "</ul>" HTML_END); if (ret != SQLITE_DONE) { free(response); RETURN_ERROR("FIND_POLLS step"); } - sqlite3_finalize(stmt); rmm = MHD_RESPMEM_MUST_FREE; content_type = "text/html; charset=UTF-8"; status_code = MHD_HTTP_OK; @@ -539,6 +616,8 @@ r: free(location); int r = MHD_queue_response(connection, status_code, httpd_response); MHD_destroy_response(httpd_response); + free_request(request); + *cls = NULL; return r; } static void handler (int s __attribute__((unused))) { @@ -555,7 +634,11 @@ int main (void) { prijave.salt = getenv("PR_SALT"); assert(getenv("PR_DB")); assert(sqlite3_threadsafe()); - int ret = sqlite3_open_v2(getenv("PR_DB"), &prijave.db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_EXRESCODE, NULL); + int ret = sqlite3_open_v2(getenv("PR_DB"), &prijave.db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX +#if SQLITE_VERSION_NUMBER >= 3037000 + | SQLITE_OPEN_EXRESCODE +#endif + , NULL); if (ret != SQLITE_OK) { fprintf(stderr, "sqlite3_open_v2: %s\n", sqlite3_errstr(ret)); goto r; @@ -589,7 +672,7 @@ c: prijave.hp = strdup("HTTP 404: !getenv(\"PR_HP\")\n"); o: ; - struct MHD_Daemon * d = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD, atoi(getenv("PR_PORT") ? getenv("PR_PORT") : PR_PORT), NULL, NULL, &httpd, &prijave, MHD_OPTION_END); + struct MHD_Daemon * d = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD, atoi(getenv("PR_PORT") ? getenv("PR_PORT") : PR_PORT), NULL, NULL, &httpd, &prijave, MHD_OPTION_NOTIFY_COMPLETED, &request_completed, NULL, MHD_OPTION_END); if (!d) { r = 1; goto r; |