mirror of
https://github.com/weechat/weechat.git
synced 2026-07-09 19:23:13 +02:00
relay: add support of Time-based One-Time Password (TOTP) as second authentication factor in weechat protocol
This commit is contained in:
@@ -62,6 +62,8 @@ struct t_config_option *relay_config_network_max_clients;
|
||||
struct t_config_option *relay_config_network_password;
|
||||
struct t_config_option *relay_config_network_ssl_cert_key;
|
||||
struct t_config_option *relay_config_network_ssl_priorities;
|
||||
struct t_config_option *relay_config_network_totp_secret;
|
||||
struct t_config_option *relay_config_network_totp_window;
|
||||
struct t_config_option *relay_config_network_websocket_allowed_origins;
|
||||
|
||||
/* relay config, irc section */
|
||||
@@ -203,7 +205,66 @@ relay_config_change_network_ssl_cert_key (const void *pointer, void *data,
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for changes on option "relay.network.ssl_priorities".
|
||||
* Checks if option "relay.network.totp_secret" is valid.
|
||||
*
|
||||
* Returns:
|
||||
* 1: value is valid
|
||||
* 0: value is not valid
|
||||
*/
|
||||
|
||||
int
|
||||
relay_config_check_network_totp_secret (const void *pointer, void *data,
|
||||
struct t_config_option *option,
|
||||
const char *value)
|
||||
{
|
||||
char *totp_secret, *secret;
|
||||
int rc, length;
|
||||
|
||||
/* make C compiler happy */
|
||||
(void) pointer;
|
||||
(void) data;
|
||||
(void) option;
|
||||
|
||||
totp_secret = NULL;
|
||||
secret = NULL;
|
||||
|
||||
totp_secret = weechat_string_eval_expression (value, NULL, NULL, NULL);
|
||||
if (totp_secret && totp_secret[0])
|
||||
{
|
||||
secret = malloc (strlen (totp_secret) + 1);
|
||||
if (!secret)
|
||||
goto error;
|
||||
length = weechat_string_base_decode (32, totp_secret, secret);
|
||||
if (length < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
goto end;
|
||||
|
||||
error:
|
||||
rc = 0;
|
||||
weechat_printf (NULL,
|
||||
_("%s%s: invalid value for option "
|
||||
"\"relay.network.totp_secret\"; it must be a valid "
|
||||
"string encoded in base32 "
|
||||
"(only letters and digits from 2 to 7)"),
|
||||
weechat_prefix ("error"), RELAY_PLUGIN_NAME);
|
||||
|
||||
end:
|
||||
if (totp_secret)
|
||||
free (totp_secret);
|
||||
if (secret)
|
||||
free (secret);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if option "relay.network.ssl_priorities" is valid.
|
||||
*
|
||||
* Returns:
|
||||
* 1: value is valid
|
||||
* 0: value is not valid
|
||||
*/
|
||||
|
||||
int
|
||||
@@ -842,6 +903,31 @@ relay_config_init ()
|
||||
&relay_config_check_network_ssl_priorities, NULL, NULL,
|
||||
&relay_config_change_network_ssl_priorities, NULL, NULL,
|
||||
NULL, NULL, NULL);
|
||||
relay_config_network_totp_secret = weechat_config_new_option (
|
||||
relay_config_file, ptr_section,
|
||||
"totp_secret", "string",
|
||||
N_("secret for the generation of the Time-based One-Time Password "
|
||||
"(TOTP), encoded in base32 (only letters and digits from 2 to 7); "
|
||||
"it is used as second factor in weechat protocol, in addition to "
|
||||
"the password, which must not be empty "
|
||||
"(empty value means no TOTP is required) "
|
||||
"(note: content is evaluated, see /help eval)"),
|
||||
NULL, 0, 0, "", NULL, 0,
|
||||
&relay_config_check_network_totp_secret, NULL, NULL,
|
||||
NULL, NULL, NULL,
|
||||
NULL, NULL, NULL);
|
||||
relay_config_network_totp_window = weechat_config_new_option (
|
||||
relay_config_file, ptr_section,
|
||||
"totp_window", "integer",
|
||||
N_("number of Time-based One-Time Passwords to accept before and "
|
||||
"after the current one: "
|
||||
"0 = accept only the current password, "
|
||||
"1 = accept one password before, the current, and one after, "
|
||||
"2 = accept two passwords before, the current, and two after, "
|
||||
"...; a high number reduces the security level "
|
||||
"(0 or 1 are recommended values)"),
|
||||
NULL, 0, 256, "0", NULL, 0,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
relay_config_network_websocket_allowed_origins = weechat_config_new_option (
|
||||
relay_config_file, ptr_section,
|
||||
"websocket_allowed_origins", "string",
|
||||
|
||||
@@ -46,6 +46,8 @@ extern struct t_config_option *relay_config_network_max_clients;
|
||||
extern struct t_config_option *relay_config_network_password;
|
||||
extern struct t_config_option *relay_config_network_ssl_cert_key;
|
||||
extern struct t_config_option *relay_config_network_ssl_priorities;
|
||||
extern struct t_config_option *relay_config_network_totp_secret;
|
||||
extern struct t_config_option *relay_config_network_totp_window;
|
||||
extern struct t_config_option *relay_config_network_websocket_allowed_origins;
|
||||
|
||||
extern struct t_config_option *relay_config_irc_backlog_max_minutes;
|
||||
@@ -59,6 +61,10 @@ extern regex_t *relay_config_regex_allowed_ips;
|
||||
extern regex_t *relay_config_regex_websocket_allowed_origins;
|
||||
extern struct t_hashtable *relay_config_hashtable_irc_backlog_tags;
|
||||
|
||||
extern int relay_config_check_network_totp_secret (const void *pointer,
|
||||
void *data,
|
||||
struct t_config_option *option,
|
||||
const char *value);
|
||||
extern int relay_config_create_option_port (const void *pointer, void *data,
|
||||
struct t_config_file *config_file,
|
||||
struct t_config_section *section,
|
||||
|
||||
@@ -228,13 +228,16 @@ relay_server_sock_cb (const void *pointer, void *data, int fd)
|
||||
void *ptr_addr;
|
||||
int client_fd, flags, set, max_clients, num_clients_on_port;
|
||||
char ipv4_address[INET_ADDRSTRLEN + 1], ipv6_address[INET6_ADDRSTRLEN + 1];
|
||||
char *ptr_ip_address;
|
||||
const char *relay_password;
|
||||
char *ptr_ip_address, *relay_password, *relay_totp_secret;
|
||||
|
||||
/* make C compiler happy */
|
||||
(void) data;
|
||||
(void) fd;
|
||||
|
||||
client_fd = -1;
|
||||
relay_password = NULL;
|
||||
relay_totp_secret = NULL;
|
||||
|
||||
server = (struct t_relay_server *)pointer;
|
||||
|
||||
if (server->ipv6)
|
||||
@@ -259,7 +262,7 @@ relay_server_sock_cb (const void *pointer, void *data, int fd)
|
||||
weechat_prefix ("error"), RELAY_PLUGIN_NAME,
|
||||
server->port, server->protocol_string,
|
||||
errno, strerror (errno));
|
||||
return WEECHAT_RC_OK;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* check if relay password is empty and if it is not allowed */
|
||||
@@ -274,8 +277,34 @@ relay_server_sock_cb (const void *pointer, void *data, int fd)
|
||||
"is empty, and option "
|
||||
"relay.network.allow_empty_password is off"),
|
||||
weechat_prefix ("error"), RELAY_PLUGIN_NAME);
|
||||
close (client_fd);
|
||||
return WEECHAT_RC_OK;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (server->protocol == RELAY_PROTOCOL_WEECHAT)
|
||||
{
|
||||
/*
|
||||
* TOTP can be enabled only as second factor, in addition to the
|
||||
* password (only for weechat protocol)
|
||||
*/
|
||||
relay_totp_secret = weechat_string_eval_expression (
|
||||
weechat_config_string (relay_config_network_totp_secret),
|
||||
NULL, NULL, NULL);
|
||||
if ((!relay_password || !relay_password[0])
|
||||
&& relay_totp_secret && relay_totp_secret[0])
|
||||
{
|
||||
weechat_printf (NULL,
|
||||
_("%s%s: Time-based One-Time Password (TOTP) "
|
||||
"can be enabled only as second factor, if the "
|
||||
"password is not empty"),
|
||||
weechat_prefix ("error"), RELAY_PLUGIN_NAME);
|
||||
goto error;
|
||||
}
|
||||
if (!relay_config_check_network_totp_secret (
|
||||
NULL, NULL, NULL,
|
||||
weechat_config_string (relay_config_network_totp_secret)))
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* check if we have reached the max number of clients on this port */
|
||||
@@ -294,8 +323,7 @@ relay_server_sock_cb (const void *pointer, void *data, int fd)
|
||||
max_clients),
|
||||
weechat_prefix ("error"), RELAY_PLUGIN_NAME,
|
||||
max_clients);
|
||||
close (client_fd);
|
||||
return WEECHAT_RC_OK;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,8 +368,7 @@ relay_server_sock_cb (const void *pointer, void *data, int fd)
|
||||
RELAY_PLUGIN_NAME,
|
||||
ptr_ip_address);
|
||||
}
|
||||
close (client_fd);
|
||||
return WEECHAT_RC_OK;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* set non-blocking mode for socket */
|
||||
@@ -360,13 +387,24 @@ relay_server_sock_cb (const void *pointer, void *data, int fd)
|
||||
"error %d %s"),
|
||||
weechat_prefix ("error"), RELAY_PLUGIN_NAME,
|
||||
"SO_REUSEADDR", set, errno, strerror (errno));
|
||||
close (client_fd);
|
||||
return WEECHAT_RC_OK;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* add the client */
|
||||
relay_client_new (client_fd, ptr_ip_address, server);
|
||||
|
||||
goto end;
|
||||
|
||||
error:
|
||||
if (client_fd >= 0)
|
||||
close (client_fd);
|
||||
|
||||
end:
|
||||
if (relay_password)
|
||||
free (relay_password);
|
||||
if (relay_totp_secret)
|
||||
free (relay_totp_secret);
|
||||
|
||||
return WEECHAT_RC_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -169,8 +169,9 @@ relay_weechat_protocol_is_sync (struct t_relay_client *ptr_client,
|
||||
|
||||
RELAY_WEECHAT_PROTOCOL_CALLBACK(init)
|
||||
{
|
||||
char **options, *pos, *password;
|
||||
int i, compression;
|
||||
char **options, *pos, *password, *totp_secret, *info_totp_args;
|
||||
const char *info_totp;
|
||||
int i, compression, length;
|
||||
|
||||
RELAY_WEECHAT_PROTOCOL_MIN_ARGS(1);
|
||||
|
||||
@@ -186,20 +187,41 @@ RELAY_WEECHAT_PROTOCOL_CALLBACK(init)
|
||||
pos++;
|
||||
if (strcmp (options[i], "password") == 0)
|
||||
{
|
||||
password = weechat_string_eval_expression (weechat_config_string (relay_config_network_password),
|
||||
NULL, NULL, NULL);
|
||||
password = weechat_string_eval_expression (
|
||||
weechat_config_string (relay_config_network_password),
|
||||
NULL, NULL, NULL);
|
||||
if (password)
|
||||
{
|
||||
if (strcmp (password, pos) == 0)
|
||||
{
|
||||
RELAY_WEECHAT_DATA(client, password_ok) = 1;
|
||||
weechat_hook_signal_send ("relay_client_auth_ok",
|
||||
WEECHAT_HOOK_SIGNAL_POINTER,
|
||||
client);
|
||||
}
|
||||
free (password);
|
||||
}
|
||||
}
|
||||
else if (strcmp (options[i], "totp") == 0)
|
||||
{
|
||||
totp_secret = weechat_string_eval_expression (
|
||||
weechat_config_string (relay_config_network_totp_secret),
|
||||
NULL, NULL, NULL);
|
||||
if (totp_secret)
|
||||
{
|
||||
length = strlen (totp_secret) + strlen (pos) + 16 + 1;
|
||||
info_totp_args = malloc (length);
|
||||
if (info_totp_args)
|
||||
{
|
||||
/* validate the OTP received from the client */
|
||||
snprintf (info_totp_args, length,
|
||||
"%s,%s,0,%d",
|
||||
totp_secret, /* the shared secret */
|
||||
pos, /* the OTP from client */
|
||||
weechat_config_integer (relay_config_network_totp_window));
|
||||
info_totp = weechat_info_get ("totp_validate", info_totp_args);
|
||||
if (info_totp && (strcmp (info_totp, "1") == 0))
|
||||
RELAY_WEECHAT_DATA(client, totp_ok) = 1;
|
||||
free (info_totp_args);
|
||||
}
|
||||
free (totp_secret);
|
||||
}
|
||||
}
|
||||
else if (strcmp (options[i], "compression") == 0)
|
||||
{
|
||||
compression = relay_weechat_compression_search (pos);
|
||||
@@ -211,8 +233,17 @@ RELAY_WEECHAT_PROTOCOL_CALLBACK(init)
|
||||
weechat_string_free_split_command (options);
|
||||
}
|
||||
|
||||
if (!RELAY_WEECHAT_DATA(client, password_ok))
|
||||
if (RELAY_WEECHAT_DATA(client, password_ok)
|
||||
&& RELAY_WEECHAT_DATA(client, totp_ok))
|
||||
{
|
||||
weechat_hook_signal_send ("relay_client_auth_ok",
|
||||
WEECHAT_HOOK_SIGNAL_POINTER,
|
||||
client);
|
||||
}
|
||||
else
|
||||
{
|
||||
relay_client_set_status (client, RELAY_STATUS_AUTH_FAILED);
|
||||
}
|
||||
|
||||
return WEECHAT_RC_OK;
|
||||
}
|
||||
@@ -1383,10 +1414,11 @@ relay_weechat_protocol_recv (struct t_relay_client *client, const char *data)
|
||||
if (strcmp (protocol_cb[i].name, command) == 0)
|
||||
{
|
||||
if ((strcmp (protocol_cb[i].name, "init") != 0)
|
||||
&& (!RELAY_WEECHAT_DATA(client, password_ok)))
|
||||
&& (!RELAY_WEECHAT_DATA(client, password_ok)
|
||||
|| !RELAY_WEECHAT_DATA(client, totp_ok)))
|
||||
{
|
||||
/*
|
||||
* command is not "init" and password is not set?
|
||||
* command is not "init" and password or totp are not set?
|
||||
* then close connection!
|
||||
*/
|
||||
relay_client_set_status (client,
|
||||
|
||||
@@ -166,15 +166,20 @@ relay_weechat_free_buffers_nicklist (struct t_hashtable *hashtable,
|
||||
void
|
||||
relay_weechat_alloc (struct t_relay_client *client)
|
||||
{
|
||||
char *password;
|
||||
char *password, *totp_secret;
|
||||
|
||||
password = weechat_string_eval_expression (weechat_config_string (relay_config_network_password),
|
||||
NULL, NULL, NULL);
|
||||
password = weechat_string_eval_expression (
|
||||
weechat_config_string (relay_config_network_password),
|
||||
NULL, NULL, NULL);
|
||||
totp_secret = weechat_string_eval_expression (
|
||||
weechat_config_string (relay_config_network_totp_secret),
|
||||
NULL, NULL, NULL);
|
||||
|
||||
client->protocol_data = malloc (sizeof (struct t_relay_weechat_data));
|
||||
if (client->protocol_data)
|
||||
{
|
||||
RELAY_WEECHAT_DATA(client, password_ok) = (password && password[0]) ? 0 : 1;
|
||||
RELAY_WEECHAT_DATA(client, totp_ok) = (totp_secret && totp_secret[0]) ? 0 : 1;
|
||||
RELAY_WEECHAT_DATA(client, compression) = RELAY_WEECHAT_COMPRESSION_ZLIB;
|
||||
RELAY_WEECHAT_DATA(client, buffers_sync) =
|
||||
weechat_hashtable_new (32,
|
||||
@@ -199,6 +204,8 @@ relay_weechat_alloc (struct t_relay_client *client)
|
||||
|
||||
if (password)
|
||||
free (password);
|
||||
if (totp_secret)
|
||||
free (totp_secret);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -221,6 +228,11 @@ relay_weechat_alloc_with_infolist (struct t_relay_client *client,
|
||||
/* general stuff */
|
||||
RELAY_WEECHAT_DATA(client, password_ok) = weechat_infolist_integer (
|
||||
infolist, "password_ok");
|
||||
/* "totp_ok" is new in WeeChat 2.4 */
|
||||
if (weechat_infolist_search_var (infolist, "totp_ok"))
|
||||
RELAY_WEECHAT_DATA(client, totp_ok) = weechat_infolist_integer (infolist, "totp_ok");
|
||||
else
|
||||
RELAY_WEECHAT_DATA(client, totp_ok) = 1;
|
||||
RELAY_WEECHAT_DATA(client, compression) = weechat_infolist_integer (
|
||||
infolist, "compression");
|
||||
|
||||
@@ -314,6 +326,8 @@ relay_weechat_add_to_infolist (struct t_infolist_item *item,
|
||||
|
||||
if (!weechat_infolist_new_var_integer (item, "password_ok", RELAY_WEECHAT_DATA(client, password_ok)))
|
||||
return 0;
|
||||
if (!weechat_infolist_new_var_integer (item, "totp_ok", RELAY_WEECHAT_DATA(client, totp_ok)))
|
||||
return 0;
|
||||
if (!weechat_infolist_new_var_integer (item, "compression", RELAY_WEECHAT_DATA(client, compression)))
|
||||
return 0;
|
||||
if (!weechat_hashtable_add_to_infolist (RELAY_WEECHAT_DATA(client, buffers_sync), item, "buffers_sync"))
|
||||
@@ -332,6 +346,7 @@ relay_weechat_print_log (struct t_relay_client *client)
|
||||
if (client->protocol_data)
|
||||
{
|
||||
weechat_log_printf (" password_ok. . . . . . : %d", RELAY_WEECHAT_DATA(client, password_ok));
|
||||
weechat_log_printf (" totp_ok. . . . . . . . : %d", RELAY_WEECHAT_DATA(client, totp_ok));
|
||||
weechat_log_printf (" compression. . . . . . : %d", RELAY_WEECHAT_DATA(client, compression));
|
||||
weechat_log_printf (" buffers_sync . . . . . : 0x%lx (hashtable: '%s')",
|
||||
RELAY_WEECHAT_DATA(client, buffers_sync),
|
||||
|
||||
@@ -36,6 +36,7 @@ enum t_relay_weechat_compression
|
||||
struct t_relay_weechat_data
|
||||
{
|
||||
int password_ok; /* password received and OK? */
|
||||
int totp_ok; /* TOTP received and OK? */
|
||||
enum t_relay_weechat_compression compression; /* compression type */
|
||||
|
||||
/* sync of buffers */
|
||||
|
||||
Reference in New Issue
Block a user