mirror of
https://github.com/pi-hole/FTL.git
synced 2024-10-26 16:52:18 +02:00
1556b72abe
Signed-off-by: DL6ER <dl6er@dl6er.de>
187 lines
6.2 KiB
C
187 lines
6.2 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
|
|
* Lua-related webserver 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 "webserver/lua_web.h"
|
|
#include "api/api.h"
|
|
|
|
// luaL_dostring()
|
|
#include "lua/lauxlib.h"
|
|
// struct config
|
|
#include "config/config.h"
|
|
// log_web()
|
|
#include "log.h"
|
|
|
|
// directory_exists()
|
|
#include "files.h"
|
|
|
|
static char *login_uri = NULL, *admin_api_uri = NULL;
|
|
void allocate_lua(void)
|
|
{
|
|
// Build login URI string (webhome + login)
|
|
// Append "login" to webhome string
|
|
const size_t login_uri_len = strlen(config.webserver.paths.webhome.v.s);
|
|
login_uri = calloc(login_uri_len + 6, sizeof(char));
|
|
memcpy(login_uri, config.webserver.paths.webhome.v.s, login_uri_len);
|
|
strcpy(login_uri + login_uri_len, "login");
|
|
login_uri[login_uri_len + 5u] = '\0';
|
|
log_debug(DEBUG_API, "Login URI: %s", login_uri);
|
|
|
|
// Build "wrong" API URI string (webhome + api)
|
|
// Append "api" to webhome string
|
|
const size_t admin_api_uri_len = strlen(config.webserver.paths.webhome.v.s);
|
|
admin_api_uri = calloc(admin_api_uri_len + 4, sizeof(char));
|
|
memcpy(admin_api_uri, config.webserver.paths.webhome.v.s, admin_api_uri_len);
|
|
strcpy(admin_api_uri + admin_api_uri_len, "api");
|
|
admin_api_uri[admin_api_uri_len + 3u] = '\0';
|
|
log_debug(DEBUG_API, "Admin API URI: %s", admin_api_uri);
|
|
}
|
|
|
|
void free_lua(void)
|
|
{
|
|
// Free login_uri
|
|
if(login_uri != NULL)
|
|
free(login_uri);
|
|
|
|
// Free admin_api_uri
|
|
if(admin_api_uri != NULL)
|
|
free(admin_api_uri);
|
|
}
|
|
|
|
void init_lua(const struct mg_connection *conn, void *L, unsigned context_flags)
|
|
{
|
|
// Set onerror handler to print errors to the log
|
|
if(luaL_dostring(L, "mg.onerror = function(e) mg.cry('Error at ' .. e) end") != LUA_OK)
|
|
{
|
|
log_err("Error setting Lua onerror handler: %s", lua_tostring(L, -1));
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
int request_handler(struct mg_connection *conn, void *cbdata)
|
|
{
|
|
// Fall back to CivetWeb's default handler if login URI is not available
|
|
// (should never happen)
|
|
if(login_uri == NULL || admin_api_uri == NULL)
|
|
return 0;
|
|
|
|
/* Handler may access the request info using mg_get_request_info */
|
|
const struct mg_request_info *req_info = mg_get_request_info(conn);
|
|
const size_t uri_raw_len = strlen(req_info->local_uri_raw);
|
|
|
|
// Build minimal api struct to check authentication
|
|
struct ftl_conn api = { 0 };
|
|
api.conn = conn;
|
|
api.request = req_info;
|
|
api.now = double_time();
|
|
|
|
// Check if the request is for the API under /admin/api
|
|
// (it is posted at /api)
|
|
if(strncmp(req_info->local_uri_raw, admin_api_uri, strlen(admin_api_uri)) == 0)
|
|
{
|
|
const size_t hint_len = 38 + strlen(admin_api_uri) + 2*strlen(config.webserver.domain.v.s);
|
|
char *hint = calloc(hint_len, sizeof(char));
|
|
snprintf(hint, hint_len, "The API is hosted at %s/api, not %s%s",
|
|
config.webserver.domain.v.s, config.webserver.domain.v.s, admin_api_uri);
|
|
hint[hint_len - 1u] = '\0';
|
|
// not found or invalid request
|
|
return send_json_error_free(&api, 400,
|
|
"bad_request",
|
|
"Bad request",
|
|
hint,
|
|
true);
|
|
}
|
|
|
|
// Check if last part of the URI contains a dot (is a file)
|
|
const char *last_dot = strrchr(req_info->local_uri_raw, '.');
|
|
const char *last_slash = strrchr(req_info->local_uri_raw, '/');
|
|
const bool no_dot = (last_dot == NULL || last_slash > last_dot);
|
|
|
|
// Check if the request is for the login page
|
|
const bool login = (strcmp(req_info->local_uri_raw, login_uri) == 0);
|
|
|
|
// Check if the request is for a LUA page (every XYZ.lp has already been
|
|
// rewritten at this point to XYZ)
|
|
if(!no_dot)
|
|
{
|
|
// Not a LUA page - fall back to CivetWeb's default handler
|
|
return 0;
|
|
}
|
|
|
|
// Every LUA page except admin/login requires authentication
|
|
const int authorized = check_client_auth(&api, false) != API_AUTH_UNAUTHORIZED;
|
|
if(!login)
|
|
{
|
|
// This is not the login page - check if the user is authenticated
|
|
// Check if the user is authenticated
|
|
if(!authorized)
|
|
{
|
|
// Append query string to target
|
|
char *target = NULL;
|
|
if(req_info->query_string != NULL)
|
|
{
|
|
target = calloc(uri_raw_len + strlen(req_info->query_string) + 2u, sizeof(char));
|
|
strcpy(target, req_info->local_uri_raw);
|
|
strcat(target, "?");
|
|
strcat(target, req_info->query_string);
|
|
}
|
|
else
|
|
{
|
|
target = strdup(req_info->local_uri_raw);
|
|
}
|
|
if(target == NULL)
|
|
{
|
|
log_err("Error allocating memory for redirection target");
|
|
return send_json_error(&api, 500,
|
|
"internal_error",
|
|
"Internal server error",
|
|
"Cannot allocate memory for redirection target");
|
|
}
|
|
|
|
// Encode target string
|
|
const size_t encoded_target_len = strlen(target) * 3u + 1u;
|
|
char *encoded_target = calloc(encoded_target_len, sizeof(char));
|
|
if(encoded_target == NULL)
|
|
{
|
|
log_err("Error allocating memory for encoded redirection target");
|
|
return send_json_error(&api, 500,
|
|
"internal_error",
|
|
"Internal server error",
|
|
"Cannot allocate memory for encoded redirection target");
|
|
}
|
|
|
|
// Encode target string
|
|
mg_url_encode(target, encoded_target, encoded_target_len);
|
|
free(target);
|
|
|
|
// User is not authenticated, redirect to login page
|
|
log_web("Authentication required, redirecting to %slogin?target=%s", config.webserver.paths.webhome.v.s, encoded_target);
|
|
mg_printf(conn, "HTTP/1.1 302 Found\r\nLocation: %slogin?target=%s\r\n\r\n", config.webserver.paths.webhome.v.s, encoded_target);
|
|
free(encoded_target);
|
|
return 302;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is the login page - check if the user is already authenticated
|
|
// Check if the user is authenticated
|
|
if(authorized)
|
|
{
|
|
// User is already authenticated, redirect to index page
|
|
log_web("User is already authenticated, redirecting to %s", config.webserver.paths.webhome.v.s);
|
|
mg_printf(conn, "HTTP/1.1 302 Found\r\nLocation: %s\r\n\r\n", config.webserver.paths.webhome.v.s);
|
|
return 302;
|
|
}
|
|
}
|
|
|
|
// No special handling required, fall back to default CivetWeb handler
|
|
return 0;
|
|
}
|