unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
webirc.c (12027B)
1 /* 2 * WebIRC / CGI:IRC Support 3 * (C) Copyright 2006-.. Bram Matthys (Syzop) and the UnrealIRCd team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 1, or (at your option) 8 * any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 #include "unrealircd.h" 20 21 /* Types */ 22 typedef struct ConfigItem_webirc ConfigItem_webirc; 23 24 typedef enum { 25 WEBIRC_PASS=1, WEBIRC_WEBIRC=2 26 } WEBIRCType; 27 28 struct ConfigItem_webirc { 29 ConfigItem_webirc *prev, *next; 30 ConfigFlag flag; 31 ConfigItem_mask *mask; 32 WEBIRCType type; 33 AuthConfig *auth; 34 }; 35 36 /* Module header */ 37 ModuleHeader MOD_HEADER 38 = { 39 "webirc", 40 "5.0", 41 "WebIRC/CGI:IRC Support", 42 "UnrealIRCd Team", 43 "unrealircd-6", 44 }; 45 46 /* Global variables */ 47 ModDataInfo *webirc_md = NULL; 48 ConfigItem_webirc *conf_webirc = NULL; 49 50 /* Forward declarations */ 51 CMD_FUNC(cmd_webirc); 52 int webirc_local_pass(Client *client, const char *password); 53 int webirc_config_test(ConfigFile *, ConfigEntry *, int, int *); 54 int webirc_config_run(ConfigFile *, ConfigEntry *, int); 55 void webirc_free_conf(void); 56 void delete_webircblock(ConfigItem_webirc *e); 57 const char *webirc_md_serialize(ModData *m); 58 void webirc_md_unserialize(const char *str, ModData *m); 59 void webirc_md_free(ModData *md); 60 int webirc_secure_connect(Client *client); 61 62 #define IsWEBIRC(x) (moddata_client(x, webirc_md).l) 63 #define IsWEBIRCSecure(x) (moddata_client(x, webirc_md).l == 2) 64 #define ClearWEBIRC(x) do { moddata_client(x, webirc_md).l = 0; } while(0) 65 #define SetWEBIRC(x) do { moddata_client(x, webirc_md).l = 1; } while(0) 66 #define SetWEBIRCSecure(x) do { moddata_client(x, webirc_md).l = 2; } while(0) 67 68 #define MSG_WEBIRC "WEBIRC" 69 70 MOD_TEST() 71 { 72 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, webirc_config_test); 73 return MOD_SUCCESS; 74 } 75 76 /** Called upon module init */ 77 MOD_INIT() 78 { 79 ModDataInfo mreq; 80 81 MARK_AS_OFFICIAL_MODULE(modinfo); 82 83 memset(&mreq, 0, sizeof(mreq)); 84 mreq.name = "webirc"; 85 mreq.type = MODDATATYPE_CLIENT; 86 mreq.serialize = webirc_md_serialize; 87 mreq.unserialize = webirc_md_unserialize; 88 mreq.free = webirc_md_free; 89 mreq.sync = MODDATA_SYNC_EARLY; 90 webirc_md = ModDataAdd(modinfo->handle, mreq); 91 if (!webirc_md) 92 { 93 config_error("could not register webirc moddata"); 94 return MOD_FAILED; 95 } 96 97 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, webirc_config_run); 98 HookAdd(modinfo->handle, HOOKTYPE_LOCAL_PASS, 0, webirc_local_pass); 99 HookAdd(modinfo->handle, HOOKTYPE_SECURE_CONNECT, 0, webirc_secure_connect); 100 101 CommandAdd(modinfo->handle, MSG_WEBIRC, cmd_webirc, MAXPARA, CMD_UNREGISTERED); 102 103 return MOD_SUCCESS; 104 } 105 106 /** Called upon module load */ 107 MOD_LOAD() 108 { 109 return MOD_SUCCESS; 110 } 111 112 /** Called upon unload */ 113 MOD_UNLOAD() 114 { 115 webirc_free_conf(); 116 return MOD_SUCCESS; 117 } 118 119 void webirc_free_conf(void) 120 { 121 ConfigItem_webirc *webirc_ptr, *next; 122 123 for (webirc_ptr = conf_webirc; webirc_ptr; webirc_ptr = next) 124 { 125 next = webirc_ptr->next; 126 delete_webircblock(webirc_ptr); 127 } 128 conf_webirc = NULL; 129 } 130 131 void delete_webircblock(ConfigItem_webirc *e) 132 { 133 unreal_delete_masks(e->mask); 134 if (e->auth) 135 Auth_FreeAuthConfig(e->auth); 136 DelListItem(e, conf_webirc); 137 safe_free(e); 138 } 139 140 int webirc_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) 141 { 142 ConfigEntry *cep; 143 int errors = 0; 144 char has_mask = 0; /* mandatory */ 145 char has_password = 0; /* mandatory */ 146 char has_type = 0; /* optional (used for dup checking) */ 147 WEBIRCType webirc_type = WEBIRC_WEBIRC; /* the default */ 148 149 if (type != CONFIG_MAIN) 150 return 0; 151 152 if (!ce) 153 return 0; 154 155 if (!strcmp(ce->name, "cgiirc")) 156 { 157 config_error("%s:%i: the cgiirc block has been renamed to webirc and " 158 "the syntax has changed in UnrealIRCd 4", 159 ce->file->filename, ce->line_number); 160 *errs = 1; 161 return -1; 162 } 163 164 if (strcmp(ce->name, "webirc")) 165 return 0; /* not interested in non-webirc stuff.. */ 166 167 /* Now actually go parse the webirc { } block */ 168 for (cep = ce->items; cep; cep = cep->next) 169 { 170 if (!cep->value) 171 { 172 config_error_empty(cep->file->filename, cep->line_number, 173 "webirc", cep->name); 174 errors++; 175 continue; 176 } 177 if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "match")) 178 { 179 if (cep->value || cep->items) 180 has_mask = 1; 181 } 182 else if (!strcmp(cep->name, "password")) 183 { 184 if (has_password) 185 { 186 config_warn_duplicate(cep->file->filename, 187 cep->line_number, "webirc::password"); 188 continue; 189 } 190 has_password = 1; 191 if (Auth_CheckError(cep) < 0) 192 errors++; 193 } 194 else if (!strcmp(cep->name, "type")) 195 { 196 if (has_type) 197 { 198 config_warn_duplicate(cep->file->filename, 199 cep->line_number, "webirc::type"); 200 } 201 has_type = 1; 202 if (!strcmp(cep->value, "webirc")) 203 webirc_type = WEBIRC_WEBIRC; 204 else if (!strcmp(cep->value, "old")) 205 webirc_type = WEBIRC_PASS; 206 else 207 { 208 config_error("%s:%i: unknown webirc::type '%s', should be either 'webirc' or 'old'", 209 cep->file->filename, cep->line_number, cep->value); 210 errors++; 211 } 212 } 213 else 214 { 215 config_error_unknown(cep->file->filename, cep->line_number, 216 "webirc", cep->name); 217 errors++; 218 } 219 } 220 if (!has_mask) 221 { 222 config_error_missing(ce->file->filename, ce->line_number, 223 "webirc::mask"); 224 errors++; 225 } 226 227 if (!has_password && (webirc_type == WEBIRC_WEBIRC)) 228 { 229 config_error_missing(ce->file->filename, ce->line_number, 230 "webirc::password"); 231 errors++; 232 } 233 234 if (has_password && (webirc_type == WEBIRC_PASS)) 235 { 236 config_error("%s:%i: webirc block has type set to 'old' but has a password set. " 237 "Passwords are not used with type 'old'. Either remove the password or " 238 "use the 'webirc' method instead.", 239 ce->file->filename, ce->line_number); 240 errors++; 241 } 242 243 *errs = errors; 244 return errors ? -1 : 1; 245 } 246 247 int webirc_config_run(ConfigFile *cf, ConfigEntry *ce, int type) 248 { 249 ConfigEntry *cep; 250 ConfigItem_webirc *webirc = NULL; 251 252 if (type != CONFIG_MAIN) 253 return 0; 254 255 if (!ce || !ce->name || strcmp(ce->name, "webirc")) 256 return 0; /* not interested */ 257 258 webirc = safe_alloc(sizeof(ConfigItem_webirc)); 259 webirc->type = WEBIRC_WEBIRC; /* default */ 260 261 for (cep = ce->items; cep; cep = cep->next) 262 { 263 if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "match")) 264 unreal_add_masks(&webirc->mask, cep); 265 else if (!strcmp(cep->name, "password")) 266 webirc->auth = AuthBlockToAuthConfig(cep); 267 else if (!strcmp(cep->name, "type")) 268 { 269 if (!strcmp(cep->value, "webirc")) 270 webirc->type = WEBIRC_WEBIRC; 271 else if (!strcmp(cep->value, "old")) 272 webirc->type = WEBIRC_PASS; 273 else 274 abort(); 275 } 276 } 277 278 AddListItem(webirc, conf_webirc); 279 280 return 0; 281 } 282 283 const char *webirc_md_serialize(ModData *m) 284 { 285 static char buf[32]; 286 if (m->i == 0) 287 return NULL; /* not set */ 288 snprintf(buf, sizeof(buf), "%d", m->i); 289 return buf; 290 } 291 292 void webirc_md_unserialize(const char *str, ModData *m) 293 { 294 m->i = atoi(str); 295 } 296 297 void webirc_md_free(ModData *md) 298 { 299 /* we have nothing to free actually, but we must set to zero */ 300 md->l = 0; 301 } 302 303 ConfigItem_webirc *find_webirc(Client *client, const char *password, WEBIRCType type, char **errorstr) 304 { 305 ConfigItem_webirc *e; 306 char *error = NULL; 307 308 for (e = conf_webirc; e; e = e->next) 309 { 310 if ((e->type == type) && unreal_mask_match(client, e->mask)) 311 { 312 if (type == WEBIRC_WEBIRC) 313 { 314 /* Check password */ 315 if (!Auth_Check(client, e->auth, password)) 316 error = "CGI:IRC -- Invalid password"; 317 else 318 return e; /* Found matching block, return straight away */ 319 } else { 320 return e; /* The WEBIRC_PASS type has no password checking */ 321 } 322 } 323 } 324 325 if (error) 326 *errorstr = error; /* Invalid password (this error was delayed) */ 327 else 328 *errorstr = "CGI:IRC -- No access"; /* No match found */ 329 330 return NULL; 331 } 332 333 #define WEBIRC_STRING "WEBIRC_" 334 #define WEBIRC_STRINGLEN (sizeof(WEBIRC_STRING)-1) 335 336 /* Does the CGI:IRC host spoofing work */ 337 void dowebirc(Client *client, const char *ip, const char *host, const char *options) 338 { 339 char oldip[64]; 340 char scratch[64]; 341 342 if (IsWEBIRC(client)) 343 { 344 exit_client(client, NULL, "Double CGI:IRC request (already identified)"); 345 return; 346 } 347 348 if (host && !strcmp(ip, host)) 349 host = NULL; /* host did not resolve, make it NULL */ 350 351 /* STEP 1: Update client->local->ip 352 inet_pton() returns 1 on success, 0 on bad input, -1 on bad AF */ 353 if (!is_valid_ip(ip)) 354 { 355 /* then we have an invalid IP */ 356 exit_client(client, NULL, "Invalid IP address"); 357 return; 358 } 359 360 /* STEP 2: Update GetIP() */ 361 strlcpy(oldip, client->ip, sizeof(oldip)); 362 safe_strdup(client->ip, ip); 363 364 /* STEP 3: Update client->local->hostp */ 365 /* (free old) */ 366 if (client->local->hostp) 367 { 368 unreal_free_hostent(client->local->hostp); 369 client->local->hostp = NULL; 370 } 371 /* (create new) */ 372 if (host && valid_host(host, 1)) 373 client->local->hostp = unreal_create_hostent(host, client->ip); 374 375 /* STEP 4: Update sockhost 376 Make sure that if this any IPv4 address is _not_ prefixed with 377 "::ffff:" by using Inet_ia2p(). 378 */ 379 // Hmm I ignored above warning. May be bad during transition period. 380 strlcpy(client->local->sockhost, client->ip, sizeof(client->local->sockhost)); 381 382 SetWEBIRC(client); 383 384 if (options) 385 { 386 char optionsbuf[BUFSIZE]; 387 char *name, *p = NULL, *p2; 388 strlcpy(optionsbuf, options, sizeof(optionsbuf)); 389 for (name = strtoken(&p, optionsbuf, " "); name; name = strtoken(&p, NULL, " ")) 390 { 391 p2 = strchr(name, '='); 392 if (p2) 393 *p2 = '\0'; 394 if (!strcmp(name, "secure") && IsSecure(client)) 395 { 396 /* The entire [client]--[webirc gw]--[server] chain is secure */ 397 SetWEBIRCSecure(client); 398 } 399 } 400 } 401 402 RunHook(HOOKTYPE_IP_CHANGE, client, oldip); 403 } 404 405 /* WEBIRC <pass> "cgiirc" <hostname> <ip> [:option1 [option2...]]*/ 406 CMD_FUNC(cmd_webirc) 407 { 408 const char *ip, *host, *password, *options; 409 ConfigItem_webirc *e; 410 char *error = NULL; 411 412 if ((parc < 5) || BadPtr(parv[4])) 413 { 414 sendnumeric(client, ERR_NEEDMOREPARAMS, "WEBIRC"); 415 return; 416 } 417 418 password = parv[1]; 419 host = !DONT_RESOLVE ? parv[3] : parv[4]; 420 ip = parv[4]; 421 options = parv[5]; /* can be NULL */ 422 423 /* Check if allowed host */ 424 e = find_webirc(client, password, WEBIRC_WEBIRC, &error); 425 if (!e) 426 { 427 exit_client(client, NULL, error); 428 return; 429 } 430 431 /* And do our job.. */ 432 dowebirc(client, ip, host, options); 433 } 434 435 int webirc_local_pass(Client *client, const char *password) 436 { 437 if (!strncmp(password, WEBIRC_STRING, WEBIRC_STRINGLEN)) 438 { 439 char buf[512]; 440 char *ip, *host; 441 ConfigItem_webirc *e; 442 char *error = NULL; 443 444 /* Work on a copy as we may trash it */ 445 strlcpy(buf, password, sizeof(buf)); 446 e = find_webirc(client, NULL, WEBIRC_PASS, &error); 447 if (e) 448 { 449 /* Ok now we got that sorted out, proceed: 450 * Syntax: WEBIRC_<ip>_<resolvedhostname> 451 * The <resolvedhostname> has been checked ip->host AND host->ip by CGI:IRC itself 452 * already so we trust it. 453 */ 454 ip = buf + WEBIRC_STRINGLEN; 455 host = strchr(ip, '_'); 456 if (!host) 457 { 458 exit_client(client, NULL, "Invalid CGI:IRC IP received"); 459 return HOOK_DENY; 460 } 461 *host++ = '\0'; 462 463 dowebirc(client, ip, host, NULL); 464 return HOOK_DENY; 465 } 466 /* fallthrough if not in webirc block.. */ 467 } 468 469 return HOOK_CONTINUE; /* not webirc */ 470 } 471 472 /** Called from register_user() right after setting user +z */ 473 int webirc_secure_connect(Client *client) 474 { 475 /* Remove secure mode (-z) if the WEBIRC gateway did not ensure 476 * us that their [client]--[webirc gateway] connection is also 477 * secure (eg: using https) 478 */ 479 if (IsWEBIRC(client) && IsSecureConnect(client) && !IsWEBIRCSecure(client)) 480 client->umodes &= ~UMODE_SECURE; 481 return 0; 482 }