Add Basic Auth authentication

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER
2023-10-29 20:35:28 +01:00
parent 1600fd06d8
commit f45faffa8c
5 changed files with 68 additions and 7 deletions
+50 -3
View File
@@ -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))
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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
+3 -1
View File
@@ -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
+12
View File
@@ -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