1
0
mirror of https://github.com/weechat/weechat.git synced 2026-07-07 18:23:13 +02:00

relay: allow password hash authentication in api relay, add option relay.network.time_window (issue #2066)

This commit is contained in:
Sébastien Helleu
2024-01-30 18:32:21 +01:00
parent 70907fc169
commit 83567fd871
14 changed files with 637 additions and 228 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ New features::
* core: use function util_strftimeval in evaluation of expression `date:xxx`
* api: add support of specifier `%!` for timestamp in function util_strftimeval
* api: add support of base64url in encode/decode functions
* relay: add "api" protocol (HTTP REST API), add option relay.look.display_clients, change option type relay.look.auto_open_buffer to string, rename option relay.weechat.commands to relay.network.commands (issue #2066)
* relay: add "api" protocol (HTTP REST API), add option relay.look.display_clients, change option type relay.look.auto_open_buffer to string, rename option relay.weechat.commands to relay.network.commands, add option relay.network.time_window (issue #2066)
* relay: add support of websocket extension "permessage-deflate" (issue #1549)
Bug fixes::
+2 -55
View File
@@ -38,60 +38,6 @@
#include "relay-api-protocol.h"
/*
* Checks authentication from client.
*
* Returns:
* 1: OK, client authenticated
* 0: client NOT authenticated
*/
int
relay_api_protocol_check_auth (struct t_relay_client *client,
struct t_relay_http_request *request)
{
if (client->status == RELAY_STATUS_CONNECTED)
return 1;
switch (relay_http_check_auth (request))
{
case 0: /* OK */
return 1;
case -1: /* missing password */
relay_api_msg_send_error_json (client,
RELAY_HTTP_401_UNAUTHORIZED,
"WWW-Authenticate: Basic realm=Password",
RELAY_HTTP_ERROR_MISSING_PASSWORD);
break;
case -2: /* invalid password */
relay_api_msg_send_error_json (client,
RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_INVALID_PASSWORD);
break;
case -3: /* missing TOTP */
relay_api_msg_send_error_json (client,
RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_MISSING_TOTP);
break;
case -4: /* invalid TOTP */
relay_api_msg_send_error_json (client,
RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_INVALID_TOTP);
break;
case -5: /* out of memory */
relay_api_msg_send_error_json (client,
RELAY_HTTP_503_SERVICE_UNAVAILABLE,
NULL,
RELAY_HTTP_ERROR_OUT_OF_MEMORY);
break;
}
return 0;
}
/*
* Returns value of an URL parameter as boolean (0 or 1), using a default value
* if the parameter is not set or if it's not a valid boolean.
@@ -677,7 +623,8 @@ relay_api_protocol_recv_http (struct t_relay_client *client,
if (!request || RELAY_CLIENT_HAS_ENDED(client))
return;
if (!relay_api_protocol_check_auth (client, request))
if ((client->status != RELAY_STATUS_CONNECTED)
&& !relay_http_check_auth (client, request))
{
relay_client_set_status (client, RELAY_STATUS_AUTH_FAILED);
return;
+1 -1
View File
@@ -134,7 +134,7 @@ relay_api_unhook_signals (struct t_relay_client *client)
void
relay_api_recv_http (struct t_relay_client *client,
struct t_relay_http_request *request)
struct t_relay_http_request *request)
{
relay_api_protocol_recv_http (client, request);
}
+172 -64
View File
@@ -22,6 +22,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <gcrypt.h>
#include "../weechat-plugin.h"
@@ -103,37 +104,28 @@ relay_auth_generate_nonce (int size)
* Checks if password received as plain text is valid.
*
* Returns:
* 1: password is valid
* 0: password is not valid
* 0: password is valid
* -1: (plain-text password ("plain") is not allowed
* -2: password is not valid
*/
int
relay_auth_check_password_plain (const char *password,
relay_auth_check_password_plain (struct t_relay_client *client,
const char *password,
const char *relay_password)
{
if (!password || !relay_password)
return 0;
if (!client || !password || !relay_password)
return -2;
return (strcmp (password, relay_password) == 0) ? 1 : 0;
}
/*
* Authenticates with password (plain text).
*
* Returns:
* 1: authentication OK
* 0: authentication failed
*/
int
relay_auth_password (struct t_relay_client *client,
const char *password, const char *relay_password)
{
if (client->password_hash_algo != RELAY_AUTH_PASSWORD_HASH_PLAIN)
return 0;
return relay_auth_check_password_plain (password, relay_password);
if (!weechat_string_match_list (
"plain",
(const char **)relay_config_network_password_hash_algo_list,
1))
{
return -1;
}
return (strcmp (password, relay_password) == 0) ? 0 : -2;
}
/*
@@ -145,6 +137,11 @@ relay_auth_password (struct t_relay_client *client,
*
* salt is the salt in hexadecimal
* hash is the hashed password with the parameters above, in hexadecimal
*
* If salt_hexa is not NULL, it is set with salt as hexadecimal, and the parsed
* salt is decoded and put in salt.
* If salt_hexa is NULL, parsed salt is considered not encoded and is put
* directly in salt.
*/
void
@@ -155,7 +152,8 @@ relay_auth_parse_sha (const char *parameters,
char **argv;
int argc;
*salt_hexa = NULL;
if (salt_hexa)
*salt_hexa = NULL;
*salt = NULL;
*salt_size = 0;
*hash = NULL;
@@ -174,18 +172,27 @@ relay_auth_parse_sha (const char *parameters,
}
/* parameter 1: salt */
*salt = malloc (strlen (argv[0]) + 1);
if (*salt)
if (salt_hexa)
{
*salt_size = weechat_string_base_decode ("16", argv[0], *salt);
if (*salt_size > 0)
*salt_hexa = strdup (argv[0]);
else
*salt = malloc (strlen (argv[0]) + 1);
if (*salt)
{
free (*salt);
*salt = NULL;
*salt_size = weechat_string_base_decode ("16", argv[0], *salt);
if (*salt_size > 0)
*salt_hexa = strdup (argv[0]);
else
{
free (*salt);
*salt = NULL;
}
}
}
else
{
*salt = strdup (argv[0]);
if (*salt)
*salt_size = strlen (*salt);
}
/* parameter 2: the SHA256 or SHA512 hash */
*hash = strdup (argv[1]);
@@ -203,6 +210,11 @@ relay_auth_parse_sha (const char *parameters,
* salt is the salt in hexadecimal
* iterations it the number of iterations (≥ 1)
* hash is the hashed password with the parameters above, in hexadecimal
*
* If salt_hexa is not NULL, it is set with salt as hexadecimal, and the parsed
* salt is decoded and put in salt.
* If salt_hexa is NULL, parsed salt is considered not encoded and is put
* directly in salt.
*/
void
@@ -213,7 +225,8 @@ relay_auth_parse_pbkdf2 (const char *parameters,
char **argv, *error;
int argc;
*salt_hexa = NULL;
if (salt_hexa)
*salt_hexa = NULL;
*salt = NULL;
*salt_size = 0;
*iterations = 0;
@@ -233,18 +246,27 @@ relay_auth_parse_pbkdf2 (const char *parameters,
}
/* parameter 1: salt */
*salt = malloc (strlen (argv[0]) + 1);
if (*salt)
if (salt_hexa)
{
*salt_size = weechat_string_base_decode ("16", argv[0], *salt);
if (*salt_size > 0)
*salt_hexa = strdup (argv[0]);
else
*salt = malloc (strlen (argv[0]) + 1);
if (*salt)
{
free (*salt);
*salt = NULL;
*salt_size = weechat_string_base_decode ("16", argv[0], *salt);
if (*salt_size > 0)
*salt_hexa = strdup (argv[0]);
else
{
free (*salt);
*salt = NULL;
}
}
}
else
{
*salt = strdup (argv[0]);
if (*salt)
*salt_size = strlen (*salt);
}
/* parameter 2: iterations */
error = NULL;
@@ -261,7 +283,12 @@ relay_auth_parse_pbkdf2 (const char *parameters,
/*
* Checks if the salt received from the client is valid.
*
* It is valid if both conditions are true:
* For "api" protocol, it is valid if both conditions are true:
* 1. the salt is a valid integer (unix timestamp)
* 2. the timestamp value is current timestamp (or +/- seconds, according to
* the option relay.network.time_window)
*
* For other protocols, it is valid if both conditions are true:
* 1. the salt is longer than the server nonce, so it means it includes a
* client nonce
* 2. the salt begins with the server nonce (client->nonce)
@@ -272,8 +299,31 @@ relay_auth_parse_pbkdf2 (const char *parameters,
*/
int
relay_auth_check_salt (struct t_relay_client *client, const char *salt_hexa)
relay_auth_check_salt (struct t_relay_client *client,
const char *salt_hexa, const char *salt, int salt_size)
{
long number;
int time_window;
char *error;
time_t time_now;
if (!client)
return 0;
if (client->protocol == RELAY_PROTOCOL_API)
{
if (!salt || (salt_size < 1))
return 0;
error = NULL;
number = strtol (salt, &error, 10);
if (!error || error[0])
return 0;
time_now = time (NULL);
time_window = weechat_config_integer (relay_config_network_time_window);
return ((number >= time_now - time_window)
&& (number <= time_now + time_window)) ? 1 : 0;
}
return (salt_hexa
&& client->nonce
&& (strlen (salt_hexa) > strlen (client->nonce))
@@ -370,8 +420,11 @@ relay_auth_check_hash_pbkdf2 (const char *hash_pbkdf2_algo,
* Authenticates with password hash.
*
* Returns:
* 1: authentication OK
* 0: authentication failed
* 0: authentication OK
* -1: invalid hash algorithm
* -2: invalid salt
* -3: invalid number of iterations
* -4: invalid password (hash)
*/
int
@@ -387,40 +440,83 @@ relay_auth_password_hash (struct t_relay_client *client,
str_hash_algo = NULL;
/* no authentication supported at all? */
if (client->password_hash_algo < 0)
/* no authentication supported at all with weechat protocol? */
if ((client->protocol == RELAY_PROTOCOL_WEECHAT)
&& (client->password_hash_algo < 0))
{
rc = -1;
goto end;
}
if (!hashed_password || !relay_password)
{
rc = -4;
goto end;
}
pos_hash = strchr (hashed_password, ':');
if (!pos_hash)
{
rc = -4;
goto end;
}
str_hash_algo = weechat_strndup (hashed_password,
pos_hash - hashed_password);
if (!str_hash_algo)
{
rc = -1;
goto end;
}
pos_hash++;
hash_algo = relay_auth_password_hash_algo_search (str_hash_algo);
if (hash_algo != client->password_hash_algo)
if (hash_algo < 0)
{
rc = -1;
goto end;
}
/* only algo negotiated in handshake is allowed for protocol "weechat" */
if ((client->protocol == RELAY_PROTOCOL_WEECHAT)
&& (hash_algo != client->password_hash_algo))
{
rc = -1;
goto end;
}
/* only algos matching allowed algos are allowed for protocol "api" */
if ((client->protocol == RELAY_PROTOCOL_API)
&& (!weechat_string_match_list (
relay_auth_password_hash_algo_name[hash_algo],
(const char **)relay_config_network_password_hash_algo_list,
1)))
{
rc = -1;
goto end;
}
salt_hexa = NULL;
switch (hash_algo)
{
case RELAY_AUTH_PASSWORD_HASH_SHA256:
case RELAY_AUTH_PASSWORD_HASH_SHA512:
relay_auth_parse_sha (pos_hash, &salt_hexa, &salt, &salt_size,
&hash_sha);
if (relay_auth_check_salt (client, salt_hexa)
&& relay_auth_check_hash_sha (str_hash_algo, salt, salt_size,
hash_sha, relay_password))
relay_auth_parse_sha (
pos_hash,
(client->protocol == RELAY_PROTOCOL_API) ? NULL : &salt_hexa,
&salt,
&salt_size,
&hash_sha);
if (!relay_auth_check_salt (client, salt_hexa, salt, salt_size))
{
rc = 1;
rc = -2;
}
else if (!relay_auth_check_hash_sha (str_hash_algo, salt, salt_size,
hash_sha, relay_password))
{
rc = -4;;
}
if (salt_hexa)
free (salt_hexa);
@@ -432,15 +528,26 @@ relay_auth_password_hash (struct t_relay_client *client,
case RELAY_AUTH_PASSWORD_HASH_PBKDF2_SHA256:
case RELAY_AUTH_PASSWORD_HASH_PBKDF2_SHA512:
hash_pbkdf2_algo = strdup (str_hash_algo + 7);
relay_auth_parse_pbkdf2 (pos_hash, &salt_hexa, &salt, &salt_size,
&iterations, &hash_pbkdf2);
if ((iterations == client->password_hash_iterations)
&& relay_auth_check_salt (client, salt_hexa)
&& relay_auth_check_hash_pbkdf2 (hash_pbkdf2_algo, salt,
salt_size, iterations,
hash_pbkdf2, relay_password))
relay_auth_parse_pbkdf2 (
pos_hash,
(client->protocol == RELAY_PROTOCOL_API) ? NULL : &salt_hexa,
&salt,
&salt_size,
&iterations,
&hash_pbkdf2);
if (iterations != weechat_config_integer (relay_config_network_password_hash_iterations))
{
rc = 1;
rc = -3;
}
else if (!relay_auth_check_salt (client, salt_hexa, salt, salt_size))
{
rc = -2;
}
else if (!relay_auth_check_hash_pbkdf2 (hash_pbkdf2_algo, salt,
salt_size, iterations,
hash_pbkdf2, relay_password))
{
rc = -4;
}
if (hash_pbkdf2_algo)
free (hash_pbkdf2_algo);
@@ -452,6 +559,7 @@ relay_auth_password_hash (struct t_relay_client *client,
free (hash_pbkdf2);
break;
case RELAY_NUM_PASSWORD_HASH_ALGOS:
rc = -4;
break;
}
+2 -6
View File
@@ -37,11 +37,9 @@ extern char *relay_auth_password_hash_algo_name[];
extern int relay_auth_password_hash_algo_search (const char *name);
extern char *relay_auth_generate_nonce (int size);
extern int relay_auth_check_password_plain (const char *password,
extern int relay_auth_check_password_plain (struct t_relay_client *client,
const char *password,
const char *relay_password);
extern int relay_auth_password (struct t_relay_client *client,
const char *password,
const char *relay_password);
extern void relay_auth_parse_sha (const char *parameters,
char **salt_hexa,
char **salt,
@@ -53,8 +51,6 @@ extern void relay_auth_parse_pbkdf2 (const char *parameters,
int *salt_size,
int *iterations,
char **hash);
extern int relay_auth_check_salt (struct t_relay_client *client,
const char *salt_hexa);
extern int relay_auth_check_hash_sha (const char *hash_algo,
const char *salt,
int salt_size,
-11
View File
@@ -1391,8 +1391,6 @@ relay_client_new (int sock, const char *address, struct t_relay_server *server)
(const char **)relay_config_network_password_hash_algo_list,
1);
new_client->password_hash_algo = (plain_text_password) ? 0 : -1;
new_client->password_hash_iterations = weechat_config_integer (
relay_config_network_password_hash_iterations);
new_client->listen_start_time = server->start_time;
new_client->start_time = time (NULL);
new_client->end_time = 0;
@@ -1665,12 +1663,6 @@ relay_client_new_with_infolist (struct t_infolist *infolist)
new_client->password_hash_algo = weechat_infolist_integer (infolist, "password_hash_algo");
else
new_client->password_hash_algo = RELAY_AUTH_PASSWORD_HASH_PLAIN;
/* "password_hash_iterations" is new in WeeChat 2.9 */
if (weechat_infolist_search_var (infolist, "password_hash_iterations"))
new_client->password_hash_iterations = weechat_infolist_integer (infolist, "password_hash_iterations");
else
new_client->password_hash_iterations = weechat_config_integer (
relay_config_network_password_hash_iterations);
new_client->listen_start_time = weechat_infolist_time (infolist, "listen_start_time");
new_client->start_time = weechat_infolist_time (infolist, "start_time");
new_client->end_time = weechat_infolist_time (infolist, "end_time");
@@ -2102,8 +2094,6 @@ relay_client_add_to_infolist (struct t_infolist *infolist,
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "password_hash_algo", client->password_hash_algo))
return 0;
if (!weechat_infolist_new_var_integer (ptr_item, "password_hash_iterations", client->password_hash_iterations))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "listen_start_time", client->listen_start_time))
return 0;
if (!weechat_infolist_new_var_time (ptr_item, "start_time", client->start_time))
@@ -2188,7 +2178,6 @@ relay_client_print_log ()
ptr_client->password_hash_algo,
(ptr_client->password_hash_algo >= 0) ?
relay_auth_password_hash_algo_name[ptr_client->password_hash_algo] : "");
weechat_log_printf (" password_hash_iterations. : %d", ptr_client->password_hash_iterations);
weechat_log_printf (" listen_start_time . . . . : %lld", (long long)ptr_client->listen_start_time);
weechat_log_printf (" start_time. . . . . . . . : %lld", (long long)ptr_client->start_time);
weechat_log_printf (" end_time. . . . . . . . . : %lld", (long long)ptr_client->end_time);
-1
View File
@@ -119,7 +119,6 @@ struct t_relay_client
/* example: server for irc protocol */
char *nonce; /* nonce used in salt of hashed pwd */
int password_hash_algo; /* password hash algo (negotiated) */
int password_hash_iterations; /* password hash iterations */
time_t listen_start_time; /* when listening started */
time_t start_time; /* time of client connection */
time_t end_time; /* time of client disconnection */
+8
View File
@@ -81,6 +81,7 @@ struct t_config_option *relay_config_network_nonce_size = NULL;
struct t_config_option *relay_config_network_password = NULL;
struct t_config_option *relay_config_network_password_hash_algo = NULL;
struct t_config_option *relay_config_network_password_hash_iterations = NULL;
struct t_config_option *relay_config_network_time_window = NULL;
struct t_config_option *relay_config_network_tls_cert_key = NULL;
struct t_config_option *relay_config_network_tls_priorities = NULL;
struct t_config_option *relay_config_network_totp_secret = NULL;
@@ -1370,6 +1371,13 @@ relay_config_init ()
"if your CPU is slow"),
NULL, 1, 1000000, "100000", NULL, 0,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
relay_config_network_time_window = weechat_config_new_option (
relay_config_file, relay_config_section_network,
"time_window", "integer",
N_("number of seconds to allow before and after the current time "
"for salted password in api protocol"),
NULL, 0, 256, "5", NULL, 0,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
relay_config_network_tls_cert_key = weechat_config_new_option (
relay_config_file, relay_config_section_network,
"tls_cert_key", "string",
+1
View File
@@ -53,6 +53,7 @@ extern struct t_config_option *relay_config_network_nonce_size;
extern struct t_config_option *relay_config_network_password;
extern struct t_config_option *relay_config_network_password_hash_algo;
extern struct t_config_option *relay_config_network_password_hash_iterations;
extern struct t_config_option *relay_config_network_time_window;
extern struct t_config_option *relay_config_network_tls_cert_key;
extern struct t_config_option *relay_config_network_tls_priorities;
extern struct t_config_option *relay_config_network_totp_secret;
+121 -51
View File
@@ -31,6 +31,7 @@
#include "../weechat-plugin.h"
#include "relay.h"
#include "relay-auth.h"
#include "relay-client.h"
#include "relay-config.h"
#include "relay-http.h"
@@ -514,7 +515,7 @@ relay_http_add_to_body (struct t_relay_http_request *request,
}
/*
* Checks if authentication is OK.
* Gets authentication status according to headers in the request.
*
* Returns:
* 0: authentication OK (password + TOTP if enabled)
@@ -522,11 +523,15 @@ relay_http_add_to_body (struct t_relay_http_request *request,
* -2: invalid password
* -3: missing TOTP
* -4: invalid TOTP
* -5: out of memory
* -5: invalid hash algorithm
* -6: invalid salt
* -7: invalid number of iterations (PBKDF2)
* -8: out of memory
*/
int
relay_http_check_auth (struct t_relay_http_request *request)
relay_http_get_auth_status (struct t_relay_client *client,
struct t_relay_http_request *request)
{
const char *auth, *client_totp, *pos;
char *relay_password, *totp_secret, *info_totp_args, *info_totp;
@@ -538,6 +543,15 @@ relay_http_check_auth (struct t_relay_http_request *request)
totp_secret = NULL;
user_pass = NULL;
relay_password = weechat_string_eval_expression (
weechat_config_string (relay_config_network_password),
NULL, NULL, NULL);
if (!relay_password)
{
rc = -8;
goto end;
}
auth = weechat_hashtable_get (request->headers, "authorization");
if (!auth || (weechat_strncasecmp (auth, "basic ", 6) != 0))
{
@@ -555,7 +569,7 @@ relay_http_check_auth (struct t_relay_http_request *request)
user_pass = malloc (length + 1);
if (!user_pass)
{
rc = -5;
rc = -8;
goto end;
}
length = weechat_string_base_decode ("64", pos, user_pass);
@@ -564,23 +578,43 @@ relay_http_check_auth (struct t_relay_http_request *request)
rc = -2;
goto end;
}
if (strncmp (user_pass, "weechat:", 8) != 0)
if (strncmp (user_pass, "plain:", 6) == 0)
{
rc = -2;
goto end;
switch (relay_auth_check_password_plain (client, user_pass + 6, relay_password))
{
case 0: /* password OK */
break;
case -1: /* "plain" is not allowed */
rc = -5;
goto end;
case -2: /* invalid password */
default:
rc = -2;
goto end;
}
}
relay_password = weechat_string_eval_expression (
weechat_config_string (relay_config_network_password),
NULL, NULL, NULL);
if (!relay_password)
else if (strncmp (user_pass, "hash:", 5) == 0)
{
rc = -5;
goto end;
switch (relay_auth_password_hash (client, user_pass + 5, relay_password))
{
case 0: /* password OK */
break;
case -1: /* invalid hash algorithm */
rc = -5;
goto end;
case -2: /* invalid salt */
rc = -6;
goto end;
case -3: /* invalid iterations */
rc = -7;
goto end;
case -4: /* invalid password */
default:
rc = -2;
goto end;
}
}
if (strcmp (user_pass + 8, relay_password) != 0)
else
{
rc = -2;
goto end;
@@ -631,6 +665,69 @@ end:
return rc;
}
/*
* Checks authentication in HTTP request.
*
* Returns:
* 1: authentication OK
* 0: authentication failed
*/
int
relay_http_check_auth (struct t_relay_client *client,
struct t_relay_http_request *request)
{
int rc;
rc = relay_http_get_auth_status (client, request);
switch (rc)
{
case 0: /* authentication OK */
break;
case -1: /* missing password */
relay_http_send_error_json (client, RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_MISSING_PASSWORD);
break;
case -2: /* invalid password */
relay_http_send_error_json (client, RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_INVALID_PASSWORD);
break;
case -3: /* missing TOTP */
relay_http_send_error_json (client, RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_MISSING_TOTP);
break;
case -4: /* invalid TOTP */
relay_http_send_error_json (client, RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_INVALID_TOTP);
break;
case -5: /* invalid hash algorithm */
relay_http_send_error_json (client, RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_INVALID_HASH_ALGO);
break;
case -6: /* invalid salt */
relay_http_send_error_json (client, RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_INVALID_SALT);
break;
case -7: /* invalid iterations */
relay_http_send_error_json (client, RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_INVALID_ITERATIONS);
break;
case -8: /* out of memory */
relay_http_send_error_json (client, RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_OUT_OF_MEMORY);
break;
}
return (rc == 0) ? 1 : 0;
}
/*
* Processes HTTP websocket request.
*/
@@ -681,41 +778,14 @@ relay_http_process_websocket (struct t_relay_client *client)
/* handshake from client is valid, auth is mandatory for "api" protocol */
if (client->protocol == RELAY_PROTOCOL_API)
{
switch (relay_http_check_auth (client->http_req))
if (relay_http_check_auth (client, client->http_req))
{
case 0: /* authentication OK */
relay_client_set_status (client, RELAY_STATUS_CONNECTED);
break;
case -1: /* missing password */
relay_http_send_error_json (client, RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_MISSING_PASSWORD);
relay_client_set_status (client, RELAY_STATUS_AUTH_FAILED);
return;
case -2: /* invalid password */
relay_http_send_error_json (client, RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_INVALID_PASSWORD);
relay_client_set_status (client, RELAY_STATUS_AUTH_FAILED);
return;
case -3: /* missing TOTP */
relay_http_send_error_json (client, RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_MISSING_TOTP);
relay_client_set_status (client, RELAY_STATUS_AUTH_FAILED);
return;
case -4: /* invalid TOTP */
relay_http_send_error_json (client, RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_INVALID_TOTP);
relay_client_set_status (client, RELAY_STATUS_AUTH_FAILED);
return;
case -5: /* out of memory */
relay_http_send_error_json (client, RELAY_HTTP_401_UNAUTHORIZED,
NULL,
RELAY_HTTP_ERROR_OUT_OF_MEMORY);
relay_client_set_status (client, RELAY_STATUS_AUTH_FAILED);
return;
relay_client_set_status (client, RELAY_STATUS_CONNECTED);
}
else
{
relay_client_set_status (client, RELAY_STATUS_AUTH_FAILED);
return;
}
}
+11 -6
View File
@@ -41,11 +41,15 @@ enum t_relay_client_http_status
#define RELAY_HTTP_500_INTERNAL_SERVER_ERROR 500, "Internal Server Error"
#define RELAY_HTTP_503_SERVICE_UNAVAILABLE 503, "Service Unvavailable"
#define RELAY_HTTP_ERROR_MISSING_PASSWORD "Missing password"
#define RELAY_HTTP_ERROR_INVALID_PASSWORD "Invalid password"
#define RELAY_HTTP_ERROR_MISSING_TOTP "Missing TOTP"
#define RELAY_HTTP_ERROR_INVALID_TOTP "Invalid TOTP"
#define RELAY_HTTP_ERROR_OUT_OF_MEMORY "Out of memory"
#define RELAY_HTTP_ERROR_MISSING_PASSWORD "Missing password"
#define RELAY_HTTP_ERROR_INVALID_PASSWORD "Invalid password"
#define RELAY_HTTP_ERROR_MISSING_TOTP "Missing TOTP"
#define RELAY_HTTP_ERROR_INVALID_TOTP "Invalid TOTP"
#define RELAY_HTTP_ERROR_INVALID_HASH_ALGO "Invalid hash algorithm " \
"(not found or not supported)"
#define RELAY_HTTP_ERROR_INVALID_SALT "Invalid salt"
#define RELAY_HTTP_ERROR_INVALID_ITERATIONS "Invalid number of iterations"
#define RELAY_HTTP_ERROR_OUT_OF_MEMORY "Out of memory"
struct t_relay_http_request
{
@@ -71,7 +75,8 @@ extern void relay_http_request_reinit (struct t_relay_http_request *request);
extern struct t_relay_http_request *relay_http_request_alloc ();
extern int relay_http_parse_method_path (struct t_relay_http_request *request,
const char *method_path);
extern int relay_http_check_auth (struct t_relay_http_request *request);
extern int relay_http_check_auth (struct t_relay_client *client,
struct t_relay_http_request *request);
extern void relay_http_recv (struct t_relay_client *client, const char *data);
extern int relay_http_send (struct t_relay_client *client,
int return_code, const char *message,
@@ -170,7 +170,9 @@ relay_weechat_protocol_handshake_reply (struct t_relay_client *client,
"password_hash_algo",
(client->password_hash_algo >= 0) ?
relay_auth_password_hash_algo_name[client->password_hash_algo] : "");
snprintf (string, sizeof (string), "%d", client->password_hash_iterations);
snprintf (string, sizeof (string),
"%d",
weechat_config_integer (relay_config_network_password_hash_iterations));
weechat_hashtable_set (
hashtable,
"password_hash_iterations",
@@ -388,13 +390,16 @@ RELAY_WEECHAT_PROTOCOL_CALLBACK(init)
if (strcmp (options[i], "password") == 0)
{
password_received = 1;
if (relay_auth_password (client, pos, relay_password))
if ((client->password_hash_algo == RELAY_AUTH_PASSWORD_HASH_PLAIN)
&& (relay_auth_check_password_plain (client, pos, relay_password) == 0))
{
RELAY_WEECHAT_DATA(client, password_ok) = 1;
}
}
else if (strcmp (options[i], "password_hash") == 0)
{
password_received = 1;
if (relay_auth_password_hash (client, pos, relay_password))
if (relay_auth_password_hash (client, pos, relay_password) == 0)
RELAY_WEECHAT_DATA(client, password_ok) = 1;
}
else if (strcmp (options[i], "totp") == 0)
+93 -12
View File
@@ -26,7 +26,16 @@ extern "C"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "src/core/wee-config-file.h"
#include "src/plugins/relay/relay.h"
#include "src/plugins/relay/relay-auth.h"
#include "src/plugins/relay/relay-client.h"
#include "src/plugins/relay/relay-config.h"
extern int relay_auth_check_salt (struct t_relay_client *client,
const char *salt_hexa,
const char *salt, int salt_size);
}
#define WEE_CHECK_PARSE_SHA(__parameters) \
@@ -98,6 +107,38 @@ TEST(RelayAuth, GenerateNonce)
free (nonce);
}
/*
* Tests functions:
* relay_auth_check_password_plain
*/
TEST(RelayAuth, CheckPasswordPlain)
{
struct t_relay_client *client;
client = (struct t_relay_client *)calloc (1, sizeof (*client));
CHECK(client);
client->protocol = RELAY_PROTOCOL_API;
/* invalid arguments */
LONGS_EQUAL(-2, relay_auth_check_password_plain (client, NULL, NULL));
LONGS_EQUAL(-2, relay_auth_check_password_plain (client, "abcd", NULL));
LONGS_EQUAL(-2, relay_auth_check_password_plain (client, NULL, "password"));
/* wrong password */
LONGS_EQUAL(-2, relay_auth_check_password_plain (client, "test", "password"));
LONGS_EQUAL(-2, relay_auth_check_password_plain (client, "Password", "password"));
/* good password */
LONGS_EQUAL(0, relay_auth_check_password_plain (client, "", ""));
LONGS_EQUAL(0, relay_auth_check_password_plain (client, "password", "password"));
/* test with "plain" disabled */
config_file_option_set (relay_config_network_password_hash_algo, "*,!plain", 1);
LONGS_EQUAL(-1, relay_auth_check_password_plain (client, "password", "password"));
config_file_option_reset (relay_config_network_password_hash_algo, 1);
}
/*
* Tests functions:
* relay_auth_parse_sha
@@ -241,23 +282,53 @@ TEST(RelayAuth, ParsePbkdf2)
/*
* Tests functions:
* relay_auth_check_password_plain
* relay_auth_check_salt
*/
TEST(RelayAuth, CheckPasswordPlain)
TEST(RelayAuth, CheckSalt)
{
/* invalid arguments */
LONGS_EQUAL(0, relay_auth_check_password_plain (NULL, NULL));
LONGS_EQUAL(0, relay_auth_check_password_plain ("abcd", NULL));
LONGS_EQUAL(0, relay_auth_check_password_plain (NULL, "password"));
struct t_relay_client *client;
time_t time_now;
char salt[128];
/* wrong password */
LONGS_EQUAL(0, relay_auth_check_password_plain ("test", "password"));
LONGS_EQUAL(0, relay_auth_check_password_plain ("Password", "password"));
client = (struct t_relay_client *)calloc (1, sizeof (*client));
CHECK(client);
client->nonce = strdup ("01aa03bb");
/* good password */
LONGS_EQUAL(1, relay_auth_check_password_plain ("", ""));
LONGS_EQUAL(1, relay_auth_check_password_plain ("password", "password"));
client->protocol = RELAY_PROTOCOL_API;
LONGS_EQUAL(0, relay_auth_check_salt (NULL, NULL, NULL, 0));
LONGS_EQUAL(0, relay_auth_check_salt (client, NULL, NULL, 0));
LONGS_EQUAL(0, relay_auth_check_salt (client, NULL, "test", 4));
LONGS_EQUAL(0, relay_auth_check_salt (client, NULL, "1234", 4));
time_now = time (NULL);
snprintf (salt, sizeof (salt), "%ld", time_now);
LONGS_EQUAL(1, relay_auth_check_salt (client, NULL, salt, strlen (salt)));
time_now = time (NULL) - 2;
snprintf (salt, sizeof (salt), "%ld", time_now);
LONGS_EQUAL(1, relay_auth_check_salt (client, NULL, salt, strlen (salt)));
time_now = time (NULL) + 2;
snprintf (salt, sizeof (salt), "%ld", time_now);
LONGS_EQUAL(1, relay_auth_check_salt (client, NULL, salt, strlen (salt)));
time_now = time (NULL) - 10;
snprintf (salt, sizeof (salt), "%ld", time_now);
LONGS_EQUAL(0, relay_auth_check_salt (client, NULL, salt, strlen (salt)));
client->protocol = RELAY_PROTOCOL_WEECHAT;
LONGS_EQUAL(0, relay_auth_check_salt (NULL, NULL, NULL, 0));
LONGS_EQUAL(0, relay_auth_check_salt (client, NULL, NULL, 0));
LONGS_EQUAL(0, relay_auth_check_salt (client, NULL, "test", 4));
LONGS_EQUAL(0, relay_auth_check_salt (client, NULL, "1234", 4));
LONGS_EQUAL(0, relay_auth_check_salt (client, "01aa", "\x01\xaa", 2));
LONGS_EQUAL(0, relay_auth_check_salt (client, "01aa03bb", "\x01\xaa\x03\xbb", 4));
LONGS_EQUAL(1, relay_auth_check_salt (client, "01aa03bbcc", "\x01\xaa\x03\xbb\xcc", 5));
LONGS_EQUAL(1, relay_auth_check_salt (client, "01AA03BBCC", "\x01\xaa\x03\xbb\xcc", 5));
free (client->nonce);
free (client);
}
/*
@@ -403,3 +474,13 @@ TEST(RelayAuth, CheckHashPbkdf2)
"f53326c3729ffd12",
"password"));
}
/*
* Tests functions:
* relay_auth_password_hash
*/
TEST(RelayAuth, PasswordHash)
{
/* TODO: write tests */
}
+217 -17
View File
@@ -28,10 +28,15 @@ extern "C"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <gcrypt.h>
#include "src/core/wee-config-file.h"
#include "src/core/wee-crypto.h"
#include "src/core/wee-hashtable.h"
#include "src/core/wee-hook.h"
#include "src/core/wee-string.h"
#include "src/plugins/relay/relay.h"
#include "src/plugins/relay/relay-client.h"
#include "src/plugins/relay/relay-config.h"
#include "src/plugins/relay/relay-http.h"
#include "src/plugins/relay/relay-websocket.h"
@@ -45,6 +50,8 @@ extern int relay_http_parse_header (struct t_relay_http_request *request,
const char *header);
extern void relay_http_add_to_body (struct t_relay_http_request *request,
char **partial_message);
extern int relay_http_get_auth_status (struct t_relay_client *client,
struct t_relay_http_request *request);
extern char *relay_http_compress (struct t_relay_http_request *request,
const char *data, int data_size,
int *compressed_size,
@@ -589,51 +596,244 @@ TEST(RelayHttp, AddToBody)
/*
* Tests functions:
* relay_http_check_auth
* relay_http_get_auth_status
*/
TEST(RelayHttp, CheckAuth)
TEST(RelayHttp, GetAuthStatus)
{
struct t_relay_client *client;
struct t_relay_http_request *request;
char *totp, *totp2;
const char *good_pwd = "secret_password";
const char *bad_pwd = "test";
char *totp, *totp2, salt[1024], salt_pass[1024], hash[1024], hash_hexa[2048];
char auth[4096], auth_base64[4096], auth_header[8192];
time_t time_now;
int hash_size;
config_file_option_set (relay_config_network_password, "secret_password", 1);
config_file_option_set (relay_config_network_password, good_pwd, 1);
client = (struct t_relay_client *)calloc (1, sizeof (*client));
CHECK(client);
client->protocol = RELAY_PROTOCOL_API;
request = relay_http_request_alloc ();
CHECK(request);
/* test password */
LONGS_EQUAL(-1, relay_http_check_auth (request));
LONGS_EQUAL(-1, relay_http_get_auth_status (client, request));
hashtable_set (request->headers, "authorization", "Basic ");
LONGS_EQUAL(-2, relay_http_check_auth (request));
LONGS_EQUAL(-2, relay_http_get_auth_status (client, request));
hashtable_set (request->headers, "authorization", "Basic \u26c4");
LONGS_EQUAL(-2, relay_http_check_auth (request));
/* set invalid user/password: "weechat:test" */
hashtable_set (request->headers, "authorization", "Basic d2VlY2hhdDp0ZXN0");
LONGS_EQUAL(-2, relay_http_check_auth (request));
/* set valid user/password: "weechat:secret_password" */
LONGS_EQUAL(-2, relay_http_get_auth_status (client, request));
/* test invalid plain-text password ("test") */
hashtable_set (request->headers, "authorization", "Basic cGxhaW46dGVzdA==");
LONGS_EQUAL(-2, relay_http_get_auth_status (client, request));
/* test valid plain-text password ("secret_password") */
hashtable_set (request->headers,
"authorization", "Basic d2VlY2hhdDpzZWNyZXRfcGFzc3dvcmQ");
LONGS_EQUAL(0, relay_http_check_auth (request));
"authorization",
"Basic cGxhaW46c2VjcmV0X3Bhc3N3b3Jk");
LONGS_EQUAL(0, relay_http_get_auth_status (client, request));
/* test invalid hash: "SHA128" */
time_now = time (NULL);
snprintf (salt_pass, sizeof (salt_pass),
"%ld%s", time_now, bad_pwd);
LONGS_EQUAL(1, weecrypto_hash (salt_pass, strlen (salt_pass),
GCRY_MD_SHA256, hash, &hash_size));
LONGS_EQUAL(64, string_base_encode ("16", hash, hash_size, hash_hexa));
snprintf (auth, sizeof (auth),
"hash:sha128:%ld:%s",
time_now,
hash_hexa);
string_base_encode ("64", auth, strlen (auth), auth_base64);
snprintf (auth_header, sizeof (auth_header), "Basic %s", auth_base64);
hashtable_set (request->headers, "authorization", auth_header);
LONGS_EQUAL(-5, relay_http_get_auth_status (client, request));
/* test invalid password hashed with SHA256: "test" */
time_now = time (NULL);
snprintf (salt_pass, sizeof (salt_pass),
"%ld%s", time_now, bad_pwd);
LONGS_EQUAL(1, weecrypto_hash (salt_pass, strlen (salt_pass),
GCRY_MD_SHA256, hash, &hash_size));
LONGS_EQUAL(64, string_base_encode ("16", hash, hash_size, hash_hexa));
snprintf (auth, sizeof (auth),
"hash:sha256:%ld:%s",
time_now,
hash_hexa);
string_base_encode ("64", auth, strlen (auth), auth_base64);
snprintf (auth_header, sizeof (auth_header), "Basic %s", auth_base64);
hashtable_set (request->headers, "authorization", auth_header);
LONGS_EQUAL(-2, relay_http_get_auth_status (client, request));
/* test invalid password hashed with SHA512: "test" */
time_now = time (NULL);
snprintf (salt_pass, sizeof (salt_pass),
"%ld%s", time_now, bad_pwd);
LONGS_EQUAL(1, weecrypto_hash (salt_pass, strlen (salt_pass),
GCRY_MD_SHA512, hash, &hash_size));
LONGS_EQUAL(128, string_base_encode ("16", hash, hash_size, hash_hexa));
snprintf (auth, sizeof (auth),
"hash:sha512:%ld:%s",
time_now,
hash_hexa);
string_base_encode ("64", auth, strlen (auth), auth_base64);
snprintf (auth_header, sizeof (auth_header), "Basic %s", auth_base64);
hashtable_set (request->headers, "authorization", auth_header);
LONGS_EQUAL(-2, relay_http_get_auth_status (client, request));
/* test valid password hashed with SHA256: "secret_password" but too old time (salt) */
time_now = time (NULL) - 10;
snprintf (salt_pass, sizeof (salt_pass),
"%ld%s", time_now, good_pwd);
LONGS_EQUAL(1, weecrypto_hash (salt_pass, strlen (salt_pass),
GCRY_MD_SHA256, hash, &hash_size));
LONGS_EQUAL(64, string_base_encode ("16", hash, hash_size, hash_hexa));
snprintf (auth, sizeof (auth),
"hash:sha256:%ld:%s",
time_now,
hash_hexa);
string_base_encode ("64", auth, strlen (auth), auth_base64);
snprintf (auth_header, sizeof (auth_header), "Basic %s", auth_base64);
hashtable_set (request->headers, "authorization", auth_header);
LONGS_EQUAL(-6, relay_http_get_auth_status (client, request));
/* test valid password hashed with SHA256: "secret_password" */
time_now = time (NULL);
snprintf (salt_pass, sizeof (salt_pass),
"%ld%s", time_now, good_pwd);
LONGS_EQUAL(1, weecrypto_hash (salt_pass, strlen (salt_pass),
GCRY_MD_SHA256, hash, &hash_size));
LONGS_EQUAL(64, string_base_encode ("16", hash, hash_size, hash_hexa));
snprintf (auth, sizeof (auth),
"hash:sha256:%ld:%s",
time_now,
hash_hexa);
string_base_encode ("64", auth, strlen (auth), auth_base64);
snprintf (auth_header, sizeof (auth_header), "Basic %s", auth_base64);
hashtable_set (request->headers, "authorization", auth_header);
LONGS_EQUAL(0, relay_http_get_auth_status (client, request));
/* test valid password hashed with SHA512: "secret_password" */
time_now = time (NULL);
snprintf (salt_pass, sizeof (salt_pass),
"%ld%s", time_now, good_pwd);
LONGS_EQUAL(1, weecrypto_hash (salt_pass, strlen (salt_pass),
GCRY_MD_SHA512, hash, &hash_size));
LONGS_EQUAL(128, string_base_encode ("16", hash, hash_size, hash_hexa));
snprintf (auth, sizeof (auth),
"hash:sha512:%ld:%s",
time_now,
hash_hexa);
string_base_encode ("64", auth, strlen (auth), auth_base64);
snprintf (auth_header, sizeof (auth_header), "Basic %s", auth_base64);
hashtable_set (request->headers, "authorization", auth_header);
LONGS_EQUAL(0, relay_http_get_auth_status (client, request));
/* test invalid number of iterations */
time_now = time (NULL);
snprintf (salt, sizeof (salt), "%ld", time_now);
LONGS_EQUAL(1, weecrypto_hash_pbkdf2 (good_pwd, strlen (good_pwd),
GCRY_MD_SHA256,
salt, strlen (salt),
123,
hash, &hash_size));
LONGS_EQUAL(64, string_base_encode ("16", hash, hash_size, hash_hexa));
snprintf (auth, sizeof (auth),
"hash:pbkdf2+sha256:%ld:123:%s",
time_now,
hash_hexa);
string_base_encode ("64", auth, strlen (auth), auth_base64);
snprintf (auth_header, sizeof (auth_header), "Basic %s", auth_base64);
hashtable_set (request->headers, "authorization", auth_header);
LONGS_EQUAL(-7, relay_http_get_auth_status (client, request));
/* test invalid password hashed with PBKDF2+SHA256: "test" */
time_now = time (NULL);
snprintf (salt, sizeof (salt), "%ld", time_now);
LONGS_EQUAL(1, weecrypto_hash_pbkdf2 (bad_pwd, strlen (bad_pwd),
GCRY_MD_SHA256,
salt, strlen (salt),
100000,
hash, &hash_size));
LONGS_EQUAL(64, string_base_encode ("16", hash, hash_size, hash_hexa));
snprintf (auth, sizeof (auth),
"hash:pbkdf2+sha256:%ld:100000:%s",
time_now,
hash_hexa);
string_base_encode ("64", auth, strlen (auth), auth_base64);
snprintf (auth_header, sizeof (auth_header), "Basic %s", auth_base64);
hashtable_set (request->headers, "authorization", auth_header);
LONGS_EQUAL(-2, relay_http_get_auth_status (client, request));
/* test valid password hashed with PBKDF2+SHA256: "secret_password" */
time_now = time (NULL);
snprintf (salt, sizeof (salt), "%ld", time_now);
LONGS_EQUAL(1, weecrypto_hash_pbkdf2 (good_pwd, strlen (good_pwd),
GCRY_MD_SHA256,
salt, strlen (salt),
100000,
hash, &hash_size));
LONGS_EQUAL(64, string_base_encode ("16", hash, hash_size, hash_hexa));
snprintf (auth, sizeof (auth),
"hash:pbkdf2+sha256:%ld:100000:%s",
time_now,
hash_hexa);
string_base_encode ("64", auth, strlen (auth), auth_base64);
snprintf (auth_header, sizeof (auth_header), "Basic %s", auth_base64);
hashtable_set (request->headers, "authorization", auth_header);
LONGS_EQUAL(0, relay_http_get_auth_status (client, request));
/* test valid password hashed with PBKDF2+SHA512: "secret_password" */
time_now = time (NULL);
snprintf (salt, sizeof (salt), "%ld", time_now);
LONGS_EQUAL(1, weecrypto_hash_pbkdf2 (good_pwd, strlen (good_pwd),
GCRY_MD_SHA512,
salt, strlen (salt),
100000,
hash, &hash_size));
LONGS_EQUAL(128, string_base_encode ("16", hash, hash_size, hash_hexa));
snprintf (auth, sizeof (auth),
"hash:pbkdf2+sha512:%ld:100000:%s",
time_now,
hash_hexa);
string_base_encode ("64", auth, strlen (auth), auth_base64);
snprintf (auth_header, sizeof (auth_header), "Basic %s", auth_base64);
hashtable_set (request->headers, "authorization", auth_header);
LONGS_EQUAL(0, relay_http_get_auth_status (client, request));
/* test missing/invalid TOTP */
config_file_option_set (relay_config_network_totp_secret, "secretbase32", 1);
config_file_option_set (relay_config_network_totp_window, "1", 1);
LONGS_EQUAL(-3, relay_http_check_auth (request));
LONGS_EQUAL(-3, relay_http_get_auth_status (client, request));
totp = hook_info_get (NULL, "totp_generate", "secretbase32");
CHECK(totp);
totp2 = strdup (totp);
totp2[0] = (totp2[0] == '1') ? '2' : '1';
hashtable_set (request->headers, "x-weechat-totp", totp2);
LONGS_EQUAL(-4, relay_http_check_auth (request));
LONGS_EQUAL(-4, relay_http_get_auth_status (client, request));
hashtable_set (request->headers, "x-weechat-totp", totp);
LONGS_EQUAL(0, relay_http_check_auth (request));
LONGS_EQUAL(0, relay_http_get_auth_status (client, request));
free (totp);
free (totp2);
config_file_option_reset (relay_config_network_totp_secret, 1);
config_file_option_reset (relay_config_network_totp_window, 1);
config_file_option_reset (relay_config_network_password, 1);
relay_http_request_free (request);
free (client);
}
/*
* Tests functions:
* relay_http_check_auth
*/
TEST(RelayHttp, CheckAuth)
{
/* TODO: write tests */
}
/*