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 }