1
0
mirror of https://github.com/weechat/weechat.git synced 2026-06-12 22:24:47 +02:00

Compare commits

...

14 Commits

Author SHA1 Message Date
Sébastien Helleu 2148829ebe Version 4.9.1 2026-05-31 13:46:04 +02:00
Sébastien Helleu 1ca2a00255 core: fix timing attack on TOTP validation (GHSA-vhv8-g2r9-cwcc)
weecrypto_totp_validate compared the generated and client-supplied OTPs
with strcmp and broke out of the time-window loop on the first match.
Both choices leaked information via response timing: strcmp leaked the
expected OTP digit-by-digit (shrinking the brute-force search from
~10^digits to a handful of guesses within the 30-second window), and
the early break leaked which window offset matched.

Compare in constant time with string_memcmp_constant_time and always
iterate the full window, OR-ing the result into otp_ok without an
early exit.

This affects both relay protocols (which call totp_validate via the
public info hook) and any other caller of the info hook.
2026-05-31 09:14:24 +02:00
Sébastien Helleu c737373d17 relay/irc: fix timing attack on PASS command (GHSA-vhv8-g2r9-cwcc)
The IRC relay protocol's PASS handler compared the server password with
the client-supplied value using strcmp, leaking the password byte-by-byte
via response timing. This is the same class of bug fixed for the api and
weechat protocols, on a separate code path that did not go through
relay_auth_check_password_plain.

Extract the HMAC-then-constant-time-compare logic from
relay_auth_check_password_plain into relay_auth_password_equals, then
use it in both the plain-auth wrapper and the IRC PASS handler.
2026-05-31 09:12:09 +02:00
Sébastien Helleu 30230498b2 relay: fix timing attack on password authentication (GHSA-vhv8-g2r9-cwcc)
The relay authentication used non-constant-time comparisons (strcasecmp,
strcmp) to verify password hashes and plaintext passwords, allowing an
attacker to derive the expected hash byte-by-byte from response timing
and then authenticate without knowing the password.

- SHA/PBKDF2 hex hash comparisons: normalize the client-supplied hash to
  uppercase and compare in constant time over the fixed expected length.
- Plaintext password comparison: HMAC-SHA256 both passwords with a fresh
  per-call random key and compare the fixed-size MACs in constant time,
  hiding both per-byte timing and the password length.

Add string_memcmp_constant_time helper in core, exposed via the plugin
API. Bump WEECHAT_PLUGIN_API_VERSION accordingly.
2026-05-31 09:11:53 +02:00
Sébastien Helleu 35699ea802 relay: limit size of decompressed websocket frame to prevent memory exhaustion (GHSA-v2v4-45wm-5cr3)
An authenticated relay client using the permessage-deflate websocket
extension could send a small compressed frame that decompresses to an
unbounded amount of data, exhausting all memory and crashing WeeChat.

The output buffer in relay_websocket_inflate is now capped to
WEBSOCKET_INFLATE_MAX_SIZE: frames decompressing beyond this limit are
rejected and the connection is closed.
2026-05-31 09:07:23 +02:00
Sébastien Helleu 5e4c165dad ci: bump poexam to version 0.0.10 2026-05-31 08:32:59 +02:00
Sébastien Helleu 23fb6bfe88 core: fix option weechat.look.color_real_white not applied when color is "white" on 16+ colors terminals (issue #1742) 2026-05-23 13:41:40 +02:00
Sébastien Helleu ec03437f9e irc: fix tag in message with list of names when joining a channel
The message with list of nicks on the channel has now tag irc_353 instead of
irc_366.
2026-05-23 13:22:33 +02:00
Sébastien Helleu 564ad2d5cd core: set max curl version to 8.21.0 for symbol CURLAUTH_DIGEST_IE 2026-05-23 13:20:58 +02:00
Sébastien Helleu f935aa3f9f ci: bump Lua from 5.3 to 5.4 2026-05-23 13:20:44 +02:00
Sébastien Helleu 76a7d5d3bd debian: bump Lua from 5.3 to 5.4 2026-05-23 13:20:43 +02:00
Luc Schrijvers f0f77e1bd9 Build fix for Haiku 2026-05-23 13:19:08 +02:00
LuK1337 8c0a3b4d81 cmake: enable position independent code (PIE)
Fixes the following build error when compiling Fedora 45 RPM:

/usr/bin/ld.bfd: tests/unit/CMakeFiles/tests.dir/tests.cpp.o: relocation R_X86_64_32 against `.rodata' can not be used when making a PIE object; recompile with -fPIE
/usr/bin/ld.bfd: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status

See: https://cmake.org/cmake/help/latest/prop_tgt/POSITION_INDEPENDENT_CODE.html
     https://cmake.org/cmake/help/latest/policy/CMP0083.html
2026-05-23 12:40:20 +02:00
Sébastien Helleu a4a06f255a Version 4.9.1-dev 2026-05-23 12:40:02 +02:00
23 changed files with 323 additions and 37 deletions
+2 -2
View File
@@ -36,7 +36,7 @@ env:
libcurl4-gnutls-dev
libgcrypt20-dev
libgnutls28-dev
liblua5.3-dev
liblua5.4-dev
libncurses-dev
libperl-dev
libphp-embed
@@ -131,7 +131,7 @@ jobs:
sudo apt-get update -qq
sudo apt-get --yes --no-install-recommends install ${{ env.CHECK_DEPS_UBUNTU }}
pipx install msgcheck ruff
cargo install --version 0.0.8 poexam
cargo install --version 0.0.10 poexam
- name: Check gettext files (msgcheck)
run: msgcheck po/*.po
+7
View File
@@ -9,7 +9,14 @@ select = [
ignore = [
"brackets",
"double-quotes",
"double-words",
"header",
"html-tags",
"paths",
"punc-space-str",
"unchanged",
"unicode-ctrl",
"urls",
]
path_words = "."
langs = [
+10
View File
@@ -6,6 +6,16 @@ SPDX-License-Identifier: GPL-3.0-or-later
# WeeChat ChangeLog
## Version 4.9.1 (2026-05-31)
### Fixed
- core: fix option weechat.look.color_real_white not applied when color is "white" on 16+ colors terminals ([#1742](https://github.com/weechat/weechat/issues/1742))
- irc: fix tag in message with list of names when joining a channel
- relay: limit size of decompressed websocket frame with permessage-deflate to prevent memory exhaustion ([GHSA-v2v4-45wm-5cr3](https://github.com/weechat/weechat/security/advisories/GHSA-v2v4-45wm-5cr3))
- relay: fix timing attack on password authentication ([GHSA-vhv8-g2r9-cwcc](https://github.com/weechat/weechat/security/advisories/GHSA-vhv8-g2r9-cwcc))
- api, relay: fix timing attack on TOTP validation ([GHSA-vhv8-g2r9-cwcc](https://github.com/weechat/weechat/security/advisories/GHSA-vhv8-g2r9-cwcc))
## Version 4.9.0 (2026-03-29)
### Changed
+1
View File
@@ -28,6 +28,7 @@ project(weechat C)
# CMake options
set(CMAKE_VERBOSE_MAKEFILE OFF)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}")
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_SKIP_RPATH ON)
# compiler options
+1 -1
View File
@@ -12,7 +12,7 @@ Build-Depends:
libperl-dev,
python3-dev,
libaspell-dev,
liblua5.3-dev,
liblua5.4-dev,
tcl8.6-dev,
guile-3.0-dev,
php-dev, libphp-embed, libargon2-dev, libsodium-dev,
+1 -1
View File
@@ -12,7 +12,7 @@ Build-Depends:
libperl-dev,
python3-dev,
libaspell-dev,
liblua5.3-dev,
liblua5.4-dev,
tcl8.6-dev,
guile-3.0-dev,
php-dev, libphp-embed, libargon2-dev, libsodium-dev,
+10 -4
View File
@@ -46,6 +46,9 @@
#include <netinet/in.h>
#include <inttypes.h>
#define BE_INT64 htonll
#elif defined(__HAIKU__)
#include <ByteOrder.h>
#define BE_INT64 B_HOST_TO_BENDIAN_INT64
#else
#define BE_INT64 htobe64
#endif
@@ -657,15 +660,18 @@ weecrypto_totp_validate (const char *secret_base32, time_t totp_time,
otp_ok = 0;
/*
* Compare in constant time and never break early: a non-constant
* compare and an early exit on match would let an observer measure
* how many digits of the expected OTP they got right and which
* time-window offset matched.
*/
for (i = moving_factor - window; i <= moving_factor + window; i++)
{
rc = weecrypto_totp_generate_internal (secret, length_secret,
i, digits, str_otp);
if (rc && (strcmp (str_otp, otp) == 0))
{
if (rc && (string_memcmp_constant_time (str_otp, otp, digits) == 0))
otp_ok = 1;
break;
}
}
free (secret);
+37
View File
@@ -922,6 +922,43 @@ string_strcmp_ignore_chars (const char *string1, const char *string2,
string_charcasecmp (string1, string2);
}
/*
* Compare two memory areas of the same size in constant time.
*
* Use to compare secrets (e.g. password hashes, MACs) without leaking
* information through the comparison's running time. The loop always
* walks the full "size" bytes and uses only bitwise operations on the
* data, so the execution time depends on "size" alone, not on the
* position of the first differing byte.
*
* If either pointer is NULL, the areas are considered different (the
* NULL check itself is not constant time but does not look at any
* secret content).
*
* Return:
* 0: areas are equal
* 1: areas differ
*/
int
string_memcmp_constant_time (const void *area1, const void *area2, size_t size)
{
const unsigned char *p1, *p2;
unsigned char diff;
size_t i;
if (!area1 || !area2)
return (area1 == area2) ? 0 : 1;
p1 = (const unsigned char *)area1;
p2 = (const unsigned char *)area2;
diff = 0;
for (i = 0; i < size; i++)
diff |= p1[i] ^ p2[i];
return (diff == 0) ? 0 : 1;
}
/*
* Search for a string in another string (locale and case independent).
*
+2
View File
@@ -69,6 +69,8 @@ extern int string_strcmp_ignore_chars (const char *string1,
const char *string2,
const char *chars_ignored,
int case_sensitive);
extern int string_memcmp_constant_time (const void *area1, const void *area2,
size_t size);
extern const char *string_strcasestr (const char *string, const char *search);
extern int string_match (const char *string, const char *mask,
int case_sensitive);
+2
View File
@@ -125,7 +125,9 @@ struct t_url_constant url_auth[] =
URL_DEF_CONST(AUTH, NTLM),
URL_DEF_CONST(AUTH, ANY),
URL_DEF_CONST(AUTH, ANYSAFE),
#if LIBCURL_VERSION_NUM < 0x081500 /* < 8.21.0 */
URL_DEF_CONST(AUTH, DIGEST_IE),
#endif
URL_DEF_CONST(AUTH, ONLY),
#if LIBCURL_VERSION_NUM < 0x080800 /* < 8.8.0 */
URL_DEF_CONST(AUTH, NTLM_WB),
+6 -3
View File
@@ -372,7 +372,8 @@ gui_window_set_weechat_color (WINDOW *window, int num_color)
* if not real white, we use default terminal foreground instead of
* white if bold attribute is set
*/
if ((fg == COLOR_WHITE) && (gui_color[num_color]->attributes & A_BOLD)
if (((fg == COLOR_WHITE + 8)
|| ((fg == COLOR_WHITE) && (gui_color[num_color]->attributes & A_BOLD)))
&& !CONFIG_BOOLEAN(config_look_color_real_white))
{
fg = -1;
@@ -442,7 +443,8 @@ gui_window_set_custom_color_fg (WINDOW *window, int fg)
* if not real white, we use default terminal foreground instead of
* white if bold attribute is set
*/
if ((fg == COLOR_WHITE) && (attributes & A_BOLD)
if (((fg == COLOR_WHITE + 8)
|| ((fg == COLOR_WHITE) && (attributes & A_BOLD)))
&& !CONFIG_BOOLEAN(config_look_color_real_white))
{
fg = -1;
@@ -535,7 +537,8 @@ gui_window_set_custom_color_fg_bg (WINDOW *window, int fg, int bg,
* if not real white, we use default terminal foreground instead of
* white if bold attribute is set
*/
if ((fg == COLOR_WHITE) && (attributes & A_BOLD)
if (((fg == COLOR_WHITE + 8)
|| ((fg == COLOR_WHITE) && (attributes & A_BOLD)))
&& !CONFIG_BOOLEAN(config_look_color_real_white))
{
fg = -1;
+18 -7
View File
@@ -208,7 +208,8 @@ irc_protocol_tags_add_cb (void *data,
*/
const char *
irc_protocol_tags (struct t_irc_protocol_ctxt *ctxt, const char *extra_tags)
irc_protocol_tags_cmd (struct t_irc_protocol_ctxt *ctxt, const char *command,
const char *extra_tags)
{
static char string[4096];
const char *ptr_tag_batch, *ptr_nick, *ptr_address;
@@ -222,7 +223,7 @@ irc_protocol_tags (struct t_irc_protocol_ctxt *ctxt, const char *extra_tags)
ptr_nick = NULL;
ptr_address = NULL;
is_numeric = irc_protocol_is_numeric_command (ctxt->command);
is_numeric = irc_protocol_is_numeric_command (command);
has_irc_tags = (ctxt->tags
&& weechat_hashtable_get_integer (ctxt->tags,
"items_count") > 0);
@@ -287,9 +288,9 @@ irc_protocol_tags (struct t_irc_protocol_ctxt *ctxt, const char *extra_tags)
}
}
if (ctxt->command && ctxt->command[0])
if (command && command[0])
{
log_level = irc_protocol_log_level_for_command (ctxt->command);
log_level = irc_protocol_log_level_for_command (command);
if (log_level > 0)
{
snprintf (str_log_level, sizeof (str_log_level),
@@ -299,8 +300,8 @@ irc_protocol_tags (struct t_irc_protocol_ctxt *ctxt, const char *extra_tags)
snprintf (string, sizeof (string),
"%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(ctxt->command && ctxt->command[0]) ? "irc_" : "",
(ctxt->command && ctxt->command[0]) ? ctxt->command : "",
(command && command[0]) ? "irc_" : "",
(command && command[0]) ? command : "",
(is_numeric) ? "," : "",
(is_numeric) ? "irc_numeric" : "",
(str_irc_tags && (*str_irc_tags)[0]) ? "," : "",
@@ -322,6 +323,16 @@ irc_protocol_tags (struct t_irc_protocol_ctxt *ctxt, const char *extra_tags)
return (string[0] == ',') ? string + 1 : string;
}
/*
* Build tags list with IRC command and optional tags and nick.
*/
const char *
irc_protocol_tags (struct t_irc_protocol_ctxt *ctxt, const char *extra_tags)
{
return irc_protocol_tags_cmd (ctxt, ctxt->command, extra_tags);
}
/*
* Build a string with nick and optional address.
*
@@ -6659,7 +6670,7 @@ IRC_PROTOCOL_CALLBACK(366)
ctxt->server, NULL, ctxt->command, "names", ptr_channel->buffer),
ctxt->date,
ctxt->date_usec,
irc_protocol_tags (ctxt, NULL),
irc_protocol_tags_cmd (ctxt, "353", NULL),
_("%sNicks %s%s%s%s: %s[%s%s]"),
weechat_prefix ("network"),
IRC_COLOR_CHAT_CHANNEL,
+1
View File
@@ -624,6 +624,7 @@ plugin_load (const char *filename, int init_plugin, int argc, char **argv)
new_plugin->strncasecmp = &string_strncasecmp;
new_plugin->strncasecmp_range = &string_strncasecmp_range;
new_plugin->strcmp_ignore_chars = &string_strcmp_ignore_chars;
new_plugin->string_memcmp_constant_time = &string_memcmp_constant_time;
new_plugin->strcasestr = &string_strcasestr;
new_plugin->strlen_screen = &gui_chat_strlen_screen;
new_plugin->string_match = &string_match;
+2 -1
View File
@@ -33,6 +33,7 @@
#include "../../weechat-plugin.h"
#include "../relay.h"
#include "relay-irc.h"
#include "../relay-auth.h"
#include "../relay-buffer.h"
#include "../relay-client.h"
#include "../relay-config.h"
@@ -1701,7 +1702,7 @@ relay_irc_recv (struct t_relay_client *client, const char *data)
NULL, NULL, NULL);
if (password)
{
if (strcmp (password, pos_password) == 0)
if (relay_auth_password_equals (password, pos_password))
{
RELAY_IRC_DATA(client, password_ok) = 1;
weechat_hook_signal_send ("relay_client_auth_ok",
+93 -10
View File
@@ -102,6 +102,66 @@ relay_auth_generate_nonce (int size)
return nonce_hexa;
}
/*
* Compare two passwords for equality in constant time.
*
* HMAC both sides with a fresh random key, then compare the fixed-size
* MACs. This hides both the per-byte comparison and the password length
* from a timing-side-channel observer.
*
* Both messages are prefixed with a zero byte so empty passwords still
* produce a valid HMAC (the underlying crypto API rejects zero-length
* messages); the prefix is identical on both sides so equal inputs
* still yield equal MACs.
*
* Return:
* 1: passwords are equal
* 0: passwords are not equal (or an error occurred)
*/
int
relay_auth_password_equals (const char *password1, const char *password2)
{
unsigned char key[32];
char hmac1[64], hmac2[64];
char *buf1, *buf2;
int buf1_size, buf2_size, hmac1_size, hmac2_size, rc;
if (!password1 || !password2)
return 0;
rc = 0;
buf1_size = strlen (password1) + 1;
buf2_size = strlen (password2) + 1;
buf1 = malloc (buf1_size);
buf2 = malloc (buf2_size);
if (buf1 && buf2)
{
buf1[0] = 0;
memcpy (buf1 + 1, password1, buf1_size - 1);
buf2[0] = 0;
memcpy (buf2 + 1, password2, buf2_size - 1);
gcry_create_nonce (key, sizeof (key));
if (weechat_crypto_hmac (key, sizeof (key),
buf1, buf1_size,
"sha256",
hmac1, &hmac1_size)
&& weechat_crypto_hmac (key, sizeof (key),
buf2, buf2_size,
"sha256",
hmac2, &hmac2_size)
&& (hmac1_size == hmac2_size)
&& (weechat_string_memcmp_constant_time (
hmac1, hmac2, hmac1_size) == 0))
{
rc = 1;
}
}
free (buf1);
free (buf2);
return rc;
}
/*
* Check if password received as plain text is valid.
*
@@ -127,7 +187,7 @@ relay_auth_check_password_plain (struct t_relay_client *client,
return -1;
}
return (strcmp (password, relay_password) == 0) ? 0 : -2;
return relay_auth_password_equals (password, relay_password) ? 0 : -2;
}
/*
@@ -347,8 +407,9 @@ relay_auth_check_hash_sha (const char *hash_algo,
const char *hash_sha,
const char *relay_password)
{
char *salt_password, hash[512 / 8], hash_hexa[((512 / 8) * 2) + 1];
int rc, length, hash_size;
char *salt_password, *hash_sha_upper, hash[512 / 8];
char hash_hexa[((512 / 8) * 2) + 1];
int rc, length, hash_size, hash_hexa_len;
rc = 0;
@@ -366,10 +427,23 @@ relay_auth_check_hash_sha (const char *hash_algo,
hash_algo,
hash, &hash_size))
{
weechat_string_base_encode ("16", hash, hash_size,
hash_hexa);
if (weechat_strcasecmp (hash_hexa, hash_sha) == 0)
hash_hexa_len = weechat_string_base_encode ("16", hash, hash_size,
hash_hexa);
/*
* Compare in constant time to defeat timing attacks: the
* client-supplied hash is normalized to uppercase to match
* the output of base16 encoding, then compared byte-for-byte
* with no early exit.
*/
hash_sha_upper = weechat_string_toupper (hash_sha);
if (hash_sha_upper
&& ((int)strlen (hash_sha_upper) == hash_hexa_len)
&& (weechat_string_memcmp_constant_time (
hash_hexa, hash_sha_upper, hash_hexa_len) == 0))
{
rc = 1;
}
free (hash_sha_upper);
}
free (salt_password);
}
@@ -393,8 +467,8 @@ relay_auth_check_hash_pbkdf2 (const char *hash_pbkdf2_algo,
const char *hash_pbkdf2,
const char *relay_password)
{
char hash[512 / 8], hash_hexa[((512 / 8) * 2) + 1];
int rc, hash_size;
char *hash_pbkdf2_upper, hash[512 / 8], hash_hexa[((512 / 8) * 2) + 1];
int rc, hash_size, hash_hexa_len;
rc = 0;
@@ -407,9 +481,18 @@ relay_auth_check_hash_pbkdf2 (const char *hash_pbkdf2_algo,
iterations,
hash, &hash_size))
{
weechat_string_base_encode ("16", hash, hash_size, hash_hexa);
if (weechat_strcasecmp (hash_hexa, hash_pbkdf2) == 0)
hash_hexa_len = weechat_string_base_encode ("16", hash, hash_size,
hash_hexa);
/* see relay_auth_check_hash_sha for rationale */
hash_pbkdf2_upper = weechat_string_toupper (hash_pbkdf2);
if (hash_pbkdf2_upper
&& ((int)strlen (hash_pbkdf2_upper) == hash_hexa_len)
&& (weechat_string_memcmp_constant_time (
hash_hexa, hash_pbkdf2_upper, hash_hexa_len) == 0))
{
rc = 1;
}
free (hash_pbkdf2_upper);
}
}
+2
View File
@@ -39,6 +39,8 @@ 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_password_equals (const char *password1,
const char *password2);
extern int relay_auth_check_password_plain (struct t_relay_client *client,
const char *password,
const char *relay_password);
+21 -5
View File
@@ -532,7 +532,7 @@ relay_websocket_inflate (const void *data, size_t size, z_stream *strm,
int rc;
unsigned char append_bytes[4] = { 0x00, 0x00, 0xFF, 0xFF };
Bytef *data2, *dest, *dest2;
uLongf size2, dest_size;
uLongf size2, dest_size, new_size;
if (!data || (size == 0) || !strm || !size_decompressed)
return NULL;
@@ -549,8 +549,13 @@ relay_websocket_inflate (const void *data, size_t size, z_stream *strm,
memcpy (data2, data, size);
memcpy (data2 + size, append_bytes, sizeof (append_bytes));
/* estimate the decompressed size, by default 10 * size */
dest_size = 10 * size2;
/*
* estimate the decompressed size, by default 10 * size, capped to the
* maximum allowed size (also prevents an integer overflow on the
* multiplication)
*/
dest_size = (size2 > WEBSOCKET_INFLATE_MAX_SIZE / 10) ?
WEBSOCKET_INFLATE_MAX_SIZE : 10 * size2;
dest = malloc (dest_size);
if (!dest)
goto error;
@@ -579,8 +584,19 @@ relay_websocket_inflate (const void *data, size_t size, z_stream *strm,
&& (strm->avail_in > 0)))
{
/* output buffer is not large enough */
strm->avail_out += dest_size;
dest_size *= 2;
if (dest_size >= WEBSOCKET_INFLATE_MAX_SIZE)
{
/*
* decompressed data is too large: reject the frame
* (protection against a "deflate bomb")
*/
goto error;
}
/* double the buffer, capped to the maximum allowed size */
new_size = (dest_size > WEBSOCKET_INFLATE_MAX_SIZE / 2) ?
WEBSOCKET_INFLATE_MAX_SIZE : dest_size * 2;
strm->avail_out += (uInt)(new_size - dest_size);
dest_size = new_size;
dest2 = realloc (dest, dest_size);
if (!dest2)
goto error;
+7
View File
@@ -41,6 +41,13 @@
#define WEBSOCKET_SUB_PROTOCOL_API_WEECHAT "api.weechat"
/*
* maximum size of a decompressed websocket frame (with "permessage-deflate"):
* used as an upper bound when inflating, to prevent a small compressed frame
* from decompressing to an unbounded amount of data ("deflate bomb")
*/
#define WEBSOCKET_INFLATE_MAX_SIZE (8 * 1024 * 1024)
struct t_relay_client;
struct t_relay_http_request;
+6 -1
View File
@@ -76,7 +76,7 @@ struct t_weelist_item;
* please change the date with current one; for a second change at same
* date, increment the 01, otherwise please keep 01.
*/
#define WEECHAT_PLUGIN_API_VERSION "20251112-01"
#define WEECHAT_PLUGIN_API_VERSION "20260530-01"
/* macros for defining plugin infos */
#define WEECHAT_PLUGIN_NAME(__name) \
@@ -348,6 +348,8 @@ struct t_weechat_plugin
int max, int range);
int (*strcmp_ignore_chars) (const char *string1, const char *string2,
const char *chars_ignored, int case_sensitive);
int (*string_memcmp_constant_time) (const void *area1, const void *area2,
size_t size);
const char *(*strcasestr) (const char *string, const char *search);
int (*strlen_screen) (const char *string);
int (*string_match) (const char *string, const char *mask,
@@ -1348,6 +1350,9 @@ extern int weechat_plugin_end (struct t_weechat_plugin *plugin);
(weechat_plugin->strcmp_ignore_chars)(__string1, __string2, \
__chars_ignored, \
__case_sensitive)
#define weechat_string_memcmp_constant_time(__area1, __area2, __size) \
(weechat_plugin->string_memcmp_constant_time)(__area1, __area2, \
__size)
#define weechat_strcasestr(__string, __search) \
(weechat_plugin->strcasestr)(__string, __search)
#define weechat_strlen_screen(__string) \
+32
View File
@@ -800,6 +800,38 @@ TEST(CoreString, StringComparison)
LONGS_EQUAL(-2, string_strcmp_ignore_chars ("è", "ê", "", 1));
}
/*
* Test functions:
* string_memcmp_constant_time
*/
TEST(CoreString, MemcmpConstantTime)
{
/* NULL handling */
LONGS_EQUAL(0, string_memcmp_constant_time (NULL, NULL, 0));
LONGS_EQUAL(0, string_memcmp_constant_time (NULL, NULL, 4));
LONGS_EQUAL(1, string_memcmp_constant_time (NULL, "abcd", 4));
LONGS_EQUAL(1, string_memcmp_constant_time ("abcd", NULL, 4));
/* zero-size compare always equal */
LONGS_EQUAL(0, string_memcmp_constant_time ("", "", 0));
LONGS_EQUAL(0, string_memcmp_constant_time ("abc", "xyz", 0));
/* equal areas */
LONGS_EQUAL(0, string_memcmp_constant_time ("abcd", "abcd", 4));
LONGS_EQUAL(0, string_memcmp_constant_time ("\x00\x01\x02\xff",
"\x00\x01\x02\xff", 4));
/* differing areas (first / middle / last byte) */
LONGS_EQUAL(1, string_memcmp_constant_time ("Xbcd", "abcd", 4));
LONGS_EQUAL(1, string_memcmp_constant_time ("aXcd", "abcd", 4));
LONGS_EQUAL(1, string_memcmp_constant_time ("abcX", "abcd", 4));
LONGS_EQUAL(1, string_memcmp_constant_time ("abcd", "abce", 4));
/* only compares "size" bytes, ignores trailing content */
LONGS_EQUAL(0, string_memcmp_constant_time ("abcd", "abcz", 3));
}
/*
* Test functions:
* string_strcasestr
@@ -109,6 +109,31 @@ TEST(RelayAuth, GenerateNonce)
free (nonce);
}
/*
* Test functions:
* relay_auth_password_equals
*/
TEST(RelayAuth, PasswordEquals)
{
/* invalid arguments */
LONGS_EQUAL(0, relay_auth_password_equals (NULL, NULL));
LONGS_EQUAL(0, relay_auth_password_equals ("abcd", NULL));
LONGS_EQUAL(0, relay_auth_password_equals (NULL, "abcd"));
/* different passwords */
LONGS_EQUAL(0, relay_auth_password_equals ("test", "password"));
LONGS_EQUAL(0, relay_auth_password_equals ("Password", "password"));
LONGS_EQUAL(0, relay_auth_password_equals ("", "password"));
LONGS_EQUAL(0, relay_auth_password_equals ("password", ""));
/* equal passwords */
LONGS_EQUAL(1, relay_auth_password_equals ("", ""));
LONGS_EQUAL(1, relay_auth_password_equals ("password", "password"));
LONGS_EQUAL(1, relay_auth_password_equals ("a really long password with spaces",
"a really long password with spaces"));
}
/*
* Test functions:
* relay_auth_check_password_plain
@@ -466,6 +466,41 @@ TEST(RelayWebsocket, Inflate)
free (payload_comp);
relay_websocket_deflate_free (ws_deflate);
/*
* protection against "deflate bomb": a small compressed frame that
* decompresses to more than WEBSOCKET_INFLATE_MAX_SIZE must be rejected
* (relay_websocket_inflate returns NULL)
*/
ws_deflate = relay_websocket_deflate_alloc ();
CHECK(ws_deflate);
ws_deflate->window_bits_deflate = 15;
ws_deflate->window_bits_inflate = 15;
ws_deflate->strm_deflate = (z_stream *)calloc (1, sizeof (*ws_deflate->strm_deflate));
CHECK(ws_deflate->strm_deflate);
LONGS_EQUAL(1, relay_websocket_deflate_init_stream_deflate (ws_deflate));
ws_deflate->strm_inflate = (z_stream *)calloc (1, sizeof (*ws_deflate->strm_inflate));
CHECK(ws_deflate->strm_inflate);
LONGS_EQUAL(1, relay_websocket_deflate_init_stream_inflate (ws_deflate));
/* highly compressible payload that decompresses past the maximum size */
size_t bomb_size = WEBSOCKET_INFLATE_MAX_SIZE + (1024 * 1024);
char *bomb = (char *)calloc (1, bomb_size);
CHECK(bomb);
payload_comp = (char *)relay_websocket_deflate (bomb, bomb_size,
ws_deflate->strm_deflate, &size_comp);
CHECK(payload_comp);
CHECK(size_comp < bomb_size);
payload_decomp = (char *)relay_websocket_inflate (payload_comp, size_comp,
ws_deflate->strm_inflate, &size_decomp);
POINTERS_EQUAL(NULL, payload_decomp);
free (payload_comp);
free (bomb);
relay_websocket_deflate_free (ws_deflate);
}
/*
+2 -2
View File
@@ -41,8 +41,8 @@
# devel-number the devel version as hex number ("0x04010000" for "4.1.0-dev")
#
weechat_stable="4.9.0"
weechat_devel="4.9.0"
weechat_stable="4.9.1"
weechat_devel="4.9.1"
stable_major=$(echo "${weechat_stable}" | cut -d"." -f1)
stable_minor=$(echo "${weechat_stable}" | cut -d"." -f2)