rem.c (18892B)
1 /* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. 2 3 Mixmaster may be redistributed and modified under certain conditions. 4 This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 5 ANY KIND, either express or implied. See the file COPYRIGHT for 6 details. 7 8 Process remailer messages 9 $Id: rem.c 934 2006-06-24 13:40:39Z rabbi $ */ 10 11 12 #include "mix3.h" 13 #include <stdlib.h> 14 #include <string.h> 15 #include <time.h> 16 #include <ctype.h> 17 #include <sys/types.h> 18 #include <sys/stat.h> 19 #ifdef POSIX 20 #include <unistd.h> 21 #else /* end of POSIX */ 22 #include <io.h> 23 #endif /* else if not POSIX */ 24 #ifndef _MSC 25 #include <dirent.h> 26 #endif /* not _MSC */ 27 #include <assert.h> 28 29 int blockrequest(BUFFER *message); 30 int create_dummy_mailin(); 31 32 #define REQUESTHELP 100 33 #define REQUESTSTATS 101 34 #define REQUESTKEY 200 35 #define REQUESTCONF 201 36 #define REQUESTOPKEY 202 37 #define REQUESTOTHER 203 38 #define BLOCKREQUEST 666 39 #define DISABLED 99 40 41 #define CPUNKMSG 1 42 #define MIXMSG 2 43 44 45 /** \brief get replies for additional information requests 46 * 47 * \param reply The buffer to store the reply in 48 * \param file The file or name of information a user requested 49 * \returns 0 on success; -1 if a ressource has a valid name 50 * but doesn't exist; 1 if the ressource name isn't valid. 51 * 52 * This function returns additional information that a 53 * user may have requested. One obvious example is help files 54 * in different languages. We don't want to hack the source every 55 * time we or somebody else adds a new language. 56 * 57 * Therefore we add a new directory where the operator may 58 * just create new files (say "remailer-help-de"). If a user 59 * requests that using this (file|ressource) name in the 60 * subject line we respond by sending it. 61 * 62 * Perhaps we should build something that returns an index of 63 * available files (FIXME if done). 64 * 65 * A ressource name needs to start with the string "remailer-" 66 * and must only consist of alphanumerical characters and dashes. 67 * Checking is done by this function and an error returned 68 * if this is violated. 69 */ 70 int get_otherrequests_reply(BUFFER *reply, BUFFER *filename) 71 { 72 FILE *f = NULL; 73 int c; 74 int err; 75 BUFFER *path; 76 77 path = buf_new(); 78 79 assert(filename); 80 assert(reply); 81 82 buf_rewind(filename); 83 err = bufileft(filename, "remailer-"); 84 if (! err) { 85 err = 1; 86 goto end; 87 }; 88 89 while ((c = buf_getc(filename)) != -1) { 90 int ok = (c >= 'A' && c <= 'Z') || 91 (c >= 'a' && c <= 'z') || 92 (c >= '0' && c <= '9') || 93 c == '-'; 94 if (!ok) { 95 err = 1; 96 goto end; 97 }; 98 }; 99 buf_rewind(filename); 100 101 buf_appends(path, REQUESTDIR); 102 buf_appends(path, "/"); 103 buf_cat(path, filename); 104 105 f = mix_openfile(path->data, "r"); 106 if (f == NULL) { 107 err = -1; 108 goto end; 109 }; 110 111 buf_read(reply, f); 112 err = 0; 113 end: 114 if (f) 115 fclose(f); 116 buf_free(path); 117 return (err); 118 } 119 120 int mix_decrypt(BUFFER *message) 121 { 122 int type = 0; 123 BUFFER *field, *content; 124 BUFFER *to, *subject, *replyto, *reply; 125 BUFFER *otherrequest; 126 FILE *f; 127 BUFFER *block; 128 int err = 0; 129 int quoted_printable = 0; /* is this message quoted printable encoded */ 130 131 mix_init(NULL); 132 field = buf_new(); 133 content = buf_new(); 134 to = buf_new(); 135 replyto = buf_new(); 136 reply = buf_new(); 137 block = buf_new(); 138 subject = buf_new(); 139 otherrequest = buf_new(); 140 buf_sets(subject, "Subject: Re: your mail"); 141 142 buf_rewind(message); 143 144 f = mix_openfile(SOURCEBLOCK, "r"); 145 if (f != NULL) { 146 buf_read(block, f); 147 fclose(f); 148 } 149 for (;;) { 150 err = buf_getheader(message, field, content); 151 if (err == 1) { 152 /* "::" marks for additional header lines */ 153 while (buf_lookahead(message, field) == 1) 154 buf_getheader(message, field, content); 155 if (isline(field, HDRMARK)) 156 continue; 157 else 158 goto hdrend; 159 } 160 if (err == -1) 161 goto hdrend; 162 163 if ((bufieq(field, "from") || bufieq(field, "sender") || bufieq(field,"received")) && 164 doblock(content, block, 1) != 0) 165 goto end; 166 167 if (bufieq(field, "to")) 168 buf_cat(to, content); 169 else if (bufieq(field, "from") && replyto->length == 0) 170 /* reply to From address if no Reply-To header present */ 171 buf_set(replyto, content); 172 else if (bufieq(field, "reply-to")) 173 buf_set(replyto, content); 174 else if (MIX && bufieq(field, "remailer-type") && 175 bufileft(content, "mixmaster")) 176 type = MIXMSG; 177 else if (bufieq(field, "subject")) { 178 if (bufieq(content, "help") || bufieq(content, "remailer-help")) 179 type = REQUESTHELP; 180 else if (bufieq(content, "remailer-stats")) 181 type = REQUESTSTATS; 182 else if (bufieq(content, "remailer-key")) 183 type = REQUESTKEY; 184 else if (bufieq(content, "remailer-adminkey")) 185 type = REQUESTOPKEY; 186 else if (bufieq(content, "remailer-conf")) 187 type = REQUESTCONF; 188 else if (bufileft(content, "remailer-")) { 189 type = REQUESTOTHER; 190 buf_set(otherrequest, content); 191 } else if (bufileft(content, "destination-block")) 192 type = BLOCKREQUEST; 193 else { 194 buf_sets(subject, "Subject: "); 195 if (!bufileft(content, "re:")) 196 buf_appends(subject, "Re: "); 197 buf_cat(subject, content); 198 } 199 } else if (bufieq(field, "test-to") || bufieq(field, "encrypted") || 200 bufieq(field, "anon-to") || 201 bufieq(field, "request-remailing-to") || 202 bufieq(field, "remail-to") || bufieq(field, "anon-post-to") || 203 bufieq(field, "post-to") || bufieq(field, "anon-send-to") || 204 bufieq(field, "send-to") || bufieq(field, "remix-to") || 205 bufieq(field, "encrypt-to")) 206 type = CPUNKMSG; 207 else if (bufieq(field, "content-transfer-encoding") 208 && bufieq(content, "quoted-printable")) { 209 quoted_printable = 1; 210 } 211 212 } 213 hdrend: 214 if (quoted_printable) 215 qp_decode_message(message); 216 217 if (type > 0 && REMAIL == 0) 218 type = DISABLED; 219 switch (type) { 220 case REQUESTHELP: 221 if (sendinfofile(HELPFILE, NULL, replyto, NULL) == -1) 222 errlog(WARNING, "No help file available.\n"); 223 break; 224 case REQUESTKEY: 225 err = key(reply); 226 if (err == 0) 227 err = sendmail(reply, REMAILERNAME, replyto); 228 break; 229 case REQUESTOPKEY: 230 err = adminkey(reply); 231 if (err == 0) 232 err = sendmail(reply, REMAILERNAME, replyto); 233 break; 234 case REQUESTSTATS: 235 err = stats(reply); 236 if (err == 0) 237 err = sendmail(reply, REMAILERNAME, replyto); 238 break; 239 case REQUESTCONF: 240 err = conf(reply); 241 if (err == 0) 242 err = sendmail(reply, REMAILERNAME, replyto); 243 break; 244 case REQUESTOTHER: 245 err = get_otherrequests_reply(reply, otherrequest); 246 if (err == 0) 247 err = sendmail(reply, REMAILERNAME, replyto); 248 break; 249 case CPUNKMSG: 250 err = t1_decrypt(message); 251 if (err != 0) { 252 errlog(LOG, "Invalid type 1 message from %b\n", replyto); 253 sendinfofile(USAGEFILE, USAGELOG, replyto, NULL); 254 logmail(err == -2 ? MAILUSAGE : MAILERROR, message); 255 } else 256 create_dummy_mailin(); 257 break; 258 case MIXMSG: 259 err = t2_decrypt(message); 260 if (err == -1) { 261 errlog(LOG, "Invalid type 2 message from %b\n", replyto); 262 sendinfofile(USAGEFILE, USAGELOG, replyto, NULL); 263 logmail(MAILERROR, message); 264 } else 265 create_dummy_mailin(); 266 break; 267 case BLOCKREQUEST: 268 blockrequest(message); 269 /* Already wrote a log entry in blockrequest() */ 270 logmail(MAILBLOCK, message); 271 break; 272 case DISABLED: 273 errlog(ERRORMSG, "Remailer is disabled.\n"); 274 buf_sets(reply, "Subject: remailer error\n\nThe remailer is disabled.\n"); 275 sendmail(reply, REMAILERNAME, replyto); 276 logmail(MAILERROR, message); 277 break; 278 default: 279 if (strifind 280 (replyto->data, "mailer-daemon")) { 281 errlog(LOG, "Bounce mail from %b\n", replyto); 282 logmail(MAILBOUNCE, message); 283 } else if (bufifind(to, REMAILERADDR) && blockrequest(message)) { 284 /* Already wrote a log entry in blockrequest() */ 285 logmail(MAILBLOCK, message); 286 } else if (bufifind(to, REMAILERADDR)) { 287 errlog(LOG, "Non-remailer message from %b\n", replyto); 288 if (AUTOREPLY) 289 sendinfofile(USAGEFILE, USAGELOG, replyto, NULL); 290 logmail(MAILUSAGE, message); 291 } else if (bufifind(to, COMPLAINTS)) { 292 errlog(WARNING, "Abuse complaint from %b\n", replyto); 293 if (AUTOREPLY) 294 sendinfofile(ABUSEFILE, NULL, replyto, subject); 295 logmail(MAILABUSE, message); 296 } else if (ANONADDR[0] && bufifind(to, ANONADDR)) { 297 errlog(LOG, "Reply to anonymous message from %b\n", replyto); 298 if (AUTOREPLY) 299 sendinfofile(REPLYFILE, NULL, replyto, subject); 300 logmail(MAILANON, message); 301 } else { 302 errlog(DEBUGINFO, "Mail from %b\n", replyto); 303 logmail(MAILBOX, message); 304 } 305 err = 1; 306 } 307 end: 308 buf_free(field); 309 buf_free(content); 310 buf_free(to); 311 buf_free(replyto); 312 buf_free(reply); 313 buf_free(block); 314 buf_free(subject); 315 buf_free(otherrequest); 316 return (err); 317 } 318 319 int create_dummy_mailin() 320 { 321 while (rnd_number(100) < INDUMMYP) { 322 errlog(DEBUGINFO, "Generating dummy message with incoming mail.\n"); 323 if (mix_encrypt(MSG_NULL, NULL, NULL, 1, NULL) == -1) 324 return -1; 325 } 326 return 0; 327 } 328 329 int t2_decrypt(BUFFER *in) 330 { 331 int err = 0; 332 BUFFER *msg; 333 334 msg = buf_new(); 335 do { 336 err = mix_dearmor(in, msg); 337 if (err != -1) { 338 err = mix2_decrypt(msg); 339 } 340 } 341 while (in->ptr + 1000 < in->length); /* accept several packets in one message */ 342 343 buf_free(msg); 344 return (err); 345 } 346 347 int mix_pool(BUFFER *msg, int type, long latent) 348 { 349 char path[PATHMAX], pathtmp[PATHMAX]; 350 FILE *f; 351 int err = -1; 352 353 f = pool_new(latent > 0 ? "lat" : "msg", pathtmp, path); 354 if (f != NULL) { 355 if (latent > 0) 356 fprintf(f, "%d %ld\n", type, latent + time(NULL)); 357 else 358 fprintf(f, "%d 0\n", type); 359 err = buf_write_sync(msg, f); 360 } 361 if (err == 0) { 362 rename(pathtmp, path); 363 errlog(DEBUGINFO, "Added message to pool.\n"); 364 } 365 return (err); 366 } 367 368 int pool_packetfile(char *fname, BUFFER *mid, int packetnum) 369 /* create a filename */ 370 { 371 #ifdef SHORTNAMES 372 sprintf(fname, "%s%cp%02x%02x%02x%01x.%02x", POOLDIR, DIRSEP, 373 mid->data[0], mid->data[1], mid->data[2], mid->data[3] & 15, 374 packetnum); 375 #else /* end of SHORTNAMES */ 376 sprintf(fname, "%s%cp%02x%02x%02x%02x%02x%02x%01x", POOLDIR, DIRSEP, 377 packetnum, mid->data[0], mid->data[1], mid->data[2], mid->data[3], 378 mid->data[4], mid->data[5] & 15); 379 #endif /* else if not SHORTNAMES */ 380 return (0); 381 } 382 383 void pool_packetexp(void) 384 { 385 char *path; 386 DIR *d; 387 struct dirent *e; 388 struct stat sb; 389 390 d = opendir(POOLDIR); 391 errlog(DEBUGINFO, "Checking for old parts.\n"); 392 if (d != NULL) 393 for (;;) { 394 e = readdir(d); 395 if (e == NULL) 396 break; 397 if (e->d_name[0] == 'p' || e->d_name[0] == 'e' || e->d_name[0] == 't') { 398 path=malloc(strlen(POOLDIR)+strlen(e->d_name)+strlen(DIRSEPSTR)+1); 399 if (path) { 400 strcpy(path, POOLDIR); 401 strcat(path, DIRSEPSTR); 402 strcat(path, e->d_name); 403 if (stat(path, &sb) == 0 && time(NULL) - sb.st_mtime > PACKETEXP) { 404 if (e->d_name[0] == 'p') { 405 errlog(NOTICE, "Expiring incomplete partial message %s.\n", 406 e->d_name); 407 } 408 else if (e->d_name[0] == 'e') { 409 errlog(NOTICE, "Expiring old error message %s.\n", 410 e->d_name); 411 } 412 else if (e->d_name[0] == 't') { 413 errlog(NOTICE, "Expiring moldy temporary message %s.\n", 414 e->d_name); 415 } 416 unlink(path); 417 } 418 free(path); 419 } 420 } 421 } 422 closedir(d); 423 } 424 425 void logmail(char *mailbox, BUFFER *message) 426 { 427 time_t t; 428 struct tm *tc; 429 char line[LINELEN]; 430 431 /* mailbox is "|program", "user@host", "stdout", "Maildir/" or "filename" */ 432 buf_rewind(message); 433 if (mailbox[0] == '\0') /* default action */ 434 mailbox = MAILBOX; 435 if (strieq(mailbox, "stdout")) 436 buf_write(message, stdout); 437 else if (mailbox[0] == '|') { 438 FILE *p; 439 440 errlog(DEBUGINFO, "Piping message to %s.\n", mailbox + 1); 441 p = openpipe(mailbox + 1); 442 if (p != NULL) { 443 buf_write(message, p); 444 closepipe(p); 445 } 446 } else if (strchr(mailbox, '@')) { 447 BUFFER *field, *content; 448 449 field = buf_new(); 450 content = buf_new(); 451 while (buf_getheader(message, field, content) == 0) 452 if (bufieq(field, "x-loop") && bufifind(content, REMAILERADDR)) { 453 errlog(WARNING, "Loop detected! Message not sent to %s.\n", mailbox); 454 goto isloop; 455 } 456 buf_sets(content, mailbox); 457 sendmail_loop(message, NULL, content); 458 isloop: 459 buf_free(field); 460 buf_free(content); 461 } else if (mailbox[strlen(mailbox)-1] == DIRSEP) { 462 /* the user is requesting Maildir delivery */ 463 if(maildirWrite(mailbox, message, 1) != 0) { 464 errlog(ERRORMSG, "Can't write to maildir %s\n", mailbox); 465 return; 466 } 467 } else { 468 FILE *mbox; 469 470 mbox = mix_openfile(mailbox, "a"); 471 if (mbox == NULL) { 472 errlog(ERRORMSG, "Can't write to mail folder %s\n", mailbox); 473 return; 474 } 475 lock(mbox); 476 if (!bufileft(message, "From ")) { 477 t = time(NULL); 478 tc = localtime(&t); 479 strftime(line, LINELEN, "From Mixmaster %a %b %d %H:%M:%S %Y\n", tc); 480 fprintf(mbox, line); 481 } 482 buf_write(message, mbox); 483 fprintf(mbox, "\n\n"); 484 unlock(mbox); 485 fclose(mbox); 486 } 487 } 488 489 int blockrequest(BUFFER *message) 490 { 491 int request = 0, num, i; 492 BUFFER *from, *line, *field, *content, *addr, *remailer_addr, *copy_addr; 493 REMAILER remailer[MAXREM]; 494 FILE *f; 495 char *destblklst = (char *)malloc( strlen(DESTBLOCK)+1 ); 496 char *destblk; 497 498 from = buf_new(); 499 line = buf_new(); 500 field = buf_new(); 501 content = buf_new(); 502 addr = buf_new(); 503 remailer_addr = buf_new(); 504 copy_addr = buf_new(); 505 506 if (destblklst == NULL) { 507 errlog(ERRORMSG, "Can't malloc %n bytes for destblklst.\n", strlen(DESTBLOCK)+1); 508 goto end; 509 }; 510 511 buf_rewind(message); 512 while (buf_getheader(message, field, content) == 0) 513 if (bufieq(field, "from")) 514 buf_set(from, content); 515 else if (bufieq(field, "subject")) 516 buf_cat(message, content); 517 /* Append the subject to the message body so destination block requests 518 in the subject line work too (we process the body a few lines down) */ 519 while (buf_getline(message, line) != -1) 520 if (bufifind(line, "destination-block")) { 521 buf_clear(addr); 522 request = 1; 523 { 524 int c = 0; 525 526 while (!strileft(line->data + line->ptr, "block")) 527 line->ptr++; 528 while (c != ' ' && c != -1) 529 c = tolower(buf_getc(line)); 530 while (c == ' ') 531 c = buf_getc(line); 532 if (c != -1) 533 do { 534 buf_appendc(addr, c); 535 c = buf_getc(line); 536 } while (c > ' '); 537 } 538 if (addr->length == 0) { 539 rfc822_addr (from, addr); 540 buf_chop(addr); 541 } 542 /* Check whether somebody wants us to block ourselves */ 543 buf_set(copy_addr, addr); 544 buf_sets(remailer_addr, REMAILERADDR); 545 if (doblock(remailer_addr, copy_addr, 1)) { 546 errlog(LOG, "Ignoring blocking request for %b from %b.\n", addr, from); 547 request = 2; 548 goto end; 549 } 550 /* Check if some evil person tries to block a known type II remailer */ 551 num = mix2_rlist(remailer, NULL); 552 for (i = 0; i < num; i++) { 553 buf_sets(remailer_addr, remailer[i].addr); 554 if (doblock(remailer_addr, copy_addr, 1)) { 555 errlog(LOG, "Ignoring blocking request for %b from %b.\n", addr, from); 556 request = 2; 557 goto end; 558 } 559 } 560 /* Check if some evil person tries to block a known type I remailer */ 561 num = t1_rlist(remailer, NULL); 562 for (i = 0; i < num; i++) { 563 buf_sets(remailer_addr, remailer[i].addr); 564 if (doblock(remailer_addr, copy_addr, 1)) { 565 errlog(LOG, "Ignoring blocking request for %b from %b.\n", addr, from); 566 request = 2; 567 goto end; 568 } 569 } 570 571 if (buf_ieq(addr, from)) 572 errlog(NOTICE, "Blocking request for %b\n", addr); 573 else 574 errlog(NOTICE, "Blocking request for %b from %b\n", addr, from); 575 if (AUTOBLOCK) { 576 buf_clear(line); 577 rfc822_addr(addr, line); 578 if (line->length == 0) { 579 errlog(LOG, "Nothing to block after rfc822_addr().\n"); 580 } else 581 if (bufleft(line, "/")) { 582 errlog(LOG, "Ignoring blocking request: %b is a regex.\n", addr); 583 } else { 584 if (strchr(line->data, '@') && strchr(strchr(line->data, '@'), '.')) { 585 strcpy( destblklst, DESTBLOCK ); 586 destblk = strtok( destblklst, " " ); 587 f = mix_openfile( destblk, "a" ); 588 if (f != NULL) { 589 lock(f); 590 591 buf_chop(line); 592 sendinfofile(BLOCKFILE, NULL, line, NULL); 593 if (line->length) { 594 fprintf(f, "%s\n", line->data); 595 } else 596 errlog(NOTICE, "%b already blocked.\n", addr); 597 unlock(f); 598 fclose(f); 599 } else 600 errlog(ERRORMSG, "Can't write to %s.\n", DESTBLOCK); 601 } else 602 errlog(WARNING, "Invalid address not added to %s: %b\n", DESTBLOCK, 603 addr); 604 } 605 } 606 } 607 608 end: 609 free( destblklst ); 610 buf_free(from); 611 buf_free(line); 612 buf_free(field); 613 buf_free(content); 614 buf_free(addr); 615 buf_free(remailer_addr); 616 buf_free(copy_addr); 617 618 return (request); 619 } 620 621 622 int idexp(void) 623 { 624 FILE *f; 625 long now, then; 626 LOCK *i; 627 idlog_t idbuf; 628 long fpi = sizeof(idlog_t), fpo = sizeof(idlog_t); 629 630 if (IDEXP == 0) 631 return (0); 632 633 f = mix_openfile(IDLOG, "rb+"); 634 if (f == NULL) 635 return (-1); 636 i = lockfile(IDLOG); 637 now = time(NULL); 638 if (fread(&idbuf, 1, sizeof(idlog_t), f) != sizeof(idlog_t)) { /* replace first line */ 639 fclose(f); 640 unlockfile(i); 641 return (-1); 642 } 643 then = idbuf.time; 644 memset(idbuf.id,0,sizeof(idbuf.id)); 645 idbuf.time = now - IDEXP; 646 fseek(f,0,SEEK_SET); 647 fwrite(&idbuf,1,sizeof(idlog_t),f); 648 fseek(f,fpi,SEEK_SET); /* this fseek does nothing, but MSVC CRT happilly reads past EOF (!!!) if we do not fseek here :-/ */ 649 while (fread(&idbuf, 1, sizeof(idlog_t), f) == sizeof(idlog_t)) { 650 fpi+=sizeof(idlog_t); 651 then = idbuf.time; 652 if (now - then < IDEXP && 653 now - then > - SECONDSPERDAY * 180 ) 654 /* also expire packets that are dated more than half a year in the future. 655 * That way we get rid of invalid packets introduced by the switch to a 656 * binary id.log. */ 657 { 658 fseek(f,fpo,SEEK_SET); 659 fwrite(&idbuf,1,sizeof(idlog_t),f); 660 fpo += sizeof(idlog_t); 661 fseek(f,fpi,SEEK_SET); 662 } 663 } 664 #ifdef _MSC 665 chsize(fileno(f),fpo); 666 #else /* end of _MSC */ 667 ftruncate(fileno(f),fpo); 668 #endif /* else if not _MSC */ 669 fclose(f); 670 unlockfile(i); 671 return (0); 672 } 673 674 675 int pgpmaxexp(void) 676 { 677 FILE *f; 678 BUFFER *b; 679 long now, then; 680 LOCK *i; 681 char temp[LINELEN]; 682 683 f = mix_openfile(PGPMAXCOUNT, "rb+"); 684 if (f == NULL) 685 return (-1); 686 i = lockfile(PGPMAXCOUNT); 687 b = buf_new(); 688 now = time(NULL); 689 690 while (fgets(temp, sizeof(temp), f) != NULL) 691 if (sscanf(temp, "%ld", &then) && 692 then >= now - SECONDSPERDAY) 693 buf_appends(b, temp); 694 695 fseek(f,0,SEEK_SET); 696 697 buf_write(b, f); 698 699 #ifdef _MSC 700 chsize(fileno(f),b->length); 701 #else /* end of _MSC */ 702 ftruncate(fileno(f),b->length); 703 #endif /* else if not _MSC */ 704 705 fclose(f); 706 unlockfile(i); 707 buf_free(b); 708 return (0); 709 }