1
0
mirror of https://github.com/unrealircd/unrealircd.git synced 2026-07-09 00:23:12 +02:00
Files
unrealircd/src/modules/link-security.c
T
Bram Matthys d3c98c73c2 Fix issue where saslmechlist could not be set by services server.
This broke SASL services autodetection and also sasl=x,y,z in CAP.
Reported by Valware in https://bugs.unrealircd.org/view.php?id=5960

Of course the easiest solution would be just to set .remote_write=1
for this, which is what I've just done for the 5.2.1.1 release.
But there seems to be a pattern here. When a server wants to write
its own object (irc1.example.net writing to the MD object of
irc1.example.net) we have the problem that that object is both
"our client" and from the other server POV it is "themselves".
On one hand you may want to allow that (eg for 'saslmechlist'), on
the other hand a server writing its own 'certfp' sounds like a bad
idea in principle.
So we now add a new option for the 'self' case and make some MD
objects use it. In fact, in the core we now have zero MD objects
using remote_write. We keep the option available though, for example
for k4be's geoip modules and possibly future features.

Module API change:
* .self_write added which allows a server to write to its own object
  (irc1.example.net writing to the MD object of irc1.example.net)
* .remote_write still exists too if you want to allow remote servers
  to write to your own objects
* Note that in all cases, servers can always write to their own
  (child) client objects.

Changes:
* The link-security MD changed from .remote_write=1 to .self_write=1
* The salmechslist MD now has .self_write=1, this fixes the actual bug
2021-07-10 09:14:18 +02:00

283 lines
7.7 KiB
C

/*
* IRC - Internet Relay Chat, src/modules/link-security.c
* (C) 2017 Syzop & The UnrealIRCd Team
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "unrealircd.h"
/* Module header */
ModuleHeader MOD_HEADER
= {
"link-security",
"5.0",
"Link Security CAP",
"UnrealIRCd Team",
"unrealircd-5",
};
/* Forward declarations */
char *link_security_md_serialize(ModData *m);
void link_security_md_unserialize(char *str, ModData *m);
EVENT(checklinksec);
char *link_security_capability_parameter(Client *client);
CMD_FUNC(cmd_linksecurity);
/* Global variables */
ModDataInfo *link_security_md;
int local_link_security = -1;
int global_link_security = -1;
int effective_link_security = -1;
/** Module initalization */
MOD_INIT()
{
ModDataInfo mreq;
MARK_AS_OFFICIAL_MODULE(modinfo);
ModuleSetOptions(modinfo->handle, MOD_OPT_PERM_RELOADABLE, 1);
memset(&mreq, 0, sizeof(mreq));
mreq.name = "link-security";
mreq.type = MODDATATYPE_CLIENT;
mreq.serialize = link_security_md_serialize;
mreq.unserialize = link_security_md_unserialize;
mreq.sync = 1;
mreq.self_write = 1;
link_security_md = ModDataAdd(modinfo->handle, mreq);
if (!link_security_md)
{
config_error("Unable to ModDataAdd() -- too many 3rd party modules loaded perhaps?");
abort();
}
CommandAdd(modinfo->handle, "LINKSECURITY", cmd_linksecurity, MAXPARA, CMD_USER);
return MOD_SUCCESS;
}
MOD_LOAD()
{
ClientCapabilityInfo cap;
memset(&cap, 0, sizeof(cap));
cap.name = "unrealircd.org/link-security";
cap.flags = CLICAP_FLAGS_ADVERTISE_ONLY;
cap.parameter = link_security_capability_parameter;
ClientCapabilityAdd(modinfo->handle, &cap, NULL);
EventAdd(modinfo->handle, "checklinksec", checklinksec, NULL, 2000, 0);
checklinksec(NULL);
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
return MOD_SUCCESS;
}
/* Magic value to differentiate between "not set" and "zero".
* Only used for internal moddata storage, not exposed
* outside these two functions.
*/
#define LNKSECMAGIC 100
char *link_security_md_serialize(ModData *m)
{
static char buf[32];
if (m->i == 0)
return NULL; /* not set */
snprintf(buf, sizeof(buf), "%d", m->i - LNKSECMAGIC);
return buf;
}
void link_security_md_unserialize(char *str, ModData *m)
{
m->i = atoi(str) + LNKSECMAGIC;
}
/** Return 1 if the server certificate is verified for
* server 'client', return 0 if not.
*/
int certificate_verification_active(Client *client)
{
ConfigItem_link *conf;
if (!client->serv || !client->serv->conf)
return 0; /* wtf? */
conf = client->serv->conf;
if (conf->verify_certificate)
return 1; /* yes, verify-certificate is 'yes' */
if ((conf->auth->type == AUTHTYPE_TLS_CLIENTCERT) ||
(conf->auth->type == AUTHTYPE_TLS_CLIENTCERTFP) ||
(conf->auth->type == AUTHTYPE_SPKIFP))
{
/* yes, verified by link::password being a
* certificate fingerprint or certificate file.
*/
return 1;
}
return 0; /* no, certificate is not verified in any way */
}
/** Calculate our (local) link-security level.
* This means stepping through the list of directly linked
* servers and determining if they are linked via SSL and
* certificate verification is active.
* @returns value from 0 to 2.
*/
int our_link_security(void)
{
Client *client;
int level = 2; /* safest */
list_for_each_entry(client, &server_list, special_node)
{
if (IsLocalhost(client))
continue; /* server connected via localhost */
if (!IsSecure(client))
return 0; /* Any non-SSL server (which is not localhost) results in level 0. */
if (!certificate_verification_active(client))
level = 1; /* downgrade to level 1 */
}
return level;
}
char *valtostr(int i)
{
static char buf[32];
snprintf(buf, sizeof(buf), "%d", i);
return buf;
}
/** Check link security. This is called every X seconds to see if there
* is a change, either local or network-wide.
*/
EVENT(checklinksec)
{
int last_local_link_security = local_link_security;
int last_global_link_security = global_link_security;
Client *client;
char *s;
int v;
int warning_sent = 0;
local_link_security = our_link_security();
if (local_link_security != last_local_link_security)
{
/* Our own link-security changed (for better or worse),
* Set and broadcast it immediately to the other servers.
*/
moddata_client_set(&me, "link-security", valtostr(local_link_security));
}
global_link_security = 2;
list_for_each_entry(client, &global_server_list, client_node)
{
s = moddata_client_get(client, "link-security");
if (s)
{
v = atoi(s);
if (v == 0)
{
global_link_security = 0;
break;
}
if (v == 1)
global_link_security = 1;
}
}
if (local_link_security < last_local_link_security)
{
sendto_realops("Local link-security downgraded from level %d to %d due to just linked in server(s)",
last_local_link_security, local_link_security);
warning_sent = 1;
}
if (global_link_security < last_global_link_security)
{
sendto_realops("Global link-security downgraded from level %d to %d due to just linked in server(s)",
last_global_link_security, global_link_security);
warning_sent = 1;
}
effective_link_security = MIN(local_link_security, global_link_security);
if (warning_sent)
{
sendto_realops("Effective (network-wide) link-security is: level %d", effective_link_security);
sendto_realops("More information about this can be found at https://www.unrealircd.org/docs/Link_security");
}
}
char *link_security_capability_parameter(Client *client)
{
return valtostr(effective_link_security);
}
/** /LINKSECURITY command */
CMD_FUNC(cmd_linksecurity)
{
Client *acptr;
char *s;
int v;
if (!IsOper(client))
{
sendnumeric(client, ERR_NOPRIVILEGES);
return;
}
sendtxtnumeric(client, "== Link Security Report ==");
sendtxtnumeric(client, "= By server =");
list_for_each_entry(acptr, &global_server_list, client_node)
{
v = -1;
s = moddata_client_get(acptr, "link-security");
if (s)
{
v = atoi(s);
sendtxtnumeric(client, "%s: level %d", acptr->name, v);
} else {
sendtxtnumeric(client, "%s: level UNKNOWN", acptr->name);
}
}
sendtxtnumeric(client, "-");
sendtxtnumeric(client, "= Network =");
sendtxtnumeric(client, "This results in an effective (network-wide) link-security of level %d", effective_link_security);
sendtxtnumeric(client, "-");
sendtxtnumeric(client, "= Legend =");
sendtxtnumeric(client, "Higher level means better link security");
sendtxtnumeric(client, "Level UNKNOWN: Not an UnrealIRCd server (eg: services) or an old version (<4.0.14)");
sendtxtnumeric(client, "Level 0: One or more servers linked insecurely (not using SSL/TLS)");
sendtxtnumeric(client, "Level 1: Servers are linked with SSL/TLS but at least one of them is not verifying certificates");
sendtxtnumeric(client, "Level 2: Servers linked with SSL/TLS and certificates are properly verified");
sendtxtnumeric(client, "-");
sendtxtnumeric(client, "= More information =");
sendtxtnumeric(client, "To understand more about link security and how to improve your level");
sendtxtnumeric(client, "see https://www.unrealircd.org/docs/Link_security");
}