unrealircd

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

oper.c (12975B)

      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, const char *autojoin_channels);
     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 	char newhost[HOSTLEN+1];
     69 
     70 	*newhost = '\0';
     71 	unreal_expand_string(host, newhost, sizeof(newhost), NULL, 0, client);
     72 	if (!valid_vhost(newhost))
     73 	{
     74 		sendnotice(client, "*** Unable to set vhost");
     75 		unreal_log(ULOG_WARNING, "oper", "OPER_VHOST_FAILED", client,
     76 		           "Unable to set vhost on oper $client.details. "
     77 		           "Vhost '$vhost_format' expanded to '$newhost' but is invalid.",
     78 		           log_data_string("vhost_format", host),
     79 		           log_data_string("newhost", newhost));
     80 		return;
     81 	}
     82 	host = newhost; /* Shadow... */
     83 
     84 	strlcpy(uhost, host, sizeof(uhost));
     85 
     86 	if ((p = strchr(uhost, '@')))
     87 	{
     88 		*p++ = '\0';
     89 		strlcpy(client->user->username, uhost, sizeof(client->user->username));
     90 		sendto_server(NULL, 0, 0, NULL, ":%s SETIDENT %s",
     91 		              client->id, client->user->username);
     92 		host = p;
     93 	}
     94 	safe_strdup(client->user->virthost, host);
     95 	if (MyConnect(client))
     96 		sendto_server(NULL, 0, 0, NULL, ":%s SETHOST :%s", client->id, client->user->virthost);
     97 	client->umodes |= UMODE_SETHOST|UMODE_HIDE;
     98 }
     99 
    100 int _make_oper(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost, const char *autojoin_channels)
    101 {
    102 	long old_umodes = client->umodes & ALL_UMODES;
    103 
    104 	if (!autojoin_channels)
    105 		autojoin_channels = OPER_AUTO_JOIN_CHANS;
    106 
    107 	userhost_save_current(client);
    108 
    109 	/* Put in the right class (if any) */
    110 	if (clientclass)
    111 	{
    112 		if (client->local->class)
    113 			client->local->class->clients--;
    114 		client->local->class = clientclass;
    115 		client->local->class->clients++;
    116 	}
    117 
    118 	/* set oper user modes */
    119 	client->umodes |= UMODE_OPER;
    120 	if (modes)
    121 		client->umodes |= modes; /* oper::modes */
    122 	else
    123 		client->umodes |= OPER_MODES; /* set::modes-on-oper */
    124 
    125 	/* oper::vhost */
    126 	if (vhost)
    127 	{
    128 		set_oper_host(client, vhost);
    129 	} else
    130 	if (iConf.oper_vhost)
    131 	{
    132 		set_oper_host(client, iConf.oper_vhost);
    133 	} else
    134 	if (IsHidden(client) && !client->user->virthost)
    135 	{
    136 		/* +x has just been set by modes-on-oper and no vhost. cloak the oper! */
    137 		safe_strdup(client->user->virthost, client->user->cloakedhost);
    138 	}
    139 
    140 	userhost_changed(client);
    141 
    142 	unreal_log(ULOG_INFO, "oper", "OPER_SUCCESS", client,
    143 		   "$client.details is now an IRC Operator [oper-block: $oper_block] [operclass: $operclass]",
    144 		   log_data_string("oper_block", operblock_name),
    145 		   log_data_string("operclass", operclass));
    146 
    147 	/* set oper snomasks */
    148 	if (snomask)
    149 		set_snomask(client, snomask); /* oper::snomask */
    150 	else
    151 		set_snomask(client, OPER_SNOMASK); /* set::snomask-on-oper */
    152 
    153 	send_umode_out(client, 1, old_umodes);
    154 	if (client->user->snomask)
    155 		sendnumeric(client, RPL_SNOMASK, client->user->snomask);
    156 
    157 	list_add(&client->special_node, &oper_list);
    158 
    159 	RunHook(HOOKTYPE_LOCAL_OPER, client, 1, operblock_name, operclass);
    160 
    161 	sendnumeric(client, RPL_YOUREOPER);
    162 
    163 	/* Update statistics */
    164 	if (IsInvisible(client) && !(old_umodes & UMODE_INVISIBLE))
    165 		irccounts.invisible++;
    166 	if (IsOper(client) && !IsHideOper(client))
    167 		irccounts.operators++;
    168 
    169 	if (SHOWOPERMOTD == 1)
    170 	{
    171 		const char *args[1] = { NULL };
    172 		do_cmd(client, NULL, "OPERMOTD", 1, args);
    173 	}
    174 
    175 	if (!BadPtr(autojoin_channels) && strcmp(autojoin_channels, "0"))
    176 	{
    177 		char *chans = strdup(autojoin_channels);
    178 		const char *args[3] = {
    179 			client->name,
    180 			chans,
    181 			NULL
    182 		};
    183 		do_cmd(client, NULL, "JOIN", 3, args);
    184 		safe_free(chans);
    185 		/* Theoretically the oper may be killed on join. Would be fun, though */
    186 		if (IsDead(client))
    187 			return 0;
    188 	}
    189 
    190 	return 1;
    191 }
    192 
    193 /*
    194 ** cmd_oper
    195 **	parv[1] = oper name
    196 **	parv[2] = oper password
    197 */
    198 CMD_FUNC(cmd_oper)
    199 {
    200 	ConfigItem_oper *operblock;
    201 	const char *operblock_name, *password;
    202 
    203 	if (!MyUser(client))
    204 		return;
    205 
    206 	if ((parc < 2) || BadPtr(parv[1]))
    207 	{
    208 		sendnumeric(client, ERR_NEEDMOREPARAMS, "OPER");
    209 		return;
    210 	}
    211 
    212 	if (SVSNOOP)
    213 	{
    214 		sendnotice(client,
    215 		    "*** This server is in NOOP mode, you cannot /oper");
    216 		return;
    217 	}
    218 
    219 	if (IsOper(client))
    220 	{
    221 		sendnotice(client, "You are already an IRC Operator. If you want to re-oper then de-oper first via /MODE yournick -o");
    222 		return;
    223 	}
    224 
    225 	operblock_name = parv[1];
    226 	password = (parc > 2) ? parv[2] : "";
    227 
    228 	/* set::plaintext-policy::oper 'deny' */
    229 	if (!IsSecure(client) && !IsLocalhost(client) && (iConf.plaintext_policy_oper == POLICY_DENY))
    230 	{
    231 		sendnotice_multiline(client, iConf.plaintext_policy_oper_message);
    232 		unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client,
    233 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    234 		           log_data_string("reason", "Not using TLS"),
    235 		           log_data_string("fail_type", "NO_TLS"),
    236 		           log_data_string("oper_block", parv[1]));
    237 		add_fake_lag(client, 7000);
    238 		return;
    239 	}
    240 
    241 	/* set::outdated-tls-policy::oper 'deny' */
    242 	if (IsSecure(client) && (iConf.outdated_tls_policy_oper == POLICY_DENY) && outdated_tls_client(client))
    243 	{
    244 		sendnotice(client, "%s", outdated_tls_client_build_string(iConf.outdated_tls_policy_oper_message, client));
    245 		unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client,
    246 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    247 		           log_data_string("reason", "Outdated TLS protocol or cipher"),
    248 		           log_data_string("fail_type", "OUTDATED_TLS_PROTOCOL_OR_CIPHER"),
    249 		           log_data_string("oper_block", parv[1]));
    250 		add_fake_lag(client, 7000);
    251 		return;
    252 	}
    253 
    254 	if (!(operblock = find_oper(operblock_name)))
    255 	{
    256 		sendnumeric(client, ERR_NOOPERHOST);
    257 		unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client,
    258 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    259 		           log_data_string("reason", "Unknown oper name"),
    260 		           log_data_string("fail_type", "UNKNOWN_OPER_NAME"),
    261 		           log_data_string("oper_block", parv[1]));
    262 		add_fake_lag(client, 7000);
    263 		return;
    264 	}
    265 
    266 	/* Below here, the oper block exists, any errors here we take (even)
    267 	 * more seriously, they are logged as errors instead of warnings.
    268 	 */
    269 
    270 	if (!user_allowed_by_security_group(client, operblock->match))
    271 	{
    272 		sendnumeric(client, ERR_NOOPERHOST);
    273 		unreal_log(ULOG_ERROR, "oper", "OPER_FAILED", client,
    274 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    275 		           log_data_string("reason", "Host does not match"),
    276 		           log_data_string("fail_type", "NO_HOST_MATCH"),
    277 		           log_data_string("oper_block", parv[1]));
    278 		add_fake_lag(client, 7000);
    279 		return;
    280 	}
    281 
    282 	if (operblock->auth && !Auth_Check(client, operblock->auth, password))
    283 	{
    284 		sendnumeric(client, ERR_PASSWDMISMATCH);
    285 		if (FAILOPER_WARN)
    286 			sendnotice(client,
    287 			    "*** Your attempt has been logged.");
    288 		unreal_log(ULOG_ERROR, "oper", "OPER_FAILED", client,
    289 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    290 		           log_data_string("reason", "Authentication failed"),
    291 		           log_data_string("fail_type", "AUTHENTICATION_FAILED"),
    292 		           log_data_string("oper_block", parv[1]));
    293 		add_fake_lag(client, 7000);
    294 		return;
    295 	}
    296 
    297 	/* Authentication of the oper succeeded (like, password, ssl cert),
    298 	 * but we still have some other restrictions to check below as well,
    299 	 * like 'require-modes' and 'maxlogins'...
    300 	 */
    301 
    302 	/* Check oper::require_modes */
    303 	if (operblock->require_modes & ~client->umodes)
    304 	{
    305 		sendnumericfmt(client, ERR_NOOPERHOST, ":You are missing user modes required to OPER");
    306 		unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client,
    307 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    308 		           log_data_string("reason", "Not matching oper::require-modes"),
    309 		           log_data_string("fail_type", "REQUIRE_MODES_NOT_SATISFIED"),
    310 		           log_data_string("oper_block", parv[1]));
    311 		add_fake_lag(client, 7000);
    312 		return;
    313 	}
    314 
    315 	if (!find_operclass(operblock->operclass))
    316 	{
    317 		sendnotice(client, "ERROR: There is a non-existant oper::operclass specified for your oper block");
    318 		unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client,
    319 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    320 		           log_data_string("reason", "Config error: invalid oper::operclass"),
    321 		           log_data_string("fail_type", "OPER_OPERCLASS_INVALID"),
    322 		           log_data_string("oper_block", parv[1]));
    323 		return;
    324 	}
    325 
    326 	if (operblock->maxlogins && (count_oper_sessions(operblock->name) >= operblock->maxlogins))
    327 	{
    328 		sendnumeric(client, ERR_NOOPERHOST);
    329 		sendnotice(client, "Your maximum number of concurrent oper logins has been reached (%d)",
    330 			operblock->maxlogins);
    331 		unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client,
    332 		           "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]",
    333 		           log_data_string("reason", "oper::maxlogins limit reached"),
    334 		           log_data_string("fail_type", "OPER_MAXLOGINS_LIMIT"),
    335 		           log_data_string("oper_block", parv[1]));
    336 		add_fake_lag(client, 4000);
    337 		return;
    338 	}
    339 
    340 	/* /OPER really succeeded now. Start processing it. */
    341 
    342 	/* Store which oper block was used to become IRCOp (for maxlogins and whois) */
    343 	safe_strdup(client->user->operlogin, operblock->name);
    344 
    345 	/* oper::swhois */
    346 	if (operblock->swhois)
    347 	{
    348 		SWhois *s;
    349 		for (s = operblock->swhois; s; s = s->next)
    350 			swhois_add(client, "oper", -100, s->line, &me, NULL);
    351 	}
    352 
    353 	make_oper(client, operblock->name, operblock->operclass, operblock->class, operblock->modes, operblock->snomask, operblock->vhost, operblock->auto_join);
    354 
    355 	/* set::plaintext-policy::oper 'warn' */
    356 	if (!IsSecure(client) && !IsLocalhost(client) && (iConf.plaintext_policy_oper == POLICY_WARN))
    357 	{
    358 		sendnotice_multiline(client, iConf.plaintext_policy_oper_message);
    359 		unreal_log(ULOG_WARNING, "oper", "OPER_UNSAFE", client,
    360 			   "Insecure (non-TLS) connection used to OPER up by $client.details [oper-block: $oper_block]",
    361 			   log_data_string("oper_block", parv[1]),
    362 		           log_data_string("warn_type", "NO_TLS"));
    363 	}
    364 
    365 	/* set::outdated-tls-policy::oper 'warn' */
    366 	if (IsSecure(client) && (iConf.outdated_tls_policy_oper == POLICY_WARN) && outdated_tls_client(client))
    367 	{
    368 		sendnotice(client, "%s", outdated_tls_client_build_string(iConf.outdated_tls_policy_oper_message, client));
    369 		unreal_log(ULOG_WARNING, "oper", "OPER_UNSAFE", client,
    370 			   "Outdated TLS protocol/cipher used to OPER up by $client.details [oper-block: $oper_block]",
    371 			   log_data_string("oper_block", parv[1]),
    372 		           log_data_string("warn_type", "OUTDATED_TLS_PROTOCOL_OR_CIPHER"));
    373 	}
    374 }
    375 
    376 int oper_connect(Client *client)
    377 {
    378 	ConfigItem_oper *e;
    379 
    380 	if (IsOper(client))
    381 		return 0;
    382 
    383 	for (e = conf_oper; e; e = e->next)
    384 	{
    385 		if (e->auto_login && user_allowed_by_security_group(client, e->match))
    386 		{
    387 			/* Ideally we would check all the criteria that cmd_oper does.
    388 			 * I'm taking a shortcut for now that is not ideal...
    389 			 */
    390 			const char *parx[3];
    391 			parx[0] = NULL;
    392 			parx[1] = e->name;
    393 			parx[2] = NULL;
    394 			do_cmd(client, NULL, "OPER", 3, parx);
    395 			return 0;
    396 		}
    397 	}
    398 
    399 	return 0;
    400 }