mirror of
https://github.com/pi-hole/FTL.git
synced 2024-10-26 16:52:18 +02:00
+50
-3
@@ -25,6 +25,8 @@
|
||||
#include "config/password.h"
|
||||
// database session functions
|
||||
#include "database/session-table.h"
|
||||
// base64_decode()
|
||||
#include "config/password.h"
|
||||
|
||||
static struct session auth_data[API_MAX_CLIENTS] = {{false, false, {false, false}, 0, 0, {0}, {0}, {0}, {0}}};
|
||||
|
||||
@@ -400,6 +402,47 @@ static void generateSID(char *sid)
|
||||
sid[SID_SIZE-1] = '\0';
|
||||
}
|
||||
|
||||
static char *basic_auth(struct ftl_conn *api)
|
||||
{
|
||||
const char *auth_header = mg_get_header(api->conn, "Authorization");
|
||||
if(auth_header == NULL)
|
||||
return NULL;
|
||||
|
||||
// Check if this is a Basic Auth header
|
||||
if(strncmp(auth_header, "Basic ", 6) != 0)
|
||||
return NULL;
|
||||
|
||||
// Decode Base64
|
||||
size_t length = 0;
|
||||
char *decoded = (char*)base64_decode(auth_header + 6, &length);
|
||||
if(decoded == NULL)
|
||||
return NULL;
|
||||
|
||||
// Extract username and password
|
||||
char *username = decoded;
|
||||
char *password = strchr(decoded, ':');
|
||||
if(password == NULL)
|
||||
{
|
||||
free(decoded);
|
||||
return NULL;
|
||||
}
|
||||
*password = '\0';
|
||||
password++;
|
||||
|
||||
// Check if username is correct
|
||||
if(strcmp(username, "pi-hole") != 0)
|
||||
{
|
||||
free(decoded);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *password_copy = strdup(password);
|
||||
free(decoded);
|
||||
|
||||
// Return copy of password
|
||||
return password_copy;
|
||||
}
|
||||
|
||||
// api/auth
|
||||
// GET: Check authentication
|
||||
// POST: Login
|
||||
@@ -417,6 +460,9 @@ int api_auth(struct ftl_conn *api)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Did the client authenticate before and we can validate this?
|
||||
int user_id = check_client_auth(api, false);
|
||||
|
||||
// Login attempt, check password
|
||||
if(api->method == HTTP_POST)
|
||||
{
|
||||
@@ -462,8 +508,9 @@ int api_auth(struct ftl_conn *api)
|
||||
password = json_password->valuestring;
|
||||
}
|
||||
|
||||
// Did the client authenticate before and we can validate this?
|
||||
int user_id = check_client_auth(api, false);
|
||||
// If there is no password, check if user provided a password via HTTP Basic Auth
|
||||
if(password == NULL || strlen(password) == 0)
|
||||
password = basic_auth(api);
|
||||
|
||||
// If this is a valid session, we can exit early at this point if no password is supplied
|
||||
if(user_id != API_AUTH_UNAUTHORIZED && (password == NULL || strlen(password) == 0))
|
||||
@@ -484,7 +531,7 @@ int api_auth(struct ftl_conn *api)
|
||||
// - Client tries to authenticate using a password, or
|
||||
// - There no password on this machine
|
||||
enum password_result result = PASSWORD_INCORRECT;
|
||||
|
||||
|
||||
// If there is no password (or empty), check if there is any password at all
|
||||
log_info("Password: \"%s\"", password);
|
||||
if(empty_password && (password == NULL || strlen(password) == 0))
|
||||
|
||||
@@ -9,7 +9,7 @@ components:
|
||||
operationId: "get_auth"
|
||||
security: []
|
||||
description: |
|
||||
The API may chose to reply with a valid session if no authentication is needed for this server.
|
||||
The API may chose to reply with a valid session if no authentication is needed for this server. It is possible to initiate a session using HTTP Basic Auth (username: `pi-hole`).
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
|
||||
@@ -77,7 +77,7 @@ static char * __attribute__((malloc)) double_sha256_password(const char *passwor
|
||||
return strdup(response);
|
||||
}
|
||||
|
||||
static char * __attribute__((malloc)) base64_encode(const uint8_t *data, const size_t length)
|
||||
char * __attribute__((malloc)) base64_encode(const uint8_t *data, const size_t length)
|
||||
{
|
||||
// Base64 encoding requires 4 bytes for every 3 bytes of input, plus
|
||||
// additional bytes for padding. The output buffer must be large enough
|
||||
@@ -94,7 +94,7 @@ static char * __attribute__((malloc)) base64_encode(const uint8_t *data, const s
|
||||
return encoded;
|
||||
}
|
||||
|
||||
static uint8_t * __attribute__((malloc)) base64_decode(const char *data, size_t *length)
|
||||
uint8_t * __attribute__((malloc)) base64_decode(const char *data, size_t *length)
|
||||
{
|
||||
// Base64 decoding requires 3 bytes for every 4 bytes of input, plus
|
||||
// additional bytes for padding. The output buffer must be large enough
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <unistd.h>
|
||||
|
||||
void sha256_raw_to_hex(uint8_t *data, char *buffer);
|
||||
char * __attribute__((malloc)) base64_encode(const uint8_t *data, const size_t length);
|
||||
uint8_t * __attribute__((malloc)) base64_decode(const char *data, size_t *length);
|
||||
char *create_password(const char *password) __attribute__((malloc));
|
||||
enum password_result verify_login(const char *password);
|
||||
enum password_result verify_password(const char *password, const char *pwhash, const bool rate_limiting);
|
||||
@@ -30,6 +32,6 @@ enum password_result {
|
||||
} __attribute__((packed));
|
||||
|
||||
// The maximum number of password attempts per second
|
||||
#define MAX_PASSWORD_ATTEMPTS_PER_SECOND 3
|
||||
#define MAX_PASSWORD_ATTEMPTS_PER_SECOND 6
|
||||
|
||||
#endif //PASSWORD_H
|
||||
|
||||
@@ -1337,6 +1337,18 @@
|
||||
[[ ${lines[0]} == "true" ]]
|
||||
}
|
||||
|
||||
@test "API authorization (HTTP Basic Auth): Incorrect password is rejected if password auth is enabled" {
|
||||
run bash -c 'curl -s -u pi-hole:XXX 127.0.0.1/api/auth | jq .session.valid'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == 'false' ]]
|
||||
}
|
||||
|
||||
@test "API authorization (HTTP Basic Auth): Correct password is accepted" {
|
||||
run bash -c 'curl -s -u pi-hole:ABC 127.0.0.1/api/auth | jq .session.valid'
|
||||
printf "%s\n" "${lines[@]}"
|
||||
[[ ${lines[0]} == 'true' ]]
|
||||
}
|
||||
|
||||
@test "Test TLS/SSL server using self-signed certificate" {
|
||||
# -s: silent
|
||||
# -I: HEAD request
|
||||
|
||||
Reference in New Issue
Block a user