unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

link-security.c (8408B)

      1 /*
      2  *   IRC - Internet Relay Chat, src/modules/link-security.c
      3  *   (C) 2017 Syzop & The UnrealIRCd Team
      4  *
      5  *   See file AUTHORS in IRC package for additional names of
      6  *   the programmers.
      7  *
      8  *   This program is free software; you can redistribute it and/or modify
      9  *   it under the terms of the GNU General Public License as published by
     10  *   the Free Software Foundation; either version 1, or (at your option)
     11  *   any later version.
     12  *
     13  *   This program is distributed in the hope that it will be useful,
     14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  *   GNU General Public License for more details.
     17  *
     18  *   You should have received a copy of the GNU General Public License
     19  *   along with this program; if not, write to the Free Software
     20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     21  */
     22 
     23 #include "unrealircd.h"
     24 
     25 /* Module header */
     26 ModuleHeader MOD_HEADER
     27   = {
     28 	"link-security",
     29 	"5.0",
     30 	"Link Security CAP",
     31 	"UnrealIRCd Team",
     32 	"unrealircd-6",
     33 	};
     34 
     35 /* Forward declarations */
     36 const char *link_security_md_serialize(ModData *m);
     37 void link_security_md_unserialize(const char *str, ModData *m);
     38 EVENT(checklinksec);
     39 const char *link_security_capability_parameter(Client *client);
     40 CMD_FUNC(cmd_linksecurity);
     41 
     42 /* Global variables */
     43 ModDataInfo *link_security_md;
     44 int local_link_security = -1;
     45 int global_link_security = -1;
     46 int effective_link_security = -1;
     47 
     48 /** Module initalization */
     49 MOD_INIT()
     50 {
     51 	ModDataInfo mreq;
     52 
     53 	MARK_AS_OFFICIAL_MODULE(modinfo);
     54 	ModuleSetOptions(modinfo->handle, MOD_OPT_PERM_RELOADABLE, 1);
     55 	
     56 	memset(&mreq, 0, sizeof(mreq));
     57 	mreq.name = "link-security";
     58 	mreq.type = MODDATATYPE_CLIENT;
     59 	mreq.serialize = link_security_md_serialize;
     60 	mreq.unserialize = link_security_md_unserialize;
     61 	mreq.sync = 1;
     62 	mreq.self_write = 1;
     63 	link_security_md = ModDataAdd(modinfo->handle, mreq);
     64 	if (!link_security_md)
     65 	{
     66 		config_error("Unable to ModDataAdd() -- too many 3rd party modules loaded perhaps?");
     67 		abort();
     68 	}
     69 	
     70 	CommandAdd(modinfo->handle, "LINKSECURITY", cmd_linksecurity, MAXPARA, CMD_USER);
     71 
     72 	return MOD_SUCCESS;
     73 }
     74 
     75 MOD_LOAD()
     76 {
     77 	ClientCapabilityInfo cap;
     78 
     79 	memset(&cap, 0, sizeof(cap));
     80 	cap.name = "unrealircd.org/link-security";
     81 	cap.flags = CLICAP_FLAGS_ADVERTISE_ONLY;
     82 	cap.parameter = link_security_capability_parameter;
     83 	ClientCapabilityAdd(modinfo->handle, &cap, NULL);
     84 
     85 	EventAdd(modinfo->handle, "checklinksec", checklinksec, NULL, 2000, 0);
     86 	checklinksec(NULL);
     87 	return MOD_SUCCESS;
     88 }
     89 
     90 MOD_UNLOAD()
     91 {
     92 	return MOD_SUCCESS;
     93 }
     94 
     95 /* Magic value to differentiate between "not set" and "zero".
     96  * Only used for internal moddata storage, not exposed
     97  * outside these two functions.
     98  */
     99 #define LNKSECMAGIC 100
    100 
    101 const char *link_security_md_serialize(ModData *m)
    102 {
    103 	static char buf[32];
    104 	if (m->i == 0)
    105 		return NULL; /* not set */
    106 	snprintf(buf, sizeof(buf), "%d", m->i - LNKSECMAGIC);
    107 	return buf;
    108 }
    109 
    110 void link_security_md_unserialize(const char *str, ModData *m)
    111 {
    112 	m->i = atoi(str) + LNKSECMAGIC;
    113 }
    114 
    115 /** Return 1 if the server certificate is verified for
    116  * server 'client', return 0 if not.
    117  */
    118 int certificate_verification_active(Client *client)
    119 {
    120 	ConfigItem_link *conf;
    121 	
    122 	if (!client->server || !client->server->conf)
    123 		return 0; /* wtf? */
    124 	conf = client->server->conf;
    125 	
    126 	if (conf->verify_certificate)
    127 		return 1; /* yes, verify-certificate is 'yes' */
    128 	
    129 	if ((conf->auth->type == AUTHTYPE_TLS_CLIENTCERT) ||
    130 	    (conf->auth->type == AUTHTYPE_TLS_CLIENTCERTFP) ||
    131 	    (conf->auth->type == AUTHTYPE_SPKIFP))
    132 	{
    133 		/* yes, verified by link::password being a
    134 		 * certificate fingerprint or certificate file.
    135 		 */
    136 	    return 1;
    137 	}
    138 
    139 	return 0; /* no, certificate is not verified in any way */
    140 }
    141 
    142 /** Calculate our (local) link-security level.
    143  * This means stepping through the list of directly linked
    144  * servers and determining if they are linked via TLS and
    145  * certificate verification is active.
    146  * @returns value from 0 to 2.
    147  */
    148 int our_link_security(void)
    149 {
    150 	Client *client;
    151 	int level = 2; /* safest */
    152 	
    153 	list_for_each_entry(client, &server_list, special_node)
    154 	{
    155 		if (IsLocalhost(client))
    156 			continue; /* server connected via localhost */
    157 		if (!IsSecure(client))
    158 			return 0; /* Any non-TLS server (which is not localhost) results in level 0. */
    159 		if (!certificate_verification_active(client))
    160 			level = 1; /* downgrade to level 1 */
    161 	}
    162 	
    163 	return level;
    164 }
    165 
    166 char *valtostr(int i)
    167 {
    168 	static char buf[32];
    169 	snprintf(buf, sizeof(buf), "%d", i);
    170 	return buf;
    171 }
    172 
    173 /** Check link security. This is called every X seconds to see if there
    174  * is a change, either local or network-wide.
    175  */
    176 EVENT(checklinksec)
    177 {
    178 	int last_local_link_security = local_link_security;
    179 	int last_global_link_security = global_link_security;
    180 	Client *client;
    181 	int v;
    182 	int warning_sent = 0;
    183 	
    184 	local_link_security = our_link_security();
    185 	if (local_link_security != last_local_link_security)
    186 	{
    187 		/* Our own link-security changed (for better or worse),
    188 		 * Set and broadcast it immediately to the other servers.
    189 		 */
    190 		moddata_client_set(&me, "link-security", valtostr(local_link_security));
    191 	}
    192 
    193 	global_link_security = 2;
    194 	list_for_each_entry(client, &global_server_list, client_node)
    195 	{
    196 		const char *s = moddata_client_get(client, "link-security");
    197 		if (s)
    198 		{
    199 			v = atoi(s);
    200 			if (v == 0)
    201 			{
    202 				global_link_security = 0;
    203 				break;
    204 			}
    205 			if (v == 1)
    206 				global_link_security = 1;
    207 		}
    208 	}
    209 	
    210 	if (local_link_security < last_local_link_security)
    211 	{
    212 		unreal_log(ULOG_INFO, "link-security", "LOCAL_LINK_SECURITY_DOWNGRADED", NULL,
    213 		           "Local link-security downgraded from level $previous_level to $new_level due to just linked in server(s)",
    214 		           log_data_integer("previous_level", last_local_link_security),
    215 		           log_data_integer("new_level", local_link_security));
    216 		warning_sent = 1;
    217 	}
    218 	
    219 	if (global_link_security < last_global_link_security)
    220 	{
    221 		unreal_log(ULOG_INFO, "link-security", "GLOBAL_LINK_SECURITY_DOWNGRADED", NULL,
    222 		           "Global link-security downgraded from level $previous_level to $new_level due to just linked in server(s)",
    223 		           log_data_integer("previous_level", last_global_link_security),
    224 		           log_data_integer("new_level", global_link_security));
    225 		warning_sent = 1;
    226 	}
    227 	
    228 	effective_link_security = MIN(local_link_security, global_link_security);
    229 
    230 	if (warning_sent)
    231 	{
    232 		unreal_log(ULOG_INFO, "link-security", "EFFECTIVE_LINK_SECURITY_REPORT", NULL,
    233 		           "Effective (network-wide) link-security is now: level $effective_link_security\n"
    234 		           "More information about this can be found at https://www.unrealircd.org/docs/Link_security",
    235 		           log_data_integer("effective_link_security", effective_link_security));
    236 	}
    237 }
    238 
    239 const char *link_security_capability_parameter(Client *client)
    240 {
    241 	return valtostr(effective_link_security);
    242 }
    243 
    244 /** /LINKSECURITY command */
    245 CMD_FUNC(cmd_linksecurity)
    246 {
    247 	Client *acptr;
    248 	
    249 	if (!IsOper(client))
    250 	{
    251 		sendnumeric(client, ERR_NOPRIVILEGES);
    252 		return;
    253 	}
    254 	
    255 	sendtxtnumeric(client, "== Link Security Report ==");
    256 	
    257 	sendtxtnumeric(client, "= By server =");
    258 	list_for_each_entry(acptr, &global_server_list, client_node)
    259 	{
    260 		const char *s = moddata_client_get(acptr, "link-security");
    261 		if (s)
    262 			sendtxtnumeric(client, "%s: level %d", acptr->name, atoi(s));
    263 		else
    264 			sendtxtnumeric(client, "%s: level UNKNOWN", acptr->name);
    265 	}
    266 	
    267 	sendtxtnumeric(client, "-");
    268 	sendtxtnumeric(client, "= Network =");
    269 	sendtxtnumeric(client, "This results in an effective (network-wide) link-security of level %d", effective_link_security);
    270 	sendtxtnumeric(client, "-");
    271 	sendtxtnumeric(client, "= Legend =");
    272 	sendtxtnumeric(client, "Higher level means better link security");
    273 	sendtxtnumeric(client, "Level UNKNOWN: Not an UnrealIRCd server (eg: services) or an old version (<4.0.14)");
    274 	sendtxtnumeric(client, "Level 0: One or more servers linked insecurely (not using TLS)");
    275 	sendtxtnumeric(client, "Level 1: Servers are linked with TLS but at least one of them is not verifying certificates");
    276 	sendtxtnumeric(client, "Level 2: Servers linked with TLS and certificates are properly verified");
    277 	sendtxtnumeric(client, "-");
    278 	sendtxtnumeric(client, "= More information =");
    279 	sendtxtnumeric(client, "To understand more about link security and how to improve your level");
    280 	sendtxtnumeric(client, "see https://www.unrealircd.org/docs/Link_security");
    281 }