Add simple /api/auth implementation. Currently, all passwords are accepted and a login session is valid for 5 minutes.

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER
2019-11-16 12:25:20 +01:00
parent f500466a1e
commit 0a3424bcff
10 changed files with 202 additions and 8 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ DNSMASQ_OPTS = -DHAVE_DNSSEC -DHAVE_DNSSEC_STATIC -DHAVE_IDN
FTL_DEPS = *.h database/*.h api/*.h version.h
FTL_DB_OBJ = database/common.o database/query-table.o database/network-table.o database/gravity-db.o database/database-thread.o \
database/sqlite3-ext.o database/message-table.o
FTL_API_OBJ = api/http.o api/ftl.o api/stats.o api/dns.o api/version.o
FTL_API_OBJ = api/http.o api/ftl.o api/stats.o api/dns.o api/version.o api/auth.o api/settings.o
FTL_OBJ = $(FTL_DB_OBJ) $(FTL_API_OBJ) main.o memory.o log.o daemon.o datastructure.o signals.o files.o setupVars.o args.o gc.o config.o dnsmasq_interface.o resolve.o regex.o shmem.o capabilities.o overTime.o timers.o vector.o
DNSMASQ_DEPS = config.h dhcp-protocol.h dns-protocol.h radv-protocol.h dhcp6-protocol.h dnsmasq.h ip6addr.h metrics.h ../dnsmasq_interface.h
+4
View File
@@ -101,6 +101,10 @@
// Default: 1000 (one second)
#define DATABASE_BUSY_TIMEOUT 1000
// After how much time does a valid API session expire? [seconds]
// Default: 300 (five minutes)
#define API_SESSION_EXPIRE 300
// FTLDNS enums
enum { QUERIES, UPSTREAMS, CLIENTS, DOMAINS, OVERTIME, WILDCARD, DNS_CACHE };
enum { DNSSEC_UNSPECIFIED, DNSSEC_SECURE, DNSSEC_INSECURE, DNSSEC_BOGUS, DNSSEC_ABANDONED };
+6
View File
@@ -41,4 +41,10 @@ int api_dns_somelist(struct mg_connection *conn,
// Version method
int api_version(struct mg_connection *conn);
// Auth method
int api_auth(struct mg_connection *conn);
// Settings methods
int api_settings_web(struct mg_connection *conn);
#endif // API_H
+67
View File
@@ -0,0 +1,67 @@
/* Pi-hole: A black hole for Internet advertisements
* (c) 2019 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* FTL Engine
* API Implementation /api/auth
*
* 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 "api.h"
#include "log.h"
#include "config.h"
int api_auth(struct mg_connection *conn)
{
bool auth = false;
// Does the client try to authenticate through a set header?
const char *xHeader = mg_get_header(conn, "X-Pi-hole-Authenticate");
if(xHeader != NULL && strlen(xHeader) > 0)
{
auth = true;
if(config.debug & DEBUG_API)
logg("Received X-Pi-hole-Authenticate: %s", xHeader);
}
// Does the client provide a user_id cookie?
int user_id = 0;
if(http_get_cookie_int(conn, "user_id", &user_id))
{
auth = true;
if(config.debug & DEBUG_API)
logg("Read user_id=%i from user-provided cookie", user_id);
}
cJSON *json = JSON_NEW_OBJ();
if(auth)
{
if(config.debug & DEBUG_API)
logg("Authentification: OK");
JSON_OBJ_REF_STR(json, "status", "success");
// Ten minutes validity
char *additional_headers = NULL;
if(asprintf(&additional_headers, "Set-Cookie: user_id=%u; Path=/; Max-Age=%u\r\n", 1, API_SESSION_EXPIRE) > 0)
{
JSON_SENT_OBJECT_AND_HEADERS(json, additional_headers);
}
else
{
JSON_SENT_OBJECT(json);
}
}
else
{
if(config.debug & DEBUG_API)
logg("Authentification: FAIL");
JSON_OBJ_REF_STR(json, "key", "unauthorized");
char *additional_headers = strdup("Set-Cookie: user_id=deleted; Path=/; Max-Age=-1\r\n");
JSON_SENT_OBJECT_AND_HEADERS_CODE(json, 401, additional_headers);
}
}
+47 -5
View File
@@ -18,12 +18,21 @@
// Server context handle
static struct mg_context *ctx = NULL;
int send_http(struct mg_connection *conn, const char *mime_type, const char *msg)
int send_http(struct mg_connection *conn, const char *mime_type,
const char *additional_headers, const char *msg)
{
mg_send_http_ok(conn, mime_type, strlen(msg));
mg_send_http_ok(conn, mime_type, additional_headers, strlen(msg));
return mg_write(conn, msg, strlen(msg));
}
int send_http_unauth(struct mg_connection *conn,
const char *additional_headers, const char *msg)
{
// MPayload will be sent with text/plain encoding due to
// the first line being "Error 401" by definition
return mg_send_http_error(conn, 401, "%s", msg);
}
int send_http_error(struct mg_connection *conn)
{
return mg_send_http_error(conn, 500, "Internal server error");
@@ -41,7 +50,7 @@ void __attribute__ ((format (gnu_printf, 3, 4))) http_send(struct mg_connection
if(!chunk)
{
// Send 200 HTTP header with content size
mg_send_http_ok(conn, "application/json", len);
mg_send_http_ok(conn, "application/json", NULL, len);
}
if(chunk && mg_send_chunk(conn, buffer, len) < 0)
{
@@ -60,7 +69,7 @@ void __attribute__ ((format (gnu_printf, 3, 4))) http_send(struct mg_connection
// Print passed string directly
static int print_simple(struct mg_connection *conn, void *input)
{
return send_http(conn, "text/plain", input);
return send_http(conn, "text/plain", NULL, input);
}
static int api_handler(struct mg_connection *conn, void *ignored)
@@ -161,6 +170,16 @@ static int api_handler(struct mg_connection *conn, void *ignored)
{
ret = api_version(conn);
}
/******************************** api/auth ****************************/
else if(strcasecmp("/api/auth", request->local_uri) == 0)
{
ret = api_auth(conn);
}
/******************************** api/settings ****************************/
else if(strcasecmp("/api/settings/web", request->local_uri) == 0)
{
ret = api_settings_web(conn);
}
/******************************** not found ******************************/
/* else
{
@@ -213,4 +232,27 @@ void http_terminate(void)
/* Un-initialize the library */
mg_exit_library();
}
}
bool http_get_cookie_int(struct mg_connection *conn, const char *cookieName, int *i)
{
// Maximum cookie length is 4KB
char cookieValue[4096];
const char *cookie = mg_get_header(conn, "Cookie");
if(mg_get_cookie(cookie, cookieName, cookieValue, sizeof(cookieValue)) > 0)
{
*i = atoi(cookieValue);
return true;
}
return false;
}
bool http_get_cookie_str(struct mg_connection *conn, const char *cookieName, char *str, size_t str_size)
{
const char *cookie = mg_get_header(conn, "Cookie");
if(mg_get_cookie(cookie, cookieName, str, str_size) > 0)
{
return true;
}
return false;
}
+8 -1
View File
@@ -24,9 +24,16 @@ void http_init(void);
void http_terminate(void);
void http_send(struct mg_connection *conn, bool chunk, const char *format, ...) __attribute__ ((format (gnu_printf, 3, 4)));
int send_http(struct mg_connection *conn, const char *mime_type, const char *msg);
int send_http(struct mg_connection *conn, const char *mime_type,
const char *additional_headers, const char *msg);
int send_http_unauth(struct mg_connection *conn,
const char *additional_headers, const char *msg);
int send_http_error(struct mg_connection *conn);
// Cookie routines
bool http_get_cookie_int(struct mg_connection *conn, const char *cookieName, int *i);
bool http_get_cookie_str(struct mg_connection *conn, const char *cookieName, char *str, size_t str_size);
// HTTP macros
#define GET_VAR(variable, destination, source) mg_get_var(source, strlen(source), variable, destination, sizeof(destination))
+40 -1
View File
@@ -77,6 +77,17 @@
cJSON_AddItemToObject(object, key, null_item); \
}
#define JSON_OBJ_ADD_BOOL(object, key, value) {\
cJSON *bool_item = cJSON_CreateBool(value); \
if(bool_item == NULL) \
{ \
cJSON_Delete(object); \
send_http_error(conn); \
return 500; \
} \
cJSON_AddItemToObject(object, key, bool_item); \
}
#define JSON_ARRAY_ADD_NUMBER(object, number){ \
cJSON *number_item = cJSON_CreateNumber((double)number); \
cJSON_AddItemToArray(object, number_item); \
@@ -119,7 +130,35 @@
send_http_error(conn); \
return 500; \
} \
send_http(conn, "application/json", msg); \
send_http(conn, "application/json", NULL, msg); \
cJSON_Delete(object); \
return 200; \
}
#define JSON_SENT_OBJECT_AND_HEADERS(object, additional_headers){ \
const char* msg = JSON_FORMATTER(object); \
if(msg == NULL) \
{ \
cJSON_Delete(object); \
send_http_error(conn); \
return 500; \
} \
send_http(conn, "application/json", additional_headers, msg); \
cJSON_Delete(object); \
free(additional_headers); \
return 200; \
}
#define JSON_SENT_OBJECT_AND_HEADERS_CODE(object, code, additional_headers){ \
const char* msg = JSON_FORMATTER(object); \
if(msg == NULL) \
{ \
cJSON_Delete(object); \
send_http_error(conn); \
return 500; \
} \
send_http_unauth(conn, additional_headers, msg); \
cJSON_Delete(object); \
free(additional_headers); \
return code; \
}
+20
View File
@@ -0,0 +1,20 @@
/* Pi-hole: A black hole for Internet advertisements
* (c) 2019 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* FTL Engine
* API Implementation /api/settings
*
* 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 "api.h"
int api_settings_web(struct mg_connection *conn)
{
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_REF_STR(json, "layout", "boxed");
JSON_OBJ_REF_STR(json, "language", "en");
JSON_SENT_OBJECT(json);
}
+6
View File
@@ -4802,6 +4802,7 @@ mg_send_http_error(struct mg_connection *conn, int status, const char *fmt, ...)
int
mg_send_http_ok(struct mg_connection *conn,
const char *mime_type,
const char *additional_headers,
long long content_length)
{
char date[64];
@@ -4823,6 +4824,11 @@ mg_send_http_ok(struct mg_connection *conn,
date,
suggest_connection_header(conn));
if(additional_headers != NULL && strlen(additional_headers) > 0)
{
mg_write(conn, additional_headers, strlen(additional_headers));
}
send_no_cache_header(conn);
send_additional_header(conn);
if (content_length < 0) {
+3
View File
@@ -939,6 +939,8 @@ CIVETWEB_API int mg_send_http_error(struct mg_connection *conn,
* Parameters:
* conn: Current connection handle.
* mime_type: Set Content-Type for the following content.
* additional_header: Pi-hole addition for sending custom additional
* information (like settingcookies)
* content_length: Size of the following content, if content_length >= 0.
* Will set transfer-encoding to chunked, if set to -1.
* Return:
@@ -946,6 +948,7 @@ CIVETWEB_API int mg_send_http_error(struct mg_connection *conn,
*/
CIVETWEB_API int mg_send_http_ok(struct mg_connection *conn,
const char *mime_type,
const char *additional_headers,
long long content_length);