]> jfr.im git - irc/quakenet/newserv.git/blob - chanserv/chanserv_relay.c
e3c5bc792b3271d2c58b513f218b7a315b84924a
[irc/quakenet/newserv.git] / chanserv / chanserv_relay.c
1 #include "chanserv.h"
2 #include "../control/control.h"
3 #include "../lib/version.h"
4 #include "../lib/irc_string.h"
5 #include "../core/config.h"
6 #include "../lib/hmac.h"
7 #include "../lib/strlfunc.h"
8 #include "../lib/cbc.h"
9 #include "authlib.h"
10
11 #include <stdio.h>
12 #include <string.h>
13
14 #define KEY_BITS 256
15 #define BLOCK_SIZE 16
16
17 MODULE_VERSION(QVERSION);
18
19 int csa_docheckhashpass(void *source, int cargc, char **cargv);
20 int csa_docreateaccount(void *source, int cargc, char **cargv);
21 int csa_dosettempemail(void *source, int cargc, char **cargv);
22 int csa_dosetemail(void *source, int cargc, char **cargv);
23 int csa_doresendemail(void *source, int cargc, char **cargv);
24 int csa_doactivateuser(void *source, int cargc, char **cargv);
25 int csa_doaddchan(void *source, int argc, char **argv);
26 static int decrypt_password(unsigned char *secret, int keybits, char *buf, int bufsize, char *encrypted);
27 static int hex_to_int(char *input, unsigned char *buf, int buflen);
28
29 static unsigned char createaccountsecret[KEY_BITS / 8];
30 static int createaccountsecret_ok = 0;
31
32 static unsigned char hexlookup[256] = {
33 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
34 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
35 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
36 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
37 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01,
38 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff,
39 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
40 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
41 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
42 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x0b, 0x0c,
43 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
44 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
45 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
46 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
47 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
48 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
49 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
50 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
51 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
52 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
53 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
54 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
55 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
56 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
57 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
58 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
59 };
60
61 void _init(void) {
62 sstring *s;
63
64 registercontrolhelpcmd("checkhashpass", NO_RELAY, 3, csa_docheckhashpass, "Usage: checkhashpass <username> <digest> ?junk?");
65 registercontrolhelpcmd("settempemail", NO_RELAY, 2, csa_dosettempemail, "Usage: settempemail <userid> <email address>");
66 registercontrolhelpcmd("setemail", NO_RELAY, 3, csa_dosetemail, "Usage: setmail <userid> <timestamp> <email address>");
67 registercontrolhelpcmd("resendemail", NO_RELAY, 1, csa_doresendemail, "Usage: resendemail <userid>");
68 registercontrolhelpcmd("activateuser", NO_RELAY, 1, csa_doactivateuser, "Usage: activateuser <userid>");
69 registercontrolhelpcmd("addchan", NO_RELAY, 3, csa_doaddchan, "Usage: addchan <channel> <userid> <channel type>");
70
71 s=getcopyconfigitem("chanserv","createaccountsecret","",128);
72 if(!s || !s->content || !s->content[0]) {
73 Error("chanserv_relay",ERR_WARNING,"createaccountsecret not set, createaccount disabled.");
74 } else if(s->length != KEY_BITS / 8 * 2 || !hex_to_int(s->content, createaccountsecret, sizeof(createaccountsecret))) {
75 Error("chanserv_relay",ERR_WARNING,"createaccountsecret must be a %d character hex string.", KEY_BITS / 8 * 2);
76 } else {
77 registercontrolhelpcmd("createaccount", NO_RELAY, 5, csa_docreateaccount, "Usage: createaccount <execute> <username> <email address> <encrypted password> <activate user>");
78 createaccountsecret_ok = 1;
79 }
80
81 freesstring(s);
82 }
83
84 void _fini(void) {
85 deregistercontrolcmd("checkhashpass", csa_docheckhashpass);
86 deregistercontrolcmd("settempemail", csa_dosettempemail);
87 deregistercontrolcmd("setemail", csa_dosetemail);
88 deregistercontrolcmd("resendemail", csa_doresendemail);
89 deregistercontrolcmd("activateuser", csa_doactivateuser);
90 deregistercontrolcmd("addchan", csa_doaddchan);
91
92 if(createaccountsecret_ok)
93 deregistercontrolcmd("createaccount", csa_docreateaccount);
94 }
95
96 int csa_docheckhashpass(void *source, int cargc, char **cargv) {
97 nick *sender=(nick *)source;
98 reguser *rup;
99 char *flags;
100
101 if(cargc<3) {
102 controlreply(sender, "CHECKHASHPASS FAIL args");
103 return CMD_ERROR;
104 }
105
106 if (!(rup=findreguserbynick(cargv[0]))) {
107 controlreply(sender, "CHECKHASHPASS FAIL user");
108 return CMD_OK;
109 }
110
111 flags = printflags(QUFLAG_ALL & rup->flags, ruflags);
112 if(UHasSuspension(rup)) {
113 controlreply(sender, "CHECKHASHPASS FAIL suspended %s %s %u", rup->username, flags, rup->ID);
114 } else if(UIsInactive(rup)) {
115 controlreply(sender, "CHECKHASHPASS FAIL inactive %s %s %u", rup->username, flags, rup->ID);
116 } else if(!checkhashpass(rup, cargc<3?NULL:cargv[2], cargv[1])) {
117 controlreply(sender, "CHECKHASHPASS FAIL digest %s %s %u", rup->username, flags, rup->ID);
118 } else {
119 controlreply(sender, "CHECKHASHPASS OK %s %s %u %s", rup->username, flags, rup->ID, rup->email?rup->email->content:"-");
120 }
121
122 return CMD_OK;
123 }
124
125 static char *email_to_error(char *email) {
126 maildomain *mdp, *smdp;
127 char *local;
128 char *dupemail;
129 int found = 0;
130 maillock *mlp;
131 reguser *ruh;
132
133 switch(csa_checkeboy_r(email)) {
134 case -1: break;
135 case QM_EMAILTOOSHORT: return "emailshort";
136 case QM_EMAILNOAT: return "emailinvalid";
137 case QM_EMAILATEND: return "emailinvalid";
138 case QM_EMAILINVCHR: return "emailinvalid";
139 case QM_NOTYOUREMAIL: return "emailnotyours";
140 case QM_INVALIDEMAIL: return "emailinvalid";
141 default: return "emailunknown";
142 }
143
144 /* maildomain BS... c&p from hello.c */
145 for(mlp=maillocks;mlp;mlp=mlp->next) {
146 if(!match(mlp->pattern->content, email)) {
147 return "emaillocked";
148 }
149 }
150
151 dupemail = strdup(email);
152 local=strchr(dupemail, '@');
153 if(!local) {
154 free(dupemail);
155 return "emailunknown";
156 }
157 *(local++)='\0';
158
159 mdp=findnearestmaildomain(local);
160 if(mdp) {
161 for(smdp=mdp; smdp; smdp=smdp->parent) {
162 if(MDIsBanned(smdp)) {
163 free(dupemail);
164 return "emaillocked";
165 }
166 if((smdp->count >= smdp->limit) && (smdp->limit > 0)) {
167 free(dupemail);
168 return "emaildomainlimit";
169 }
170 }
171 }
172
173 mdp=findmaildomainbydomain(local);
174 if(mdp) {
175 for (ruh=mdp->users; ruh; ruh=ruh->nextbydomain) {
176 if (ruh->localpart)
177 if (!strcasecmp(dupemail, ruh->localpart->content)) {
178 found++;
179 }
180 }
181
182 if((found >= mdp->actlimit) && (mdp->actlimit > 0)) {
183 free(dupemail);
184 return "emailaddresslimit";
185 }
186 }
187
188 free(dupemail);
189
190 return NULL;
191 }
192
193 static void sendemail(reguser *rup) {
194 csdb_createmail(rup, QMAIL_ACTIVATEEMAIL);
195 }
196
197 int csa_docreateaccount(void *source, int cargc, char **cargv) {
198 nick *sender=(nick *)source;
199 int execute;
200 char *error_username = NULL, *error_password = NULL, *error_email = NULL;
201 char *username = NULL, *password = NULL, *email = NULL;
202 char account_info[512];
203 char passbuf[512];
204 int do_create;
205 int activate;
206
207 if(cargc<5) {
208 controlreply(sender, "CREATEACCOUNT FALSE args");
209 return CMD_ERROR;
210 }
211
212 execute = cargv[0][0] == '1';
213 if(strcmp(cargv[1], "0"))
214 username = cargv[1];
215 if(strcmp(cargv[2], "0"))
216 email = cargv[2];
217 if(strcmp(cargv[3], "0")) {
218 int errorcode = decrypt_password(createaccountsecret, KEY_BITS, passbuf, sizeof(passbuf), cargv[3]);
219 if(errorcode) {
220 Error("chanserv_relay",ERR_WARNING,"createaccount unable to decrypt password, error code: %d", errorcode);
221 controlreply(sender, "CREATEACCOUNT FALSE args");
222 return CMD_ERROR;
223 }
224 password = passbuf;
225 }
226 activate = cargv[4][0] == '1';
227
228 if(username) {
229 if (findreguserbynick(username)) {
230 error_username = "usernameinuse";
231 } else if(csa_checkaccountname_r(username)) {
232 error_username = "usernameinvalid";
233 }
234 }
235
236 if(email)
237 error_email = email_to_error(email);
238
239 if(password) {
240 int r = csa_checkpasswordquality(password);
241 if(r == QM_PWTOSHORT) {
242 error_password = "passwordshort";
243 } else if(r == QM_PWTOLONG) {
244 error_password = "passwordlong";
245 } else if(r == QM_PWTOWEAK) {
246 error_password = "passwordweak";
247 } else if(r != -1) {
248 error_password = "passwordunknown";
249 }
250 }
251
252 if(execute && email && password && username && !error_email && !error_password && !error_username) {
253 reguser *rup;
254 do_create = 1;
255
256 rup = csa_createaccount(username, password, email);
257
258 if(!activate)
259 USetInactive(rup);
260
261 cs_log(sender,"CREATEACCOUNT created auth %s (%s) %s",rup->username,rup->email->content,activate?"(active)": "(inactive)");
262 csdb_createuser(rup);
263 snprintf(account_info, sizeof(account_info), " %u %lu", rup->ID, (unsigned long)rup->lastpasschange);
264
265 if(!activate)
266 sendemail(rup);
267 } else {
268 account_info[0] = '\0';
269 do_create = 0;
270 }
271
272 controlreply(sender, "CREATEACCOUNT %s%s%s%s%s%s%s%s",
273 do_create ? "TRUE" : "FALSE",
274 account_info,
275 email && error_email ? " " : "", email && error_email ? error_email : "",
276 password && error_password ? " " : "", password && error_password ? error_password : "",
277 username && error_username ? " " : "", username && error_username ? error_username : ""
278 );
279
280 return CMD_OK;
281 }
282
283 int csa_dosettempemail(void *source, int cargc, char **cargv) {
284 char *email;
285 char *error;
286 reguser *rup;
287 nick *sender=(nick *)source;
288
289 if(cargc<2) {
290 controlreply(sender, "SETTEMPEMAIL FALSE args");
291 return CMD_ERROR;
292 }
293
294 rup = findreguserbyID(atoi(cargv[0]));
295 if(rup == NULL) {
296 controlreply(sender, "SETTEMPEMAIL FALSE useridnotexist");
297 return CMD_ERROR;
298 }
299
300 if(!UIsInactive(rup)) {
301 controlreply(sender, "SETTEMPEMAIL FALSE accountactive");
302 return CMD_ERROR;
303 }
304
305 email = cargv[1];
306 error = email_to_error(email);
307 if(error) {
308 controlreply(sender, "SETTEMPEMAIL FALSE %s", error);
309 return CMD_ERROR;
310 }
311
312 freesstring(rup->email);
313 rup->email=getsstring(email,EMAILLEN);
314 cs_log(sender,"SETTEMPEMAIL OK username %s email %s",rup->username, rup->email->content);
315
316 csdb_updateuser(rup);
317 sendemail(rup);
318
319 controlreply(sender, "SETTEMPEMAIL TRUE");
320
321 return CMD_OK;
322 }
323
324 int csa_dosetemail(void *source, int cargc, char **cargv) {
325 char *email;
326 char *error;
327 reguser *rup;
328 nick *sender=(nick *)source;
329
330 if(cargc<3) {
331 controlreply(sender, "SETEMAIL FALSE args");
332 return CMD_ERROR;
333 }
334
335 rup = findreguserbyID(atoi(cargv[0]));
336 if(rup == NULL) {
337 controlreply(sender, "SETEMAIL FALSE useridnotexist");
338 return CMD_ERROR;
339 }
340
341 if(UHasStaffPriv(rup)) {
342 controlreply(sender, "SETEMAIL FALSE privuser");
343 return CMD_ERROR;
344 }
345
346 if(UHasSuspension(rup)) {
347 controlreply(sender, "SETEMAIL FALSE suspended");
348 return CMD_ERROR;
349 }
350
351 if(rup->lastpasschange > atoi(cargv[1])) {
352 controlreply(sender, "SETEMAIL FALSE passwordchanged");
353 return CMD_ERROR;
354 }
355
356 email = cargv[2];
357
358 if(!strcmp(email, rup->email->content)) {
359 /* setting to the same thing? fine! */
360 controlreply(sender, "SETEMAIL TRUE");
361 return CMD_OK;
362 }
363
364 error = email_to_error(email);
365 if(error) {
366 controlreply(sender, "SETEMAIL FALSE %s", error);
367 return CMD_ERROR;
368 }
369
370 freesstring(rup->email);
371 rup->email=getsstring(email,EMAILLEN);
372 cs_log(sender,"SETEMAIL OK username %s email %s",rup->username, rup->email->content);
373
374 csdb_updateuser(rup);
375 sendemail(rup);
376
377 controlreply(sender, "SETEMAIL TRUE");
378
379 return CMD_OK;
380 }
381
382 int csa_doresendemail(void *source, int cargc, char **cargv) {
383 reguser *rup;
384 nick *sender=(nick *)source;
385
386 if(cargc<1) {
387 controlreply(sender, "RESENDEMAIL FALSE args");
388 return CMD_ERROR;
389 }
390
391 rup = findreguserbyID(atoi(cargv[0]));
392 if(rup == NULL) {
393 controlreply(sender, "RESENDEMAIL FALSE useridnotexist");
394 return CMD_ERROR;
395 }
396
397 if(!UIsInactive(rup)) {
398 controlreply(sender, "RESENDEMAIL FALSE accountactive");
399 return CMD_ERROR;
400 }
401
402 sendemail(rup);
403 controlreply(sender, "RESENDEMAIL TRUE");
404 cs_log(sender,"RESENDEMAIL OK username %s",rup->username);
405
406 return CMD_OK;
407 }
408
409 int csa_doactivateuser(void *source, int cargc, char **cargv) {
410 reguser *rup;
411 nick *sender=(nick *)source;
412
413 if(cargc<1) {
414 controlreply(sender, "ACTIVATEUSER FALSE args");
415 return CMD_ERROR;
416 }
417
418 rup = findreguserbyID(atoi(cargv[0]));
419 if(rup == NULL) {
420 controlreply(sender, "ACTIVATEUSER FALSE useridnotexist");
421 return CMD_ERROR;
422 }
423
424 if(!UIsInactive(rup)) {
425 controlreply(sender, "ACTIVATEUSER FALSE accountactive");
426 return CMD_ERROR;
427 }
428
429 UClearInactive(rup);
430 csdb_updateuser(rup);
431
432 cs_log(sender,"ACTIVATEUSER OK username %s",rup->username);
433 controlreply(sender, "ACTIVATEUSER TRUE");
434
435 return CMD_OK;
436 }
437
438 int csa_doaddchan(void *source, int cargc, char **cargv) {
439 nick *sender=(nick *)source;
440 reguser *rup = getreguserfromnick(sender), *founder;
441 chanindex *cip;
442 short type;
443 regchan *rcp;
444
445 if(cargc<3) {
446 controlreply(sender, "ADDCHAN FALSE args");
447 return CMD_ERROR;
448 }
449
450 if (*cargv[0] != '#' || strlen(cargv[0]) > CHANNELLEN) {
451 controlreply(sender, "ADDCHAN FALSE invalidchannel");
452 return CMD_ERROR;
453 }
454
455 if (!(cip=findorcreatechanindex(cargv[0]))) {
456 controlreply(sender, "ADDCHAN FALSE invalidchannel");
457 return CMD_ERROR;
458 }
459
460 founder = findreguserbyID(atoi(cargv[1]));
461 if(founder == NULL) {
462 controlreply(sender, "ADDCHAN FALSE useridnotexist");
463 return CMD_ERROR;
464 }
465
466 if(UIsInactive(founder)) {
467 controlreply(sender, "ADDCHAN FALSE accountinactive");
468 return CMD_ERROR;
469 }
470
471 for(type=CHANTYPES-1;type;type--)
472 if(!ircd_strcmp(chantypes[type]->content, cargv[2]))
473 break;
474
475 if(!type) {
476 controlreply(sender, "ADDCHAN FALSE invalidchantype");
477 return CMD_ERROR;
478 }
479
480 rcp = cs_addchan(cip, sender, rup, founder, QCFLAG_JOINED | QCFLAG_AUTOOP | QCFLAG_BITCH | QCFLAG_FORCETOPIC | QCFLAG_PROTECT | QCFLAG_TOPICSAVE, CHANMODE_NOCTCP | CHANMODE_DELJOINS | CHANMODE_MODNOAUTH | CHANMODE_NONOTICE | CHANMODE_NOEXTMSG | CHANMODE_SINGLETARG | CHANMODE_TOPICLIMIT | CHANMODE_NOQUITMSG, 0, type);
481 if(!rcp) {
482 controlreply(sender, "ADDCHAN FALSE alreadyregistered");
483 return CMD_ERROR;
484 }
485
486 cs_log(sender, "ADDCHAN %s #%s %s %s", cip->name->content, founder->username, printflags(rcp->flags, rcflags), chantypes[type]->content);
487 controlreply(sender, "ADDCHAN TRUE %u", rcp->ID);
488 return CMD_OK;
489 }
490
491 static int hex_to_int(char *input, unsigned char *buf, int buflen) {
492 int i;
493 for(i=0;i<buflen;i++) {
494 if((0xff == hexlookup[(int)input[i * 2]]) || (0xff == hexlookup[(int)input[i * 2 + 1]])) {
495 return 0;
496 } else {
497 buf[i] = (hexlookup[(int)input[i * 2]] << 4) | hexlookup[(int)input[i * 2 + 1]];
498 }
499 }
500 return 1;
501 }
502
503 static int decrypt_password(unsigned char *secret, int keybits, char *buf, int bufsize, char *encrypted) {
504 size_t ciphertextlen, datalen;
505 unsigned char iv[BLOCK_SIZE];
506 rijndaelcbc *c;
507 int i, j;
508
509 datalen = strlen(encrypted);
510 if(datalen % 2 != 0)
511 return 1;
512 if(datalen < BLOCK_SIZE * 2 * 2)
513 return 2;
514
515 if(!hex_to_int(encrypted, iv, BLOCK_SIZE))
516 return 3;
517
518 ciphertextlen = (datalen - (BLOCK_SIZE * 2)) / 2;
519 if(ciphertextlen > bufsize || ciphertextlen % BLOCK_SIZE != 0)
520 return 4;
521
522 if(!hex_to_int(encrypted + BLOCK_SIZE * 2, (unsigned char *)buf, ciphertextlen))
523 return 5;
524
525 c = rijndaelcbc_init(secret, keybits, iv, 1);
526
527 for(i=0;i<ciphertextlen;i+=BLOCK_SIZE) {
528 char *p = &buf[i];
529 unsigned char *r = rijndaelcbc_decrypt(c, (unsigned char *)p);
530 memcpy(p, r, BLOCK_SIZE);
531
532 for(j=0;j<BLOCK_SIZE;j++) {
533 if(*(p + j) == '\0') {
534 rijndaelcbc_free(c);
535 return 0;
536 }
537 }
538 }
539
540 rijndaelcbc_free(c);
541 return 6;
542 }