Detect combination of non-UTF8 nickchars in use and websocket type text.

In such a case we refuse to run since the consequences are too big.
(Actually I may change the non-UTF8 channel warning to an error as well,
 right now it isn't.. simply because I cannot read a certain setting)

From both the non-UTF8 channel and user warning/error, we now refer to:
https://www.unrealircd.org/docs/WebSocket_support#websockets-and-non-utf8
which contains a bit more detailed information as to the WHY.
This commit is contained in:
Bram Matthys 2019-09-23 10:13:56 +02:00
parent 9669e32447
commit d7ef752888
No known key found for this signature in database
GPG key ID: BF8116B163EAAE98
5 changed files with 77 additions and 39 deletions

View file

@ -938,3 +938,4 @@ extern int minimum_msec_since_last_run(struct timeval *tv_old, long minimum);
extern int unrl_utf8_validate(const char *str, const char **end);
extern char *unrl_utf8_make_valid(const char *str);
extern void utf8_test(void);
extern MODVAR int non_utf8_nick_chars_in_use;

View file

@ -1168,7 +1168,7 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum
#define CALLBACKTYPE_REPUTATION_STARTTIME 5
/* To add a new efunction, only if you are an UnrealIRCd coder:
* 1) Add a new entry here at the end
* 1) Add a new entry here
* 2) Add the function in src/api-efunctions.c
* 3) Add the initalization in src/api-efunctions.c
* 4) Add the extern entry in include/h.h in the

View file

@ -53,7 +53,7 @@ MBList *mblist = NULL, *mblist_tail = NULL;
/* Use this to prevent mixing of certain combinations
* (such as GBK & high-ascii, etc)
*/
static int langav;
static int langav = 0;
char langsinuse[4096];
/* bitmasks: */
@ -67,11 +67,12 @@ char langsinuse[4096];
#define LANGAV_W1251 0x000080 /* windows-1251 (eg: russian) */
#define LANGAV_LATIN2W1250 0x000100 /* Compatible with both latin2 AND windows-1250 (eg: hungarian) */
#define LANGAV_ISO8859_6 0x000200 /* arabic */
#define LANGAV_GBK 0x001000 /* (Chinese) GBK encoding */
#define LANGAV_LATIN_UTF8 0x002000 /* UTF8: latin script */
#define LANGAV_CYRILLIC_UTF8 0x004000 /* UTF8: cyrillic script */
#define LANGAV_GREEK_UTF8 0x008000 /* UTF8: greek script */
#define LANGAV_HEBREW_UTF8 0x010000 /* UTF8: hebrew script */
#define LANGAV_GBK 0x001000 /* (Chinese) GBK encoding */
#define LANGAV_UTF8 0x002000 /* any UTF8 encoding */
#define LANGAV_LATIN_UTF8 0x004000 /* UTF8: latin script */
#define LANGAV_CYRILLIC_UTF8 0x008000 /* UTF8: cyrillic script */
#define LANGAV_GREEK_UTF8 0x010000 /* UTF8: greek script */
#define LANGAV_HEBREW_UTF8 0x020000 /* UTF8: hebrew script */
typedef struct LangList LangList;
struct LangList
{
@ -83,57 +84,57 @@ struct LangList
/* MUST be alphabetized (first column) */
static LangList langlist[] = {
/* { "arabic", "ara", LANGAV_ASCII|LANGAV_ISO8859_6 }, -- TODO: check if this has issues first! */
{ "belarussian-utf8", "blr-utf8", LANGAV_ASCII|LANGAV_CYRILLIC_UTF8 },
{ "belarussian-utf8", "blr-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_CYRILLIC_UTF8 },
{ "belarussian-w1251", "blr", LANGAV_ASCII|LANGAV_W1251 },
{ "catalan", "cat", LANGAV_ASCII|LANGAV_LATIN1 },
{ "catalan-utf8", "cat-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "catalan-utf8", "cat-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "chinese", "chi-j,chi-s,chi-t", LANGAV_GBK },
{ "chinese-ja", "chi-j", LANGAV_GBK },
{ "chinese-simp", "chi-s", LANGAV_GBK },
{ "chinese-trad", "chi-t", LANGAV_GBK },
{ "cyrillic-utf8", "blr-utf8,rus-utf8,ukr-utf8", LANGAV_ASCII|LANGAV_CYRILLIC_UTF8 },
{ "cyrillic-utf8", "blr-utf8,rus-utf8,ukr-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_CYRILLIC_UTF8 },
{ "czech", "cze-m", LANGAV_ASCII|LANGAV_W1250 },
{ "czech-utf8", "cze-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "czech-utf8", "cze-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "danish", "dan", LANGAV_ASCII|LANGAV_LATIN1 },
{ "danish-utf8", "dan-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "danish-utf8", "dan-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "dutch", "dut", LANGAV_ASCII|LANGAV_LATIN1 },
{ "dutch-utf8", "dut-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "dutch-utf8", "dut-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "french", "fre", LANGAV_ASCII|LANGAV_LATIN1 },
{ "french-utf8", "fre-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "french-utf8", "fre-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "gbk", "chi-s,chi-t,chi-j", LANGAV_GBK },
{ "german", "ger", LANGAV_ASCII|LANGAV_LATIN1 },
{ "german-utf8", "ger-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "german-utf8", "ger-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "greek", "gre", LANGAV_ASCII|LANGAV_ISO8859_7 },
{ "greek-utf8", "gre-utf8", LANGAV_ASCII|LANGAV_GREEK_UTF8 },
{ "greek-utf8", "gre-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_GREEK_UTF8 },
{ "hebrew", "heb", LANGAV_ASCII|LANGAV_ISO8859_8I },
{ "hebrew-utf8", "heb", LANGAV_ASCII|LANGAV_HEBREW_UTF8 },
{ "hebrew-utf8", "heb-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_HEBREW_UTF8 },
{ "hungarian", "hun", LANGAV_ASCII|LANGAV_LATIN2W1250 },
{ "hungarian-utf8","hun-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "hungarian-utf8","hun-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "icelandic", "ice", LANGAV_ASCII|LANGAV_LATIN1 },
{ "icelandic-utf8","ice-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "icelandic-utf8","ice-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "italian", "ita", LANGAV_ASCII|LANGAV_LATIN1 },
{ "italian-utf8", "ita-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "latin-utf8", "cat-utf8,cze-utf8,dan-utf8,dut-utf8,fre-utf8,ger-utf8,hun-utf8,ice-utf8,ita-utf8,pol-utf8,rum-utf8,slo-utf8,spa-utf8,swe-utf8,tur-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "italian-utf8", "ita-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "latin-utf8", "cat-utf8,cze-utf8,dan-utf8,dut-utf8,fre-utf8,ger-utf8,hun-utf8,ice-utf8,ita-utf8,pol-utf8,rum-utf8,slo-utf8,spa-utf8,swe-utf8,tur-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "latin1", "cat,dut,fre,ger,ita,spa,swe", LANGAV_ASCII|LANGAV_LATIN1 },
{ "latin2", "hun,pol,rum", LANGAV_ASCII|LANGAV_LATIN2 },
{ "polish", "pol", LANGAV_ASCII|LANGAV_LATIN2 },
{ "polish-utf8", "pol-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "polish-utf8", "pol-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "polish-w1250", "pol-m", LANGAV_ASCII|LANGAV_W1250 },
{ "romanian", "rum", LANGAV_ASCII|LANGAV_LATIN2W1250 },
{ "romanian-utf8","rum-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "russian-utf8", "rus-utf8", LANGAV_ASCII|LANGAV_CYRILLIC_UTF8 },
{ "romanian-utf8","rum-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "russian-utf8", "rus-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_CYRILLIC_UTF8 },
{ "russian-w1251","rus", LANGAV_ASCII|LANGAV_W1251 },
{ "slovak", "slo-m", LANGAV_ASCII|LANGAV_W1250 },
{ "slovak-utf8", "slo-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "slovak-utf8", "slo-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "spanish", "spa", LANGAV_ASCII|LANGAV_LATIN1 },
{ "spanish-utf8", "spa-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "spanish-utf8", "spa-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "swedish", "swe", LANGAV_ASCII|LANGAV_LATIN1 },
{ "swedish-utf8", "swe-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "swedish-utf8", "swe-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "swiss-german", "swg", LANGAV_ASCII|LANGAV_LATIN1 },
{ "swiss-german-utf8", "swg-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "swiss-german-utf8", "swg-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "turkish", "tur", LANGAV_ASCII|LANGAV_ISO8859_9 },
{ "turkish-utf8", "tur-utf8", LANGAV_ASCII|LANGAV_LATIN_UTF8 },
{ "ukrainian-utf8", "ukr-utf8", LANGAV_ASCII|LANGAV_CYRILLIC_UTF8 },
{ "turkish-utf8", "tur-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_LATIN_UTF8 },
{ "ukrainian-utf8", "ukr-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_CYRILLIC_UTF8 },
{ "ukrainian-w1251", "ukr", LANGAV_ASCII|LANGAV_W1251 },
{ "windows-1250", "cze-m,pol-m,rum,slo-m,hun", LANGAV_ASCII|LANGAV_W1250 },
{ "windows-1251", "rus,ukr,blr", LANGAV_ASCII|LANGAV_W1251 },
@ -365,6 +366,7 @@ void charsys_reset(void)
void charsys_reset_pretest(void)
{
langav = 0;
non_utf8_nick_chars_in_use = 0;
}
static inline void ilang_swap(ILangList *one, ILangList *two)
@ -578,14 +580,25 @@ int mid;
return NULL;
}
static LangList *charsys_find_language_code(char *code)
{
int i;
for (i = 0; langlist[i].code; i++)
if (!strcasecmp(langlist[i].code, code))
return &langlist[i];
return NULL;
}
/** Check if language is available. */
int charsys_test_language(char *name)
{
LangList *l = charsys_find_language(name);
LangList *l = charsys_find_language(name);
if (l)
{
langav |= l->setflags;
if (!(l->setflags & LANGAV_UTF8))
non_utf8_nick_chars_in_use = 1;
return 1;
}
if (!strcmp(name, "euro-west"))

View file

@ -119,6 +119,7 @@ int websocket_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
int errors = 0;
ConfigEntry *cep;
int has_type = 0;
static char errored_once_nick = 0;
if (type != CONFIG_LISTEN_OPTIONS)
return 0;
@ -133,7 +134,25 @@ int websocket_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
{
CheckNull(cep);
has_type = 1;
if (!strcmp(cep->ce_varname, "binary") || !strcmp(cep->ce_varname, "text"))
if (!strcmp(cep->ce_vardata, "text"))
{
if (non_utf8_nick_chars_in_use && !errored_once_nick)
{
/* This one is a hard error, since the consequences are grave */
config_error("You have a websocket listener with type 'text' AND your set::allowed-nickchars contains at least one non-UTF8 character set.");
config_error("This is a very BAD idea as this makes your websocket vulnerable to UTF8 conversion attacks. "
"This can cause things like unkickable users and 'ghosts' for websocket users.");
config_error("You have 4 options: 1) Remove the websocket listener, 2) Use websocket type 'binary', "
"3) Remove the non-UTF8 character set from set::allowed-nickchars, 4) Replace the non-UTF8 with an UTF8 character set in set::allowed-nickchars");
config_error("For more details see https://www.unrealircd.org/docs/WebSocket_support#websockets-and-non-utf8");
errored_once_nick = 1;
errors++;
}
}
else if (!strcmp(cep->ce_vardata, "binary"))
{
}
else
{
config_error("%s:%i: listen::options::websocket::type must be either 'binary' or 'text' (not '%s')",
cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata);
@ -163,7 +182,7 @@ int websocket_config_run_ex(ConfigFile *cf, ConfigEntry *ce, int type, void *ptr
{
ConfigEntry *cep, *cepp;
ConfigItem_listen *l;
static int warned_once = 0;
static char warned_once_channel = 0;
if (type != CONFIG_LISTEN_OPTIONS)
return 0;
@ -183,13 +202,15 @@ int websocket_config_run_ex(ConfigFile *cf, ConfigEntry *ce, int type, void *ptr
else if (!strcmp(cep->ce_vardata, "text"))
{
l->websocket_options = WEBSOCKET_TYPE_TEXT;
// FIXME: check for non-utf8 nickchars as well
if ((tempiConf.allowed_channelchars == ALLOWED_CHANNELCHARS_ANY) && !warned_once)
if ((tempiConf.allowed_channelchars == ALLOWED_CHANNELCHARS_ANY) && !warned_once_channel)
{
config_warn("You have a websocket listener with type 'text' AND your set::allowed-channelchars is set to 'any'. "
"This is not a recommended combination as this makes your websocket vulnerable to UTF8 conversion attacks.");
/* This one is a warning, since the consequences are less grave than with nicks */
config_warn("You have a websocket listener with type 'text' AND your set::allowed-channelchars is set to 'any'.");
config_warn("This is not a recommended combination as this makes your websocket vulnerable to UTF8 conversion attacks. "
"This can cause things like unpartable channels for websocket users.");
config_warn("It is highly recommended that you use set { allowed-channelchars utf8; }");
warned_once = 1;
config_warn("For more details see https://www.unrealircd.org/docs/WebSocket_support#websockets-and-non-utf8");
warned_once_channel = 1;
}
}
}

View file

@ -36,7 +36,10 @@ extern char *me_hash;
extern char backupbuf[];
static char buf[BUFSIZE];
int labeled_response_inhibit = 0;
MODVAR int labeled_response_inhibit = 0;
/** Set to 1 if an UTF8 incompatible nick character set is in use */
MODVAR int non_utf8_nick_chars_in_use = 0;
void iNAH_host(Client *sptr, char *host)
{