summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/web.c103
-rw-r--r--src/web.h3
2 files changed, 99 insertions, 7 deletions
diff --git a/src/web.c b/src/web.c
index 8c37bfe..df2dc81 100644
--- a/src/web.c
+++ b/src/web.c
@@ -13,6 +13,7 @@
#include <microhttpd.h>
#include <sqlite3.h>
#include <cjson/cJSON.h>
+#include <fcntl.h>
#define PAGE "<html><head><title>libmicrohttpd demo</title>"\
"</head><body>libmicrohttpd demo</body></html>"
@@ -22,6 +23,21 @@ static void sqliteError(void *pArg, int iErrCode, const char *zMsg) {
fprintf(stderr, "(%d) %s\n", iErrCode, zMsg);
}
+static int generate_secret(char *out) {
+ unsigned char bytes[16];
+ int fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0) return -1;
+ ssize_t n = read(fd, bytes, 16);
+ close(fd);
+ if (n != 16) return -1;
+ static const char hex[] = "0123456789abcdef";
+ for (int i = 0; i < 16; i++) {
+ out[i*2] = hex[(bytes[i] >> 4) & 0x0f];
+ out[i*2+1] = hex[bytes[i] & 0x0f];
+ }
+ out[32] = '\0';
+ return 0;
+}
void prepare_database(struct App *app) {
// @CLEANUP: Move this to main? doesn't belong here at least
@@ -176,6 +192,36 @@ void prepare_database(struct App *app) {
abort();
}
}
+
+ if(maxVersion < 4) {
+ char *prog =
+ "BEGIN TRANSACTION;"
+ "ALTER TABLE clients ADD COLUMN secret TEXT NOT NULL DEFAULT ''";
+ if(sqlite3_exec(conn, prog, NULL, NULL, NULL) != SQLITE_OK) {
+ abort();
+ }
+
+ sqlite3_stmt *stmt;
+ if(sqlite3_prepare_v2(conn, "INSERT INTO migration (id) VALUES (?1)", -1, &stmt, NULL) != SQLITE_OK) {
+ abort();
+ }
+
+ if(sqlite3_bind_int64(stmt, 1, 4) != SQLITE_OK) {
+ abort();
+ }
+
+ if(sqlite3_step(stmt) != SQLITE_DONE) {
+ abort();
+ }
+
+ if(sqlite3_finalize(stmt) != SQLITE_OK) {
+ abort();
+ }
+
+ if(sqlite3_exec(conn, "COMMIT TRANSACTION", NULL, NULL, NULL) != SQLITE_OK) {
+ abort();
+ }
+ }
}
enum ReportEvent {
@@ -184,7 +230,8 @@ enum ReportEvent {
};
// @CLEANUP: This should live somewhere else, but I don't have a spot for it yet.
-int submit_report(struct App *app, char *client, enum ReportEvent event) {
+// Returns 0 on success, -1 on error, -2 on secret mismatch
+int submit_report(struct App *app, char *client, char *secret, enum ReportEvent event) {
sqlite3 *conn;
int err;
if((err = sqlite3_open(app->dbname, &conn)) != SQLITE_OK) {
@@ -196,7 +243,7 @@ int submit_report(struct App *app, char *client, enum ReportEvent event) {
int64_t clientId;
{
sqlite3_stmt *stmt;
- if(sqlite3_prepare_v2(conn, "SELECT id FROM clients WHERE name = ?1", -1, &stmt, NULL) != SQLITE_OK) {
+ if(sqlite3_prepare_v2(conn, "SELECT id, secret FROM clients WHERE name = ?1", -1, &stmt, NULL) != SQLITE_OK) {
abort();
}
if(sqlite3_bind_text(stmt, 1, client, -1, SQLITE_STATIC) != SQLITE_OK) {
@@ -212,6 +259,13 @@ int submit_report(struct App *app, char *client, enum ReportEvent event) {
}
clientId = sqlite3_column_int64(stmt, 0);
+ const char *stored_secret = (const char *)sqlite3_column_text(stmt, 1);
+ if(stored_secret == NULL || strcmp(stored_secret, secret) != 0) {
+ sqlite3_finalize(stmt);
+ sqlite3_close(conn);
+ return -2;
+ }
+
if(sqlite3_step(stmt) != SQLITE_DONE) {
abort();
}
@@ -313,14 +367,21 @@ int create_client(struct Request *req, char *name, struct CreateClientResult *re
abort();
}
+ if(generate_secret(result->secret) != 0) {
+ abort();
+ }
+
{
sqlite3_stmt *stmt;
- if(sqlite3_prepare_v2(conn, "INSERT INTO clients (name) VALUES (?1)", -1, &stmt, NULL) != SQLITE_OK) {
+ if(sqlite3_prepare_v2(conn, "INSERT INTO clients (name, secret) VALUES (?1, ?2)", -1, &stmt, NULL) != SQLITE_OK) {
abort();
}
if(sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC) != SQLITE_OK) {
abort();
}
+ if(sqlite3_bind_text(stmt, 2, result->secret, -1, SQLITE_STATIC) != SQLITE_OK) {
+ abort();
+ }
if(sqlite3_step(stmt) != SQLITE_DONE) {
abort();
@@ -373,7 +434,7 @@ int list_clients(struct Request *req, struct ListClientResult *result) {
{
sqlite3_stmt *stmt;
- if(sqlite3_prepare_v2(conn, "SELECT c.id, c.name FROM clients c WHERE c.id > ?1 ORDER BY c.id ASC LIMIT ?2", -1, &stmt, NULL) != SQLITE_OK) {
+ if(sqlite3_prepare_v2(conn, "SELECT c.id, c.name, c.secret FROM clients c WHERE c.id > ?1 ORDER BY c.id ASC LIMIT ?2", -1, &stmt, NULL) != SQLITE_OK) {
abort();
}
if(sqlite3_bind_int64(stmt, 1, -1) != SQLITE_OK) {
@@ -394,9 +455,11 @@ int list_clients(struct Request *req, struct ListClientResult *result) {
int64_t clientId = sqlite3_column_int64(stmt, 0);
const uint8_t *name = sqlite3_column_text(stmt, 1);
+ const uint8_t *secret = sqlite3_column_text(stmt, 2);
result->clients[index].id = clientId;
strncpy(result->clients[index].name, (char*)name, CLIENT_NAME_MAX);
+ strncpy(result->clients[index].secret, (char*)secret, CLIENT_SECRET_LEN);
index++;
}
@@ -443,12 +506,13 @@ static char *render_clients_page(struct Request *request, size_t *out_len) {
"<body>\n"
" <h1>Clients</h1>\n"
" <table border=\"1\">\n"
- " <tr><th>Name</th><th>Actions</th></tr>\n");
+ " <tr><th>Name</th><th>Secret</th><th>Actions</th></tr>\n");
for (int i = 0; i < result->clients_len; i++) {
fprintf(f,
" <tr>\n"
" <td>%s</td>\n"
+ " <td>%s</td>\n"
" <td>\n"
" <form method=\"POST\" action=\"/clients/delete\" style=\"display:inline\">\n"
" <input type=\"hidden\" name=\"client_id\" value=\"%ld\">\n"
@@ -457,6 +521,7 @@ static char *render_clients_page(struct Request *request, size_t *out_len) {
" </td>\n"
" </tr>\n",
result->clients[i].name,
+ result->clients[i].secret,
result->clients[i].id
);
}
@@ -639,6 +704,11 @@ static enum MHD_Result handler(
abort();
}
+ cJSON *secretJson = cJSON_GetObjectItemCaseSensitive(rootJson, "secret");
+ if(!cJSON_IsString(secretJson) || secretJson->valuestring == NULL) {
+ abort();
+ }
+
cJSON *eventJson = cJSON_GetObjectItemCaseSensitive(rootJson, "event");
if(!cJSON_IsString(eventJson) || eventJson->valuestring == NULL) {
abort();
@@ -653,7 +723,26 @@ static enum MHD_Result handler(
}
- ret = submit_report(server->app, clientJson->valuestring, event);
+ ret = submit_report(server->app, clientJson->valuestring, secretJson->valuestring, event);
+ if(ret == -2) {
+ cJSON_Delete(rootJson);
+ destroy_post_collector(&request->pp);
+
+ struct MHD_Response *response = MHD_create_response_from_buffer(
+ 0,
+ "",
+ MHD_RESPMEM_PERSISTENT
+ );
+ ret = MHD_add_response_header(response, "Content-Type", "application/json");
+ if(ret != MHD_YES) return ret;
+ ret = MHD_queue_response(
+ connection,
+ MHD_HTTP_UNAUTHORIZED,
+ response
+ );
+ MHD_destroy_response(response);
+ return ret;
+ }
if(ret != 0) {
// Should actually return an error page, but this will do for now
return MHD_NO;
@@ -697,7 +786,7 @@ static enum MHD_Result handler(
destroy_post_collector(&request->pp);
char *buf = malloc(1024);
- size_t buf_len = snprintf(buf, 1024, "{ \"id\": \"%ld\" }", result.id);
+ size_t buf_len = snprintf(buf, 1024, "{ \"id\": \"%ld\", \"secret\": \"%s\" }", result.id, result.secret);
if(buf_len >= 1024) {
abort();
}
diff --git a/src/web.h b/src/web.h
index 96e5f7f..95c0e27 100644
--- a/src/web.h
+++ b/src/web.h
@@ -21,6 +21,7 @@ int web_begin(struct Server *server, int port);
int web_join(struct Server *server);
#define CLIENT_NAME_MAX 32
+#define CLIENT_SECRET_LEN 33
struct PostCollector {
char *data;
@@ -45,6 +46,7 @@ struct Request {
struct CreateClientResult {
int64_t id;
char* name;
+ char secret[CLIENT_SECRET_LEN];
};
int create_client(struct Request *req, char *name, struct CreateClientResult *result);
@@ -53,6 +55,7 @@ int delete_client(struct Request *req, int64_t client_id);
struct ListClientResultClient {
int64_t id;
char name[CLIENT_NAME_MAX];
+ char secret[CLIENT_SECRET_LEN];
};
struct ListClientResult {