mirror of
https://github.com/pi-hole/FTL.git
synced 2024-10-26 16:52:18 +02:00
64144a960c
Signed-off-by: DL6ER <dl6er@dl6er.de>
384 lines
11 KiB
C
384 lines
11 KiB
C
/* Pi-hole: A black hole for Internet advertisements
|
|
* (c) 2023 Pi-hole, LLC (https://pi-hole.net)
|
|
* Network-wide ad blocking via your own hardware.
|
|
*
|
|
* FTL Engine
|
|
* Sessions table database routines
|
|
*
|
|
* This file is copyright under the latest version of the EUPL.
|
|
* Please see LICENSE file for your rights under this license. */
|
|
|
|
#include "FTL.h"
|
|
#include "database/session-table.h"
|
|
#include "database/common.h"
|
|
#include "config/config.h"
|
|
// get_memdb()
|
|
#include "database/query-table.h"
|
|
|
|
bool create_session_table(sqlite3 *db)
|
|
{
|
|
// Start transaction of database update
|
|
SQL_bool(db, "BEGIN TRANSACTION;");
|
|
|
|
// Create session table
|
|
SQL_bool(db, "CREATE TABLE session (id INTEGER PRIMARY KEY, "\
|
|
"login_at TIMESTAMP NOT NULL, "\
|
|
"valid_until TIMESTAMP NOT NULL, "\
|
|
"remote_addr TEXT NOT NULL, "\
|
|
"user_agent TEXT, "\
|
|
"sid TEXT NOT NULL, "\
|
|
"csrf TEXT NOT NULL, "\
|
|
"tls_login BOOL, "\
|
|
"tls_mixed BOOL);");
|
|
|
|
// Update database version to 15
|
|
if(!db_set_FTL_property(db, DB_VERSION, 15))
|
|
{
|
|
log_err("create_session_table(): Failed to update database version!");
|
|
return false;
|
|
}
|
|
|
|
// Finish transaction
|
|
SQL_bool(db, "COMMIT");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool add_session_app_column(sqlite3 *db)
|
|
{
|
|
// Start transaction of database update
|
|
SQL_bool(db, "BEGIN TRANSACTION;");
|
|
|
|
// Create session table
|
|
SQL_bool(db, "ALTER TABLE session ADD COLUMN app BOOL;");
|
|
|
|
// Update database version to 16
|
|
if(!db_set_FTL_property(db, DB_VERSION, 16))
|
|
{
|
|
log_err("add_session_app_column(): Failed to update database version!");
|
|
return false;
|
|
}
|
|
|
|
// Finish transaction
|
|
SQL_bool(db, "COMMIT");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool add_session_cli_column(sqlite3 *db)
|
|
{
|
|
// Start transaction of database update
|
|
SQL_bool(db, "BEGIN TRANSACTION;");
|
|
|
|
// Create session table
|
|
SQL_bool(db, "ALTER TABLE session ADD COLUMN cli BOOL;");
|
|
|
|
// Update database version to 18
|
|
if(!db_set_FTL_property(db, DB_VERSION, 18))
|
|
{
|
|
log_err("add_session_cli_column(): Failed to update database version!");
|
|
return false;
|
|
}
|
|
|
|
// Finish transaction
|
|
SQL_bool(db, "COMMIT");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool add_session_x_forwarded_for_column(sqlite3 *db)
|
|
{
|
|
// Start transaction of database update
|
|
SQL_bool(db, "BEGIN TRANSACTION;");
|
|
|
|
// Create session table
|
|
SQL_bool(db, "ALTER TABLE session ADD COLUMN x_forwarded_for TEXT;");
|
|
|
|
// Update database version to 18
|
|
if(!db_set_FTL_property(db, DB_VERSION, 19))
|
|
{
|
|
log_err("add_session_x_forwarded_for_column(): Failed to update database version!");
|
|
return false;
|
|
}
|
|
|
|
// Finish transaction
|
|
SQL_bool(db, "COMMIT");
|
|
|
|
return true;
|
|
}
|
|
|
|
// Store all session in database
|
|
bool backup_db_sessions(struct session *sessions, const uint16_t max_sessions)
|
|
{
|
|
if(!config.webserver.session.restore.v.b)
|
|
{
|
|
log_debug(DEBUG_API, "Session restore is disabled, not adding sessions to database");
|
|
return true;
|
|
}
|
|
|
|
sqlite3 *db = dbopen(false, false);
|
|
if(db == NULL)
|
|
{
|
|
log_warn("Failed to open database in backup_db_sessions()");
|
|
return false;
|
|
}
|
|
|
|
// Insert session into database
|
|
sqlite3_stmt *stmt = NULL;
|
|
if(sqlite3_prepare_v2(db, "INSERT INTO session (login_at, valid_until, remote_addr, user_agent, sid, csrf, tls_login, tls_mixed, app, cli, x_forwarded_for) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", -1, &stmt, 0) != SQLITE_OK)
|
|
{
|
|
log_err("SQL error in backup_db_sessions(): %s (%d)",
|
|
sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
|
|
unsigned int api_sessions = 0;
|
|
for(unsigned int i = 0; i < max_sessions; i++)
|
|
{
|
|
// Get session
|
|
struct session *sess = &sessions[i];
|
|
|
|
// Skip unused sessions
|
|
if(!sess->used)
|
|
continue;
|
|
|
|
// Bind values to statement
|
|
// 1: login_at
|
|
if(sqlite3_bind_int64(stmt, 1, sess->login_at) != SQLITE_OK)
|
|
{
|
|
log_err("Cannot bind login_at = %ld in backup_db_sessions(): %s (%d)",
|
|
(long int)sess->login_at, sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
// 2: valid_until
|
|
if(sqlite3_bind_int64(stmt, 2, sess->valid_until) != SQLITE_OK)
|
|
{
|
|
log_err("Cannot bind valid_until = %ld in backup_db_sessions(): %s (%d)",
|
|
(long int)sess->valid_until, sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
// 3: remote_addr
|
|
if(sqlite3_bind_text(stmt, 3, sess->remote_addr, -1, SQLITE_STATIC) != SQLITE_OK)
|
|
{
|
|
log_err("Cannot bind remote_addr = %s in backup_db_sessions(): %s (%d)",
|
|
sess->remote_addr, sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
// 4: user_agent
|
|
if(sqlite3_bind_text(stmt, 4, sess->user_agent, -1, SQLITE_STATIC) != SQLITE_OK)
|
|
{
|
|
log_err("Cannot bind user_agent = %s in backup_db_sessions(): %s (%d)",
|
|
sess->user_agent, sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
// 5: sid
|
|
if(sqlite3_bind_text(stmt, 5, sess->sid, -1, SQLITE_STATIC) != SQLITE_OK)
|
|
{
|
|
log_err("Cannot bind sid = %s in backup_db_sessions(): %s (%d)",
|
|
sess->sid, sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
// 6: csrf
|
|
if(sqlite3_bind_text(stmt, 6, sess->csrf, -1, SQLITE_STATIC) != SQLITE_OK)
|
|
{
|
|
log_err("Cannot bind csrf = %s in backup_db_sessions(): %s (%d)",
|
|
sess->csrf, sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
// 7: tls_login
|
|
if(sqlite3_bind_int(stmt, 7, sess->tls.login ? 1 : 0) != SQLITE_OK)
|
|
{
|
|
log_err("Cannot bind tls_login = %d in backup_db_sessions(): %s (%d)",
|
|
sess->tls.login ? 1 : 0, sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
// 8: tls_mixed
|
|
if(sqlite3_bind_int(stmt, 8, sess->tls.mixed ? 1 : 0) != SQLITE_OK)
|
|
{
|
|
log_err("Cannot bind tls_mixed = %d in backup_db_sessions(): %s (%d)",
|
|
sess->tls.mixed ? 1 : 0, sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
// 9: app
|
|
if(sqlite3_bind_int(stmt, 9, sess->app ? 1 : 0) != SQLITE_OK)
|
|
{
|
|
log_err("Cannot bind app = %d in backup_db_sessions(): %s (%d)",
|
|
sess->app ? 1 : 0, sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
// 10: cli
|
|
if(sqlite3_bind_int(stmt, 10, sess->cli ? 1 : 0) != SQLITE_OK)
|
|
{
|
|
log_err("Cannot bind cli = %d in backup_db_sessions(): %s (%d)",
|
|
sess->cli ? 1 : 0, sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
// 11: x_forwarded_for
|
|
if(sqlite3_bind_text(stmt, 11, sess->x_forwarded_for, -1, SQLITE_STATIC) != SQLITE_OK)
|
|
{
|
|
log_err("Cannot bind x_forwarded_for = %s in backup_db_sessions(): %s (%d)",
|
|
sess->x_forwarded_for, sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
|
|
// Execute statement
|
|
if(sqlite3_step(stmt) != SQLITE_DONE)
|
|
{
|
|
log_err("SQL error in backup_db_sessions(): %s (%d)",
|
|
sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
|
|
// Clear bindings
|
|
if(sqlite3_clear_bindings(stmt) != SQLITE_OK)
|
|
{
|
|
log_err("SQL error in backup_db_sessions(): %s (%d)",
|
|
sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
|
|
// Reset statement
|
|
if(sqlite3_reset(stmt) != SQLITE_OK)
|
|
{
|
|
log_err("SQL error in backup_db_sessions(): %s (%d)",
|
|
sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
|
|
api_sessions++;
|
|
}
|
|
|
|
// Finalize statement
|
|
if(sqlite3_finalize(stmt) != SQLITE_OK)
|
|
{
|
|
log_err("SQL error in backup_db_sessions(): %s (%d)",
|
|
sqlite3_errmsg(db), sqlite3_errcode(db));
|
|
return false;
|
|
}
|
|
|
|
log_info("Stored %u API session%s in the database",
|
|
api_sessions, api_sessions == 1 ? "" : "s");
|
|
|
|
// Close database connection
|
|
dbclose(&db);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Restore all sessions found in the database
|
|
bool restore_db_sessions(struct session *sessions, const uint16_t max_sessions)
|
|
{
|
|
if(!config.webserver.session.restore.v.b)
|
|
{
|
|
log_debug(DEBUG_API, "Session restore is disabled, not restoring sessions from database");
|
|
return true;
|
|
}
|
|
|
|
sqlite3 *memdb = get_memdb();
|
|
|
|
// Remove expired sessions from database
|
|
SQL_bool(memdb, "DELETE FROM disk.session WHERE valid_until < strftime('%%s', 'now');");
|
|
|
|
// Get all sessions from database
|
|
sqlite3_stmt *stmt = NULL;
|
|
if(sqlite3_prepare_v2(memdb, "SELECT login_at, valid_until, remote_addr, user_agent, sid, csrf, tls_login, tls_mixed, app, cli, x_forwarded_for FROM disk.session;", -1, &stmt, 0) != SQLITE_OK)
|
|
{
|
|
log_err("SQL error in restore_db_sessions(): %s (%d)",
|
|
sqlite3_errmsg(memdb), sqlite3_errcode(memdb));
|
|
return false;
|
|
}
|
|
|
|
// Iterate over all still valid sessions
|
|
unsigned int i = 0;
|
|
while(sqlite3_step(stmt) == SQLITE_ROW && i < max_sessions)
|
|
{
|
|
// Allocate memory for new session
|
|
struct session *sess = &sessions[i];
|
|
|
|
// Get values from database
|
|
// 1: login_at
|
|
sess->login_at = sqlite3_column_int64(stmt, 0);
|
|
|
|
// 2: valid_until
|
|
sess->valid_until = sqlite3_column_int64(stmt, 1);
|
|
|
|
// 3: remote_addr
|
|
const char *remote_addr = (const char *)sqlite3_column_text(stmt, 2);
|
|
if(remote_addr != NULL)
|
|
{
|
|
strncpy(sess->remote_addr, remote_addr, sizeof(sess->remote_addr)-1);
|
|
sess->remote_addr[sizeof(sess->remote_addr)-1] = '\0';
|
|
}
|
|
|
|
// 4: user_agent
|
|
const char *user_agent = (const char *)sqlite3_column_text(stmt, 3);
|
|
if(user_agent != NULL)
|
|
{
|
|
strncpy(sess->user_agent, user_agent, sizeof(sess->user_agent)-1);
|
|
sess->user_agent[sizeof(sess->user_agent)-1] = '\0';
|
|
}
|
|
|
|
// 5: sid
|
|
const char *sid = (const char *)sqlite3_column_text(stmt, 4);
|
|
if(sid != NULL)
|
|
{
|
|
strncpy(sess->sid, sid, sizeof(sess->sid)-1);
|
|
sess->sid[sizeof(sess->sid)-1] = '\0';
|
|
}
|
|
|
|
// 6: csrf
|
|
const char *csrf = (const char *)sqlite3_column_text(stmt, 5);
|
|
if(csrf != NULL)
|
|
{
|
|
strncpy(sess->csrf, csrf, sizeof(sess->csrf)-1);
|
|
sess->csrf[sizeof(sess->csrf)-1] = '\0';
|
|
}
|
|
|
|
// 7: tls_login
|
|
sess->tls.login = sqlite3_column_int(stmt, 6) == 1 ? true : false;
|
|
|
|
// 8: tls_mixed
|
|
sess->tls.mixed = sqlite3_column_int(stmt, 7) == 1 ? true : false;
|
|
|
|
// 9: app
|
|
sess->app = sqlite3_column_int(stmt, 8) == 1 ? true : false;
|
|
|
|
// 10: cli
|
|
sess->cli = sqlite3_column_int(stmt, 9) == 1 ? true : false;
|
|
|
|
// 11: x_forwarded_for
|
|
const char *x_forwarded_for = (const char *)sqlite3_column_text(stmt, 10);
|
|
if(x_forwarded_for != NULL)
|
|
{
|
|
strncpy(sess->x_forwarded_for, x_forwarded_for, sizeof(sess->x_forwarded_for)-1);
|
|
sess->x_forwarded_for[sizeof(sess->x_forwarded_for)-1] = '\0';
|
|
}
|
|
|
|
// Mark session as used
|
|
sess->used = true;
|
|
|
|
i++;
|
|
}
|
|
|
|
log_info("Restored %u API session%s from the database",
|
|
i, i == 1 ? "" : "s");
|
|
|
|
// Finalize statement
|
|
if(sqlite3_finalize(stmt) != SQLITE_OK)
|
|
{
|
|
log_err("SQL error in restore_db_sessions(): %s (%d)",
|
|
sqlite3_errmsg(memdb), sqlite3_errcode(memdb));
|
|
return false;
|
|
}
|
|
|
|
// Delete all sessions from database after restoring them
|
|
// We use secure_delete to make sure the sessions are really gone
|
|
// In this mode, SQLite overwrites the deleted content with zeros
|
|
// (https://www.sqlite.org/pragma.html#pragma_secure_delete)
|
|
SQL_bool(memdb, "PRAGMA secure_delete = ON;");
|
|
SQL_bool(memdb, "DELETE FROM disk.session;");
|
|
SQL_bool(memdb, "PRAGMA secure_delete = OFF;");
|
|
|
|
return true;
|
|
}
|