unrealircd

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

oper.c (12251B)

      1 /*
      2  *   Unreal Internet Relay Chat Daemon, src/modules/oper.c
      3  *   (C) 2000-2001 Carsten V. Munk and the UnrealIRCd Team
      4  *   Moved to modules by Fish (Justin Hammond)
      5  *
      6  *   This program is free software; you can redistribute it and/or modify
      7  *   it under the terms of the GNU General Public License as published by
      8  *   the Free Software Foundation; either version 1, or (at your option)
      9  *   any later version.
     10  *
     11  *   This program is distributed in the hope that it will be useful,
     12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  *   GNU General Public License for more details.
     15  *
     16  *   You should have received a copy of the GNU General Public License
     17  *   along with this program; if not, write to the Free Software
     18  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     19  */
     20 
     21 #include "unrealircd.h"
     22 
     23 #define MSG_OPER        "OPER"  /* OPER */
     24 
     25 ModuleHeader MOD_HEADER
     26   = {
     27 	"oper",	/* Name of module */
     28 	"5.0", /* Version */
     29 	"command /oper", /* Short description of module */
     30 	"UnrealIRCd Team",
     31 	"unrealircd-6",
     32     };
     33 
     34 /* Forward declarations */
     35 CMD_FUNC(cmd_oper);
     36 int _make_oper(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost);
     37 int oper_connect(Client *client);
     38 
     39 MOD_TEST()
     40 {
     41 	MARK_AS_OFFICIAL_MODULE(modinfo);
     42 	EfunctionAdd(modinfo->handle, EFUNC_MAKE_OPER, _make_oper);
     43 	return MOD_SUCCESS;
     44 }
     45 
     46 MOD_INIT()
     47 {
     48 	MARK_AS_OFFICIAL_MODULE(modinfo);
     49 	CommandAdd(modinfo->handle, MSG_OPER, cmd_oper, MAXPARA, CMD_USER);
     50 	HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, 0, oper_connect);
     51 	return MOD_SUCCESS;
     52 }
     53 
     54 MOD_LOAD()
     55 {
     56 	return MOD_SUCCESS;
     57 }
     58 
     59 MOD_UNLOAD()
     60 {
     61 	return MOD_SUCCESS;
     62 }
     63 
     64 void set_oper_host(Client *client, const char *host)
     65 {
     66 	char uhost[HOSTLEN + USERLEN + 1];
     67 	char *p;
     68 
     69 	if (!valid_vhost(host))
     70 		return;
     71 
     72 	strlcpy(uhost, host, sizeof(uhost));
     73 
     74 	if ((p = strchr(uhost, '@')))
     75 	{
     76 		*p++ = '\0';
     77 		strlcpy(client->user->username, uhost, sizeof(client->user->username));
     78 		sendto_server(NULL, 0, 0, NULL, ":%s SETIDENT %s",
     79 		              client->id, client->user->username);
     80 		host = p;
     81 	}
     82 	safe_strdup(client->user->virthost, host);
     83 	if (MyConnect(client))
     84 		sendto_server(NULL, 0, 0, NULL, ":%s SETHOST :%s", client->id, client->user->virthost);
     85 	client->umodes |= UMODE_SETHOST|UMODE_HIDE;
     86 }
     87 
     88 int _make_oper(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost)
     89 {
     90 	long old_umodes = client->umodes & ALL_UMODES;
     91 
     92 	userhost_save_current(client);
     93 
     94 	/* Put in the right class (if any) */
     95 	if (clientclass)
     96 	{
     97 		if (client->local->class)
     98 			client->local->class->clients--;
     99 		client->local->class = clientclass;
    100 		client->local->class->clients++;
    101 	}
    102 
    103 	/* set oper user modes */
    104 	client->umodes |= UMODE_OPER;
    105 	if (modes)
    106 		client->umodes |= modes; /* oper::modes */
    107 	else
    108 		client->umodes |= OPER_MODES; /* set::modes-on-oper */
    109 
    110 	/* oper::vhost */
    111 	if (vhost)
    112 	{
    113 		set_oper_host(client, vhost);
    114 	} else
    115 	if (IsHidden(client) && !client->user->virthost)
    116 	{
    117 		/* +x has just been set by modes-on-oper and no vhost. cloak the oper! */
    118 		safe_strdup(client->user->virthost, client->user->cloakedhost);
    119 	}
    120 
    121 	userhost_changed(client);
    122 
    123 	unreal_log(ULOG_INFO, "oper", "OPER_SUCCESS", client,
    124 		   "$client.details is now an IRC Operator [oper-block: $oper_block] [operclass: $operclass]",
    125 		   log_data_string("oper_block", operblock_name),
    126 		   log_data_string("operclass", operclass));
    127 
    128 	/* set oper snomasks */
    129 	if (snomask)
    130 		set_snomask(client, snomask); /* oper::snomask */
    131 	else
    132 		set_snomask(client, OPER_SNOMASK); /* set::snomask-on-oper */
    133 
    134 	send_umode_out(client, 1, old_umodes);
    135 	if (client->user->snomask)
    136 		sendnumeric(client, RPL_SNOMASK, client->user->snomask);
    137 
    138 	list_add(&client->special_node, &oper_list);
    139 
    140 	RunHook(HOOKTYPE_LOCAL_OPER, client, 1, operblock_name, operclass);
    141 
    142 	sendnumeric(client, RPL_YOUREOPER);
    143 
    144 	/* Update statistics */
    145 	if (IsInvisible(client) && !(old_umodes & UMODE_INVISIBLE))
    146 		irccounts.invisible++;
    147 	if (IsOper(client) && !IsHideOper(client))
    148 		irccounts.operators++;
    149 
    150 	if (SHOWOPERMOTD == 1)
    151 	{
    152 		const char *args[1] = { NULL };
    153 		do_cmd(client, NULL, "OPERMOTD", 1, args);
    154 	}
    155 
    156 	if (!BadPtr(OPER_AUTO_JOIN_CHANS) && strcmp(OPER_AUTO_JOIN_CHANS, "0"))
    157 	{
    158 		char *chans = strdup(OPER_AUTO_JOIN_CHANS);
    159 		const char *args[3] = {
    160 			client->name,
    161 			chans,
    162 			NULL
    163 		};
    164 		do_cmd(client, NULL, "JOIN", 3, args);
    165 		safe_free(chans);
    166 		/* Theoretically the oper may be killed on join. Would be fun, though */
    167 		if (IsDead(client))
    168 			return 0;
    169 	}
    170 
    171 	return 1;
    172 }
    173 
    174 /*
    175 ** cmd_oper
    176 **	parv[1] = oper name
    177 **	parv[2] = oper password
    178 */
    179 CMD_FUNC(cmd_oper)
    180 {
    181 	ConfigItem_oper *operblock;
    182 	const char *operblock_name, *password;
    183 
    184 	if (!MyUser(client))
    185 		return;
    186 
    187 	if ((parc < 2) || BadPtr(parv[1]))
    188 	{
    189 		sendnumeric(client, ERR_NEEDMOREPARAMS, "OPER");
    190 		return;
    191 	}
    192 
    193 	if (SVSNOOP)
    194 	{
    195 		sendnotice(client,
    196 		    "*** This server is in NOOP mode, you cannot /oper");
    197 		return;
    198 	}
    199 
    200 	if (IsOper(client))
    201 	{
    202 		sendnotice(client, "You are already an IRC Operator. If you want to re-oper then de-oper first via /MODE yournick -o");
    203 		return;
    204 	}
    205 
    206 	operblock_name = parv[1];
    207 	password = (parc > 2) ? parv[2] : "";
    208 
    209 	/* set::plaintext-policy::oper 'deny' */
    210 	if (!IsSecure(client) && !IsLocalhost(client) && (iConf.plaintext_policy_oper == POLICY_DENY))
    211 	{
    212 		sendnotice_multiline(client, iConf.plaintext_policy_oper_message);
    213 		unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client,
    214 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    215 		           log_data_string("reason", "Not using TLS"),
    216 		           log_data_string("fail_type", "NO_TLS"),
    217 		           log_data_string("oper_block", parv[1]));
    218 		add_fake_lag(client, 7000);
    219 		return;
    220 	}
    221 
    222 	/* set::outdated-tls-policy::oper 'deny' */
    223 	if (IsSecure(client) && (iConf.outdated_tls_policy_oper == POLICY_DENY) && outdated_tls_client(client))
    224 	{
    225 		sendnotice(client, "%s", outdated_tls_client_build_string(iConf.outdated_tls_policy_oper_message, client));
    226 		unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client,
    227 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    228 		           log_data_string("reason", "Outdated TLS protocol or cipher"),
    229 		           log_data_string("fail_type", "OUTDATED_TLS_PROTOCOL_OR_CIPHER"),
    230 		           log_data_string("oper_block", parv[1]));
    231 		add_fake_lag(client, 7000);
    232 		return;
    233 	}
    234 
    235 	if (!(operblock = find_oper(operblock_name)))
    236 	{
    237 		sendnumeric(client, ERR_NOOPERHOST);
    238 		unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client,
    239 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    240 		           log_data_string("reason", "Unknown oper operblock_name"),
    241 		           log_data_string("fail_type", "UNKNOWN_OPER_NAME"),
    242 		           log_data_string("oper_block", parv[1]));
    243 		add_fake_lag(client, 7000);
    244 		return;
    245 	}
    246 
    247 	/* Below here, the oper block exists, any errors here we take (even)
    248 	 * more seriously, they are logged as errors instead of warnings.
    249 	 */
    250 
    251 	if (!user_allowed_by_security_group(client, operblock->match))
    252 	{
    253 		sendnumeric(client, ERR_NOOPERHOST);
    254 		unreal_log(ULOG_ERROR, "oper", "OPER_FAILED", client,
    255 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    256 		           log_data_string("reason", "Host does not match"),
    257 		           log_data_string("fail_type", "NO_HOST_MATCH"),
    258 		           log_data_string("oper_block", parv[1]));
    259 		add_fake_lag(client, 7000);
    260 		return;
    261 	}
    262 
    263 	if (operblock->auth && !Auth_Check(client, operblock->auth, password))
    264 	{
    265 		sendnumeric(client, ERR_PASSWDMISMATCH);
    266 		if (FAILOPER_WARN)
    267 			sendnotice(client,
    268 			    "*** Your attempt has been logged.");
    269 		unreal_log(ULOG_ERROR, "oper", "OPER_FAILED", client,
    270 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    271 		           log_data_string("reason", "Authentication failed"),
    272 		           log_data_string("fail_type", "AUTHENTICATION_FAILED"),
    273 		           log_data_string("oper_block", parv[1]));
    274 		add_fake_lag(client, 7000);
    275 		return;
    276 	}
    277 
    278 	/* Authentication of the oper succeeded (like, password, ssl cert),
    279 	 * but we still have some other restrictions to check below as well,
    280 	 * like 'require-modes' and 'maxlogins'...
    281 	 */
    282 
    283 	/* Check oper::require_modes */
    284 	if (operblock->require_modes & ~client->umodes)
    285 	{
    286 		sendnumericfmt(client, ERR_NOOPERHOST, ":You are missing user modes required to OPER");
    287 		unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client,
    288 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    289 		           log_data_string("reason", "Not matching oper::require-modes"),
    290 		           log_data_string("fail_type", "REQUIRE_MODES_NOT_SATISFIED"),
    291 		           log_data_string("oper_block", parv[1]));
    292 		add_fake_lag(client, 7000);
    293 		return;
    294 	}
    295 
    296 	if (!find_operclass(operblock->operclass))
    297 	{
    298 		sendnotice(client, "ERROR: There is a non-existant oper::operclass specified for your oper block");
    299 		unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client,
    300 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    301 		           log_data_string("reason", "Config error: invalid oper::operclass"),
    302 		           log_data_string("fail_type", "OPER_OPERCLASS_INVALID"),
    303 		           log_data_string("oper_block", parv[1]));
    304 		return;
    305 	}
    306 
    307 	if (operblock->maxlogins && (count_oper_sessions(operblock->name) >= operblock->maxlogins))
    308 	{
    309 		sendnumeric(client, ERR_NOOPERHOST);
    310 		sendnotice(client, "Your maximum number of concurrent oper logins has been reached (%d)",
    311 			operblock->maxlogins);
    312 		unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client,
    313 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    314 		           log_data_string("reason", "oper::maxlogins limit reached"),
    315 		           log_data_string("fail_type", "OPER_MAXLOGINS_LIMIT"),
    316 		           log_data_string("oper_block", parv[1]));
    317 		add_fake_lag(client, 4000);
    318 		return;
    319 	}
    320 
    321 	/* /OPER really succeeded now. Start processing it. */
    322 
    323 	/* Store which oper block was used to become IRCOp (for maxlogins and whois) */
    324 	safe_strdup(client->user->operlogin, operblock->name);
    325 
    326 	/* oper::swhois */
    327 	if (operblock->swhois)
    328 	{
    329 		SWhois *s;
    330 		for (s = operblock->swhois; s; s = s->next)
    331 			swhois_add(client, "oper", -100, s->line, &me, NULL);
    332 	}
    333 
    334 	make_oper(client, operblock->name, operblock->operclass, operblock->class, operblock->modes, operblock->snomask, operblock->vhost);
    335 
    336 	/* set::plaintext-policy::oper 'warn' */
    337 	if (!IsSecure(client) && !IsLocalhost(client) && (iConf.plaintext_policy_oper == POLICY_WARN))
    338 	{
    339 		sendnotice_multiline(client, iConf.plaintext_policy_oper_message);
    340 		unreal_log(ULOG_WARNING, "oper", "OPER_UNSAFE", client,
    341 			   "Insecure (non-TLS) connection used to OPER up by $client.details [oper-block: $oper_block]",
    342 			   log_data_string("oper_block", parv[1]),
    343 		           log_data_string("warn_type", "NO_TLS"));
    344 	}
    345 
    346 	/* set::outdated-tls-policy::oper 'warn' */
    347 	if (IsSecure(client) && (iConf.outdated_tls_policy_oper == POLICY_WARN) && outdated_tls_client(client))
    348 	{
    349 		sendnotice(client, "%s", outdated_tls_client_build_string(iConf.outdated_tls_policy_oper_message, client));
    350 		unreal_log(ULOG_WARNING, "oper", "OPER_UNSAFE", client,
    351 			   "Outdated TLS protocol/cipher used to OPER up by $client.details [oper-block: $oper_block]",
    352 			   log_data_string("oper_block", parv[1]),
    353 		           log_data_string("warn_type", "OUTDATED_TLS_PROTOCOL_OR_CIPHER"));
    354 	}
    355 }
    356 
    357 int oper_connect(Client *client)
    358 {
    359 	ConfigItem_oper *e;
    360 
    361 	if (IsOper(client))
    362 		return 0;
    363 
    364 	for (e = conf_oper; e; e = e->next)
    365 	{
    366 		if (e->auto_login && user_allowed_by_security_group(client, e->match))
    367 		{
    368 			/* Ideally we would check all the criteria that cmd_oper does.
    369 			 * I'm taking a shortcut for now that is not ideal...
    370 			 */
    371 			const char *parx[3];
    372 			parx[0] = NULL;
    373 			parx[1] = e->name;
    374 			parx[2] = NULL;
    375 			do_cmd(client, NULL, "OPER", 3, parx);
    376 			return 0;
    377 		}
    378 	}
    379 
    380 	return 0;
    381 }