mixmaster

mixmaster 3.0 patched for libressl
git clone git://parazyd.org/mixmaster.git
Log | Files | Refs | README

commit 50de79fe7f9e194b5a6bdd3a63fb3bdb455588a8
Author: parazyd <parazyd@dyne.org>
Date:   Thu, 15 Sep 2016 01:44:49 +0200

initial sources import

Diffstat:
ABUILD.Win32 | 30++++++++++++++++++++++++++++++
ACOPYRIGHT | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AHISTORY | 502+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AInstall | 1403+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AREADME | 191+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/Makefile.deps | 46++++++++++++++++++++++++++++++++++++++++++++++
ASrc/Makefile.in | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/buffers.c | 816+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/chain.c | 384+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/chain1.c | 301+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/chain2.c | 685+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/compress.c | 210+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/config.h | 403+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/crypto.c | 492+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/crypto.h | 48++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/dllmain.c | 35+++++++++++++++++++++++++++++++++++
ASrc/dummy.c | 16++++++++++++++++
ASrc/keymgt.c | 434+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/mail.c | 898+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/maildir.c | 323+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/main.c | 820+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/menu.c | 1003+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/menu.h | 48++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/menunym.c | 472+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/menusend.c | 556+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/menustats.c | 445+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/menuutil.c | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/mime.c | 814+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/mix.c | 1262+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/mix.h | 917+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/mix3.h | 443+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/mixlib.def | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/mpgp.c | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/nym.c | 669+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/parsedate.y | 879+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/pgp.c | 494+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/pgp.h | 189+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/pgpcreat.c | 848+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/pgpdata.c | 1539+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/pgpdb.c | 583+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/pgpget.c | 870+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/pool.c | 981+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/random.c | 210+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/rem.c | 709+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/rem1.c | 599+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/rem2.c | 486+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/remailer.c | 36++++++++++++++++++++++++++++++++++++
ASrc/rfc822.c | 585+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/rndseed.c | 157+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/service.c | 331+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/stats.c | 442+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/tests/test-parse_yearmonthday.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/util.c | 704+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASrc/version.h | 1+
ATHANKS | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ATODO | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconf/abuse.txt.in | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconf/adminkey.txt | 1+
Aconf/blocked.txt.in | 20++++++++++++++++++++
Aconf/dest.alw | 41+++++++++++++++++++++++++++++++++++++++++
Aconf/dest.blk | 2++
Aconf/end.hlp | 36++++++++++++++++++++++++++++++++++++
Aconf/header.blk | 22++++++++++++++++++++++
Aconf/intro.hlp | 15+++++++++++++++
Aconf/mix.cfg | 14++++++++++++++
Aconf/mix.cfg.ex | 192+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconf/mix.hlp | 45+++++++++++++++++++++++++++++++++++++++++++++
Aconf/mlist.txt | 48++++++++++++++++++++++++++++++++++++++++++++++++
Aconf/news.hlp | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconf/pgp.hlp | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconf/pgponly.hlp | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconf/pubring.asc | 544+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconf/pubring.mix | 384+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconf/rab.blk | 0
Aconf/reply.txt.in | 32++++++++++++++++++++++++++++++++
Aconf/rlist.txt | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconf/type1.hlp | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconf/usage.txt.in | 24++++++++++++++++++++++++
Aidea.txt | 34++++++++++++++++++++++++++++++++++
Amixmaster.1 | 1136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ampgp.1 | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awin32/installer/mixinstall.nsi | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awin32/mix.sln | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awin32/mix.vcproj | 193+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awin32/mixlib.vcproj | 459+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awin32/pcre.vcproj | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awin32/pcre_chartables.vcproj | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awin32/pdcurses.vcproj | 607+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awin32/zlib.vcproj | 217+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
89 files changed, 31462 insertions(+), 0 deletions(-)

diff --git a/BUILD.Win32 b/BUILD.Win32 @@ -0,0 +1,30 @@ +Mixmaster on Windows is known to build with Microsoft Visual Studio .NET +2003 Professional. + +You will need openssl, zlib, pcre, and pdcurses. + +First, build openssl as described in the openssl documentation. Place +the entire build directory in Src/openssl. + +zlib, pcre, and pdcurses sources are assumed to be in Src/zlib-1.1.4, +Src/pcre-2.08, and Src/pdcurses respectively. + +Open the mixmaster project win32/mix.sln, and build the mix solution. +You should find the results in win32/release. + + +References: + - http://www.openssl.org/ + - http://pdcurses.sourceforge.net/ + +-- +Peter Palfrader, Sat, 1 May 2004 20:31:48 +0200 + + +[Note to users of Mixmaster 3.0rc1 and earlier: mix.cfg.txt and pop3.cfg +are now named mix.ini and pop3.ini, respectively, on WIN32. You will +need to manually rename your custom config files, if appropriate.] + +-- +Len Sassaman, Thu, 13 Sep 2007 14:56:37 +0200 + diff --git a/COPYRIGHT b/COPYRIGHT @@ -0,0 +1,134 @@ +Copyright (c) 1999-2000 Anonymizer Inc. +Copyright (c) 2000-2002 Ulf Moeller +Copyright (c) 2001-2002 Janis Jagars +Copyright (c) 2001-2007 Peter Palfrader +Copyright (c) 2001-2008 Len Sassaman +Copyright (c) 2004-2008 Colin Tuckley +Copyright (c) 2007-2008 Steve Crook + + +MIXMASTER LICENSE AGREEMENT + +1. Grant of License. + + Anonymizer Inc. grants you the following non-exclusive license for + the Mixmaster program and its associated documentation (the "Program"), + subject to all of the following terms and conditions: + + a) You may use the Program, and copy and distribute verbatim copies + of the Program as you receive it, in any medium. + + Local regulations may exist which limit your rights to distribute or + use cryptographic software. In certain jurisdictions, parts of this + software may be protected by patents. It is your responsibility to + obtain the appropriate licenses. + + b) You may modify the Program or incorporate the Program or any + portion of it into other computer programs. You may copy and + distribute such modifications or work, provided that you: + + (i) cause the modified Program to carry a prominent notice + stating that it has been modified, and cause the modified files + to carry notices stating that you changed the files and the + date of any change; + + (ii) reproduce and include this Agreement, the copyright + notices and disclaimer of warranty on any copy; and + + (iii) provide Anonymizer Inc. with a copy of the Source Code of + such modifications or work via electronic mail to the address + mixmaster@anonymizer.com, and grant Anonymizer Inc. a perpetual, + royalty-free license to use and distribute the modifications or + work in its products. + + "Source Code" means the preferred form of a work for making + modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to + control compilation and installation of an executable. + + c) Should Anonymizer Inc. be acquired by another entity, you: + + (i) will grant to the acquiring entity the items in section + 1.b.(iii) in leiu of Anonymizer, Inc.; + + d) Should Anonymizer Inc. cease to exist, and no aquiring entity be + available to accept Source Code modifications, you: + + (i) will grant Lance Cottrell the items in section 1.b.(iii) in leiu + of Anonymizer, Inc. + + (ii) should Mr. Cottrell be deceased, section 1.b.(iii) of this + license will be rendered null and void. + + e) In the case that the electronic mail address mixmaster@anonymizer.com + ceases to accept electronic mail, + + (i) submission of changes to the Mixmaster project at SourceForge + will be accceptable; + + (ii) if Mixmaster development is no longer hosted by SourceForge, + submission of changes to any open source repository similar to + SourceForge, or + + (iii) submission to the Internet news group alt.privacy.anon-server + will be acceptable. + + f) Submission of changes is required as a "best effort". If it is not + possible for you to access any of the notification locations, a notation + in the modified code stating that the modifications should be submitted by + any capable parties who subsequently make use of the modified code will + be acceptable in lieu of code submission. + +2. Reservation of Rights. + + No rights are granted to the Program except as expressly set forth + herein. You may not copy, modify, sublicense, or distribute the + Program except as expressly provided under this Agreement. Any + attempt otherwise to copy, modify, sublicense or distribute the + Program is void, and will automatically terminate your rights under + this Agreement. + +3. DISCLAIMER OF WARRANTY. + + BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY + FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. THE + PROGRAM IS PROVIDED ON AN ``AS IS'' BASIS, WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, + WARRANTIES THAT THE PROGRAM IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR + A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE + QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE + PROGRAM PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT ANONYMIZER INC. OR + ANY DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY + NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF + WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF THE + PROGRAM IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +4. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL ANONYMIZER INC. + OR ANY DEVELOPER OR ANY OTHER CONTRIBUTOR OR ANY SUPPLIER OF ANY OF + SUCH PARTIES, BE LIABLE TO YOU OR ANY OTHER PERSON FOR ANY INDIRECT, + SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER + INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK + STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THAT EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +5. General. + + This license represents the complete agreement concerning subject + matter hereof. If any provision of this Agreement is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This Agreement shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + The application of the United Nations Convention on Contracts for the + International Sale of Goods is expressly excluded. Any law or + regulation which provides that the language of a contract shall be + construed against the drafter shall not apply to this License. diff --git a/HISTORY b/HISTORY @@ -0,0 +1,502 @@ + 1998/1999 2.9 written from scratch. + +1999-04-14 2.9beta0 public preview release. + +1999-05-17 2.9beta1 Bug fixes (remix, OpenPGP encryption, FreeBSD + name conflict); Win32 DLL. + +1999-05-18 2.9beta2 Install bug fixes. -N and -n options renamed. + +1999-05-19 2.9beta3 OpenSSL-related bug fix. Type 1 remailer fixes + (pointed out by <kev@drule.org>). + +1999-05-20 2.9beta4 Read and generate OpenPGP encrypted secret keys. + +1999-05-20 2.9beta5 The client sent messages if PGP encryption failed. + +1999-05-28 2.9beta6 Message-ID generation bug fixes. Contributed by: + Johannes Kroeger <hanne@squirrel.owl.de>. + Remix-To bug fix. + +1999-06-09 2.9beta7 More (minor) remailer and Install script fixes. + +1999-06-10 2.9beta8 Regular expression bug fix. Thanks to Johannes + and Kevin for help with debugging! + +1999-07-20 2.9beta9 Bug fixes (remailer, nym creation). + +1999-08-03 2.9beta10 Fix for buffer overrun error. + "Chain:" pseudo-header may contain the number of + copies like this: `Chain: *,*,*,*; copies=3' + +1999-09-09 2.9beta11 Support MIME attachments and OpenPGP/MIME in the + client. + Do not select cpunk remailers if PGP key is missing. + Fix error in nym creation. + Header lines can be edited when composing new + messages in the mail reader. + Accept empty pass phrase to allow storing the + nym database on an encrypted file system. + More verbose error messages. + Various minor bug fixes. + * Thanks to Gerd Beuster for many good suggestions! + +1999-09-22 2.9beta12 OpenSSL 0.9.3 or newer is now required. + For the Mixmaster DLL, allow the application to + seed the random number generator. + +1999-09-29 2.9beta13 Fix OpenPGP 3DES decryption. + Store DSA secret keys in PGP5 compatible format. + Support new "ekx" capability. + Use the more secure new style OpenPGP conventional + encryption to protect the nym database and nym + keys. + +1999-10-01 2.9beta14 Bug fix. + +1999-10-01 2.9beta15 Bug fix: create mixrand.bin in Mix directory. + Support "Encrypt-IDEA" directive. + +1999-10-11 2.9beta16 Fix memory leaks. + +1999-11-03 2.9beta17 Bug fix. + Sending messages is logged as DEBUGINFO. + +1999-11-09 2.9beta18 Bug fix for rlist with trailing spaces. + Print remailer reliability (by Gerd Beuster). + +1999-12-19 2.9beta19 (internal) + +1999-12-19 2.9beta20 Output remailer RSA keys separately from the + DSA/ElGamal keys to avoid problems with old + versions of PGP. + Messages in mail folders can be deleted. Nym + messages and other encrypted mail will be + written back as plain text (by Gerd Beuster). + SMTP bug fix. + Support multiple OpenPGP decryption subkeys. + Fix remailer bug with Newsgroups header in encrypted + T1 messages. + Fix MIME-decoding bug (pointed out by Gerd Beuster). + Nym creation bug fix (by Gerd Beuster). + +2000-03-09 2.9beta21 Support for PGP partial length packets (by + Christian Mock). + +2000-03-16 2.9beta22 Bug fixes (by Antonomasia) and minor changes. + +2000-06-29 2.9beta23 Bug fix for nym creation with several newsgroups + reply blocks (by Gerd Beuster). + --nym option bug fix (by Adam Back). + +2001-09-11 2.9beta24 Changed pool.c to allow Mixmaster keys to pass + even when binary blocking is enabled. Note that + the solution is not a nice one: It does not + recognize Mix keys, it simply allows 10 lines of + binary garbage instead of 3. This should be enough + for Mix keys to come through (by Peter Palfrader). + Fixed a bug in pgpdata.c affecting v3 OpenPGP keys. + (by Michael Young). + +2001-09-14 2.9beta25 Now builds with pcre3 (by Peter Palfrader). + Added support for destination.allow (by Peter + Palfrader). + If the sender email address or IP address matches + anything in source.blk, ignore the message (by + cmeclax). + Added support for the Mutt -T option (by Bill + O'Hanlon). + Patches merged (by Len Sassaman). + +2001-09-17 2.9beta30 Version renamed to avoid conflicts with other + unofficial releases. + +2001-09-19 2.9beta31 Fixed a bug in mime.c that sometimes resulted in + malformed text attachments (by Michael Young). + Better error handling (by Scott Renfro). + Added support for multiple dest.blk files. This + is needed for the Remailer Abuse Blocklist (by + Markus Stöger). + Added support for remailer-adminkey replies to + provide a better way for remops to distribute + their keys. (by Markus Stöger). + Fixed errors with pcre2.08 (by Rodney Thayer). + Added long command option --type-list for the -T + option, and updated help (by Len Sassaman). + Removed redundant "encoded" variable in mime.c. + Fixed Installer bugs. + +2001-11-06 2.9beta32 Client functionality updates. + POP sockets now properly close. + Memory may be freed without allocating. + Correct time is written to mbox. + Key flags correctly set in key.txt. (all by + Disastry). + OpenSSL and OpenBSD Install script issues + addressed. + +2001-12-16 2.9b33 Support for Mixmaster as a service on Windows + platforms added (by Disastry). + Problem transparently remixing to Type I remailers + debugged and corrected (by Andy Dustman, + Disastry, Senshi-Admin). + Fixed an error in chain.c that was causing + segfaults with chains greater than 20 remailers. + Non-multipart MIME message errors fixed. + Fixed an error in rfc822.c (by Scott Renfro). + Fixed pgpget.c errors. (by Ulf Möller). + No longer permits automatic blocking of entire + domains or newsgroups. + Help files re-written (by Lucky Green). + Fixed inconsistencies between software name and + package name. + +2002-07-01 2.9b34 Encrypt-to directive is now supported. + Partial packets now properly expire if not + reassembled (by cmeclax). + Fixed an address blocking error introduced in + the last version (Peter Palfrader). + Various command line bug fixes. + +2002-07-10 2.9b35 Updated zlib due to security reasons. + Does not generate keys in client mode. + Uses binary format for id.log. + Assorted mpgp fixes (by Disastry). + Added support for storing the key passphrase + in the mix.cfg file. (by Disastry). + Now reports the contents of dest.alw for + middleman remailers (by Kat). + Reworked the OpenSSL version check in the + Install script. + +2002-08-09 2.9b36 Removed duplicate define of NYMDB from menu.h. + Fix a strncat() to undefined string variable in + mix.c (Closes: #584381). + Have the Makefile list all prerequisites for each + build target (Closes: #584386). + Change »majordomo@*« to »majordomo@« in default + dest.blk. The dest block engine does not under- + stand shell globs. Either substring matches or + regexen. + Fixed -T switch: if type2.list is not available fall + back to pubring.mix. + USE_IDEA is no longer default in config.h. It always + gets defined by the Install script instead. + Only create OpenPGP RSA keys if we compiled with + IDEA. + Make all filenames configurable in mix.cfg. + Add global mix.cfg support (compile time option). + The -G option now forces creation of new keys even in + client mode (Closes: #585176). + Random Documentation updates. + Default to not installing a .forward file in Install + script. + Fix unused variable warning on OpenBSD. + Fix public remailer keys getting re-signed + every time keys are requested (Closes: #478383). + Make smtp sending similar to local /usr/lib/sendmail + sending (wrt header/body separation; + Closes: #482052). + Add X-Loop header on mailbox forwarded messages. + Several small fixes by Sami Farin et al. + Detach correctly in daemon mode. + Minor Install script fixes. + +2002-08-20 2.9b37 OpenPGP enhancement release (fixes by Disastry). + Fix a small bug in pgpdata.c that stopped Mixmaster + from reading cipher preferences. + Fixed Passphrase reading in mpgp (the test program) + on Windows platform. + Add Hash: header when clearsigning. + Properly handle RSA keys whose key size is not a + multiple of 64. + Remove leading zeros from MPI. + Use MDC packets whenever possible. + List CAST5 and AES128 in cipher preferences. + Now displays Mixmaster version in the PGP version + header for non remailer/nym messages. + +2002-09-11 2.9b38 Install script deals with lack of patented IDEA + algorithm in a sane way (closes: #479020). + Compiled-in passphrase is now deprecated. + When expiring packet ids from id.log also expire + packets that are dated more than half a year in + the future. That way we get rid of invalid + packets introduced by the switch to a binary file. + The stats in remailer-stats replies always had a + peak at 00:00 GMT which was wrong. Fixed. + (closes: #597688). + Fixed a bug with reading armored keyrings consisting + of more than one armored block or having comments + in front of the one armored block. + In RSA PGP keys, we now set e=0x11. + Mixmaster now deletes error and temporary files + older than PACKETEXP time along with expired + partial packets. + Linux PPC fixes (and all other archs where char is + unsigned). + +2002-10-07 2.9b39 Added a new feature, --store-mail (-I), which will + deliver an encrypted mix packet to the message pool + without attempting being decrypted. + Made minor updates for WIN32 DLL. + When sending type II messages interactively you may + now choose a middleman remailer as the last hop + in your chain (closes: #481244). + If a footer.txt file exists its content will be + appended to outgoing messages leaving the remailer + network at this hop (closes: #490117). + List known remailers in remailer-conf reply (closes: + #480330). + The files created with "SENDMAIL outfile" have + different names now to scale beyond 10k files + (closes: #587593). + Fixed the "is a mailfolder" checking for -f. + Various fixes for Mixmaster when not using ncurses. + Added new option --config to allow loading of + configuration information from an alternate file. + POOL is now used correctly if set in mix.cfg. + ASCII armor checksum is now verified on PGP keys. + Corrected a bug where 1/4096 of pgp messages was + destroyed due an improper armor checksum + interpretation. + Added password-based authenticated SMTP for mix. + Currently, only AUTH LOGIN is supported. + Mixmaster now handles <CR><LF> in pubring.mix. + Removed incorrect NT service checks in mix.c. + Mixmaster now keeps no stats in client mode. + The pool is autmatically checked for waiting + messages in the client configuration. + Mixmaster now bears a DFSG-compliant license. + Fixed permissions on tarball release. + Documentation updates. + +2002-10-16 2.9b40 New option MAILIN that can be set to either a mbox + or Maildir folder. New mail will be read from it + and the folder cleared every time Mixmaster + processes its pool, or at MAILINTIME intervals + (closes: #597043). + The Mixmaster daemon now writes a pid file. + Mixmaster in daemon mode now catches SIGTERM and + SIGINT and finishes its current queue run and then + exits successfully. + Minor code formating cleanup and Install script + fixes. + +2002-12-15 2.9b41 The Mixmaster protocol version is now prepended + to the software version in the Mixmaster cap- + string. + Minor configuration default changes and Install + script fixes. + Install script now always uses "make" and not + "gmake". + IDEA detection is fixed on systems that provide + the header files but then turn out to not + have the required functions upon linking. + Install now properly identifies system-wide + installations of pcre and/or zlib if they + are installed in /usr/local/. + Mixmaster will now ensure that an address + submitted in a blocking request does not + match that of a known remailer before + adding it to the dest.blk file (patch + submitted by Trek. Vulnerability originally + discovered by noise and rabbi.) + Minor documentation fixes. + +2002-12-16 2.9b42 Minor documentation fixes. + Append another newline character to mbox folders + when storing a mail so that the mandatory empty + line is there. + +2002-12-16 2.9.0rc1 Release candidate. Packaging changes only. + +2002-12-25 2.9.0 Release version. Minor documentation changes + and version number change only. + +2003-11-08 2.9.1 Several changes for the Windows build. + Some Install script fixes. + Fixed a problem in blockrequest() where a buffer + could have been used after it was free()'d which + resulted in segfaults. + Check that feedback buffer is not null before + operating on it in chain_select(). + Closes #631353, thanks Sami Farin. + Make sure DH/DSA param file is actually opened + before writing to it. Fixes a segfault in + case it is not. + Handle a pool we cannot read correctly: don't close + the NULL dir handle (segfaults on *BSD). We also + print a warning in that case now. + Minor stats fix (gmtime vs localtime). + Fix pool stats bug. + +2004-03-20 3.0b1 FEATURE ENHANCEMENTS: + + The secret pgp keyring is now stored ASCII armored + with one key per ascii armor. + NB: Due to the bug with reading armored keyrings and + secring being stored armored now, it is not + advisable to downgrade Mixmaster unless special + action is taken to preserve the secret pgp + keyring. + Mixmaster now prompts for secret key passphrase when + started in daemon mode. + Mixmaster checks expiration and revocation status of + pgp keys, userids, and subkeys. + Mixmaster will not encrypt or sign with a revoked + or expired key. + When encrypting, Mixmaster uses preferences from + the primary userid (or the latest userid, if zero + or more than one primary userid is present.) + Mixmaster keys now have creation and expiration date. + It is not secured by any crypto voodoo, it's only + informational for clients to decide which keys to + use should they have more than one per remailer. + - on the client side we do not show remailers (and + therefore not use them) if their key is expired. + - the remailer refuses to decrypt messages to keys + that expired one month ago or earlier. + - the remailer automatically creates new Mixmaster + keys if the current ones are about to expire or + already are expired. + - the latest key from secring.mix is written to + key.txt. It used to be the first one. Since + creation of new mix key appends the key, this + seemed sensible. + Mixmaster now generates dummy messages automatically + as mail enters and exits the pool. + Applied Maildir feature patch by drt@un.bewaff.net, + with some changes by PP: + MAILBOX can now be a Maildir (closes: #586223). + New Star-Exclude feature by Colin Tuckley: + User-selected remailers can be excluded from + being chosen as random hops. + Have stats on intermediate vs. final hop count + (closes: #649900). + Add max capability for Type I. + Config option EXTFLAGS allows appending additional + flags to the capabilities string. (Hauke Lampe) + Config option PRECEDENCE allows setting the + Precedence: header on all outgoing mail. + (Hauke Lampe) + In order to serve help files in different languages + we need a way to reply to requests like + remailer-help-it. In order to not have to modify + the code for each and every new ressource, + Mixmaster now sends the file + requests/remailer-<something> to + remailer-<something> requests. + remailer-{help,key, stats,conf,adminkey} still are + special cases though. + Drop messages without timestamps and messages with + future timestamps. This abandons backwards + compatibility with Mixmaster 2.0.3 and earlier. + Mixmaster attempts to detect system clock + misconfigurations and refuses to run as a + remailer if there is a problem suspected. + Only applies to Mixmaster in remailer mode. + + BUG FIXES: + + Mixmaster in daemon mode reloads configuration on + SIGHUP. + In the curses interface chain selection it was not + possible to select a random last hop with a usenet + post message. Fixed (closes: #719165). + If remix was enabled and we had a Type-I Anon-Post-To + request we accidently randhoped it via the + configured default remailing chain (default: + *,*,*,*). + Fixed (closes: #729494). + In client mode (REMAIL n) the pool is flushed every + time mixmaster is run unless CLIENTAUTOFLUSH is + set to n. (closes: #676794: Rate implementation + doubled) + Found that weird bug that sometimes led to "Unknown + remailer version!" errors: In chain_randfinal() we + selected a random value between 0 and maxrem + instead of 0 and maxrem - 1. Mixmaster now uses + broken-chain info from stats. + Warn if remailer stats are older than a day or + from the future. + Don't send messages to ourselves via the mailsystem + but instead place them in the pool as incoming + messages so that they will get processed with the + next pool run. + No longer try to send a message if there are no + recipients left. + Set default max-randhops from 20 to 4. + Remix-To chain is limited by max-randhops limit as + well. + Messages to more than one remailer are dropped. + Nym support is not compiled in by default anymore. + The OpenPGP module mpgp now includes a man + page (large contributions by Trek). + Ignore 'No reliable remailers' problems when + randhopping messages in middleman mode. + That is better than dropping them. + Experimental feature: --redirect -l <chain>. + If you have a mixmaster message with a + chain starting with hop1 (you cannot know any + more because it already is encrypted) then + mix --redirect -l foo,bar < file + redirect the message so the chain is actually + foo,bar,hop1,... and places it in your pool. + If the total number of hops (which cannot be + known) exceeds 20 the message is damanged + and will fail at the 20th node. + +2004-05-06 3.0b2 + Use /dev/arandom instead of /dev/srandom on + OpenBSD (Nikolay Sturm). + Fall back to 3DES as Encrypt-Key cipher if we don't + have IDEA. - Laurent Fousse <laurent@komite.net> + Also sort mail into the various mboxes if autoreply + is not set. + Properly ignore whitespace in chain selection. + Removed unused functions in keymgt.c. + Added new options -V, --version, and --about. + Made manpage corrections. + Minor ncurses display tweaks. + General improvements for Win32 support (by + goblin and Peter Palfrader). + Preliminary Windows Installer work. + On Win32, default to Application Data/Mixmaster for + mixmaster's basedirectory. This can still be + overridden by MIXPATH or the registry entry + HKEY_CURRENT_USER\Software\Mixmaster\MixDir + Introduced new option "(e)dit configuration + file" in the main menu. + Changed 'q)uit' to 'q)uit w/o sending' in + menusend.c. + Added stats downloading support. Currently + works under Win32 only (by goblin). + Fixed bug in buffers.c. + +2006-06-24 3.0rc1 + Prefer pubring.asc over secring.pgp. + Support an unpublished dest.alw file. + Added MINLAT directive. Ensures randhopped + messages are sent through remailers of + latency of MINLAT time or greater + (suggested by Steve Crook). + Improved OpenSSL version checking in the + Install script. + Added full stats download support. + Fixed buffer overflow bug in keymgt.c. + +2008-03-03 3.0 + Changed name of WIN32 default config file + from mix.cfg.txt to mix.ini. + Changed pop3.cfg to pop3.ini on WIN32. + Updated Install script. + Minor documentation changes. + + + +Mixmaster maintainer history: + +1998-2000: Ulf Möller -- versions 2.9beta0 through 2.9beta22. +2000: Johannes Kroeger -- version 2.9beta23. +2001-2008: Len Sassaman -- versions 2.9beta24 through present. diff --git a/Install b/Install @@ -0,0 +1,1403 @@ +#!/bin/sh +# Mixmaster version 3.0 -- (C) 1999-2008 Anonymizer Inc. and others. + +# Mixmaster may be redistributed and modified under certain conditions. +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +# ANY KIND, either express or implied. See the file COPYRIGHT for +# details. + +# $Id: Install 979 2008-03-03 18:17:07Z rabbi $ + +#whereis program default-path +whereis() +{ + #echo "Looking for $1..." + found="" + for i in $* `which $1 2>&1` + do + if [ -f "$i" -a -x "$i" ] + then + found=$i + fi + done + if [ "$found" = "" ] + then + found=$2 +# echo "$1 not found. Using $found." +# else +# echo "$1 is at $found." + fi +} + +if echo -n | grep n >/dev/null +then + echo1="" + echo2="\c" +else + echo1="-n" + echo2="" +fi + +# readln text default +readln() +{ + echo $echo1 "$1 [$2] $echo2" + read ans + if [ -z "$ans" ] + then + ans="$2" + fi +} + +# findlib libxxx.a -- find and configure libraries +# Input: +# $1 library name +# $CONFIG library configure options +# $INCDIR possible include directories +# $SRCDIR possible library source directories +# $LIBDIR possible library binary directories +# +# Output: +# $found library directory +# $lib library name +# $INCDIR include directory if required, empty otherwise +# $LDFLAG linker options +# $LIB path to library file +# $MAKELIB Makefile entry to compile library +findlib() +{ + lib=$1 + libso=`echo $lib | sed 's/\.a$/.so/'` + echo "Looking for $lib..." + + found= + source= + type= + LIB= + LDFLAG= + MAKELIB= + + for i in /usr/local/lib /usr/lib /lib /usr/lib64 + do + if [ -r $i/$lib -o -r $i/$libso ] + then + found=$i + type=system + fi + done + + for i in $LIBDIR + do + if [ -r $i/$lib -o -r $i/$libso ] + then + found=$i + type=installed + fi + done + + for i in $SRCDIR + do + if [ -r $i/$lib -o -r $i/lib/$lib ] + then + found=$i + type=binary + fi + done + + if [ -r "$found/$libso" ] + then + echo "Found at $found/$libso." + elif [ -r "$found/$lib" ] + then + echo "Found at $found/$lib." + elif [ -r "$found/lib/$lib" ] + then + echo "Found at $found/lib/$lib." + fi + + for i in $SRCDIR + do + if [ -d $i -a ! "$type" = binary ] + then + source=$i + fi + done + + if [ "$source" != "" ] + then + echo "Found source directory $source." + if [ "$found" = "" ] + then + ans=y + else + echo "Use the source if the pre-installed library causes compilation problems." + readln "Use source?" n + fi + if [ "$ans" = "y" ] + then + found=$source + type=source + fi + fi + + if [ "$found" = "" ] + then + echo "Not found." + fi + + if [ -r $found/lib/$lib ] + then + LIB=$found/lib/$lib + else + LIB=$found/$lib + fi + if [ "$type" = system ] + then + LIB= + LDFLAG="-l`echo $lib | sed 's/^lib//;s/\.a$//'` -L$found" + if [ "$found" = "/usr/local/lib" ] + then + INCDIR="/usr/local/include $INCDIR" + fi + fi + + incdir=$INCDIR + INCDIR= + for i in $incdir + do + if [ -d $i ] + then + INCDIR=$i + fi + done + + if [ "$type" = source -o "$type" = binary ] + then + if [ ! -r $found/lib/$lib ] + then + MAKELIB="$found/$lib: + cd $found; make $lib" + fi + if [ -d $found/include ] + then + INCDIR=$found/include + else + INCDIR=$found + fi + fi + + if [ "$type" = source ] + then + dir=`pwd` + if [ "$dir" = "" ] + then + dir=$PWD + fi + + cd $found + if [ -x configure ] + then + echo "Configuring..." + ./configure $CONFIG + fi + if [ "$lib" = "libcrypto.a" ] + then + if [ -f config ] + then + sh config + elif [ -x Configure ] + then + ./Configure 2>tmp.$$ + cat tmp.$$ + readln "Your system?" `cat tmp.$$ | tr ' ' '\n' | grep -i \`uname\` | tail -1` + rm -f tmp.$$ + echo "Configuring..." + ./Configure $ans + fi + fi + cd $dir + fi +} + +# Global installation. + + +########################################################################## +umask 077 + +#FIXME -- Mixmaster now should be installed as root, and Install should +#create a remailer user. /var/spool/mixmaster is a good home. + +if [ `whoami` = root ] +then + echo "Please create a new user, e.g, \`mix', and install Mixmaster under that +user id. Installing Mixmaster as root is not recommended." + readln "Continue anyway?" n + if [ "$ans" = n ] + then + exit 1 + fi +fi + +MIXDIR="$PWD" +if [ "$MIXDIR" = "" ] +then + MIXDIR=`pwd` +fi +MIXCFG="$MIXDIR/conf" +MIXSRC="$MIXDIR/Src" +MIXDEST0=${MIXPATH:-$HOME/Mix} + +system=`uname` +if [ "$system" = "MS-DOS" ] +then + system=msdos +fi + +if [ "$HOSTNAME" = "" ] +then + HOSTNAME=`hostname` +fi +if [ "$HOSTNAME" = "" ] +then + HOSTNAME=msdos + system=msdos +fi + +if [ "$system" = msdos ] +then + MIXDEST0=${MIXPATH:-C:/Mix} +fi + +if [ -f "$MIXSRC/Makefile" ] +then + if grep "#Makefile generated.*$HOSTNAME" $MIXSRC/Makefile + then + echo "Found a Makefile for this system." + readln "Use this Makefile?" y + if [ "$ans" = n ] + then + rm -f "$MIXSRC/Makefile" + fi + else + readln "Remove old Makefile?" y + if [ "$ans" = y ] + then + rm -f "$MIXSRC/Makefile" + fi + fi +fi + +if [ -f "$MIXSRC/Makefile" ] +then + MIXDEST=`grep "DSPOOL=" $MIXSRC/Makefile | sed 's/.*DSPOOL=..//;s/\".*//'` + if [ "$MIXDEST" = "" ] + then + MIXDEST="$MIXDEST0" + fi +fi + +if [ "$MIXDEST" = "" ] +then + readln "Mixmaster directory?" "$MIXDEST0" + MIXDEST=$ans +else + echo "Mixmaster directory: $MIXDEST" +fi + +if [ ! -d "$MIXDEST" ] +then + echo "Creating directory $MIXDEST" + mkdir "$MIXDEST" +fi + +if [ ! -d "$MIXDEST" ] +then + echo "Cannot not create $MIXDEST" + exit 1 +fi + +if [ -f "$MIXDEST/mix.cfg" ] +then + if [ -f "$MIXDEST/secring.mix" ] + then + remailer=y + if grep PASSPHRASE "$MIXDEST/mix.cfg" >/dev/null + then + PASSINCONFIG=1 + fi + else + readln "Do you want to set up a remailer?" n + remailer=$ans + fi +elif [ -f "$MIXDEST/mixmaster.conf" ] +then + echo "Upgrading from Mixmaster 2.0.*" + remailer=n +else + readln "Do you want to set up a remailer?" y + remailer=$ans +fi + + +ans="" +if [ "$remailer" = "y" ] +then + ans="n" + if [ "$PASSINCONFIG" != 1 ] + then + echo "" + echo "You can either compile your secret passphrase into the binary +or you can set it in your config file. Note that the former does not +really increase security as the passphrase can still be discovered by +running something like \`strings mixmaster'." + echo "" + echo "Most users should answer \`n' to this question:" + echo "" + readln "Do you want to compile the passphrase into the binary?" n + fi + + rm -f "$MIXSRC/mix.o" # make sure our new passphrase takes effect + if [ "$ans" = "y" ] + then + ans="" + echo "Please enter a passphrase for your remailer (must be the same +whenever you re-compile Mixmaster)." + read ans + + if [ "$ans" != "" ] + then + PASS="PASS=$ans" + else + echo "WARNING: not setting a passphrase" + fi + else + if [ "$PASSINCONFIG" != 1 ] + then + ans="" + echo "Please enter a passphrase for your remailer (it will be +stored in mix.cfg in clear)." + read ans + + if [ "$ans" = "" ] + then + echo "WARNING: setting empty passphrase" + fi + PASSPHRASE="PASSPHRASE $ans" + if [ -f $MIXDEST/mix.cfg ] + then + echo "$PASSPHRASE" >> $MIXDEST/mix.cfg + fi + fi + fi +fi + + +cd "$MIXSRC" +if [ ! -f Makefile ] +then + LIBS= + INC= + DEF= + LDFLAGS= + + if [ ! -z "$PASS" ] + then + DEF="$DEF -DCOMPILEDPASS='\"\$(PASS)\"'" + fi + + if [ "$system" = msdos ] + then + readln "Use WIN32 GUI?" y + if [ "$ans" = y ] + then + system=win32 + LDFLAGS=-lwsock32 + fi + fi + if [ "$system" = SunOS ] + then + LDFLAGS="-lsocket -lnsl" + fi + + LIBDIR= + INCDIR= + SRCDIR=zlib* + findlib libz.a + if [ "$found" = "" ] + then + readln "Continue anyway?" n + if [ "$ans" = "n" ] + then + echo "Please install zlib 1.1.4 or 1.2.3 or greater now." + exit 1 + fi + else + ZLIB="$MAKELIB" + DEF="$DEF -DUSE_ZLIB" + LIBS="$LIBS $LIB" + LDFLAGS="$LDFLAGS $LDFLAG" + if [ "$INCDIR" != "" ] + then + INC="$INC -I$INCDIR" + fi + fi + + LIBDIR= + INCDIR="/usr/include /usr/include/pcre /usr/local/pcre/include" + SRCDIR=pcre* + findlib libpcre.a + if [ "$found" != "" ] + then + PCRE="$MAKELIB" + DEF="$DEF -DUSE_PCRE" + LIBS="$LIBS $LIB" + LDFLAGS="$LDFLAGS $LDFLAG" + if [ "$INCDIR" != "" ] + then + INC="$INC -I$INCDIR" + fi + fi + + opensslinfo="Please get OpenSSL 0.9.6l or greater from http://www.openssl.org/" + opensslwarning6="WARNING: This version of OpenSSL contains known vulnerabilities. Please upgrade to OpenSSL 0.9.6l or greater!" + opensslwarning7="WARNING: This version of OpenSSL contains known vulnerabilities. Please upgrade to OpenSSL 0.9.7c or greater!" + opensslwarning0=$opensslwarning7 + LIBDIR=/usr/local/ssl/lib + INCDIR="/usr/include /usr/include/ssl /usr/lib/ssl/include /usr/local/ssl/include" + SRCDIR="openssl*" + + opensslwarn() + { + if [ "$1" = "6" ] + then + echo $opensslwarning6 + elif [ "$1" = "7" ] + then + echo $opensslwarning7 + else + echo $opensslwarning0 + fi + readln "Continue anyway?" y + if [ "$ans" = "n" ] + then + echo $opensslinfo + exit 1 + fi + } + + if [ "$system" = win32 ] + then + findlib libeay32.a + else + findlib libcrypto.a + fi + if [ "$found" = "" ] + then + echo $opensslinfo + exit 1 + fi + + OPENSSLLIB="$LIB" + LIBS="$LIBS $LIB" + LDFLAGS="$LDFLAGS $LDFLAG" + if [ "$MAKELIB" != "" ] + then + OPENSSL="$found/$lib: + cd $found/crypto; make" + fi + if [ -d "$INCDIR/openssl" ] + then + INC="$INC -I$INCDIR" + else + # detect old SSLeay versions + if [ -f "$INCDIR/crypto.h" ] + then + version=800 + if grep OPENSSL "$INCDIR/crypto.h" > /dev/null + then + version=920 + fi + fi + fi + + # Find the OpenSSL version header + if [ -f "$INCDIR/openssl/opensslv.h" ] + then + version=`grep 'SSL.*_VERSION_NUMBER.*0x' $INCDIR/openssl/opensslv.h | sed 's/.*0x0*//;s/[ ].*//;s/L$//' | tr '[a-f]' '[A-F]'` + elif [ -f "$INCDIR/opensslv.h" ] + then + version=`grep 'SSL.*_VERSION_NUMBER.*0x' $INCDIR/opensslv.h | sed 's/.*0x0*//;s/[ ].*//;s/L$//' | tr '[a-f]' '[A-F]'` + fi + if [ "$version" = "" ] + then + echo "Warning: Can't find OpenSSL version number!" + readln "Continue anyway?" y + if [ "$ans" = "n" ] + then + echo $opensslinfo + exit 1 + fi +# +# Here we match against known OpenSSL versions +# + elif [ "$version" = "90581F" ] + then + decimalversion=9459743 + echo "Compiling with OpenSSL 0.9.5a." + opensslwarn 6 + elif [ "$version" = "90601F" ] + then + decimalversion=9461791 + echo "Compiling with OpenSSL 0.9.6a." + opensslwarn 6 + elif [ "$version" = "90602F" ] + then + decimalversion=9461807 + echo "Compiling with OpenSSL 0.9.6b." + opensslwarn 6 + elif [ "$version" = "90603F" ] + then + decimalversion=9461823 + echo "Compiling with OpenSSL 0.9.6c." + opensslwarn 6 + elif [ "$version" = "90604F" ] + then + decimalversion=9461839 + echo "Compiling with OpenSSL 0.9.6d." + opensslwarn 6 + elif [ "$version" = "90605F" ] + then + decimalversion=9461855 + echo "Compiling with OpenSSL 0.9.6e." + opensslwarn 6 + elif [ "$version" = "90606F" ] + then + decimalversion=9461871 + echo "Compiling with OpenSSL 0.9.6f." + opensslwarn 6 + elif [ "$version" = "90607F" ] + then + decimalversion=9461887 + echo "Compiling with OpenSSL 0.9.6g." + opensslwarn 6 + elif [ "$version" = "90608F" ] + then + decimalversion=9461903 + echo "Compiling with OpenSSL 0.9.6h." + opensslwarn 6 +elif [ "$version" = "90609F" ] + then + decimalversion=9461919 + echo "Compiling with OpenSSL 0.9.6i." + opensslwarn 6 + elif [ "$version" = "9060A0" ] + then + decimalversion=9461920 + echo "Compiling with OpenSSL 0.9.6j." + opensslwarn 6 + elif [ "$version" = "9060B0" ] + then + decimalversion=9461936 + echo "Compiling with OpenSSL 0.9.6k." + opensslwarn 6 + elif [ "$version" = "9060C0" ] + then + decimalversion=9461952 + echo "Compiling with OpenSSL 0.9.6l." + elif [ "$version" = "9060D0" ] + then + decimalversion=9461968 + echo "Compiling with OpenSSL 0.9.6m." + elif [ "$version" = "90700F" ] + then + decimalversion=9465871 + echo "Compiling with OpenSSL 0.9.7." + opensslwarn 7 + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90701F" ] + then + decimalversion=9465887 + echo "Compiling with OpenSSL 0.9.7a." + opensslwarn 7 + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90702F" ] + then + decimalversion=9465903 + echo "Compiling with OpenSSL 0.9.7b." + opensslwarn 7 + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90703F" ] + then + decimalversion=9465919 + echo "Compiling with OpenSSL 0.9.7c." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90704F" ] + then + decimalversion=9465935 + echo "Compiling with OpenSSL 0.9.7d." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90705F" ] + then + decimalversion=9465951 + echo "Compiling with OpenSSL 0.9.7e." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90706F" ] + then + decimalversion=9465967 + echo "Compiling with OpenSSL 0.9.7f." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90707F" ] + then + decimalversion=9465983 + echo "Compiling with OpenSSL 0.9.7g." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90708F" ] + then + decimalversion=9465999 + echo "Compiling with OpenSSL 0.9.7h." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90709F" ] + then + decimalversion=9466015 + echo "Compiling with OpenSSL 0.9.7i." + DEF="$DEF -DUSE_AES" + + elif [ "$version" = "9070AF" ] + then + decimalversion=9466031 + echo "Compiling with OpenSSL 0.9.7j." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "9070BF" ] + then + decimalversion=9466047 + echo "Compiling with OpenSSL 0.9.7k." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "9070CF" ] + then + decimalversion=9466063 + echo "Compiling with OpenSSL 0.9.7l." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "9070DF" ] + then + decimalversion=9466079 + echo "Compiling with OpenSSL 0.9.7m." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "9070EF" ] + then + decimalversion=9466095 + echo "Compiling with OpenSSL 0.9.7n." + echo "This version was unreleased at the time of packaging." + echo "It is not guaranteed to work. Please report any problems." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90800F" ] + then + decimalversion=9469967 + echo "Compiling with OpenSSL 0.9.8." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90801F" ] + then + decimalversion=9469983 + echo "Compiling with OpenSSL 0.9.8a." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90802F" ] + then + decimalversion=9469999 + echo "Compiling with OpenSSL 0.9.8b." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90803F" ] + then + decimalversion=9470015 + echo "Compiling with OpenSSL 0.9.8c." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90804F" ] + then + decimalversion=9470031 + echo "Compiling with OpenSSL 0.9.8d." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90805F" ] + then + decimalversion=9470047 + echo "Compiling with OpenSSL 0.9.8e." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90806F" ] + then + decimalversion=9470063 + echo "Compiling with OpenSSL 0.9.8f." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90807F" ] + then + decimalversion=9470079 + echo "Compiling with OpenSSL 0.9.8g." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90808F" ] + then + decimalversion=9470095 + echo "Compiling with OpenSSL 0.9.8h." + DEF="$DEF -DUSE_AES" + elif [ "$version" = "90809F" ] + then + decimalversion=9470111 + echo "Compiling with OpenSSL 0.9.8h." + echo "This version was unreleased at the time of packaging." + echo "It is not guaranteed to work. Please report any problems." + DEF="$DEF -DUSE_AES" + fi +# +# Now we try to guess about unknown versions: +# + if [ "$decimalversion" = "" ] + then + decimalversion=`echo 16i $version p | dc` + fi + if [ "$decimalversion" = "" ] + then + echo "Warning: This version: ${version} of OpenSSL is not recognized." + readln "Continue anyway?" y + if [ "$ans" = "n" ] + then + echo $opensslinfo + exit 1 + else + echo "Does this version of OpenSSL contain AES support?" + readln "If unsure of the answer, say \`n'" n + if [ "$ans" = "y" ] + then + DEF="$DEF -DUSE_AES" + fi + fi + elif [ "$decimalversion" -lt "2336" ] # 920 + then + echo "This version: ${version} of SSLeay is not supported." + echo $opensslinfo + exit 1 + elif [ "$decimalversion" -lt "9449728" ] # 903100 + then + echo "This version: ${version} of OpenSSL is not supported." + echo $opensslinfo + exit 1 + elif [ "$decimalversion" -gt "9470111" ] # 0.9.8h + then + echo "Warning: This version: ${version} of OpenSSL is untested." + readln "Continue anyway?" y + if [ "$ans" = "n" ] + then + echo $opensslinfo + exit 1 + else + echo "Does this version of OpenSSL contain AES support?" + readln "If unsure of the answer, say \`n'" n + if [ "$ans" = "y" ] + then + DEF="$DEF -DUSE_AES" + fi + fi + fi + + LIBDIR= + INCDIR=/usr/include/ncurses + SRCDIR=ncurses* + CONFIG=--enable-termcap + if [ "$TERMINFO" != "" ] + then + CONFIG="--datadir=$TERMINFO" + fi + if [ -d /usr/share/terminfo ] + then + CONFIG= + fi + if [ -d /usr/lib/terminfo ] + then + CONFIG=--datadir=/usr/lib/terminfo + fi + + if [ `uname` = OpenBSD ] + then + findlib libcurses.a + else + findlib libncurses.a + fi + if [ "$found" = "" ] + then + if [ "$system" != win32 ] + then + readln "Do you want to use Mixmaster's menu-based user interface?" y + if [ "$ans" = "y" ] + then + echo "Please install ncurses now. It is available from http://www.clark.net/pub/dickey/ncurses/ncurses.tar.gz" + exit 1 + fi + fi + else + DEF="$DEF -DUSE_NCURSES" + if [ "$type" = system -o "$type" = installed ] + then + LIBS="$LIBS $LIB" + LDFLAGS="$LDFLAGS $LDFLAG" + else + LIBS="$LIBS $found/lib/$lib" + NCURSES="$found/lib/$lib: + cd $found/ncurses; make ../lib/$lib" + fi + if [ "$INCDIR" != "" ] + then + INC="$INC -I$INCDIR" + elif [ -f "/usr/include/ncurses.h" ] + then + DEF="$DEF -DHAVE_NCURSES_H" + fi + fi + + ideawarn() + { + echo " + WARNING: Your version of OpenSSL has been configured without IDEA support. + If you continue, Mixmaster will be installed with reduced functionality. + This means (among other things) that Mixmaster will not create an RSA + OpenPGP key (to avoid mail loss in the Type I system). You may want to + re-install OpenSSL before proceeding. + + This will not concern you if you only plan to run a type II remailer or + simply want a type II client." + readln "Continue anyway?" y + if [ "$ans" = "n" ] + then + exit 1 + fi + } + + if [ "$system" = OpenBSD ] + then + LIBDIR= + INCDIR= + SRCDIR=idea* + findlib libidea.a + if [ "$found" = "" ] + then + ideawarn + else + DEF="$DEF -DUSE_IDEA" + IDEALIB="$MAKELIB" + LIBS="$LIBS $LIB" + LDFLAGS="$LDFLAGS $LDFLAG" + if [ "$INCDIR" != "" ] + then + INC="$INC -I$INCDIR" + fi + fi + elif [ "$system" = msdos -o "$system" = win32 ] + then + DEF="$DEF -DUSE_IDEA" + else + echo "Checking for IDEA support..." + cat <<END >tmptst.c +#include <openssl/idea.h> +int main() { + void *dummy; + dummy = idea_cfb64_encrypt; + exit(0); +} +END + if gcc $LDFLAGS $INC tmptst.c -o tmptst $OPENSSLLIB + then + DEF="$DEF -DUSE_IDEA" + else + ideawarn + fi + rm -f tmptst.c tmptst + fi + + echo "testing for setenv()..." + cat <<END >tmptst.c +int main() { +#include <stdlib.h> + setenv("TZ", "GMT", 0); + exit(0); +} +END + if gcc tmptst.c -o tmptst + then + DEF="$DEF -DHAVE_SETENV" + fi + echo "done" + rm -f tmptst.c tmptst + +# if [ "$MIXDEST" = "$HOME/Mix" ] +# then +# SPOOL= +# else + SPOOL=-DSPOOL=\'\"$MIXDEST\"\' +# fi + + echo "Generating Makefile." + echo "#Makefile generated on $HOSTNAME `date`" >Makefile + sed -e "s#%MIXDIR#$SPOOL#" \ + -e "s#%LIBS#$LIBS#" \ + -e "s#%LDFLAGS#$LDFLAGS#" \ + -e "s#%INC#$INC#" \ + -e "s#%DEF#$DEF#" < Makefile.in >> Makefile +# echo "$ZLIB" >>Makefile +# echo "$PCRE" >>Makefile + echo "$IDEALIB" >>Makefile + echo "$NCURSES" >>Makefile + echo "$OPENSSL" >>Makefile +fi + + + + + +echo "Compiling. Please wait." +whereis make +make=$found + +if [ "$system" = win32 ] +then +# (cd zlib*; make libz.a) +# (cd pcre*; make libpcre.a) + if [ "$PASS" != "" ] + then + $make "$PASS" dllmix + else + $make dllmix + fi +else + if [ "$PASS" != "" ] + then + $make "$PASS" + else + $make + fi +fi + +if [ -x mixmaster ] +then + echo +else + echo "Error: The compilation failed. Please consult the documentation (section +\`Installation problems')." + readln "Remove the old Makefile?" y + if [ "$ans" = y ] + then + rm -f Makefile + fi + exit 1 +fi + +if [ -f "$MIXDEST/mixmaster.conf" -a ! -f "$MIXDEST/mix.cfg" ] +then + export MIXDEST + export MIXDIR + export MIXSRC + "${MIXDIR}/upgrade" + exit 0 +fi + +if [ -f mixmaster.exe ] +then + cp mixmaster.exe "$MIXDEST" +else + cp mixmaster "$MIXDEST" +fi + +cd "$MIXCFG" +for i in mlist.txt pubring.mix rlist.txt pubring.asc +do + if [ ! -f "$MIXDEST/$i" ] + then + cp "$i" "$MIXDEST" + fi +done + +if [ "$remailer" = "y" ] +then + cd "$MIXCFG" + for i in adminkey.txt dest.alw + do + if [ ! -f "$MIXDEST/$i" ] + then + cp "$i" "$MIXDEST" + fi + done +fi + +if [ "$remailer" = "n" ] +then + if [ ! -f "$MIXDEST/mix.cfg" ] + then + whereis sendmail /usr/lib/sendmail /usr/sbin/sendmail + echo "SENDMAIL $found -t" >"$MIXDEST/mix.cfg" + cat mix.cfg >>"$MIXDEST/mix.cfg" + fi + echo "Client installation complete." + exit +fi + +for i in *.blk +do + if [ ! -f "$MIXDEST/$i" ] + then + cp "$i" "$MIXDEST" + fi +done + +cd "$MIXDEST" + +installed=n +if [ -f mix.cfg ] +then + if grep REMAILERADDR mix.cfg >/dev/null + then + installed=y + fi +fi + +if [ "$installed" = "n" ] +then + Date=`date` + whereis sendmail /usr/lib/sendmail /usr/sbin/sendmail + sendmail=$found + + echo "Mixmaster can be installed in the low-maintenance \`middleman' mode. +In that mode, it will send mail to other remailers only, to avoid +complaints about anonymous messages." + readln "Install as middleman?" n + middle=$ans + + readln "The e-mail address of your remailer:" `whoami`@$HOSTNAME + RMA=$ans + + echo "Do you want Mixmaster to send auto-replies to messages it does not +understand (If the address <$RMA> is also used" + readln "for mail to be read by a human, type \`n')?" y + autoreply=$ans + + if [ "$middle" = n ] + then + readln "An address to appear in the \`From:' line of anonymous messages:" `echo $RMA | sed 's/.*@/nobody@/'` + RAA=$ans + + readln "Address for complaints to be sent to:" `echo $RMA | sed 's/.*@/abuse@/'` + CA=$ans + else + RAA=$RMA + CA=$RMA + fi + + echo "Choose a name for your remailer. It will appear in remailer status messages." + readln "Long name:" "Anonymous Remailer" + RMN=$ans + + if [ "$middle" = n ] + then + echo "Choose a name to be used in the \`From:' line of remailed messages." + readln "Anon long name:" "Anonymous" + RAN=$ans + fi + + readln "A short name to appear in lists:" `echo $HOSTNAME|sed 's/\..*//'` + SN=$ans + + readln "Accept Mixmaster (Type II) messages?" y + mix=$ans + + readln "Accept PGP (Type I) remailer messages?" n + pgp=$ans + + unencrypted=n + if [ "$pgp" = "y" ] + then + readln "Accept unencrypted remailer messages?" n + unencrypted=$ans + fi + + echo "Mixmaster will log error messages and warnings. Do you want to log" + readln "informational messages about normal operation as well?" y + if [ "$ans" = y ] + then + verbose=2 + else + verbose=1 + fi + + readln "Filter binary attachments?" n + binfilter=$ans + + if [ "$middle" = n ] + then + if [ "$autoreply" = y ] + then + readln "Allow users to add themselves to the list of blocked addresses?" y + autoblock=$ans + fi + + echo "Do you want to allow posting? Newsgroups can be restricted in dest.blk. +y)es, post locally; use m)ail-to-news gateway; n)o." + readln "Allow posting to Usenet?" m + post="$ans" + if [ "$ans" = y ] + then + whereis inews /usr/lib/news/inews + readln "News posting software:" "$found -h" + news=$ans + readln "Organization line for anonymous Usenet posts:" "Anonymous Posting Service" + orga=$ans + readln "Use anti-spam message IDs?" y + mid=$ans + elif [ "$ans" = m ] + then + readln "Mail-to-news gateway:" mail2news@nym.alias.net + news=$ans + fi + fi + +# Commented the poolsize question out, since poolsize is the least +# important of the three pool parameters. +# +# echo "How many messages do you want to keep in the reordering pool? +#A larger pool is more secure, but also causes higher latency. +#0 means to remail immediately." +# readln "Pool size:" 45 +# poolsize=$ans + + mbox= + if [ -f ~/.forward ] + then + mbox=`head -1 ~/.forward | sed 's/^"//;s/"$//'` + if echo "$mbox" | grep 'mix' >/dev/null 2>/dev/null + then + mbox= + elif echo "$mbox" | grep 'procmail' >/dev/null 2>/dev/null + then + if grep mix ~/.procmailrc >/dev/null 2>/dev/null + then + mbox= + fi + fi + fi + + if [ "$mbox" = "" ] + then + mbox=${MAIL:-/usr/spool/mail/$NAME} + touch "$mbox" + if [ ! -w "$mbox" ] + then + echo "$mbox is not writeable." + readln "Mailbox for non-remailer messages:" "${MIXDEST}/mbox" + mbox=$ans + fi + fi + + cat <<END >mix.cfg +# mix.cfg -- installed $Date +SENDMAIL $sendmail -t + +# Where to store non-remailer messages: +MAILBOX $mbox +#MAILABUSE mbox.abuse +#MAILBLOCK mbox.block +#MAILUSAGE mbox.usage +#MAILANON mbox.anon +#MAILERROR mbox.error +#MAILBOUNCE mbox.bounce + +REMAIL y +MIDDLEMAN $middle + +BINFILTER $binfilter +AUTOBLOCK $autoblock + +ERRLOG error.log +VERBOSE $verbose + +# Remailer name and addresses +REMAILERADDR $RMA +ANONADDR $RAA +COMPLAINTS $CA + +SHORTNAME $SN +REMAILERNAME $RMN +ANONNAME $RAN + +# Supported formats: +MIX $mix +PGP $pgp +UNENCRYPTED $unencrypted + +# Maximum message size in kB (0 for no limit): +SIZELIMIT 0 + +# Usenet news: +NEWS $news +ORGANIZATION $orga +MID $mid + +# Remailing strategy: +SENDPOOLTIME 15m +POOLSIZE 45 +RATE 65 +INDUMMYP 10 +OUTDUMMYP 90 +CHAIN *,*,*,* +IDEXP 7d +PACKETEXP 7d + +$PASSPHRASE + +END + +fi # not yet installed + + +REPLACE="s/%RMN/$RMN/g;s/%RMA/$RMA/g;s/%CA/$CA/g;s/%RAA/$RAA/g" +if [ "$installed" = "n" ] +then + cd "$MIXCFG" + if [ ! -f "$MIXDEST/help.txt" ] + then + sed "$REPLACE" < intro.hlp >"$MIXDEST/help.txt" + if [ "$mix" = y ] + then + sed "$REPLACE" < mix.hlp >>"$MIXDEST/help.txt" + fi + if [ "$unencrypted" = y ] + then + sed "$REPLACE" < type1.hlp >>"$MIXDEST/help.txt" + if [ "$pgp" = y ] + then + sed "$REPLACE" < pgp.hlp >>"$MIXDEST/help.txt" + fi + elif [ "$pgp" = y ] + then + sed "$REPLACE" < pgponly.hlp >>"$MIXDEST/help.txt" + fi + if [ "$post" = y ] + then + if [ "$pgp" = y -o "$unencrypted" = y ] + then + sed "$REPLACE" < news.hlp >>"$MIXDEST/help.txt" + fi + fi + sed "$REPLACE" < end.hlp >>"$MIXDEST/help.txt" + fi + + for i in *.txt.in + do + j=`echo $i | sed 's/\.in$//'` + if [ ! -f "$MIXDEST/$j" ] + then + sed "$REPLACE" < "$i" >"$MIXDEST/$j" + fi + done + cd "$MIXDEST" +fi + +echo +if [ ! -f secring.mix ] +then + echo "Generating secret keys. This may take a while..." +else + echo "Updating secret keys..." +fi +./mixmaster -K +if [ -f key.txt ] +then + echo "Done." + echo +else + echo "Installation failed. Please consult the Mixmaster documentation." + exit 1 +fi + +if [ "$system" = msdos -o "$system" = win32 ] +then + exit +fi + +umask 033 + +# Set .forward? +# +set=y +# FIXME -- Mixmastger should run in daemon mode, not from procmail +# Make the Install script do that. + +if grep procmail ~/.forward >/dev/null 2>/dev/null +then + if grep mix ~/.procmailrc >/dev/null 2>/dev/null + then + echo "Mixmaster is installed in your .procmailrc file." + set=n + fi +fi + +if [ "$set" = y -a -f ~/.forward ] +then + echo "Your current .forward is:" + cat ~/.forward + echo + if grep mix ~/.forward >/dev/null 2>/dev/null + then + echo "Mixmaster already is installed in your .forward file." + set=n + elif [ "$mbox" != "" ] + then + if echo "$mbox" | grep '|' >/dev/null 2>/dev/null + then + echo "Mixmaster will pipe messages to $mbox" + elif echo $mbox | grep '@' >/dev/null 2>/dev/null + then + echo "Mixmaster will forward messages to $mbox" + else + echo "Mixmaster will store messages to $mbox" + fi + fi +fi + +if [ "$set" = y ] +then + echo "Set .forward to the following line:" + echo "\"|${MIXDEST}/mixmaster -RM\"" + if [ -f ~/.forward ] + then + readln "Overwrite now?" n + else + readln "Do that now?" n + fi + if [ "$ans" = "y" ] + then + echo "\"|${MIXDEST}/mixmaster -RM\"" >~/.forward + fi +fi + +#FIXME -- we need a second script that can re-generate help files +# when the conf changes. + +if [ "$RMA" != "" ] +then + echo " +Mixmaster will send the following files as auto-replies: +Mail to <$RMA> with Subject: remailer-help => help.txt" + echo "Mail to <$RMA> with Subject: remailer-adminkey => adminkey.txt +Remember to add your Remailer Admin public PGP key to the adminkey.txt file." + if [ "$autoblock" = y ] + then + echo "Mail to <$RMA> with line DESTINATION-BLOCK => blocked.txt" + fi + if [ "$autoreply" = y ] + then + echo "Other mail to <$RMA> => usage.txt" + echo + if [ "$CA" != "$RMA" ] + then + echo "If you arrange for mail to <$CA> and <$RAA> +to be forwarded to <$RMA>: +Mail to <$CA> => abuse.txt +Mail to <$RAA> => reply.txt" + fi + fi +fi + +echo +echo "Mixmaster installation complete." + diff --git a/README b/README @@ -0,0 +1,191 @@ +Mixmaster 3.0 -- anonymous remailer software -- (C) 1999 - 2000 Anonymizer Inc. + (C) 2000-2008 The Mixmaster Development Team +------------------------------------------------------------------------------- + +This program consists of + +* a remailer client: + + The remailer client supports sending anonymous mail using Cypherpunk and + Mixmaster remailers. It supports OpenPGP encryption (compatible with PGP 2, + PGP 5 and up, and GnuPG). + + The client can be used with a menu-based user interface and with command line + options. + +* a remailer: + + The remailer supports the Cypherpunk and Mixmaster message formats. It can + be integrated with the mail delivery system of Unix-based computers or use + the POP3 and SMTP protocols for mail transfer. Mixmaster includes an + automated abuse-handling system. + +Please report any problems via the bug and patch trackers at +http://sourceforge.net/projects/mixmaster/ + + +Installation: +------------ + +Libraries: + + Mixmaster requires the libraries OpenSSL, zlib, and pcre. + + If you want to use the menu-based user interface, you also need the ncurses + library. If these libraries are not installed on your system, you will need + to obtain the latest versions from the sources below and extract them in the + the Src/ directory first. + + OpenSSL is available from http://www.openssl.org/source/ + + Ncurses can be obtained from http://ftp.gnu.org/pub/gnu/ncurses/ + + The Perl Compatable Regular Expressions library can be obtained from + ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/ + + The zlib compression libraries can be obtained from + http://www.gzip.org/zlib/ + +To install or upgrade Mixmaster, type `./Install'. + +Mixmaster clients rely on pingers to compile statistics and keyrings for +currently operating remailers. A list of public pingers can be obtained from +http://www.noreply.org/allpingers/. + +Alternatively clients can operate their own pingers to generate statistics. +Pinger software can be obtained from http://www.palfrader.org/echolot/. If you +choose this option, please publish the pinger results for the benefit of other +Mixmaster users and notify the metastats maintainer at admin@mixmin.net. + +The required files published by pingers are:- + pubring.asc Type 1 remailer keys + pubring.mix Type 2 remailer keys + rlist.txt List of reliable type 1 remailers + mlist.txt List of reliable type 2 remailers + type2.list List of known type 2 remailers (optional) + +Using the remailer client: +------------------------- + +To use the menu-based user interface, simply run `mixmaster'. To send an +anonymous or pseudonymous reply to a message from within your mail or news +reader, you can pipe it to `mixmaster'. + +The interactive mode supports sending mail and contains a simple mail reading +function. OpenPGP messages are encrypted and decrypted automatically. + +In the non-interactive mode, Mixmaster reads a message from a file or from its +standard input. The command line options are described in the manual page +(mixmaster.1). + + +Mixmaster as a remailer: +----------------------- + +The Mixmaster remailer can be installed on any account that can receive mail. +Non-remailer messages will be delivered as usual. If you have root access, you +may want to create a new user (e.g., `remailer') and install Mixmaster under +that user id. + +The Install script provides a simple way to set up the remailer. More +information about configuring Mixmaster can be found in the manual page. +Typically, incoming mail is piped to "mixmaster -RM". In a UUCP setting, it may +be useful to use just "mixmaster -R", and run "mixmaster -S" once all messages +have arrived. + +Announcing a new remailer to the public is most commonly done by posting the +remailer keys and capabilities to alt.privacy.anon-server as well as the +"remops" mailing list. Information about the remops list can be found here: +http://lists.mixmin.net/mailman/listinfo/remops + + +Installation problems: +--------------------- + +In case one of the libraries Mixmaster uses is installed incorrectly on your +system, place the library source code (available from the locations listed +above) in the Src directory, remove the old Makefile, run the Install script +again and answer `y' when asked whether to use the source code. + +The ncurses library can use termcap and terminfo databases. The Mixmaster +Install script tries to find out whether terminfo is available. If you get a +"Can't open display" error when starting the Mixmaster menu, run "./configure +--enable-termcap; make lib/libncurses.a" in the ncurses directory. + + +Security notes: +-------------- + +The ciphers and the anonymizing mix-net protocol used in Mixmaster correspond +to the state of the art (see the Security Considerations section of the +Mixmaster Protocol specification for details). However, no security proofs +exist for any practical cryptosystem. It is unlikely that their security will +be broken, but there is no "perfect security". Software can also contain +implementation errors. The complete Mixmaster source code is available for +public review, so that everyone can verify what the program does, and it is +unlikely that security related errors or secret back doors in the software +would go unnoticed. + +No software is secure if run in an insecure environment. For that reason you +must make sure that there is no malicious software (such as viruses) running on +your computer. Deleted files and even passphrases can in many cases be read +from the hard disk if an adversary has access to the computer. The use of disk +encryption programs is recommended to avoid this risk. + +Anonymous messages are secure as long as at least one of the remailers you use +in a chain is honest. You can use up to 20 remailers in a chain, but +reliability and speed decrease with longer chains. Four is a reasonable number +of remailers to use. Many remailer operators sign their keys. You should verify +those signatures with OpenPGP to make sure that you have the actual remailer +keys. + +Anonymous keys usually cannot be introduced to the OpenPGP web of trust without +giving up anonymity. For that reason, this client will use any OpenPGP key +found on the key ring, whether it is certified or not. Your key ring must not +contain any invalid keys when used with this program. + +If you want to use a pseudonym, the client will ask you for a passphrase to +protect the nym database. Your passphrase should be long, and hard to guess. +Anyone who gets hold of your nym database and private keys and can determine +the passphrase will be able to compromise your pseudonymous identities. Note +that some operating systems may store your passphrase on your hard disk in +clear. + +While a good client passphrase can protect your keys if someone gets hold of +your files, the remailer passphrase offers only casual protection for the +remailer keys. If you install a remailer, the remailer passphrase must be +different from your private passphrases. + +Note that nym.alias.net style nym-servers are trivially breakable by an +adversary performing a long-term intersection attack. Discussion of +these attacks can be found in section 4.2 of The Pynchon Gate, by +Sassaman, Cohen, and Mathewson, 2005. Use of Type I remailers for any +purpose is discouraged. + + +Copyright: +--------- + +Mixmaster may be redistributed and modified under certain conditions. This +software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, +either express or implied. See the file COPYRIGHT for details. + +A license is required to use the IDEA(TM) algorithm for commercial purposes; +see the file idea.txt for details. + +Mixmaster uses the compression library zlib by Jean-loup Gailly and Mark Adler, +the free ncurses library and the regex library by Philip Hazel. This product +includes cryptographic software written by Eric Young (eay@cryptsoft.com). This +product includes software developed by the OpenSSL Project for use in the +OpenSSL Toolkit (http://www.OpenSSL.org/). For some platforms: This product +includes software developed by the University of California, Berkeley and its +contributors. + +Additionally, this software uses code provided by the members of the +Mixmaster development team. The members respectively hold the copyright +to the code in question, having elected to make it available under the +Mixmaster license. + +All trademarks are the property of their respective owners. + +$Id: README 974 2008-03-03 17:40:11Z rabbi $ diff --git a/Src/Makefile.deps b/Src/Makefile.deps @@ -0,0 +1,46 @@ +# Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + +# Mixmaster may be redistributed and modified under certain conditions. +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +# ANY KIND, either express or implied. See the file COPYRIGHT for +# details. + +# $Id: $ + +buffers.o: mix3.h config.h version.h mix.h +chain.o: mix3.h config.h version.h mix.h +chain1.o: mix3.h config.h version.h mix.h pgp.h +chain2.o: mix3.h config.h version.h mix.h +chain3.o: mix3.h config.h version.h mix.h +compress.o: mix3.h config.h version.h mix.h +crypto.o: mix3.h config.h version.h mix.h crypto.h +dllmain.o: mix3.h config.h version.h mix.h +dummy.o: mix3.h config.h version.h mix.h +keymgt.o: mix3.h config.h version.h mix.h +mail.o: mix3.h config.h version.h mix.h +maildir.o: mix3.h config.h version.h mix.h +main.o: mix3.h config.h version.h mix.h +menu.o: menu.h mix3.h config.h version.h mix.h +menunym.o: menu.h mix3.h config.h version.h mix.h +menusend.o: menu.h mix3.h config.h version.h mix.h +menuutil.o: menu.h mix3.h config.h version.h mix.h +mime.o: mix3.h config.h version.h mix.h +mix.o: mix3.h config.h version.h mix.h menu.h +nym.o: mix3.h config.h version.h mix.h pgp.h +pgp.o: mix3.h config.h version.h mix.h pgp.h +pgpcreat.o: mix3.h config.h version.h mix.h pgp.h crypto.h +pgpdata.o: mix3.h config.h version.h mix.h pgp.h crypto.h +pgpdb.o: mix3.h config.h version.h mix.h pgp.h +pgpget.o: mix3.h config.h version.h mix.h pgp.h crypto.h +mpgp.o: mix3.h config.h version.h mix.h pgp.h +pool.o: mix3.h config.h version.h mix.h +random.o: mix3.h config.h version.h mix.h crypto.h +rem.o: mix3.h config.h version.h mix.h +rem1.o: mix3.h config.h version.h mix.h +rem2.o: mix3.h config.h version.h mix.h +rem3.o: mix3.h config.h version.h mix.h +remailer.o: mix.h +rfc822.o: mix3.h config.h version.h mix.h +rndseed.o: mix3.h config.h version.h mix.h +stats.o: mix3.h config.h version.h mix.h +util.o: mix3.h config.h version.h mix.h diff --git a/Src/Makefile.in b/Src/Makefile.in @@ -0,0 +1,83 @@ +# Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + +# Mixmaster may be redistributed and modified under certain conditions. +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +# ANY KIND, either express or implied. See the file COPYRIGHT for +# details. + +# $Id: Makefile.in 647 2003-10-25 23:34:13Z weasel $ + +# Edit according to the libraries you want to use: +INC = %INC +DEF = %DEF -DUSE_SOCK %MIXDIR +LIBS = %LIBS +LDFLAGS = %LDFLAGS + +OPT = -g -Wall +# OPT = -g -pg -Wall -DDEBUG +# OPT = -O2 -Wall + +CFLAGS = $(INC) $(DEF) $(OPT) +CC = gcc +AR = ar rc +RANLIB = ranlib +#MAKE = make + +OBJ = mix.o rem.o rem1.o rem2.o chain.o chain1.o chain2.o nym.o pgp.o pgpdb.o pgpdata.o pgpget.o pgpcreat.o pool.o mail.o rfc822.o mime.o keymgt.o compress.o stats.o crypto.o random.o util.o buffers.o maildir.o parsedate.tab.o + +MIXOBJ = rndseed.o menu.o menusend.o menunym.o menuutil.o menustats.o +NOMENUOBJ = rndseed.o dummy.o +WINOBJ = winmain.o winutil.o + +all: mixmaster + +mixmaster: $(OBJ) $(MIXOBJ) main.o $(LIBS) + $(CC) $(OBJ) $(MIXOBJ) main.o $(LIBS) $(LDFLAGS) -o mixmaster + +libmix.a: $(OBJ) $(MIXOBJ) dllmain.o + $(AR) libmix.a $(OBJ) $(MIXOBJ) dllmain.o + +libmix32.a: libmix.a mixlib.def + dllwrap --dllname mixlib.dll --def mixlib.def --output-lib libmix32.a libmix.a zlib-1.1.4/libz.a pcre-2.08/libpcre.a openssl/libeay32.a -lwsock32 + +dllmix: main.o libmix32.a + $(CC) main.o libmix32.a -o dllmix + +winmix.exe: $(WINOBJ) libmix32.a + $(CC) $(WINOBJ) libmix32.a -lgdi32 -luser32 $(LDFLAGS) -o mixmaster.exe + +winmix: winmenu.res #winmix.exe + rsrc winmenu.res mixmaster.exe + +winmenu.o: winmenu.rc winmenu.h + windres winmenu.rc winmenu.o + +remailer: $(OBJ) $(NOMENUOBJ) remailer.o $(LIBS) + $(CC) $(OBJ) $(NOMENUOBJ) remailer.o $(LIBS) $(LDFLAGS) -o remailer + +mpgp: $(OBJ) $(NOMENUOBJ) mpgp.o $(LIBS) + $(CC) $(OBJ) $(NOMENUOBJ) mpgp.o $(LIBS) $(LDFLAGS) -o mpgp + +test: $(OBJ) test.o $(NOMENUOBJ) $(LIBS) + $(CC) $(OBJ) test.o $(NOMENUOBJ) $(LIBS) $(LDFLAGS) -o test + +clean: + -rm -f *.o *.a *.res *~ mixmaster mix *.exe remailer test mpgp core gmon.out + +allclean: clean + -rm -f Makefile + +distclean: allclean + +ci: clean + cd ~/src/mix3; ci -l * Mix/* Mix/Src/*; echo + +parsedate.tab.c: parsedate.y + @echo Expect 6 shift/reduce conflicts + bison parsedate.y + +parsedate: parsedate.tab.c + gcc -DTEST parsedate.tab.c -o parsedate + + +include Makefile.deps diff --git a/Src/buffers.c b/Src/buffers.c @@ -0,0 +1,816 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Dynamically allocated buffers + $Id: buffers.c 934 2006-06-24 13:40:39Z rabbi $ */ + + +#include "mix3.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdarg.h> +#ifdef WIN32 +#include <io.h> +#endif /* WIN32 */ +#include <assert.h> +#ifdef POSIX +#include <unistd.h> +#endif /* POSIX */ + +static void fail(void) +{ + errlog(ERRORMSG, "Out of memory!\n"); + abort(); +} + +#define space 128 /* allocate some additional space */ + +static void alloc(BUFFER *b) +{ + b->data = malloc(space); + if (b->data == NULL) + fail(); + b->data[0] = 0; + b->size = space; +} + +#undef buf_new /* DEBUG */ +BUFFER *buf_new(void) +{ + BUFFER *b; + + b = malloc(sizeof(BUFFER)); + if (b == NULL) + fail(); + alloc(b); + b->length = 0; + b->ptr = 0; + b->sensitive = 0; + + return (b); +} + +#ifdef DEBUG +static void sanity_check(BUFFER *b) +{ + assert(b != NULL); + assert(b->size > 0); + assert(b->data != NULL); + assert(b->length >= 0 && b->length < b->size); + assert(b->ptr >= 0 && b->ptr <= b->length); +} +#else /* not DEBUG */ +#define sanity_check(arg) +#endif /* else not DEBUG */ + +int buf_reset(BUFFER *buffer) +{ + sanity_check(buffer); + + buffer->length = 0; + buffer->ptr = 0; + if (buffer->sensitive) + memset(buffer->data, 0, buffer->size); + free(buffer->data); + alloc(buffer); + return (0); +} + +int buf_free(BUFFER *buffer) +{ + int err = 0; + + if (buffer->sensitive) + memset(buffer->data, 0, buffer->size); + free(buffer->data); + free(buffer); + return (err); +} + +int buf_clear(BUFFER *buffer) +{ + sanity_check(buffer); + buffer->data[0] = '\0'; + buffer->length = 0; + buffer->ptr = 0; + return (0); +} + +int buf_append(BUFFER *buffer, byte *msg, int len) +{ + assert(len >= 0); + sanity_check(buffer); + + if (buffer->length + len >= buffer->size) { + register byte *new; + register long newsize; + + newsize = 2 * buffer->length; /* double buffer size */ + if (newsize < buffer->length + len + space) + newsize = buffer->length + len + space; + new = malloc(newsize); + if (new == NULL) + fail(); + memcpy(new, buffer->data, buffer->length); + if (buffer->sensitive) + memset(buffer->data, 0, buffer->size); + free(buffer->data); + buffer->data = new; + buffer->size = newsize; + } + if (msg != NULL) + memcpy(buffer->data + buffer->length, msg, len); + buffer->length += len; + + buffer->data[buffer->length] = 0; + return (0); +} + +int buf_appendrnd(BUFFER *to, int n) +{ + buf_append(to, NULL, n); + rnd_bytes(to->data + to->length - n, n); + return (0); +} + +int buf_appendzero(BUFFER *to, int n) +{ + buf_append(to, NULL, n); + memset(to->data + to->length - n, 0, n); + return (0); +} + +int buf_setrnd(BUFFER *b, int n) +{ + buf_prepare(b, n); + rnd_bytes(b->data, n); + return (0); +} + +int buf_cat(BUFFER *to, BUFFER *from) +{ + return (buf_append(to, from->data, from->length)); +} + +int buf_set(BUFFER *to, BUFFER *from) +{ + buf_reset(to); + return (buf_cat(to, from)); +} + +int buf_appendc(BUFFER *to, byte b) +{ + return (buf_append(to, &b, 1)); +} + +int buf_rest(BUFFER *to, BUFFER *from) +{ + assert(from != to); + return (buf_append(to, from->data + from->ptr, from->length - from->ptr)); +} + +int buf_appends(BUFFER *buffer, char *s) +{ + return (buf_append(buffer, s, strlen(s))); +} + +int buf_sets(BUFFER *buffer, char *s) +{ + buf_clear(buffer); + return (buf_appends(buffer, s)); +} + +int buf_setc(BUFFER *buffer, byte c) +{ + buf_clear(buffer); + return (buf_appendc(buffer, c)); +} + +int buf_nl(BUFFER *b) +{ + return (buf_append(b, "\n", 1)); +} + +int buf_vappendf(BUFFER *b, char *fmt, va_list args) +{ + for (; *fmt != '\0'; fmt++) + if (*fmt == '%') { + int lzero = 0; + int longvar = 0; + int len = 0; + + for (;;) { + if (*++fmt == '\0') + return (-1); + if (*fmt == '%') { + buf_appendc(b, '%'); + break; + } else if (*fmt == 'b') { /* extension of printf */ + buf_cat(b, va_arg(args, BUFFER *)); + + break; + } else if (*fmt == 'c') { + buf_appendc(b, va_arg(args, int)); + + break; + } else if (*fmt == 's') { + buf_appends(b, va_arg(args, char *)); + + break; + } else if (*fmt == 'd' || *fmt == 'X') { + int base, val, sign = 0; + BUFFER *out; + + out = buf_new(); + base = *fmt == 'd' ? 10 : 16; + if (longvar) + val = va_arg(args, long); + + else + val = va_arg(args, int); + + if (val < 0) + sign = 1, val = -val; + do { + if (val % base > 9) + buf_appendc(out, val % base - 10 + 'A'); + else + buf_appendc(out, val % base + '0'); + val /= base; + } while (val > 0); + if (sign) + len--; + while (len-- > out->length) + buf_appendc(b, lzero ? '0' : ' '); + if (sign) + buf_appendc(b, '-'); + for (len = out->length - 1; len >= 0; len--) + buf_appendc(b, out->data[len]); + buf_free(out); + break; + } else if (*fmt == 'l') + longvar = 1; + else if (*fmt == '0' && len == 0) + lzero = 1; + else if (isdigit(*fmt)) + len = len * 10 + *fmt - '0'; + else + assert(0); + } + } else + buf_appendc(b, *fmt); + return (0); +} + +int buf_setf(BUFFER *b, char *fmt, ...) +{ + va_list args; + int ret; + + va_start(args, fmt); + buf_clear(b); + ret = buf_vappendf(b, fmt, args); + va_end(args); + return (ret); +} + +int buf_appendf(BUFFER *b, char *fmt, ...) +{ + va_list args; + int ret; + + va_start(args, fmt); + ret = buf_vappendf(b, fmt, args); + va_end(args); + return (ret); +} + +int buf_pad(BUFFER *buffer, int size) +{ + assert(size - buffer->length >= 0); + buf_appendrnd(buffer, size - buffer->length); + return (0); +} + +int buf_prepare(BUFFER *buffer, int size) +{ + buf_clear(buffer); + buf_append(buffer, NULL, size); + return (0); +} + +int buf_read(BUFFER *outmsg, FILE *infile) +{ + char buf[BUFSIZE]; + int n; + int err = -1; + + assert(infile != NULL); + sanity_check(outmsg); + + for (;;) { + n = fread(buf, 1, BUFSIZE, infile); + if (n > 0) + err = 0; + buf_append(outmsg, buf, n); + if (n < BUFSIZE) + break; +#ifdef BUFFER_MAX + if (outmsg->length > BUFFER_MAX) { + errlog(ERRORMSG, "Message file too large. Giving up.\n"); + return (1); + } +#endif /* BUFFER_MAX */ + } + +#ifdef WIN32 + if (isatty(fileno(infile)) && isatty(fileno(stdout))) + printf("\n"); +#endif /* WIN32 */ + + return (err); +} + +int buf_write(BUFFER *buffer, FILE *out) +{ + assert(out != NULL); + sanity_check(buffer); + + return (fwrite(buffer->data, 1, buffer->length, out) == buffer->length + ? 0 : -1); +} + +int buf_write_sync(BUFFER *buffer, FILE *out) +{ + int ret = 0; + + if (buf_write(buffer, out) == -1) { + fclose(out); + return -1; + } + + if (fflush(out) != 0) + ret = -1; + +#ifdef POSIX + /* dir entry not synced */ + if (fsync(fileno(out)) != 0) + ret = -1; +#endif /* POSIX */ + + if (fclose(out) != 0) + ret = -1; + + return ret; +} + +int buf_rewind(BUFFER *buffer) +{ + sanity_check(buffer); + + buffer->ptr = 0; + return (0); +} + +int buf_get(BUFFER *buffer, BUFFER *to, int n) +{ + sanity_check(buffer); + sanity_check(to); + assert(n > 0); + assert(buffer != to); + + buf_clear(to); + if (buffer->length - buffer->ptr < n) + return (-1); + buf_append(to, buffer->data + buffer->ptr, n); + buffer->ptr += n; + return (0); +} + +int buf_getc(BUFFER *buffer) +{ + sanity_check(buffer); + if (buffer->ptr == buffer->length) + return (-1); + else + return (buffer->data[buffer->ptr++]); +} + +void buf_ungetc(BUFFER *buffer) +{ + sanity_check(buffer); + if (buffer->ptr > 0) + buffer->ptr--; +} + +int buf_getline(BUFFER *buffer, BUFFER *line) +{ + register int i; + int ret = 0; + int nl = 0; + + sanity_check(buffer); + + if (line != NULL) + buf_clear(line); + if (buffer->ptr == buffer->length) + return (-1); + + for (i = buffer->ptr; i < buffer->length; i++) { + if (buffer->data[i] > '\r') + continue; + if (buffer->data[i] == '\0' || buffer->data[i] == '\n') { + nl = 1; + break; + } + if (buffer->data[i] == '\r' && + i + 1 <= buffer->length && buffer->data[i + 1] == '\n') { + nl = 2; + break; + } + } + + if (line != NULL) + buf_append(line, buffer->data + buffer->ptr, i - buffer->ptr); + if (i == buffer->ptr) + ret = 1; + buffer->ptr = i + nl; + + return (ret); +} + +int buf_chop(BUFFER *b) +{ + int i; + + sanity_check(b); + + for (i = 0; i < b->length; i++) + if (b->data[i] == '\0' || b->data[i] == '\n' || + (b->data[i] == '\r' && i + 1 < b->length && + b->data[i + 1] == '\n')) + b->length = i; + b->data[b->length] = 0; + return (0); +} + +int buf_isheader(BUFFER *buffer) +{ + BUFFER *line; + long p; + int i; + int err; + int ret; + + line = buf_new(); + p = buffer->ptr; + ret = 0; + err = buf_getline(buffer, line); + if (err != 0) + goto end; + + for (i = 0; i < line->length; i++) { + if (line->data[i] == ' ' || line->data[i] == '\t') + break; + if (line->data[i] == ':') { + ret = 1; + break; + } + } + +end: + buffer->ptr = p; + buf_free(line); + return(ret); +} + +int buf_getheader(BUFFER *buffer, BUFFER *field, BUFFER *content) +{ + BUFFER *line; + long p; + int i; + int err; + + line = buf_new(); + buf_reset(field); + buf_reset(content); + + err = buf_getline(buffer, line); + if (err != 0) + goto end; + + err = -1; + for (i = 0; i < line->length; i++) { + if (line->data[i] == ' ' || line->data[i] == '\t') + break; + if (line->data[i] == ':') { + err = 0; + break; + } + } + if (err == -1 && bufileft(line, "From ")) { + buf_set(field, line); + err = 0; + } + if (err == -1) { + /* badly formatted message -- try to process anyway */ + buf_sets(field, "X-Invalid"); + buf_set(content, line); + err = 0; + goto end; + } + buf_append(field, line->data, i); + if (i < line->length) + i++; + else + err = 1; + while (i < line->length && + (line->data[i] == ' ' || line->data[i] == '\t')) + i++; + buf_append(content, line->data + i, line->length - i); + + for (;;) { + p = buffer->ptr; + if (buf_getline(buffer, line) != 0) + break; + if (line->data[0] != ' ' && line->data[0] != '\t') + break; +#if 1 + buf_nl(content); + buf_cat(content, line); +#else /* end of 1 */ + buf_appendc(content, ' '); + buf_appends(content, line->data + 1); +#endif /* else if not 1 */ + } + buffer->ptr = p; +end: + buf_free(line); + return (err); +} + +int buf_appendheader(BUFFER *buffer, BUFFER *field, BUFFER *content) +{ + return buf_appendf(buffer, "%b: %b\n", field, content); +} + +int buf_lookahead(BUFFER *buffer, BUFFER *line) +{ + long p; + int e; + + p = buffer->ptr; + e = buf_getline(buffer, line); + buffer->ptr = p; + return (e); +} + +int buf_eq(BUFFER *b1, BUFFER *b2) +{ + sanity_check(b1); + sanity_check(b2); + + if (b1->length != b2->length) + return (0); + if (b1->length > 0 && memcmp(b1->data, b2->data, b1->length) != 0) + return (0); + return (1); +} + +int buf_ieq(BUFFER *b1, BUFFER *b2) +{ + int i; + sanity_check(b1); + sanity_check(b2); + + if (b1->length != b2->length) + return (0); + for (i = 0; i < b1->length; i++) + if (tolower(b1->data[i]) != tolower(b2->data[i])) + return (0); + return (1); +} + +void buf_move(BUFFER *dest, BUFFER *src) + /* equivalent to buf_set(dest, src); buf_reset(src); */ +{ + BUFFER temp; + sanity_check(src); + buf_reset(dest); + temp.data = dest->data, temp.size = dest->size; + dest->data = src->data, dest->size = src->size, dest->length = src->length; + src->data = temp.data, src->size = temp.size, src->length = 0; + dest->ptr = 0, src->ptr = 0; +} + +int buf_appendl(BUFFER *b, long l) +{ + buf_appendc(b, (l >> 24) & 255); + buf_appendc(b, (l >> 16) & 255); + buf_appendc(b, (l >> 8) & 255); + buf_appendc(b, l & 255); + return (0); +} + +int buf_appendl_lo(BUFFER *b, long l) +{ + buf_appendc(b, l & 255); + buf_appendc(b, (l >> 8) & 255); + buf_appendc(b, (l >> 16) & 255); + buf_appendc(b, (l >> 24) & 255); + return (0); +} + +long buf_getl(BUFFER *b) +{ + long l; + + if (b->ptr + 4 > b->length) + return (-1); + l = buf_getc(b) << 24; + l += buf_getc(b) << 16; + l += buf_getc(b) << 8; + l += buf_getc(b); + return (l); +} + +long buf_getl_lo(BUFFER *b) +{ + long l; + + if (b->ptr + 4 > b->length) + return (-1); + l = buf_getc(b); + l += buf_getc(b) << 8; + l += buf_getc(b) << 16; + l += buf_getc(b) << 24; + + return (l); +} + +int buf_appendi(BUFFER *b, int i) +{ + buf_appendc(b, (i >> 8) & 255); + buf_appendc(b, i & 255); + return (0); +} + +int buf_appendi_lo(BUFFER *b, int i) +{ + buf_appendc(b, i & 255); + buf_appendc(b, (i >> 8) & 255); + return (0); +} + +int buf_geti(BUFFER *b) +{ + int i; + + if (b->ptr + 2 > b->length) + return (-1); + i = buf_getc(b) << 8; + i += buf_getc(b); + return (i); +} + +int buf_geti_lo(BUFFER *b) +{ + int i; + + if (b->ptr + 2 > b->length) + return (-1); + i = buf_getc(b); + i += buf_getc(b) << 8; + return (i); +} + +int buf_getb(BUFFER *b, BUFFER *p) +{ + long l; + + l = buf_getl(b); + return (buf_get(b, p, l)); +} + +int buf_appendb(BUFFER *b, BUFFER *p) +{ + long l; + + l = p->length; + buf_appendl(b, l); + return (buf_cat(b, p)); +} + +int bufleft(BUFFER *b, char *k) { + return(strleft(b->data, k)); +} + +int bufileft(BUFFER *b, char *k) { + return(strileft(b->data, k)); +} + +int buffind(BUFFER *b, char *k) { + return(strfind(b->data, k)); +} + +int bufifind(BUFFER *b, char *k) { + return(strifind(b->data, k)); +} + +int bufiright(BUFFER *b, char *k) { + int l = strlen(k); + if (l <= b->length) + return (strieq(b->data + b->length - l, k)); + return(0); +} + +int bufeq(BUFFER *b, char *k) { + return(streq(b->data, k)); +} + +int bufieq(BUFFER *b, char *k) { + return(strieq(b->data, k)); +} + +/* void buf_cut_out(BUFFER *buffer, BUFFER *cut_out, BUFFER *rest, + * int from, int len); + * + * Cut a chunk out of the buffer. + * + * Starting with from, len bytes are cut out of buffer. The chunk + * of text that has been cut out is returned in cut_out, the + * remainings of buffer are returned in rest. + * + * This function was added by Gerd Beuster. (gb@uni-koblenz.de) + */ + +void buf_cut_out(BUFFER *buffer, BUFFER *cut_out, BUFFER *rest, + int from, int len){ + + assert((len >= 0) && (from >= 0)); + assert(from+len <= buffer->length); + assert(cut_out != rest); + + buffer->ptr = 0; + if(from > 0) + buf_get(buffer, rest, from); + buf_get(buffer, cut_out, len); + buf_rest(rest, buffer); +} + + +#ifdef DEBUG +/* check for memory leaks */ +#undef malloc +#undef free +void *malloc(size_t size); +void free(void *ptr); +#define max 100000 +static int n=0; +static void *m[max]; +static char *mm[max]; + +void mix3_leaks(void) { + int i, ok=1; + for (i = 0; i < n; i++) + if (m[i]) { + fprintf(stderr, "Leak [%d] %s\n", i, mm[i]); + ok = 0; + } + if (ok) + fprintf(stderr, "No memory leaks.\n"); +} + +void *mix3_malloc(size_t size) { + void *ptr; + if (n == 0) + atexit(mix3_leaks); + ptr = malloc(size); + if (n >= max) abort(); + m[n++] = ptr; + mm[n] = "?"; + return(ptr); +} + +void mix3_free(void *ptr) { + int i; + for (i = 0; i < n; i++) + if (m[i] == ptr) { + m[i] = 0; + break; + } + free(ptr); +} + +BUFFER *mix3_bufnew(char *file, int line, char *func) { + mm[n] = malloc(strlen(file) + strlen(func) + 15); + sprintf(mm[n], "in %s %s:%d", func, file, line); + return(buf_new()); +} +#endif /* DEBUG */ diff --git a/Src/chain.c b/Src/chain.c @@ -0,0 +1,384 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Prepare messages for remailer chain + $Id: chain.c 934 2006-06-24 13:40:39Z rabbi $ */ + + +#include "mix3.h" +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <stdlib.h> + +void clienterr(BUFFER *msgbuf, char *err) +{ + if (msgbuf) { + buf_sets(msgbuf, "Error: "); + buf_appends(msgbuf, err); + } else + errlog(ERRORMSG, "%s\n", err); +} + +void parse_badchains(int badchains[MAXREM][MAXREM], char *file, char *startindicator, REMAILER *remailer, int maxrem) { + int i,j; + FILE *list; + char line[LINELEN]; + + if (!badchains) + return; + + for (i = 0; i < maxrem; i++ ) + for (j = 0; j < maxrem; j++ ) + badchains[i][j] = 0; + list = mix_openfile(TYPE2REL, "r"); + if (list != NULL) { + while (fgets(line, sizeof(line), list) != NULL && + !strleft(line, startindicator)) ; + while (fgets(line, sizeof(line), list) != NULL && + strleft(line, "(")) { + char *left, *right, *tmp; + int lefti, righti; + + left = line + 1; + while (*left == ' ') + left ++; + + tmp = left + 1; + while (*tmp != ' ' && *tmp != '\0' && *tmp != ')') + tmp ++; + if (*tmp == '\0' || *tmp == ')') + /* parsing this line failed */ + continue; + *tmp = '\0'; + + right = tmp+1; + while (*right == ' ') + right ++; + tmp = right + 1; + while (*tmp != ' ' && *tmp != '\0' && *tmp != ')') + tmp ++; + if (*tmp == '\0') + /* parsing this line failed */ + continue; + *tmp = '\0'; + + lefti = -1; + righti = -1; + for (i = 1; i < maxrem; i++) { + if (strcmp(remailer[i].name, left) == 0) + lefti = i; + if (strcmp(remailer[i].name, right) == 0) + righti = i; + } + if (strcmp(left, "*") == 0) + lefti = 0; + if (strcmp(right, "*") == 0) + righti = 0; + + if (lefti == -1 || righti == -1) + /* we don't know about one or both remailers */ + continue; + badchains[lefti][righti] = 1; + } + fclose(list); + /* If some broken chain includes all remailers (*) mark it broken for + * every single remailer - this simplifies handling in other places */ + for (i=1; i < maxrem; i++ ) { + if (badchains[0][i]) + for (j=1; j < maxrem; j++ ) + badchains[j][i] = 1; + if (badchains[i][0]) + for (j=1; j < maxrem; j++ ) + badchains[i][j] = 1; + } + } +} + + +int chain_select(int hop[], char *chainstr, int maxrem, REMAILER *remailer, + int type, BUFFER *feedback) +{ + int len = 0; + int i, j, k; + BUFFER *chain, *selected, *addr; + chain = buf_new(); + selected = buf_new(); + addr = buf_new(); + + if (chainstr == NULL || chainstr[0] == '\0') + buf_sets(chain, CHAIN); + else + buf_sets(chain, chainstr); + + /* put the chain backwards: final hop is in hop[0] */ + + for (i = chain->length; i >= 0; i--) + if (i == 0 || chain->data[i - 1] == ',' + || chain->data[i - 1] == ';' || chain->data[i - 1] == ':') { + for (j = i; isspace(chain->data[j]);) /* ignore whitespace */ + j++; + if (chain->data[j] == '\0') + break; + + if (chain->data[j] == '*') + k = 0; +#if 0 + else if (isdigit(chain->data[j])) + k = atoi(chain->data + j); +#endif /* 0 */ + else { + buf_sets(selected, chain->data + j); + rfc822_addr(selected, addr); + buf_clear(selected); + buf_getline(addr, selected); + if (!selected->length) + buf_sets(selected, chain->data + j); + + for (k = 0; k < maxrem; k++) + if (((remailer[k].flags.mix && type == 0) || + (remailer[k].flags.cpunk && type == 1) || + (remailer[k].flags.newnym && type == 2)) && + (streq(remailer[k].name, selected->data) || + strieq(remailer[k].addr, selected->data) || + (selected->data[0] == '@' && strifind(remailer[k].addr, + selected->data)))) + break; + } + if (k < 0 || k >= maxrem) { + if (feedback != NULL) { + buf_appendf(feedback, "No such remailer: %b", selected); + buf_nl(feedback); + } +#if 0 + k = 0; +#else /* end of 0 */ + len = -1; + goto end; +#endif /* else not 0 */ + } + hop[len++] = k; + if (len >= 20) { /* array passed in is has length 20 */ + if (feedback != NULL) { + buf_appends(feedback, "Chain too long.\n"); + } + break; + } + if (i > 0) + chain->data[i - 1] = '\0'; + } +end: + buf_free(chain); + buf_free(selected); + buf_free(addr); + return len; +} + +int chain_randfinal(int type, REMAILER *remailer, int badchains[MAXREM][MAXREM], int maxrem, int rtype, int chain[], int chainlen, int ignore_constraints_if_necessary) +{ + int randavail; + int i; + int t; + int select[MAXREM]; + int secondtolasthop = (chainlen <= 1 ? -1 : chain[1]); + int constraints_ignored = 0; + + t = rtype; + if (rtype == 2) + t = 1; + +start: + randavail = 0; + /* select a random final hop */ + for (i = 1; i < maxrem; i++) { + select[i] = + ((remailer[i].flags.mix && rtype == 0) || /* remailer supports type */ + (remailer[i].flags.pgp && remailer[i].flags.ek && rtype == 1) || + (remailer[i].flags.newnym && rtype == 2)) && + (remailer[i].info[t].reliability >= 100 * RELFINAL || constraints_ignored ) && /* remailer has sufficient reliability */ + (remailer[i].info[t].latency <= MAXLAT || constraints_ignored ) && /* remailer has low enough latency */ + (remailer[i].info[t].latency >= MINLAT || constraints_ignored ) && /* remailer has high enough latency */ + (type == MSG_NULL || !remailer[i].flags.middle) && /* remailer is not middleman */ + !remailer[i].flags.star_ex && /* remailer is not excluded from random selection */ + (remailer[i].flags.post || type != MSG_POST) && /* remailer supports post when this is a post */ + ((secondtolasthop == -1) || !badchains[secondtolasthop][i]); + /* we only have hop or the previous one can send to this (may be random) */ + randavail += select[i]; + } + + for (i = 1; i <= DISTANCE; i++) + if (i < chainlen && select[chain[i]] && chain[i]) { + select[chain[i]] = 0; + randavail--; + } + + assert(randavail >= 0); + if (randavail == 0) { + if (ignore_constraints_if_necessary && !constraints_ignored) { + errlog(WARNING, "No reliable remailers. Ignoring for randhop\n"); + constraints_ignored = 1; + goto start; + }; + i = -1; + } else { + do + i = rnd_number(maxrem - 1) + 1; + while (!select[i]); + } + return (i); +} + +int chain_rand(REMAILER *remailer, int badchains[MAXREM][MAXREM], int maxrem, + int thischain[], int chainlen, int t, int ignore_constraints_if_necessary) + /* set random chain. returns 0 if not random, 1 if random, -1 on error */ +/* t... 0 for mixmaster Type II + * 1 for cypherpunk Type I + */ +{ + int hop; + int err = 0; + int constraints_ignored = 0; + + assert(t == 0 || t == 1); + +start: + for (hop = 0; hop < chainlen; hop++) + if (thischain[hop] == 0) { + int select[MAXREM]; + int randavail = 0; + int i; + + err = 1; + if (hop > 0) + assert(thischain[hop-1]); /* we already should have chosen a remailer after this one */ + for (i = 1; i < maxrem; i++) { + select[i] = ((remailer[i].flags.mix && t == 0) || /* remailer supports type */ + (remailer[i].flags.pgp && remailer[i].flags.ek && t == 1)) && + (remailer[i].info[t].reliability >= 100 * MINREL || constraints_ignored ) && /* remailer has sufficient reliability */ + (remailer[i].info[t].latency <= MAXLAT || constraints_ignored ) && /* remailer has low enough latency */ + (remailer[i].info[t].latency >= MINLAT || constraints_ignored ) && /* remailer has high enough latency */ + !remailer[i].flags.star_ex && /* remailer is not excluded from random selection */ + !badchains[i][0] && !badchains[i][thischain[hop-1]] && /* remailer can send to the next one */ + (hop == chainlen-1 || !badchains[thischain[hop+1]][i]); + /* we are at the first hop or the previous one can send to this (may be random) */ + randavail += select[i]; + } + + for (i = hop - DISTANCE; i <= hop + DISTANCE; i++) + if (i >= 0 && i < chainlen && select[thischain[i]] && thischain[i]) { + select[thischain[i]] = 0; + randavail--; + } + + + assert(randavail >= 0); + if (randavail < 1) { + if (ignore_constraints_if_necessary && !constraints_ignored) { + errlog(WARNING, "No reliable remailers. Ignoring for randhop\n"); + constraints_ignored = 1; + goto start; + }; + err = -1; + goto end; + } + do + thischain[hop] = rnd_number(maxrem - 1) + 1; + while (!select[thischain[hop]]); + } +end: + return (err); +} + +int mix_encrypt(int type, BUFFER *message, char *chainstr, int numcopies, + BUFFER *chainlist) +{ + return (mix2_encrypt(type, message, chainstr, numcopies, 0, chainlist)); +} + +/* float chain_reliablity(char *chain, int chaintype, + char *reliability_string); + * + * Compute reliablity of a chain. + * + * We get the reliablity of the chain by multiplying the reliablity of + * every remailer in the chain. The return value is the reliablity of + * the chain, or a negative number if the reliablity can not be + * calculated. There are two reasons why may not be able to calculated + * the reliablity: A remailer in the chain is selected randomly, or we + * don't have statistics about one of the remailers in the chain. + * remailer_type indicates the remailer type: + * 0 = Mixmaster, 1 = Cypherpunk + * + * If reliability_string is non-NULL, the reliability is also returned + * as a string in this variable. The size of the string must be at + * least 9 characters! + * + * This function has been added by Gerd Beuster. (gb@uni-koblenz.de) + *--------------------------------------------------------------------*/ + +float chain_reliability(char *chain, int chaintype, + char *reliability_string){ + + float acc_reliability = 1; /* Accumulated reliablity */ + char *name_start, *name_end; /* temporary pointers used + in string scanning */ + char remailer_name[20]; /* The length of the array is taken from mix3.h. */ + int error = 0; + int maxrem; + int i; + int previous = -1; + REMAILER remailer[MAXREM]; + int badchains[MAXREM][MAXREM]; + + /* chaintype 0=mix 1=ek 2=newnym */ + assert((chaintype == 0) || (chaintype == 1)); + maxrem = (chaintype == 0 ? mix2_rlist(remailer, badchains) : t1_rlist(remailer, badchains)); + + /* Dissect chain */ + name_start = chain; + name_end = chain; + while(*name_end != '\0'){ /* While string not scanned completely */ + do /* Get next remailer */ + name_end+=sizeof(char); + while( (*name_end != ',') && (*name_end != '\0')); + strncpy(remailer_name, name_start, + (name_end - name_start) / sizeof(char) + 1*sizeof(char)); + remailer_name[name_end-name_start]='\0'; + /* Lookup reliablity for remailer remailer_name */ + for(i=0; + (i < maxrem) && (strcmp(remailer[i].name, remailer_name) != 0); + i++); + if(!strcmp(remailer[i].name, remailer_name)) { /* Found it! */ + acc_reliability *= + ((float) remailer[i].info[chaintype].reliability) / 10000; + if (previous != -1) { + if (badchains[previous][i] || badchains[0][i]) + acc_reliability = 0; + } + previous = i; + } else + error = 1; /* Did not find this remailer. We can't calculate + the reliablity for the whole chain. */ + name_start = name_end+sizeof(char); + } + + if(error || (name_start==name_end)) + acc_reliability = -1; + + /* Convert reliability into string, if appropriate */ + if(reliability_string){ + if(acc_reliability < 0) + sprintf(reliability_string, " n/a "); + else{ + sprintf(reliability_string, "%6.2f", acc_reliability*100); + *(reliability_string+6*sizeof(char)) = '%'; + } + } + + return acc_reliability; +} + diff --git a/Src/chain1.c b/Src/chain1.c @@ -0,0 +1,301 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Encrypt message for Cypherpunk remailer chain + $Id: chain1.c 934 2006-06-24 13:40:39Z rabbi $ */ + + +#include "mix3.h" +#include "pgp.h" +#include <string.h> +#include <ctype.h> + +#define N(X) (isdigit(X) ? (X)-'0' : 0) + +int t1_rlist(REMAILER remailer[], int badchains[MAXREM][MAXREM]) +{ + FILE *list, *excl; + int i, listed = 0; + int n = 0; + char line[2 * LINELEN], l2[LINELEN], name[LINELEN], *flags; + BUFFER *starex; + + starex = buf_new(); + excl = mix_openfile(STAREX, "r"); + if (excl != NULL) { + buf_read(starex, excl); + fclose(excl); + } + + list = mix_openfile(TYPE1LIST, "r"); + if (list == NULL) { + buf_free(starex); + return (-1); + } + + while (fgets(line, sizeof(line), list) != NULL && n < MAXREM) { + if (strleft(line, "$remailer") && + strchr(line, '<') && strchr(line, '>') && + strchr(line, '{') && strchr(line, '{') + 4 < strchr(line, '}')) { + if (line[strlen(line) - 1] == '\n') + line[strlen(line) - 1] = '\0'; + if (line[strlen(line) - 1] == '\r') + line[strlen(line) - 1] = '\0'; + while (line[strlen(line) - 1] == ' ') + line[strlen(line) - 1] = '\0'; + if (line[strlen(line) - 1] != ';' + && fgets(l2, sizeof(l2), list) != NULL) + strcatn(line, l2, LINELEN); + flags = strchr(line, '>'); + strncpy(name, strchr(line, '{') + 2, + strchr(line, '}') - strchr(line, '{') - 3); + name[strchr(line, '}') - strchr(line, '{') - 3] = '\0'; + name[20] = '\0'; + + for (i = 1; i <= n; i++) + if (streq(name, remailer[i].name)) + break; + if (i > n) { + /* not in mix list */ + n++; + strcpy(remailer[i].name, name); + strncpy(remailer[i].addr, strchr(line, '<') + 1, + strchr(line, '>') - strchr(line, '<')); + remailer[i].addr[strchr(line, '>') - strchr(line, '<') - 1] + = '\0'; + remailer[i].flags.mix = 0; + remailer[i].flags.post = strifind(flags, " post"); + } + remailer[i].flags.cpunk = strfind(flags, " cpunk"); + remailer[i].flags.pgp = strfind(flags, " pgp"); + remailer[i].flags.pgponly = strfind(flags, " pgponly"); + remailer[i].flags.latent = strfind(flags, " latent"); + remailer[i].flags.middle = strfind(flags, " middle"); + remailer[i].flags.ek = strfind(flags, " ek"); + remailer[i].flags.esub = strfind(flags, " esub"); + remailer[i].flags.newnym = strfind(flags, " newnym"); + remailer[i].flags.nym = strfind(flags, " nym"); + remailer[i].info[1].reliability = 0; + remailer[i].info[1].latency = 0; + remailer[i].info[1].history[0] = '\0'; + remailer[i].flags.star_ex = bufifind(starex, name); + } + if (strleft(line, + "-----------------------------------------------------------------------")) + break; + } + n++; /* ?? */ + while (fgets(line, sizeof(line), list) != NULL) { + if (strlen(line) >= 72 && strlen(line) <= 73) + for (i = 1; i < n; i++) + if (strleft(line, remailer[i].name) && + line[strlen(remailer[i].name)] == ' ') { + strncpy(remailer[i].info[1].history, line + 42, 12); + remailer[i].info[1].history[12] = '\0'; + remailer[i].info[1].reliability = 10000 * N(line[64]) + + 1000 * N(line[65]) + 100 * N(line[66]) + + 10 * N(line[68]) + N(line[69]); + remailer[i].info[1].latency = 36000 * N(line[55]) + + 3600 * N(line[56]) + 600 * N(line[58]) + + 60 * N(line[59]) + 10 * N(line[61]) + + N(line[62]); + listed++; + } + } + fclose(list); + parse_badchains(badchains, TYPE1LIST, "Broken type-I remailer chains", remailer, n); + if (listed < 4) /* we have no valid reliability info */ + for (i = 1; i < n; i++) + remailer[i].info[1].reliability = 10000; + +#ifdef USE_PGP + pgp_rlist(remailer, n); +#endif /* USE_PGP */ + buf_free(starex); + return (n); +} + +int t1_ek(BUFFER *key, BUFFER *seed, int num) +{ + buf_reset(key); + buf_appendc(key, (byte) num); + buf_cat(key, seed); + digest_md5(key, key); + encode(key, 0); +#ifdef DEBUG + fprintf(stderr, "passphrase=%s (%2X%2X%2X%2X %d)\n", key->data, + seed->data[0], seed->data[1], seed->data[2], seed->data[3], num); +#endif /* DEBUG */ + return (0); +} + +int t1_encrypt(int type, BUFFER *message, char *chainstr, int latency, + BUFFER *ek, BUFFER *feedback) +{ + BUFFER *b, *rem, *dest, *line, *field, *content; + REMAILER remailer[MAXREM]; + int badchains[MAXREM][MAXREM]; + int maxrem, chainlen = 0; + int chain[20]; + int hop; + int hashmark = 0; + int err = 0; + + b = buf_new(); + rem = buf_new(); + dest = buf_new(); + line = buf_new(); + field = buf_new(); + content = buf_new(); + + maxrem = t1_rlist(remailer, badchains); + if (maxrem < 1) { + clienterr(feedback, "No remailer list!"); + err = -1; + goto end; + } + chainlen = chain_select(chain, chainstr, maxrem, remailer, 1, line); + if (chainlen < 1) { + if (line->length) + clienterr(feedback, line->data); + else + clienterr(feedback, "Invalid remailer chain!"); + err = -1; + goto end; + } + if (chain[0] == 0) + chain[0] = chain_randfinal(type, remailer, badchains, maxrem, 1, chain, chainlen, 0); + + if (chain[0] == -1) { + clienterr(feedback, "Invalid remailer chain!"); + err = -1; + goto end; + } + if (chain_rand(remailer, badchains, maxrem, chain, chainlen, 1, 0) == -1) { + clienterr(feedback, "No reliable remailers!"); + err = -1; + goto end; + } + while (buf_getheader(message, field, content) == 0) { + hdr_encode(content, 0); + if (type == MSG_POST && bufieq(field, "newsgroups") && + remailer[chain[0]].flags.post) { + buf_appendf(dest, "Anon-Post-To: %b\n", content); + } else if (type == MSG_MAIL && bufieq(field, "to")) { + buf_appendf(dest, "Anon-To: %b\n", content); + } else { + /* paste header */ + if (type == MSG_POST && bufieq(field, "newsgroups")) + buf_appendf(dest, "Anon-To: %s\n", MAILtoNEWS); + if (hashmark == 0) { + buf_appends(b, "##\n"); + hashmark = 1; + } + buf_appendheader(b, field, content); + } + } + buf_nl(b); + buf_rest(b, message); + buf_move(message, b); + + if (type != MSG_NULL && dest->length == 0) { + clienterr(feedback, "No destination address!"); + err = -1; + goto end; + } + if (type == MSG_NULL) { + buf_sets(dest, "Null:\n"); + } + for (hop = 0; hop < chainlen; hop++) { + if (hop == 0) { + buf_sets(b, "::\n"); + buf_cat(b, dest); + } else { + buf_sets(b, "::\nAnon-To: "); + buf_appends(b, remailer[chain[hop - 1]].addr); + buf_nl(b); + } + if (remailer[chain[hop]].flags.latent && latency > 0) + buf_appendf(b, "Latent-Time: +%d:00r\n", latency); + if (ek && remailer[chain[hop]].flags.ek) { + t1_ek(line, ek, hop); + buf_appendf(b, "Encrypt-Key: %b\n", line); + } + buf_nl(b); + buf_cat(b, message); +#ifdef USE_PGP + if (remailer[chain[hop]].flags.pgp) { + buf_clear(message); + buf_clear(rem); + buf_setf(rem, "<%s>", remailer[chain[hop]].addr); + err = pgp_encrypt(PGP_ENCRYPT | PGP_REMAIL | PGP_TEXT, b, rem, + NULL, NULL, NULL, NULL); + if (err < 0) { + buf_setf(line, "No PGP key for remailer %s!\n", + remailer[chain[hop]].name); + clienterr(feedback, line->data); + goto end; + } + buf_appends(message, "::\nEncrypted: PGP\n\n"); + buf_cat(message, b); + } else +#endif /* USE_PGP */ + { + if (remailer[chain[hop]].flags.pgponly) { + buf_setf(line, "PGP encryption needed for remailer %s!\n", + remailer[chain[hop]].name); + clienterr(feedback, line->data); + goto end; + } + buf_move(message, b); + } + if (ek && remailer[chain[hop]].flags.ek) + buf_appends(message, "\n**\n"); + } + buf_clear(b); + if (chainlen == 0) { + buf_appends(b, "::\n"); + buf_cat(b, dest); + } else { + buf_appendf(b, "%s: %s\n", ek ? "::\nAnon-To" : "To", + remailer[chain[chainlen - 1]].addr); + } + buf_nl(b); + buf_cat(b, message); + buf_move(message, b); +end: + buf_free(b); + buf_free(rem); + buf_free(dest); + buf_free(line); + buf_free(field); + buf_free(content); + return (err); +} + +#ifdef USE_PGP +int t1_getreply(BUFFER *msg, BUFFER *ek, int len) +{ + BUFFER *key, *decrypt; + int err = -1; + int hop = 0; + + key = buf_new(); + decrypt = buf_new(); + + do { + t1_ek(key, ek, hop); + buf_set(decrypt, msg); + if (pgp_decrypt(decrypt, key, NULL, NULL, NULL) == 0 + && decrypt->data != NULL) + err = 0, buf_move(msg, decrypt); + } + while (hop++ < len); + return (err); +} + +#endif /* USE_PGP */ diff --git a/Src/chain2.c b/Src/chain2.c @@ -0,0 +1,685 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Encrypt message for Mixmaster chain + $Id: chain2.c 934 2006-06-24 13:40:39Z rabbi $ */ + + +#include "mix3.h" +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include <ctype.h> +#include <assert.h> + +#define N(X) (isdigit(X) ? (X)-'0' : 0) + +int prepare_type2list(BUFFER *out) +{ + FILE *list; + char line[LINELEN], name[LINELEN], addr[LINELEN], keyid[LINELEN], + version[LINELEN], flags[LINELEN], createdstr[LINELEN], expiresstr[LINELEN]; + int assigned; + time_t created, expires; + + list = mix_openfile(PUBRING, "r"); + if (list == NULL) + return (-1); + while (fgets(line, sizeof(line), list) != NULL) { + if (strleft(line, begin_key)) { + while (fgets(line, sizeof(line), list) != NULL && + !strleft(line, end_key)) ; + } else if (strlen(line) > 36 && line[0] != '#') { + assigned = sscanf(line, "%127s %127s %127s %127s %127s %127s %127s", + name, addr, keyid, version, flags, createdstr, expiresstr); + if (assigned < 4) + continue; + if (assigned >= 6) { + created = parse_yearmonthday(createdstr); + if (created == 0 || created == -1) { + errlog(WARNING, "Cannot parse creation date of key %s.\n", keyid); + continue; + }; + if (created > time(NULL)) { + errlog(WARNING, "Key %s created in the future.\n", keyid); + continue; + }; + } + if (assigned >= 7) { + expires = parse_yearmonthday(expiresstr); + if (expires == 0 || expires == -1) { + errlog(WARNING, "Cannot parse expiration date of key %s.\n", keyid); + continue; + }; + if (expires < time(NULL)) { + errlog(WARNING, "Key %s has expired.\n", keyid); + continue; + }; + } + buf_appends(out, line); + } + } + fclose(list); + return (0); +} + +int mix2_rlist(REMAILER remailer[], int badchains[MAXREM][MAXREM]) +{ + FILE *list, *excl; + int n, i, listed = 0; + + char line[LINELEN], name[LINELEN], addr[LINELEN], keyid[LINELEN], + version[LINELEN], flags[LINELEN], createdstr[LINELEN], expiresstr[LINELEN]; + int assigned; + time_t created, expires; + BUFFER *starex; + + starex = buf_new(); + excl = mix_openfile(STAREX, "r"); + if (excl != NULL) { + buf_read(starex, excl); + fclose(excl); + } + + list = mix_openfile(PUBRING, "r"); + if (list == NULL) { + buf_free(starex); + return (-1); + } + for (n = 1; fgets(line, sizeof(line), list) != NULL && n < MAXREM;) + if (strleft(line, begin_key)) { + while (fgets(line, sizeof(line), list) != NULL && + !strleft(line, end_key)) ; + } else if (strlen(line) > 36 && line[0] != '#') { + flags[0] = '\0'; + assigned = sscanf(line, "%127s %127s %127s %127s %127s %127s %127s", + name, addr, keyid, version, flags, createdstr, expiresstr); + if (assigned < 4) + continue; + if (assigned >= 6) { + created = parse_yearmonthday(createdstr); + if (created == 0 || created == -1) { + errlog(WARNING, "Cannot parse creation date of key %s.\n", keyid); + continue; + }; + if (created > time(NULL)) { + errlog(WARNING, "Key %s created in the future.\n", keyid); + continue; + }; + } + if (assigned >= 7) { + expires = parse_yearmonthday(expiresstr); + if (expires == 0 || expires == -1) { + errlog(WARNING, "Cannot parse expiration date of key %s.\n", keyid); + continue; + }; + if (expires < time(NULL)) { + errlog(WARNING, "Key %s has expired.\n", keyid); + continue; + }; + } + strncpy(remailer[n].name, name, sizeof(remailer[n].name)); + remailer[n].name[sizeof(remailer[n].name) - 1] = '\0'; + strncpy(remailer[n].addr, addr, sizeof(remailer[n].addr)); + remailer[n].addr[sizeof(remailer[n].addr) - 1] = '\0'; + remailer[n].flags.mix = 1; + remailer[n].flags.cpunk = 0; + remailer[n].flags.nym = 0; + remailer[n].flags.newnym = 0; + id_decode(keyid, remailer[n].keyid); + remailer[n].version = N(version[0]); + remailer[n].flags.compress = strfind(flags, "C"); + remailer[n].flags.post = strfind(flags, "N"); + remailer[n].flags.middle = strfind(flags, "M"); + remailer[n].info[0].reliability = 0; + remailer[n].info[0].latency = 0; + remailer[n].info[0].history[0] = '\0'; + remailer[n].flags.star_ex = bufifind(starex, name); + n++; + } + fclose(list); + list = mix_openfile(TYPE2REL, "r"); + if (list != NULL) { + while (fgets(line, sizeof(line), list) != NULL && + !strleft(line, "--------------------------------------------")) { + if (strleft(line, "Last update:")) { + int generated; + int now = time(NULL); + char *tmp = line + strlen("Last update:") + 1; + generated = parsedate(tmp); + if (generated == -1) { + /* For some weird reason, this isn't rfc822 */ + if (strleft(tmp, "Mon") || + strleft(tmp, "Tue") || + strleft(tmp, "Wed") || + strleft(tmp, "Thu") || + strleft(tmp, "Fri") || + strleft(tmp, "Sat") || + strleft(tmp, "Sun")) + tmp += 3; + generated = parsedate(tmp); + } + now = time(NULL); + if (generated != -1 && generated < now - SECONDSPERDAY) + errlog(WARNING, "Remailer Reliability Statistics are older than one day (check your clock?).\n"); + if (generated != -1 && generated > now) + errlog(WARNING, "Remailer Reliability Statistics are from the future (check your clock?).\n"); + } + }; + while (fgets(line, sizeof(line), list) != NULL && + !strleft(line, "</PRE>")) + if (strlen(line) >= 44 && strlen(line) <= 46) + for (i = 1; i < n; i++) + if (strleft(line, remailer[i].name) && + line[strlen(remailer[i].name)] == ' ') { + strncpy(remailer[i].info[0].history, line + 15, 12); + remailer[i].info[0].history[12] = '\0'; + remailer[i].info[0].reliability = 10000 * N(line[37]) + + 1000 * N(line[38]) + 100 * N(line[39]) + + 10 * N(line[41]) + N(line[42]); + remailer[i].info[0].latency = 36000 * N(line[28]) + + 3600 * N(line[29]) + 600 * N(line[31]) + + 60 * N(line[32]) + 10 * N(line[34]) + + N(line[35]); + listed++; + } + fclose(list); + } + + parse_badchains(badchains, TYPE2REL, "Broken type-II remailer chains", remailer, n); + if (listed < 4) /* we have no valid reliability info */ + for (i = 1; i < n; i++) + remailer[i].info[0].reliability = 10000; + buf_free(starex); + return (n); +} + +static int send_packet(int numcopies, BUFFER *packet, int chain[], + int chainlen, int packetnum, int numpackets, + BUFFER *mid, REMAILER remailer[], int badchains[MAXREM][MAXREM], + int maxrem, char *redirect_to, int ignore_constraints_if_necessary, + BUFFER *feedback) +/* + * Puts a mix packet in the pool. + * + * numcopies ... how often to put this packet into the pool + * i.e. send it. required that random remailers are in the chain. + * packet ... the payload, 10240 bytes in size. + * chain ... the chain to send this message along + * chainlen ... length of the chain + * packetnum ... in multi-packet messages (fragmented) the serial of this packet + * numpackets ... the total number of packets + * mid ... the message ID (required for fragmented packets + * remailer ... information about remailers, their reliabilities, capabilities, etc. + * badchains ... broken chain information + * maxrem ... the number of remailers in remailer[] and badchains[] + * redirect_to ... if this is not-null it needs to be an email address. + * in this case packet needs to be not only the body, but a + * complete mixmaster packet of 20480 bytes in size (20 headers + body). + * the chain given is prepended to the one already encrypted in + * the existing message. If this exceeds the allowed 20 hops in total + * the message is corrupted, the last node will realize this. + * This is useful if you want to reroute an existing mixmaster message + * that has foo as the next hop via a chain so that the packet will + * actually flow hop1,hop2,hop3,foo,.... + * ignore_constraints_if_necessary .. to be used when randhopping messages. + * if a chain can not be constructed otherwhise, maxlat, minlat, + * and minrel are ignored. + * feedback ... a buffer to write feedback to + */ +{ + BUFFER *pid, *out, *header, *other, *encrypted, *key, *body; + BUFFER *iv, *ivarray, *temp; + BUFFER *pubkey; + char addr[LINELEN]; + int thischain[20]; + int hop; + int c, i; + int timestamp = 0; + int israndom = 0; + int err = 1; + + body = buf_new(); + pid = buf_new(); + out = buf_new(); + header = buf_new(); + other = buf_new(); + key = buf_new(); + encrypted = buf_new(); + iv = buf_new(); + ivarray = buf_new(); + temp = buf_new(); + + if (redirect_to != NULL) { + assert(packet->length == 20480); + buf_append(header, packet->data, 10240); + buf_append(temp, packet->data + 10240, 10240); + buf_clear(packet); + buf_cat(packet, temp); + } else + assert(packet->length == 10240); + + buf_setrnd(pid, 16); + + for (c = 0; c < numcopies; c++) { + buf_set(body, packet); + + for (hop = 0; hop < chainlen; hop++) + thischain[hop] = chain[hop]; + + israndom = chain_rand(remailer, badchains, maxrem, thischain, chainlen, 0, ignore_constraints_if_necessary); + if (israndom == -1) { + err = -1; + clienterr(feedback, "No reliable remailers!"); + } + if ((numcopies > 1 || numpackets > 1) && !israndom && (chainlen != 1)) { + clienterr(feedback, + "Multi-packet message without random remailers!"); + err = -1; + goto end; + } + for (hop = 0; hop < chainlen; hop++) { + switch (remailer[thischain[hop]].version) { + case 2: + case 3: /* not implemented yet; fall back to version 2 */ + /* create header chart to be encrypted with the session key */ + if (numcopies > 1 && hop == 0 && redirect_to == NULL) + buf_set(encrypted, pid); /* same ID only at final hop */ + else + buf_setrnd(encrypted, 16); + buf_setrnd(key, 24); /* key for encrypting the body */ + buf_cat(encrypted, key); + buf_setrnd(iv, 8); /* IV for encrypting the body */ + + if (hop > 0 || redirect_to != NULL) { + /* IVs for header chart encryption */ + buf_setrnd(ivarray, 18 * 8); + buf_cat(ivarray, iv); /* 19th IV equals the body IV */ + + buf_appendc(encrypted, 0); + buf_cat(encrypted, ivarray); + memset(addr, 0, 80); + if (hop == 0) { + assert(redirect_to != NULL); + strncpy(addr, redirect_to, 80); + } else { + assert(hop > 0); + strcpy(addr, remailer[thischain[hop - 1]].addr); + }; + buf_append(encrypted, addr, 80); + } else { + if (numpackets == 1) + buf_appendc(encrypted, 1); + else { + buf_appendc(encrypted, 2); + buf_appendc(encrypted, (byte) packetnum); + buf_appendc(encrypted, (byte) numpackets); + } + buf_cat(encrypted, mid); + buf_cat(encrypted, iv); /* body encryption IV */ + } + + /* timestamp */ + buf_appends(encrypted, "0000"); + buf_appendc(encrypted, '\0'); /* timestamp magic */ + timestamp = time(NULL) / SECONDSPERDAY - rnd_number(4); + buf_appendi_lo(encrypted, timestamp); + + /* message digest for this header */ + digest_md5(encrypted, temp); + buf_cat(encrypted, temp); + buf_pad(encrypted, 328); + + /* encrypt message body */ + buf_crypt(body, key, iv, ENCRYPT); + + if (hop > 0 || redirect_to != NULL) { + /* encrypt the other header charts */ + buf_clear(other); + for (i = 0; i < 19; i++) { + buf_clear(iv); + buf_clear(temp); + buf_append(iv, ivarray->data + 8 * i, 8); + buf_append(temp, header->data + 512 * i, 512); + buf_crypt(temp, key, iv, ENCRYPT); + buf_cat(other, temp); + } + } else + buf_setrnd(other, 19 * 512); /* fill with random data */ + + /* create session key and IV to encrypt the header ... */ + buf_setrnd(key, 24); + buf_setrnd(iv, 8); + buf_crypt(encrypted, key, iv, ENCRYPT); + pubkey = buf_new(); + err = db_getpubkey(remailer[thischain[hop]].keyid, pubkey); + if (err == -1) + goto end; + err = pk_encrypt(key, pubkey); /* ... and encrypt the + session key */ + buf_free(pubkey); + if (err == -1 || key->length != 128) { + clienterr(feedback, "Encryption failed!"); + err = -1; + goto end; + } + /* now build the new header */ + buf_clear(header); + buf_append(header, remailer[thischain[hop]].keyid, 16); + buf_appendc(header, 128); + buf_cat(header, key); + buf_cat(header, iv); + buf_cat(header, encrypted); + buf_pad(header, 512); + buf_cat(header, other); + break; + default: + err = -1; + goto end; + } + } + + /* build the message */ + buf_sets(out, remailer[thischain[chainlen - 1]].addr); + buf_nl(out); + buf_cat(out, header); + buf_cat(out, body); + assert(header->length == 10240 && body->length == 10240); + mix_pool(out, INTERMEDIATE, -1); + + if (feedback) { + for (hop = chainlen - 1; hop >= 0; hop--) { + buf_appends(feedback, remailer[thischain[hop]].name); + if (hop > 0) + buf_appendc(feedback, ','); + } + buf_nl(feedback); + } + } +end: + buf_free(pid); + buf_free(body); + buf_free(out); + buf_free(header); + buf_free(temp); + buf_free(other); + buf_free(key); + buf_free(encrypted); + buf_free(iv); + buf_free(ivarray); + return (err); +} + +int redirect_message(BUFFER *sendmsg, char *chainstr, int numcopies, BUFFER *feedback) +{ + BUFFER *field; + BUFFER *content; + BUFFER *line; + char recipient[80] = ""; + int num = 0; + int err = 0; + int c; + int hop; + + REMAILER remailer[MAXREM]; + int chain[20]; + int thischain[20]; + int chainlen; + int badchains[MAXREM][MAXREM]; + int maxrem; + int tempchain[20]; + int tempchainlen; + int israndom; + + field = buf_new(); + content = buf_new(); + line = buf_new(); + + if (numcopies == 0) + numcopies = NUMCOPIES; + if (numcopies > 10) + numcopies = 10; + + /* Find the recipient */ + while (buf_getheader(sendmsg, field, content) == 0) + if (bufieq(field, "to")) { + strncpy(recipient, content->data, sizeof(recipient)); + num++; + }; + if (num != 1) { + clienterr(feedback, "Did not find exactly one To: address!"); + err = -1; + goto end; + }; + + /* Dearmor the message */ + err = mix_dearmor(sendmsg, sendmsg); + if (err == -1) + goto end; + assert (sendmsg->length == 20480); + + /* Check the chain */ + maxrem = mix2_rlist(remailer, badchains); + if (maxrem < 1) { + clienterr(feedback, "No remailer list!"); + err = -1; + goto end; + } + chainlen = chain_select(chain, chainstr, maxrem, remailer, 0, line); + if (chainlen < 1) { + if (line->length) + clienterr(feedback, line->data); + else + clienterr(feedback, "Invalid remailer chain!"); + err = -1; + goto end; + } else if (chainlen >= 20) { + clienterr(feedback, "A chainlength of 20 will certainly destroy the message!"); + err = -1; + goto end; + }; + + + for (c = 0; c < numcopies; c++) { + /* if our recipient is a remailer we want to make sure we're not using a known broken chain. + * therefore we need to pick the final remailer with care */ + for (hop = 0; hop < chainlen; hop++) + thischain[hop] = chain[hop]; + if (thischain[0] == 0) { + /* Find out, if recipient is a remailer */ + tempchainlen = chain_select(tempchain, recipient, maxrem, remailer, 0, line); + if (tempchainlen < 1 && line->length == 0) { + /* recipient is apparently not a remailer we know about */ + ; + } else { + /* Build a new chain, based on the one we already selected but + * with the recipient as the final hop. + * This is so that chain_rand properly selects nodes based on + * broken chains and DISTANCE */ + assert(chainlen < 20); + for (hop = 0; hop < chainlen; hop++) + thischain[hop+1] = thischain[hop]; + thischain[0] = tempchain[0]; + + israndom = chain_rand(remailer, badchains, maxrem, thischain, chainlen + 1, 0, 0); + if (israndom == -1) { + err = -1; + clienterr(feedback, "No reliable remailers!"); + goto end; + } + + /* Remove the added recipient hop */ + for (hop = 0; hop < chainlen; hop++) + thischain[hop] = thischain[hop + 1]; + }; + }; + + /* queue the packet */ + if (send_packet(1, sendmsg, thischain, chainlen, + -1, -1, NULL, + remailer, badchains, maxrem, recipient, 0, feedback) == -1) + err = -1; + }; + +end: + buf_free(field); + buf_free(content); + buf_free(line); + return (err); +} + +int mix2_encrypt(int type, BUFFER *message, char *chainstr, int numcopies, + int ignore_constraints_if_necessary, + BUFFER *feedback) +{ + /* returns 0 on success, -1 on error. feedback contains the selected + remailer chain or an error message + + ignore_constraints_if_necessary .. to be used when randhopping messages. + if a chain can not be constructed otherwhise, + maxlat, minlat, and minrel are ignored. + */ + + REMAILER remailer[MAXREM]; + int badchains[MAXREM][MAXREM]; + int maxrem; + BUFFER *line, *field, *content, *header, *msgdest, *msgheader, *body, + *temp, *mid; + byte numdest = 0, numhdr = 0; + char hdrline[LINELEN]; + BUFFER *packet; + int chain[20]; + int chainlen; + int i; + int err = 0; + + mix_init(NULL); + packet = buf_new(); + line = buf_new(); + field = buf_new(); + content = buf_new(); + msgheader = buf_new(); + msgdest = buf_new(); + body = buf_new(); + temp = buf_new(); + mid = buf_new(); + header = buf_new(); + if (feedback) + buf_reset(feedback); + + if (numcopies == 0) + numcopies = NUMCOPIES; + if (numcopies > 10) + numcopies = 10; + + maxrem = mix2_rlist(remailer, badchains); + if (maxrem < 1) { + clienterr(feedback, "No remailer list!"); + err = -1; + goto end; + } + chainlen = chain_select(chain, chainstr, maxrem, remailer, 0, line); + if (chainlen < 1) { + if (line->length) + clienterr(feedback, line->data); + else + clienterr(feedback, "Invalid remailer chain!"); + err = -1; + goto end; + } + if (chain[0] == 0) + chain[0] = chain_randfinal(type, remailer, badchains, maxrem, 0, chain, chainlen, ignore_constraints_if_necessary); + + if (chain[0] == -1) { + clienterr(feedback, "No reliable remailers!"); + err = -1; + goto end; + } + switch (remailer[chain[0]].version) { + case 2: + if (type == MSG_NULL) { + memset(hdrline, 0, 80); + strcpy(hdrline, "null:"); + buf_append(msgdest, hdrline, 80); + numdest++; + } else + while (buf_getheader(message, field, content) == 0) { + if (bufieq(field, "to")) { + memset(hdrline, 0, 80); + strncpy(hdrline, content->data, 80); + buf_append(msgdest, hdrline, 80); + numdest++; + } else if (type == MSG_POST && bufieq(field, "newsgroups")) { + memset(hdrline, 0, 80); + strcpy(hdrline, "post: "); + strcatn(hdrline, content->data, 80); + buf_append(msgdest, hdrline, 80); + numdest++; + } else { + buf_clear(header); + buf_appendheader(header, field, content); + hdr_encode(header, 80); + while (buf_getline(header, line) == 0) { + /* paste in encoded header entry */ + memset(hdrline, 0, 80); + strncpy(hdrline, line->data, 80); + buf_append(msgheader, hdrline, 80); + numhdr++; + } + } + } + buf_appendc(body, numdest); + buf_cat(body, msgdest); + buf_appendc(body, numhdr); + buf_cat(body, msgheader); + + if (type != MSG_NULL) { + buf_rest(temp, message); + if (temp->length > 10236 && remailer[chain[0]].flags.compress) + buf_compress(temp); + buf_cat(body, temp); + buf_reset(temp); + } + buf_setrnd(mid, 16); /* message ID */ + for (i = 0; i <= body->length / 10236; i++) { + long length; + + length = body->length - i * 10236; + if (length > 10236) + length = 10236; + buf_clear(packet); + buf_appendl_lo(packet, length); + buf_append(packet, body->data + i * 10236, length); + buf_pad(packet, 10240); + if (send_packet(numcopies, packet, chain, chainlen, + i + 1, body->length / 10236 + 1, + mid, remailer, badchains, maxrem, NULL, ignore_constraints_if_necessary, feedback) == -1) + err = -1; + } + break; + case 3: + NOT_IMPLEMENTED; + break; + default: + fprintf(stderr, "%d\n", chain[0]); + clienterr(feedback, "Unknown remailer version!"); + err = -1; + } + +end: + buf_free(packet); + buf_free(line); + buf_free(field); + buf_free(content); + buf_free(header); + buf_free(msgheader); + buf_free(msgdest); + buf_free(body); + buf_free(temp); + buf_free(mid); + return (err); +} diff --git a/Src/compress.c b/Src/compress.c @@ -0,0 +1,210 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Buffer compression (interface to zlib) + $Id: compress.c 934 2006-06-24 13:40:39Z rabbi $ */ + + +#include "mix3.h" +#include <stdio.h> +#include <assert.h> + +static byte gz_magic[2] = +{0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 +#define HEAD_CRC 0x02 +#define EXTRA_FIELD 0x04 +#define ORIG_NAME 0x08 +#define COMMENT 0x10 +#define RESERVED 0xE0 +#define Z_DEFLATED 8 + +#ifdef USE_ZLIB +#include "zlib.h" + +int buf_unzip(BUFFER *in, int type) +{ + BUFFER *out; + z_stream s; + long outstart; + int err; + int ret = 0; + + out = buf_new(); + + s.zalloc = (alloc_func) 0; + s.zfree = (free_func) 0; + s.opaque = (voidpf) 0; + + s.next_in = in->data + in->ptr; + s.avail_in = in->length + 1 - in->ptr; /* terminating 0 byte as "dummy" */ + s.next_out = NULL; + + if (type == 1) + err = inflateInit(&s); /* zlib */ + else + err = inflateInit2(&s, -MAX_WBITS); + if (err != Z_OK) { + ret = -1; + goto end; + } + outstart = 0; + buf_append(out, NULL, in->length * 15 / 10); + + for (;;) { + s.next_out = out->data + s.total_out + outstart; + s.avail_out = out->length - outstart - s.total_out; + err = inflate(&s, Z_PARTIAL_FLUSH); + out->length -= s.avail_out; + if (err != Z_OK) + break; + buf_append(out, NULL, BUFSIZE); + } + if (err != Z_STREAM_END) + errlog(WARNING, "Decompression error %d\n", err); + + err = inflateEnd(&s); + if (err != Z_OK) + ret = -1; +end: + if (ret != 0) + switch (err) { + case Z_STREAM_ERROR: + errlog(ERRORMSG, "Decompression error Z_STREAM_ERROR.\n", err); + break; + case Z_MEM_ERROR: + errlog(ERRORMSG, "Decompression error Z_MEM_ERROR.\n", err); + break; + case Z_BUF_ERROR: + errlog(ERRORMSG, "Decompression error Z_BUF_ERROR.\n", err); + break; + case Z_VERSION_ERROR: + errlog(ERRORMSG, "Decompression error Z_VERSION_ERROR.\n", err); + break; + default: + errlog(ERRORMSG, "Decompression error %d.\n", err); + } + buf_move(in, out); + buf_free(out); + return (ret); +} + +int buf_zip(BUFFER *out, BUFFER *in, int bits) +{ + z_stream s; + long outstart; + int err = -1; + + assert(in != out); + + s.zalloc = (alloc_func) 0; + s.zfree = (free_func) 0; + s.opaque = (voidpf) 0; + s.next_in = NULL; + + if (bits == 0) + bits = MAX_WBITS; + + if (deflateInit2(&s, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -bits, 8, 0) != Z_OK) + goto end; + + outstart = out->length; + /* 12 is overhead, 1.01 is maximum expansion, and 1 is there to force a round-up */ + buf_append(out, NULL, (int)13+in->length*1.01); /* fit it in one chunk */ + + s.next_in = in->data; + s.avail_in = in->length; + + for (;;) { + s.next_out = out->data + s.total_out + outstart; + s.avail_out = out->length - outstart - s.total_out; + err = deflate(&s, Z_FINISH); + out->length -= s.avail_out; + if (err != Z_OK) + break; + errlog(ERRORMSG, "Compressed data did not fit in one chunk.\n"); + buf_append(out, NULL, BUFSIZE); + } + if (deflateEnd(&s) != Z_OK || err != Z_STREAM_END) + err = -1; + else + err = 0; +end: + if (err != 0) + errlog(ERRORMSG, "Compression error.\n"); + return (err); +} + +#else /* end of USE_ZLIB */ +int buf_zip(BUFFER *out, BUFFER *in, int bits) +{ + return (-1); +} + +int buf_unzip(BUFFER *b, int type) +{ + errlog(ERRORMSG, "Can't uncompress: no zlib\n"); + return (-1); +} +#endif /* else not USE_ZLIB */ + +int compressed(BUFFER *b) +{ + return (b->length >= 10 && b->data[0] == gz_magic[0] && + b->data[1] == gz_magic[1]); +} + +int buf_uncompress(BUFFER *in) +{ + int type; + int err = -1; + unsigned int len; + + if (!compressed(in)) + return (0); + type = in->data[3]; + if (in->data[2] != Z_DEFLATED || (type & RESERVED) == 0) { + in->ptr = 10; + if ((type & EXTRA_FIELD) != 0) { + len = buf_geti(in); + in->ptr += len; + } + if ((type & ORIG_NAME) != 0) + while (buf_getc(in) > 0) ; + if ((type & COMMENT) != 0) + while (buf_getc(in) > 0) ; + if ((type & HEAD_CRC) != 0) + buf_geti(in); + err = buf_unzip(in, 0); + } + return (err); +} + +int buf_compress(BUFFER *in) +{ + BUFFER *out; + int err; + + if (compressed(in)) + return (0); + + out = buf_new(); + buf_appendc(out, gz_magic[0]); + buf_appendc(out, gz_magic[1]); + buf_appendc(out, Z_DEFLATED); + buf_appendc(out, 0); /* flags */ + buf_appendl(out, 0); /* time */ + buf_appendc(out, 0); /* xflags */ + buf_appendc(out, 3); /* Unix */ + err = buf_zip(out, in, 0); + if (err == 0) + buf_move(in, out); + buf_free(out); + return (err); +} diff --git a/Src/config.h b/Src/config.h @@ -0,0 +1,403 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2008 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Configuration + $Id: config.h 973 2008-03-03 16:55:38Z rabbi $ */ + + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "version.h" +#ifndef WIN32 +#include "sys/param.h" +#endif /* WIN32 */ + +/* Disclaimer to be inserted in all anonymous messages: */ +#define DISCLAIMER \ + "Comments: This message did not originate from the Sender address above.\n" \ + "\tIt was remailed automatically by anonymizing remailer software.\n" \ + "\tPlease report problems or inappropriate use to the\n" \ + "\tremailer administrator at <%s>.\n" /* (%s is the complaints address) */ + +/* Additional disclaimer to be inserted in the body of messages with + * user-supplied From lines, e.g. + * "NOTE: The above From: line has not been authenticated!\n\n" */ +#define FROMDISCLAIMER "" + +/* Additional disclaimer to be inserted at the bottom of the body of all + * messages */ +#define MSGFOOTER "" + +/* Comment to be inserted when a binary attachment is filtered out: */ +#define BINDISCLAIMER \ + "[...]" + +/* Character set for MIME-encoded mail header lines */ +#define MIMECHARSET "iso-8859-1" +#if 1 +#define DEFLTENTITY "" +#else +#define DEFLTENTITY "text/plain; charset=" MIMECHARSET +#endif + +#ifdef WIN32 +/* Use the PCRE regular expression library for destination blocking? */ +#define USE_PCRE +/* Use zlib for compression? */ +#define USE_ZLIB +/* Use ncurses? */ +#define USE_NCURSES +/* Use the WIN GUI? */ +#define USE_WINGUI +/* Use sockets to deliver mail */ +#define USE_SOCK +/* Compile in Win32 service support */ +#define WIN32SERVICE +#endif /* WIN32 */ + +/** System dependencies **********************************************/ +/* Macros: UNIX for Unix-style systems + POSIX for systems with POSIX header files (including DJGPP) + MSDOS for 32 bit DOS + WIN32 for Windows 95/NT */ + +#if defined(_WIN32) && !defined(WIN32) +#define WIN32 +#endif + +#if defined(__RSXNT__) && !defined(WIN32) +#define WIN32 +#endif + +#if !defined(UNIX) && !defined(WIN32) && !defined(MSDOS) +#define UNIX +#endif + +#if defined(UNIX) && !defined(POSIX) +#define POSIX +#endif + +#ifdef UNIX +#define DEV_URANDOM "/dev/urandom" +#ifdef __OpenBSD__ +#define DEV_RANDOM "/dev/arandom" +#else +#define DEV_RANDOM "/dev/random" +#endif +#endif + +#ifdef POSIX +# define HAVE_TERMIOS +#endif /* POSIX */ + +#ifdef MSDOS +#define SHORTNAMES +#ifndef WIN32 +#define HAVE_GETKEY +#undef USE_SOCK +#endif +#endif + +#if defined(USE_WINGUI) && !defined(WIN32) +#error "The GUI requires Win32!" +#endif + +#if defined(WIN32) && !defined(_USRDLL) +#define DLLIMPORT __declspec(dllimport) +#else +#define DLLIMPORT +#endif + +/* This block includes the config.h created by autoconf/configure. + * Eventually this old config.h stuff should be merged with the autoconf + * stuff perhaps. */ +#ifdef HAVE_CONFIG_H +# include "../config.h" +#else /* End of HAVE_CONFIG_H */ + +/* Setup for stuff that happens when autoconf isn't run. This should be + * removed once we finally nuke that damn Install script. */ + +/** Libraries and library functions **********************************/ + +/* Use the OpenSSL crypto library (required) */ +#define USE_OPENSSL +/* Use IDEA algorithm? (See file idea.txt) */ +/* #define USE_IDEA */ +/* Use AES algorithm? - should be handled by Install script setting compiler option -DUSE_AES */ +/* #define USE_AES */ +/* Support the OpenPGP message format? */ +#define USE_PGP + +#ifdef UNIX +# define HAVE_UNAME +# define HAVE_GECOS +#endif + +#if defined(POSIX) || defined(USE_SOCK) +# define HAVE_GETHOSTNAME +#endif + +#ifdef POSIX +/* not a POSIX function, but avaiable on virtually all Unix systems */ +# define HAVE_GETTIMEOFDAY +#endif + +#ifdef linux +# define HAVE_GETDOMAINNAME +#endif + +#ifdef WIN32 +# ifdef _MSC_VER +#pragma warning(disable: 4018) /* signed/unsigned mismatch */ +#pragma warning(disable: 4761) /* integral size mismatch */ +# endif +#endif + + +#endif /* End of not HAVE_CONFIG_H */ + +/** Constants *********************************************************/ + +/* Give up if a file is larger than BUFFER_MAX bytes: */ +/* #define BUFFER_MAX 64*1024*1024 */ + +#ifdef MAXPATHLEN +#define PATHMAX MAXPATHLEN +#else /* MAXPATHLEN */ +#ifdef _MSC +#define PATHMAX MAX_PATH +#else /* _MSC */ +#define PATHMAX 512 +#endif /* not _MSC */ +#endif /* not MAXPATHLEN */ +#define LINELEN 128 +#define BUFSIZE 4096 + +/** if it is a systemwide installation defined GLOBALMIXCONF **********/ +/* #define GLOBALMIXCONF "/etc/mix.cfg" */ + +/* The path to append to a user's homedirectory for his local Mix dir */ +#ifndef HOMEMIXDIR +#define HOMEMIXDIR "Mix" +#endif + +/** file names ********************************************************/ + +#ifdef WIN32 +#define DEFAULT_MIXCONF "mix.cfg" /* change to mix.ini eventually */ +#else +#define DEFAULT_MIXCONF "mix.cfg" /* mixmaster configuration file */ +#endif +#define DEFAULT_DISCLAIMFILE "disclaim.txt" +#define DEFAULT_FROMDSCLFILE "fromdscl.txt" +#define DEFAULT_MSGFOOTERFILE "footer.txt" +#ifdef WIN32 +#define DEFAULT_POP3CONF "pop3.cfg" /* change to pop3.ini eventually */ +#else +#define DEFAULT_POP3CONF "pop3.cfg" +#endif +#define DEFAULT_HELPFILE "help.txt" +#define DEFAULT_REQUESTDIR "requests" +#define DEFAULT_ABUSEFILE "abuse.txt" +#define DEFAULT_REPLYFILE "reply.txt" +#define DEFAULT_USAGEFILE "usage.txt" +#define DEFAULT_USAGELOG "usage.log" +#define DEFAULT_BLOCKFILE "blocked.txt" +#define DEFAULT_ADMKEYFILE "adminkey.txt" +#define DEFAULT_KEYFILE "key.txt" +#define DEFAULT_PGPKEY "pgpkey.txt" +#define DEFAULT_DSAPARAMS "dsaparam.mix" +#define DEFAULT_DHPARAMS "dhparam.mix" +#define DEFAULT_MIXRAND "mixrand.bin" +#define DEFAULT_SECRING "secring.mix" +#define DEFAULT_PUBRING "pubring.mix" +#define DEFAULT_IDLOG "id.log" +#define DEFAULT_STATS "stats.log" +#define DEFAULT_PGPMAXCOUNT "pgpmaxcount.log" +/* To enable multiple dest.blk files, edit the following line. */ +/* Filenames must be seperated by one space. */ +#define DEFAULT_DESTBLOCK "dest.blk rab.blk" +#define DEFAULT_DESTALLOW "dest.alw" +#define DEFAULT_DESTALLOW2 "dest.alw.nonpublished" +#define DEFAULT_SOURCEBLOCK "source.blk" +#define DEFAULT_HDRFILTER "header.blk" +#define DEFAULT_REGULAR "time.log" +#define DEFAULT_POOL "pool" /* remailer pool subdirectory */ +#define DEFAULT_TYPE1LIST "rlist.txt" +#define DEFAULT_TYPE2REL "mlist.txt" +#define DEFAULT_PIDFILE "mixmaster.pid" +#define DEFAULT_STATSSRC "stats-src.txt" + +#define DEFAULT_PGPREMPUBRING "pubring.pgp" +#define DEFAULT_PGPREMPUBASC "pubring.asc" +#define DEFAULT_PGPREMSECRING "secring.pgp" +#define DEFAULT_NYMSECRING "nymsec.pgp" +#define DEFAULT_NYMDB "secrets.mix" +#define DEFAULT_STAREX "starex.txt" +#define DEFAULT_ALLPINGERSURL "http://www.noreply.org/allpingers/allpingers.txt" +#define DEFAULT_ALLPINGERSFILE "allpingers.txt" +#define DEFAULT_WGET "wget" + +DLLIMPORT extern char MIXCONF[]; +extern char DISCLAIMFILE[]; +extern char FROMDSCLFILE[]; +extern char MSGFOOTERFILE[]; +extern char POP3CONF[]; +extern char HELPFILE[]; +extern char REQUESTDIR[]; +extern char ABUSEFILE[]; +extern char REPLYFILE[]; +extern char USAGEFILE[]; +extern char USAGELOG[]; +extern char BLOCKFILE[]; +extern char ADMKEYFILE[]; +extern char KEYFILE[]; +extern char PGPKEY[]; +extern char DSAPARAMS[]; +extern char DHPARAMS[]; +extern char MIXRAND[]; +extern char SECRING[]; +extern char PUBRING[]; +extern char IDLOG[]; +extern char STATS[]; +extern char PGPMAXCOUNT[]; +extern char DESTBLOCK[]; +extern char DESTALLOW[]; +extern char DESTALLOW2[]; +extern char SOURCEBLOCK[]; +extern char HDRFILTER[]; +extern char REGULAR[]; +extern char POOL[]; +extern char TYPE1LIST[]; +extern char TYPE2REL[]; +extern char PIDFILE[]; +extern char STAREX[]; + +extern char PGPREMPUBRING[]; +extern char PGPREMPUBASC[]; +extern char PGPREMSECRING[]; +DLLIMPORT extern char NYMSECRING[]; +extern char NYMDB[]; + +/* string constants */ +#define remailer_type "Remailer-Type: Mixmaster " +#define mixmaster_protocol "2" +#define begin_remailer "-----BEGIN REMAILER MESSAGE-----" +#define end_remailer "-----END REMAILER MESSAGE-----" +#define begin_key "-----Begin Mix Key-----" +#define end_key "-----End Mix Key-----" +#define begin_pgp "-----BEGIN PGP " +#define end_pgp "-----END PGP " +#define begin_pgpmsg "-----BEGIN PGP MESSAGE-----" +#define end_pgpmsg "-----END PGP MESSAGE-----" +#define begin_pgpkey "-----BEGIN PGP PUBLIC KEY BLOCK-----" +#define end_pgpkey "-----END PGP PUBLIC KEY BLOCK-----" +#define begin_pgpseckey "-----BEGIN PGP PRIVATE KEY BLOCK-----" +#define end_pgpseckey "-----END PGP PRIVATE KEY BLOCK-----" +#define begin_pgpsigned "-----BEGIN PGP SIGNED MESSAGE-----" +#define begin_pgpsig "-----BEGIN PGP SIGNATURE-----" +#define end_pgpsig "-----END PGP SIGNATURE-----" +#define info_beginpgp "=====BEGIN PGP MESSAGE=====" +#define info_endpgp "=====END PGP MESSAGE=====" +#define info_pgpsig "=====Sig: " + + +/*********************************************************************** + * The following variables are read from mix.cfg, with default values + * defined in mix.c */ + +int REMAIL; +int MIX; +int PGP; +int UNENCRYPTED; +int REMIX; +int REPGP; +extern char MIXDIR[]; +extern char POOLDIR[]; +extern char EXTFLAGS[]; +extern char SENDMAIL[]; +extern char SENDANONMAIL[]; +extern char PRECEDENCE[]; +extern char SMTPRELAY[]; +extern char SMTPUSERNAME[]; +extern char SMTPPASSWORD[]; +extern char NEWS[]; +extern char MAILtoNEWS[]; +extern char ORGANIZATION[]; +extern char MID[]; +extern char TYPE1[]; +extern char ERRLOG[]; +extern char NAME[]; +extern char ADDRESS[]; +extern char REMAILERADDR[]; +extern char ANONADDR[]; +extern char REMAILERNAME[]; +extern char ANONNAME[]; +extern char COMPLAINTS[]; +extern int AUTOREPLY; +extern char HELONAME[]; +extern char ENVFROM[]; +extern char SHORTNAME[]; +extern int POOLSIZE; +DLLIMPORT extern int RATE; +extern int INDUMMYP; +extern int OUTDUMMYP; +extern int MIDDLEMAN; +extern int AUTOBLOCK; +extern int STATSDETAILS; +extern char FORWARDTO[]; +extern int SIZELIMIT; +extern int INFLATEMAX; +extern int MAXRANDHOPS; +extern int BINFILTER; +extern int LISTSUPPORTED; +extern long PACKETEXP; +extern long IDEXP; +DLLIMPORT extern int VERBOSE; +DLLIMPORT extern long SENDPOOLTIME; +extern long MAILINTIME; +extern long KEYLIFETIME; +extern long KEYOVERLAPPERIOD; +extern long KEYGRACEPERIOD; +extern int NUMCOPIES; +extern char CHAIN[]; +extern int DISTANCE; +extern int MINREL; +extern int RELFINAL; +extern long MAXLAT; +extern long MINLAT; +DLLIMPORT extern char PGPPUBRING[]; +DLLIMPORT extern char PGPSECRING[]; +DLLIMPORT extern char PASSPHRASE[]; +extern long POP3TIME; +extern int POP3DEL; +extern int POP3SIZELIMIT; +extern char MAILBOX[]; +extern char MAILIN[]; +extern char MAILABUSE[]; +extern char MAILBLOCK[]; +extern char MAILUSAGE[]; +extern char MAILANON[]; +extern char MAILERROR[]; +extern char MAILBOUNCE[]; +DLLIMPORT extern int CLIENTAUTOFLUSH; +extern int MAXRECIPIENTS; +extern long TIMESKEW_FORWARD; +extern long TIMESKEW_BACK; +extern int TEMP_FAIL; +extern char ALLPINGERSURL[]; +extern char ALLPINGERSFILE[]; + +extern char WGET[]; +extern char STATSSRC[]; +extern int STATSAUTOUPDATE; +extern long STATSINTERVAL; + +DLLIMPORT extern char ENTEREDPASSPHRASE[LINELEN]; + +#endif diff --git a/Src/crypto.c b/Src/crypto.c @@ -0,0 +1,492 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Interface to cryptographic library + $Id: crypto.c 934 2006-06-24 13:40:39Z rabbi $ */ + + +#include "mix3.h" +#include "crypto.h" +#include <assert.h> +#include <string.h> +#include <time.h> + +#ifdef USE_OPENSSL +int digestmem_md5(byte *b, int n, BUFFER *md) +{ + byte m[MD5_DIGEST_LENGTH]; + + MD5(b, n, m); + buf_reset(md); + buf_append(md, m, MD5_DIGEST_LENGTH); + return (0); +} + +int digest_md5(BUFFER *b, BUFFER *md) +{ + return (digestmem_md5(b->data, b->length, md)); +} + +int isdigest_md5(BUFFER *b, BUFFER *md) +{ + int ret; + BUFFER *newmd; + + newmd = buf_new(); + digest_md5(b, newmd); + ret = buf_eq(md, newmd); + buf_free(newmd); + return (ret); +} + +static int digestmem_sha1(byte *b, int n, BUFFER *md) +{ + byte m[SHA_DIGEST_LENGTH]; + + SHA1(b, n, m); + buf_reset(md); + buf_append(md, m, SHA_DIGEST_LENGTH); + return (0); +} + +int digest_sha1(BUFFER *b, BUFFER *md) +{ + return (digestmem_sha1(b->data, b->length, md)); +} + +static int digestmem_rmd160(byte *b, int n, BUFFER *md) +{ + byte m[RIPEMD160_DIGEST_LENGTH]; + + RIPEMD160(b, n, m); + buf_reset(md); + buf_append(md, m, RIPEMD160_DIGEST_LENGTH); + return (0); +} + +int digest_rmd160(BUFFER *b, BUFFER *md) +{ + return (digestmem_rmd160(b->data, b->length, md)); +} + +#define MAX_RSA_MODULUS_LEN 128 + +static int read_seckey(BUFFER *buf, SECKEY *key, const byte id[]) +{ + BUFFER *md; + int bits; + int len, plen; + byte *ptr; + int err = 0; + + md = buf_new(); + bits = buf->data[0] + 256 * buf->data[1]; + len = (bits + 7) / 8; + plen = (len + 1) / 2; + + /* due to encryption, buffer size is multiple of 8 */ + if (3 * len + 5 * plen + 8 < buf->length || 3 * len + 5 * plen > buf->length) + return (-1); + + ptr = buf->data + 2; + + key->n = BN_bin2bn(ptr, len, NULL); + buf_append(md, ptr, len); + ptr += len; + + key->e = BN_bin2bn(ptr, len, NULL); + buf_append(md, ptr, len); + ptr += len; + + key->d = BN_bin2bn(ptr, len, NULL); + ptr += len; + + key->p = BN_bin2bn(ptr, plen, NULL); + ptr += plen; + + key->q = BN_bin2bn(ptr, plen, NULL); + ptr += plen; + + key->dmp1 = BN_bin2bn(ptr, plen, NULL); + ptr += plen; + + key->dmq1 = BN_bin2bn(ptr, plen, NULL); + ptr += plen; + + key->iqmp = BN_bin2bn(ptr, plen, NULL); + ptr += plen; + + digest_md5(md, md); + if (id) + err = (memcmp(id, md->data, 16) == 0) ? 0 : -1; + buf_free(md); + return (err); +} + +static int read_pubkey(BUFFER *buf, PUBKEY *key, const byte id[]) +{ + BUFFER *md; + int bits; + int len; + byte *ptr; + int err = 0; + + md = buf_new(); + bits = buf->data[0] + 256 * buf->data[1]; + len = (bits + 7) / 8; + + if (2 * len + 2 != buf->length) + return (-1); + + ptr = buf->data + 2; + + key->n = BN_bin2bn(ptr, len, NULL); + buf_append(md, ptr, len); + ptr += len; + + key->e = BN_bin2bn(ptr, len, NULL); + buf_append(md, ptr, len); + ptr += len; + + digest_md5(md, md); + if (id) + err = (memcmp(id, md->data, 16) == 0) ? 0 : -1; + buf_free(md); + return (err); +} + +static int write_seckey(BUFFER *sk, SECKEY *key, byte keyid[]) +{ + byte l[128]; + int n; + BUFFER *b, *temp; + + b = buf_new(); + temp = buf_new(); + + n = BN_bn2bin(key->n, l); + assert(n <= 128); + if (n < 128) + buf_appendzero(b, 128 - n); + buf_append(b, l, n); + + n = BN_bn2bin(key->e, l); + assert(n <= 128); + if (n < 128) + buf_appendzero(b, 128 - n); + buf_append(b, l, n); + + digest_md5(b, temp); + memcpy(keyid, temp->data, 16); + + buf_appendc(sk, 0); + buf_appendc(sk, 4); + buf_cat(sk, b); + + n = BN_bn2bin(key->d, l); + assert(n <= 128); + if (n < 128) + buf_appendzero(sk, 128 - n); + buf_append(sk, l, n); + + n = BN_bn2bin(key->p, l); + assert(n <= 64); + if (n < 64) + buf_appendzero(sk, 64 - n); + buf_append(sk, l, n); + + n = BN_bn2bin(key->q, l); + assert(n <= 64); + if (n < 64) + buf_appendzero(sk, 64 - n); + buf_append(sk, l, n); + + n = BN_bn2bin(key->dmp1, l); + assert(n <= 64); + if (n < 64) + buf_appendzero(sk, 64 - n); + buf_append(sk, l, n); + + n = BN_bn2bin(key->dmq1, l); + assert(n <= 64); + if (n < 64) + buf_appendzero(sk, 64 - n); + buf_append(sk, l, n); + + n = BN_bn2bin(key->iqmp, l); + assert(n <= 64); + if (n < 64) + buf_appendzero(sk, 64 - n); + buf_append(sk, l, n); + + buf_pad(sk, 712); /* encrypt needs a block size multiple of 8 */ + + buf_free(temp); + buf_free(b); + return (0); +} + +static int write_pubkey(BUFFER *pk, PUBKEY *key, byte keyid[]) +{ + byte l[128]; + int n; + + buf_appendc(pk, 0); + buf_appendc(pk, 4); + n = BN_bn2bin(key->n, l); + assert(n <= 128); + if (n < 128) + buf_appendzero(pk, 128 - n); + buf_append(pk, l, n); + n = BN_bn2bin(key->e, l); + assert(n <= 128); + if (n < 128) + buf_appendzero(pk, 128 - n); + buf_append(pk, l, n); + return (0); +} + +int seckeytopub(BUFFER *pub, BUFFER *sec, byte keyid[]) +{ + RSA *k; + int err = 0; + + k = RSA_new(); + err = read_seckey(sec, k, keyid); + if (err == 0) + err = write_pubkey(pub, k, keyid); + RSA_free(k); + return (err); +} + +int check_pubkey(BUFFER *buf, const byte id[]) +{ + RSA *tmp; + int ret; + + tmp = RSA_new(); + ret = read_pubkey(buf, tmp, id); + RSA_free(tmp); + return (ret); +} + +int check_seckey(BUFFER *buf, const byte id[]) +{ + RSA *tmp; + int ret; + + tmp = RSA_new(); + ret = read_seckey(buf, tmp, id); + RSA_free(tmp); + return (ret); +} + +int v2createkey(void) +{ + RSA *k; + BUFFER *b, *ek, *iv; + int err; + FILE *f; + byte keyid[16]; + char line[33]; + + b = buf_new(); + ek = buf_new(); + iv = buf_new(); + + errlog(NOTICE, "Generating RSA key.\n"); + k = RSA_generate_key(1024, 65537, NULL, NULL); + err = write_seckey(b, k, keyid); + RSA_free(k); + if (err == 0) { + f = mix_openfile(SECRING, "a"); + if (f != NULL) { + time_t now = time(NULL); + struct tm *gt; + gt = gmtime(&now); + strftime(line, LINELEN, "%Y-%m-%d", gt); + fprintf(f, "%s\nCreated: %s\n", begin_key, line); + if (KEYLIFETIME) { + now += KEYLIFETIME; + gt = gmtime(&now); + strftime(line, LINELEN, "%Y-%m-%d", gt); + fprintf(f, "Expires: %s\n", line); + } + id_encode(keyid, line); + buf_appends(ek, PASSPHRASE); + digest_md5(ek, ek); + buf_setrnd(iv, 8); + buf_crypt(b, ek, iv, ENCRYPT); + encode(b, 40); + encode(iv, 0); + fprintf(f, "%s\n0\n%s\n", line, iv->data); + buf_write(b, f); + fprintf(f, "%s\n\n", end_key); + fclose(f); + } else + err = -1; + } + if (err != 0) + errlog(ERRORMSG, "Key generation failed.\n"); + + buf_free(b); + buf_free(ek); + buf_free(iv); + return (err); +} + +int pk_decrypt(BUFFER *in, BUFFER *keybuf) +{ + int err = 0; + BUFFER *out; + RSA *key; + + out = buf_new(); + key = RSA_new(); + read_seckey(keybuf, key, NULL); + + buf_prepare(out, in->length); + out->length = RSA_private_decrypt(in->length, in->data, out->data, key, + RSA_PKCS1_PADDING); + if (out->length == -1) + err = -1, out->length = 0; + + RSA_free(key); + buf_move(in, out); + buf_free(out); + return (err); +} + +int pk_encrypt(BUFFER *in, BUFFER *keybuf) +{ + BUFFER *out; + RSA *key; + int err = 0; + + out = buf_new(); + key = RSA_new(); + read_pubkey(keybuf, key, NULL); + + buf_prepare(out, RSA_size(key)); + out->length = RSA_public_encrypt(in->length, in->data, out->data, key, + RSA_PKCS1_PADDING); + if (out->length == -1) + out->length = 0, err = -1; + buf_move(in, out); + buf_free(out); + RSA_free(key); + return (err); +} +int buf_crypt(BUFFER *buf, BUFFER *key, BUFFER *iv, int enc) +{ + des_key_schedule ks1; + des_key_schedule ks2; + des_key_schedule ks3; + des_cblock i; + + assert(enc == ENCRYPT || enc == DECRYPT); + assert((key->length == 16 || key->length == 24) && iv->length == 8); + assert(buf->length % 8 == 0); + + memcpy(i, iv->data, 8); /* leave iv buffer unchanged */ + des_set_key((const_des_cblock *) key->data, ks1); + des_set_key((const_des_cblock *) (key->data + 8), ks2); + if (key->length == 16) + des_set_key((const_des_cblock *) key->data, ks3); + else + des_set_key((const_des_cblock *) (key->data + 16), ks3); + des_ede3_cbc_encrypt(buf->data, buf->data, buf->length, ks1, ks2, ks3, + &i, enc); + return (0); +} + +int buf_3descrypt(BUFFER *buf, BUFFER *key, BUFFER *iv, int enc) +{ + int n = 0; + des_key_schedule ks1; + des_key_schedule ks2; + des_key_schedule ks3; + + assert(enc == ENCRYPT || enc == DECRYPT); + assert(key->length == 24 && iv->length == 8); + + des_set_key((const_des_cblock *) key->data, ks1); + des_set_key((const_des_cblock *) (key->data + 8), ks2); + des_set_key((const_des_cblock *) (key->data + 16), ks3); + des_ede3_cfb64_encrypt(buf->data, buf->data, buf->length, ks1, ks2, ks3, + (des_cblock *) iv->data, &n, enc); + return (0); +} + +int buf_bfcrypt(BUFFER *buf, BUFFER *key, BUFFER *iv, int enc) +{ + int n = 0; + BF_KEY ks; + + if (key == NULL || key->length == 0) + return (-1); + + assert(enc == ENCRYPT || enc == DECRYPT); + assert(key->length == 16 && iv->length == 8); + BF_set_key(&ks, key->length, key->data); + BF_cfb64_encrypt(buf->data, buf->data, buf->length, &ks, iv->data, &n, + enc == ENCRYPT ? BF_ENCRYPT : BF_DECRYPT); + return (0); +} + +int buf_castcrypt(BUFFER *buf, BUFFER *key, BUFFER *iv, int enc) +{ + int n = 0; + CAST_KEY ks; + + if (key == NULL || key->length == 0) + return (-1); + + assert(enc == ENCRYPT || enc == DECRYPT); + assert(key->length == 16 && iv->length == 8); + CAST_set_key(&ks, 16, key->data); + CAST_cfb64_encrypt(buf->data, buf->data, buf->length, &ks, iv->data, &n, + enc == ENCRYPT ? CAST_ENCRYPT : CAST_DECRYPT); + return (0); +} + +#ifdef USE_AES +int buf_aescrypt(BUFFER *buf, BUFFER *key, BUFFER *iv, int enc) +{ + int n = 0; + AES_KEY ks; + + if (key == NULL || key->length == 0) + return (-1); + + assert(enc == ENCRYPT || enc == DECRYPT); + assert((key->length == 16 || key->length == 24 || key->length == 32) && iv->length == 16); + AES_set_encrypt_key(key->data, key->length<<3, &ks); + AES_cfb128_encrypt(buf->data, buf->data, buf->length, &ks, iv->data, &n, + enc == ENCRYPT ? AES_ENCRYPT : AES_DECRYPT); + return (0); +} +#endif /* USE_AES */ + +#ifdef USE_IDEA +int buf_ideacrypt(BUFFER *buf, BUFFER *key, BUFFER *iv, int enc) +{ + int n = 0; + IDEA_KEY_SCHEDULE ks; + + if (key == NULL || key->length == 0) + return (-1); + + assert(enc == ENCRYPT || enc == DECRYPT); + assert(key->length == 16 && iv->length == 8); + idea_set_encrypt_key(key->data, &ks); + idea_cfb64_encrypt(buf->data, buf->data, buf->length, &ks, iv->data, &n, + enc == ENCRYPT ? IDEA_ENCRYPT : IDEA_DECRYPT); + return (0); +} +#endif /* USE_IDEA */ +#endif /* USE_OPENSSL */ diff --git a/Src/crypto.h b/Src/crypto.h @@ -0,0 +1,48 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Interface to cryptographic library + $Id: crypto.h 934 2006-06-24 13:40:39Z rabbi $ */ + + +#ifndef _CRYPTO_H +#define _CRYPTO_H +#include "mix3.h" + +#ifdef USE_OPENSSL +#include <openssl/opensslv.h> +#if (OPENSSL_VERSION_NUMBER < 0x0903100) +#error "This version of OpenSSL is not supported. Please get a more current version from http://www.openssl.org" +#endif /* version check */ +#include <openssl/des.h> +#include <openssl/blowfish.h> +#include <openssl/md5.h> +#include <openssl/sha.h> +#include <openssl/ripemd.h> +#include <openssl/bn.h> +#include <openssl/dh.h> +#include <openssl/dsa.h> +#include <openssl/rsa.h> +#ifdef USE_IDEA +#include <openssl/idea.h> +#endif /* USE_IDEA */ +#ifdef USE_AES +#include <openssl/aes.h> +#endif /* USE_AES */ +#include <openssl/cast.h> +#include <openssl/rand.h> + +typedef RSA PUBKEY; +typedef RSA SECKEY; + +#else /* end of USE_OPENSSL */ +/* #error "No crypto library." */ +typedef void PUBKEY; +typedef void SECKEY; +#endif /* else not USE_OPENSSL */ + +#endif /* ifndef _CRYPTO_H */ diff --git a/Src/dllmain.c b/Src/dllmain.c @@ -0,0 +1,35 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Mixmaster DLL startup + $Id: dllmain.c 934 2006-06-24 13:40:39Z rabbi $ */ + + +#include "mix3.h" +#ifdef WIN32 +int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved) +{ + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + if(!is_nt_service()) { + rnd_state = RND_WILLSEED; + mix_init(NULL); + if (rnd_state == RND_WILLSEED) + rnd_state = RND_NOTSEEDED; + } + break; + case DLL_PROCESS_DETACH: + if(!is_nt_service()) + mix_exit(); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + return(0); + } + return(1); +} +#endif /* WIN32 */ diff --git a/Src/dummy.c b/Src/dummy.c @@ -0,0 +1,16 @@ +/* Dummy function for programs that don't use menuutil.c */ + +#include "mix3.h" + +int menu_getuserpass(BUFFER *b, int i) +{ + return -1; +} + +void cl(int y, int x) +{} + +int download_stats(char *sourcename) +{ + return -1; +} diff --git a/Src/keymgt.c b/Src/keymgt.c @@ -0,0 +1,434 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Key management + $Id: keymgt.c 934 2006-06-24 13:40:39Z rabbi $ */ + + +#include "mix3.h" +#include <string.h> +#include <time.h> +#include <assert.h> + +int getv2seckey(byte keyid[], BUFFER *key); +static int getv2pubkey(byte keyid[], BUFFER *key); + +int db_getseckey(byte keyid[], BUFFER *key) +{ + if (getv2seckey(keyid, key) == -1) + return (-1); + else + return (0); +} + +int db_getpubkey(byte keyid[], BUFFER *key) +{ + if (getv2pubkey(keyid, key) == -1) + return (-1); + else + return (0); +} + +/* now accepts NULL keyid too, with NULL keyid any key + * will be matched, with valid passphrase of course */ +int getv2seckey(byte keyid[], BUFFER *key) +{ + FILE *keyring; + BUFFER *iv, *pass, *temp; + char idstr[KEY_ID_LEN+2]; + char line[LINELEN]; + int err = -1; + char *res; + time_t created, expires; + + pass = buf_new(); + iv = buf_new(); + temp = buf_new(); + if (keyid) + id_encode(keyid, idstr); + else + idstr[0] = 0; + strcat(idstr, "\n"); + if ((keyring = mix_openfile(SECRING, "r")) == NULL) { + errlog(ERRORMSG, "No secret key file!\n"); + } else { + while (err == -1) { + buf_clear(key); + if (fgets(line, sizeof(line), keyring) == NULL) + break; + if (strleft(line, begin_key)) { + expires = 0; + created = 0; + do { + res = fgets(line, sizeof(line), keyring); + if (strileft(line, "created:")) { + created = parse_yearmonthday(strchr(line, ':')+1); + if (created == -1) + created = 0; + } else if (strileft(line, "expires:")) { + expires = parse_yearmonthday(strchr(line, ':')+1); + if (expires == -1) + expires = 0; + } + /* Fetch lines until we fail or get a non-header line */ + } while ( res != NULL && strchr(line, ':') != NULL ); + if (res == NULL) + break; + if (keyid && (strncmp(line, idstr, KEY_ID_LEN) != 0)) + continue; + if (created != 0 && (created > time(NULL))) { + errlog(ERRORMSG, "Key is not valid yet (creation date in the future): %s", idstr); + break; + } + if (expires != 0 && (expires + KEYGRACEPERIOD < time(NULL))) { + errlog(ERRORMSG, "Key is expired: %s", idstr); + break; + } + fgets(line, sizeof(line), keyring); + fgets(line, sizeof(line), keyring); + buf_sets(iv, line); + decode(iv, iv); + for (;;) { + if (fgets(line, sizeof(line), keyring) == NULL) + break; + if (strleft(line, end_key)) { + if (decode(key, key) == -1) { + errlog(ERRORMSG, "Corrupt secret key.\n"); + break; + } + buf_sets(pass, PASSPHRASE); + digest_md5(pass, pass); + buf_crypt(key, pass, iv, DECRYPT); + err = check_seckey(key, keyid); + if (err == -1) + errlog(ERRORMSG, "Corrupt secret key. Bad passphrase?\n"); + break; + } + buf_append(key, line, strlen(line) - 1); + } + break; + } + } + fclose(keyring); + } + + buf_free(pass); + buf_free(iv); + buf_free(temp); + return (err); +} + +static int getv2pubkey(byte keyid[], BUFFER *key) +{ + FILE *keyring; + BUFFER *b, *temp, *iv; + char idstr[KEY_ID_LEN+2]; + char line[LINELEN]; + int err = 0; + + b = buf_new(); + iv = buf_new(); + temp = buf_new(); + id_encode(keyid, idstr); + if ((keyring = mix_openfile(PUBRING, "r")) == NULL) { + errlog(ERRORMSG, "Can't open %s!\n", PUBRING); + err = -1; + goto end; + } + for (;;) { + if (fgets(line, sizeof(line), keyring) == NULL) + break; + if (strleft(line, begin_key)) { + if (fgets(line, sizeof(line), keyring) == NULL) + break; + if ((strlen(line) > 0) && (line[strlen(line)-1] == '\n')) + line[strlen(line)-1] = '\0'; + if ((strlen(line) > 0) && (line[strlen(line)-1] == '\r')) + line[strlen(line)-1] = '\0'; + if (strncmp(line, idstr, KEY_ID_LEN) != 0) + continue; + fgets(line, sizeof(line), keyring); /* ignore length */ + for (;;) { + if (fgets(line, sizeof(line), keyring) == NULL) + goto done; + if (strleft(line, end_key)) + goto done; + buf_append(key, line, strlen(line)); + } + break; + } + } +done: + fclose(keyring); + + if (key->length == 0) { + errlog(ERRORMSG, "No such public key: %s\n", idstr); + err = -1; + goto end; + } + err = decode(key, key); + if (err != -1) + err = check_pubkey(key, keyid); + if (err == -1) + errlog(ERRORMSG, "Corrupt public key %s\n", idstr); +end: + buf_free(b); + buf_free(iv); + buf_free(temp); + return (err); +} + +int key(BUFFER *out) +{ + int err = -1; + FILE *f; + BUFFER *tmpkey; + + tmpkey = buf_new(); + + buf_sets(out, "Subject: Remailer key for "); + buf_appends(out, SHORTNAME); + buf_appends(out, "\n\n"); + + keymgt(0); + + conf_premail(out); + buf_nl(out); + +#ifdef USE_PGP + if (PGP) { + if (pgp_latestkeys(tmpkey, PGP_ES_RSA) == 0) { + buf_appends(out, "Here is the RSA PGP key:\n\n"); + buf_cat(out, tmpkey); + buf_nl(out); + err = 0; + } + if (pgp_latestkeys(tmpkey, PGP_S_DSA) == 0) { + buf_appends(out, "Here is the DSA PGP key:\n\n"); + buf_cat(out, tmpkey); + buf_nl(out); + err = 0; + } + } +#endif /* USE_PGP */ + if (MIX) { + if ((f = mix_openfile(KEYFILE, "r")) != NULL) { + buf_appends(out, "Here is the Mixmaster key:\n\n"); + buf_appends(out, "=-=-=-=-=-=-=-=-=-=-=-=\n"); + buf_read(out, f); + buf_nl(out); + fclose(f); + err = 0; + } + } + if (err == -1 && UNENCRYPTED) { + buf_appends(out, "The remailer accepts unencrypted messages.\n"); + err = 0; + } + if (err == -1) + errlog(ERRORMSG, "Cannot create remailer keys!"); + + buf_free(tmpkey); + + return (err); +} + +int adminkey(BUFFER *out) +{ + int err = -1; + FILE *f; + + buf_sets( out, "Subject: Admin key for the " ); + buf_appends( out, SHORTNAME ); + buf_appends( out, " remailer\n\n" ); + + if ( (f = mix_openfile( ADMKEYFILE, "r" )) != NULL ) { + buf_read( out, f ); + buf_nl( out ); + fclose( f ); + err = 0; + } + + if ( err == -1 ) + errlog( ERRORMSG, "Can not read admin key file!\n" ); + + return err; +} + +int v2keymgt(int force) +/* + * Mixmaster v2 Key Management + * + * This function triggers creation of mix keys (see parameter force) which are + * stored in secring.mix. One public mix key is also written to key.txt. This + * is the key with the latest expiration date (keys with no expiration date + * are always considered newer if they appear later in the secret mix file + * - key creation appends keys). + * + * force: + * 0, 1: create key when necessary: + * - no key exists as of yet + * - old keys are due to expire/already expired + * 2: always create a new mix key. + * + * (force = 0 is used in mix_daily, and before remailer-key replies) + * (force = 1 is used by mixmaster -K) + * (force = 2 is used by mixmaster -G) + */ +{ + FILE *keyring, *f; + char line[LINELEN]; + byte k1[16], k1_found[16]; + BUFFER *b, *temp, *iv, *pass, *pk, *pk_found; + int err = 0; + int found, foundnonexpiring; + time_t created, expires, created_found, expires_found; + char *res; + + b = buf_new(); + temp = buf_new(); + iv = buf_new(); + pass = buf_new(); + pk = buf_new(); + pk_found = buf_new(); + + foundnonexpiring = 0; + for (;;) { + found = 0; + created_found = 0; + expires_found = 0; + + keyring = mix_openfile(SECRING, "r"); + if (keyring != NULL) { + for (;;) { + if (fgets(line, sizeof(line), keyring) == NULL) + break; + if (strleft(line, begin_key)) { + expires = 0; + created = 0; + do { + res = fgets(line, sizeof(line), keyring); + if (strileft(line, "created:")) { + created = parse_yearmonthday(strchr(line, ':')+1); + if (created == -1) + created = 0; + } else if (strileft(line, "expires:")) { + expires = parse_yearmonthday(strchr(line, ':')+1); + if (expires == -1) + expires = 0; + } + /* Fetch lines until we fail or get a non-header line */ + } while ( res != NULL && strchr(line, ':') != NULL ); + if (res == NULL) + break; + if (((created != 0) && (created > time(NULL))) || + ((expires != 0) && (expires < time(NULL)))) { + /* Key already is expired or has creation date in the future */ + continue; + } + id_decode(line, k1); + fgets(line, sizeof(line), keyring); + if (fgets(line, sizeof(line), keyring) == NULL) + break; + buf_sets(iv, line); + decode(iv, iv); + buf_reset(b); + for (;;) { + if (fgets(line, sizeof(line), keyring) == NULL) + break; + if (strleft(line, end_key)) + break; + buf_append(b, line, strlen(line) - 1); + } + if (decode(b, b) == -1) + break; + buf_sets(temp, PASSPHRASE); + digest_md5(temp, pass); + buf_crypt(b, pass, iv, DECRYPT); + buf_clear(pk); + if (seckeytopub(pk, b, k1) == 0) { + found = 1; + if (expires == 0 || (expires - KEYOVERLAPPERIOD >= time(NULL))) + foundnonexpiring = 1; + if (expires == 0 || (expires_found <= expires)) { + buf_clear(pk_found); + buf_cat(pk_found, pk); + memcpy(&k1_found, &k1, sizeof(k1)); + expires_found = expires; + created_found = created; + } + } + } + } + fclose(keyring); + } + + if (!foundnonexpiring || (force == 2)) { + v2createkey(); + foundnonexpiring = 1; + force = 1; + } else + break; + }; + + if (found) { + if ((f = mix_openfile(KEYFILE, "w")) != NULL) { + id_encode(k1_found, line); + fprintf(f, "%s %s %s %s:%s %s%s", SHORTNAME, + REMAILERADDR, line, mixmaster_protocol, VERSION, + MIDDLEMAN ? "M" : "", + NEWS[0] == '\0' ? "C" : (strchr(NEWS, '@') ? "CNm" : "CNp")); + if (created_found) { + struct tm *gt; + gt = gmtime(&created_found); + strftime(line, LINELEN, "%Y-%m-%d", gt); + fprintf(f, " %s", line); + if (expires_found) { + struct tm *gt; + gt = gmtime(&expires_found); + strftime(line, LINELEN, "%Y-%m-%d", gt); + fprintf(f, " %s", line); + } + } + fprintf(f, "\n\n%s\n", begin_key); + id_encode(k1_found, line); + fprintf(f, "%s\n258\n", line); + encode(pk_found, 40); + buf_write(pk_found, f); + fprintf(f, "%s\n\n", end_key); + fclose(f); + } + } else + err = -1; + + buf_free(b); + buf_free(temp); + buf_free(iv); + buf_free(pass); + buf_free(pk); + buf_free(pk_found); + + return (err); +} + +int keymgt(int force) +{ + /* force = 0: write key file if there is none + force = 1: update key file + force = 2: generate new key */ + int err = 0; + + if (REMAIL || force == 2) { + if (MIX && (err = v2keymgt(force)) == -1) + err = -1; +#ifdef USE_PGP + if (PGP && (err = pgp_keymgt(force)) == -1) + err = -1; +#endif /* USE_PGP */ + } + return (err); +} diff --git a/Src/mail.c b/Src/mail.c @@ -0,0 +1,898 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Socket-based mail transport services + $Id: mail.c 934 2006-06-24 13:40:39Z rabbi $ */ + + +#include "mix3.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if defined(UNIX) && defined(USE_SOCK) +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#endif /* defined(UNIX) && defined(USE_SOCK) */ + +#include <fcntl.h> +#include <time.h> +#include <sys/stat.h> +#include <errno.h> + + +int sendinfofile(char *name, char *logname, BUFFER *address, BUFFER *header) +{ + FILE *f = NULL, *log = NULL; + BUFFER *msg, *addr; + char line[LINELEN]; + int ret = -1; + + if (bufeq(address, ANONNAME)) + return (0); /* don't reply to our own anon messages */ + f = mix_openfile(name, "r"); + if (f == NULL) + return (-1); + + addr = buf_new(); + rfc822_addr(address, addr); + if (addr->length == 0) + buf_set(addr, address), buf_nl(addr); + + if (logname != NULL) { + if ((log = mix_openfile(logname, "r+")) != NULL) { + /* log recipients to prevent mail loop */ + while (fgets(line, sizeof(line), log) != NULL) + if (strieq(line, addr->data)) + goto end; + } else if ((log = mix_openfile(logname, "w")) == NULL) { + errlog(ERRORMSG, "Can't create %s.\n", logname); + ret = -1; + goto end; + } + fprintf(log, "%s", addr->data); + } + msg = buf_new(); + if (header) + buf_cat(msg, header), buf_nl(msg); + while (fgets(line, sizeof(line), f) != NULL) { + if (streq(line, "DESTINATION-BLOCK\n")) + buf_appendf(msg, "destination-block %b", addr); + else + buf_appends(msg, line); + } + ret = sendmail(msg, REMAILERNAME, address); + buf_free(msg); +end: + if (f) + fclose(f); + if (log) + fclose(log); + buf_free(addr); + return (ret); +} + +int smtpsend(BUFFER *head, BUFFER *message, char *from); + + +int sendmail_loop(BUFFER *message, char *from, BUFFER *address) +{ + BUFFER *msg; + int err; + + msg = buf_new(); + buf_appendf(msg, "X-Loop: %s\n", REMAILERADDR); + buf_cat(msg, message); + err = sendmail(msg, from, address); + buf_free(msg); + + return(err); +} + +/* Returns true if more than one of the recipients in the + * rcpt buffer is a remailer + */ +int has_more_than_one_remailer(BUFFER *rcpts) +{ + BUFFER *newlinelist; + BUFFER *line; + int remailers = 0; + REMAILER type1[MAXREM]; + REMAILER type2[MAXREM]; + int num1; + int num2; + int i; + + newlinelist = buf_new(); + line = buf_new(); + + num1 = t1_rlist(type1, NULL); + num2 = mix2_rlist(type2, NULL); + + rfc822_addr(rcpts, newlinelist); + + while (buf_getline(newlinelist, line) != -1) { + int found = 0; + printf("%s\n", line->data); + + for (i = 0; i < num2; i++) + if (strcmp(type2[i].addr, line->data) == 0) { + found = 1; + break; + } + if (!found) + for (i = 0; i < num1; i++) + if (strcmp(type1[i].addr, line->data) == 0) { + found = 1; + break; + } + if (found) + remailers++; + } + printf("found %d\n", remailers); + + buf_free(newlinelist); + buf_free(line); + + return(remailers > 1); +} + +int sendmail(BUFFER *message, char *from, BUFFER *address) +{ + /* returns: 0: ok 1: problem, try again -1: failed */ + + BUFFER *head, *block, *rcpt; + FILE *f; + int err = -1; + int rcpt_cnt; + + head = buf_new(); + rcpt = buf_new(); + + block = readdestblk( ); + if ( !block ) block = buf_new( ); + + if (address != NULL && + (address->length == 0 || doblock(address, block, 1) == -1)) + goto end; + + if (from != NULL) { + buf_setf(head, "From: %s", from); + hdr_encode(head, 255); + buf_nl(head); + } + if (address != NULL) + buf_appendf(head, "To: %b\n", address); + + if (PRECEDENCE[0]) + buf_appendf(head, "Precedence: %s\n", PRECEDENCE); + + buf_rewind(message); + + /* search recipient addresses */ + if (address == NULL) { + BUFFER *field, *content; + field = buf_new(); + content = buf_new(); + + rcpt_cnt = 0; + while (buf_getheader(message, field, content) == 0) { + if (bufieq(field, "to") || bufieq(field, "cc") || bufieq(field, "bcc")) { + int thislinercpts = 1; + char *tmp = content->data; + while ((tmp = strchr(tmp+1, ','))) + thislinercpts ++; + rcpt_cnt += thislinercpts; + + if ( rcpt->data[0] ) + buf_appends(rcpt, ", "); + buf_cat(rcpt, content); + } + } + buf_free(field); + buf_free(content); + } else if (address->data[0]) { + char *tmp = address->data; + rcpt_cnt = 1; + while ((tmp = strchr(tmp+1, ','))) + rcpt_cnt ++; + + buf_set(rcpt, address); + } else + rcpt_cnt = 0; + + buf_rewind(message); + + if ( ! rcpt_cnt ) { + errlog(NOTICE, "No recipients found.\n"); + err = 0; + } else if ( rcpt_cnt > MAXRECIPIENTS ) { + errlog(NOTICE, "Too many recipients. Dropping message.\n"); + err = 0; + } else if ( rcpt_cnt > 1 && has_more_than_one_remailer(rcpt) ) { + errlog(NOTICE, "Message is destined to more than one remailer. Dropping.\n"); + err = 0; + } else if ( REMAIL && strcmp(REMAILERADDR, rcpt->data) == 0) { + buf_cat(head, message); + errlog(LOG, "Message loops back to us; storing in pool rather than sending it.\n"); + err = pool_add(head, "inf"); + } else if (SMTPRELAY[0]) + err = smtpsend(head, message, from); + else if (strieq(SENDMAIL, "outfile")) { + char path[PATHMAX]; + FILE *f = NULL; +#ifdef SHORTNAMES + int i; + for (i = 0; i < 10000; i++) { + snprintf(path, PATHMAX, "%s%cout%i.txt", POOLDIR, DIRSEP, i); + f = fopen(path, "r"); + if (f) + fclose(f); + else + break; + } + f = fopen(path, "w"); +#else /* end of SHORTNAMES */ + static unsigned long namecounter = 0; + struct stat statbuf; + int count; + char hostname[64]; + + hostname[0] = '\0'; + gethostname(hostname, 63); + hostname[63] = '\0'; + + /* Step 2: Stat the file. Wait for ENOENT as a response. */ + for (count = 0;; count++) { + snprintf(path, PATHMAX, "%s%cout.%lu.%u_%lu.%s,S=%lu.txt", + POOLDIR, DIRSEP, time(NULL), getpid(), namecounter++, hostname, head->length + message->length); + + if (stat(path, &statbuf) == 0) + errno = EEXIST; + else if (errno == ENOENT) { /* create the file (at least try) */ + f = fopen(path, "w"); + if (f != NULL) + break; /* we managed to open the file */ + } + if (count > 5) + break; /* Too many retries - give up */ + sleep(2); /* sleep and retry */ + } +#endif /* else not SHORTNAMES */ + if (f != NULL) { + err = buf_write(head, f); + err = buf_write(message, f); + fclose(f); + } else + errlog(ERRORMSG, "Can't create %s!\n", path); + } else { + if (SENDANONMAIL[0] != '\0' && (from == NULL || streq(from, ANONNAME))) + f = openpipe(SENDANONMAIL); + else + f = openpipe(SENDMAIL); + if (f != NULL) { + err = buf_write(head, f); + err = buf_write(message, f); + closepipe(f); + } + } + if (err != 0) { + errlog(ERRORMSG, "Unable to execute sendmail. Check path!\n"); + err = 1; /* error while sending, retry later */ + } + +end: + buf_free(block); + buf_free(head); + buf_free(rcpt); + return (err); +} + +/* socket communication **********************************************/ + +#ifdef USE_SOCK +#ifdef WIN32 +WSADATA w; + +int sock_init() +{ + if (WSAStartup(MAKEWORD(2, 0), &w) != 0) { + errlog(ERRORMSG, "Unable to initialize WINSOCK.\n"); + return 0; + } + return 1; +} + +void sock_exit(void) +{ + WSACleanup(); +} +#endif /* WIN32 */ + +SOCKET opensocket(char *hostname, int port) +{ + struct hostent *hp; + struct sockaddr_in server; + SOCKET s; + + if ((hp = gethostbyname(hostname)) == NULL) + return (INVALID_SOCKET); + + memset((char *) &server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_addr.s_addr = *(unsigned long *) hp->h_addr; + server.sin_port = htons((unsigned short) port); + + s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s != INVALID_SOCKET) + if (connect(s, (struct sockaddr *) &server, sizeof(server)) < 0) { + closesocket(s); + return (INVALID_SOCKET); + } + return (s); +} + +#ifndef WIN32 +int closesocket(SOCKET s) +{ + return (close(s)); +} +#endif /* ifndef WIN32 */ + +int sock_getline(SOCKET s, BUFFER *line) +{ + char c; + int ok; + + buf_clear(line); + while ((ok = recv(s, &c, 1, 0)) > 0) { + if (c == '\n') + break; + if (c != '\r') + buf_appendc(line, c); + } + if (ok <= 0) + return (-1); + if (line->length == 0) + return (1); + return (0); +} + +int sock_cat(SOCKET s, BUFFER *b) +{ + int p = 0, n; + + do { + n = send(s, b->data, b->length, 0); + if (n < 0) + return (-1); + p += n; + } while (p < b->length); + return (0); +} +#else /* end of USE_SOCK */ +SOCKET opensocket(char *hostname, int port) +{ + return (INVALID_SOCKET); +} + +int closesocket(SOCKET s) +{ + return (INVALID_SOCKET); +} + +int sock_getline(SOCKET s, BUFFER *line) +{ + return (-1); +} + +int sock_cat(SOCKET s, BUFFER *b) +{ + return (-1); +} +#endif /* else not USE_SOCK */ + +/* send messages by SMTP ************************************************/ + +static int sock_getsmtp(SOCKET s, BUFFER *response) +{ + int ret; + BUFFER *line; + line = buf_new(); + + buf_clear(response); + do { + ret = sock_getline(s, line); + buf_cat(response, line); + } while (line->length >= 4 && line->data[3] == '-'); + buf_free(line); + return (ret); +} + +SOCKET smtp_open(void) +{ + int s = INVALID_SOCKET; + int esmtp = 0; + BUFFER *line; + +#ifdef USE_SOCK + if (SMTPRELAY[0] != '\0') + s = opensocket(SMTPRELAY, 25); + if (s != INVALID_SOCKET) { + line = buf_new(); + sock_getsmtp(s, line); + if (bufifind(line, "ESMTP")) + esmtp = 1; + if (line->data[0] != '2') { + errlog(ERRORMSG, "SMTP relay not ready. %b\n", line); + closesocket(s); + s = INVALID_SOCKET; + } else { + errlog(DEBUGINFO, "Opening SMTP connection.\n"); + if (esmtp) + buf_sets(line, "EHLO "); + else + buf_sets(line, "HELO "); + if (HELONAME[0]) + buf_appends(line, HELONAME); + else if (strchr(ENVFROM, '@')) + buf_appends(line, strchr(ENVFROM, '@') + 1); + else { + struct sockaddr_in sa; + int len = sizeof(sa); + struct hostent *hp; + + if (getsockname(s, (struct sockaddr *) &sa, &len) == 0 && + (hp = gethostbyaddr((char *) &sa.sin_addr, sizeof(sa.sin_addr), + AF_INET)) != NULL) + buf_appends(line, (char *) hp->h_name); + else if (strchr(REMAILERADDR, '@')) + buf_appends(line, strchr(REMAILERADDR, '@') + 1); + else + buf_appends(line, SHORTNAME); + } + buf_chop(line); + buf_appends(line, "\r\n"); + sock_cat(s, line); + sock_getsmtp(s, line); + if (line->data[0] != '2') { + errlog(ERRORMSG, "SMTP relay refuses HELO: %b\n", line); + closesocket(s); + s = INVALID_SOCKET; + } else if (SMTPUSERNAME[0] && esmtp && bufifind(line, "AUTH") && bufifind(line, "LOGIN")) { + buf_sets(line, "AUTH LOGIN\r\n"); + sock_cat(s, line); + sock_getsmtp(s, line); + if (!bufleft(line, "334")) { + errlog(ERRORMSG, "SMTP AUTH fails: %b\n", line); + goto err; + } + buf_sets(line, SMTPUSERNAME); + encode(line, 0); + buf_appends(line, "\r\n"); + sock_cat(s, line); + sock_getsmtp(s, line); + if (!bufleft(line, "334")) { + errlog(ERRORMSG, "SMTP username rejected: %b\n", line); + goto err; + } + buf_sets(line, SMTPPASSWORD); + encode(line, 0); + buf_appends(line, "\r\n"); + sock_cat(s, line); + sock_getsmtp(s, line); + if (!bufleft(line, "235")) + errlog(ERRORMSG, "SMTP authentication failed: %b\n", line); + } + } +err: + buf_free(line); + } +#endif /* USE_SOCK */ + return (s); +} + +int smtp_close(SOCKET s) +{ + BUFFER *line; + int ret = -1; + +#ifdef USE_SOCK + line = buf_new(); + buf_sets(line, "QUIT\r\n"); + sock_cat(s, line); + if (sock_getsmtp(s, line) == 0 && line->data[0] == '2') { + errlog(DEBUGINFO, "Closing SMTP connection.\n"); + ret = 0; + } else + errlog(WARNING, "SMTP quit failed: %b\n", line); + closesocket(s); + buf_free(line); +#endif /* USE_SOCK */ + return (ret); +} + +int smtp_send(SOCKET relay, BUFFER *head, BUFFER *message, char *from) +{ + BUFFER *rcpt, *line, *field, *content; + int ret = -1; + +#ifdef USE_SOCK + line = buf_new(); + field = buf_new(); + content = buf_new(); + rcpt = buf_new(); + + while (buf_getheader(head, field, content) == 0) + if (bufieq(field, "to")) +#ifdef BROKEN_MTA + if (!bufifind(rcpt, content->data)) + /* Do not add the same recipient twice. + Needed for brain-dead MTAs. */ +#endif /* BROKEN_MTA */ + rfc822_addr(content, rcpt); + buf_rewind(head); + + while (buf_isheader(message) && buf_getheader(message, field, content) == 0) { + if (bufieq(field, "to") || bufieq(field, "cc") || bufieq(field, "bcc")) { +#ifdef BROKEN_MTA + if (!bufifind(rcpt, content->data)) + /* Do not add the same recipient twice. + Needed for brain-dead MTAs. */ +#endif /* BROKEN_MTA */ + rfc822_addr(content, rcpt); + } + if (!bufieq(field, "bcc")) + buf_appendheader(head, field, content); + } + buf_nl(head); + + buf_clear(content); + if (from) { + buf_sets(line, from); + rfc822_addr(line, content); + buf_chop(content); + } + if (bufieq(content, REMAILERADDR) || bufieq(content, ANONADDR)) + buf_clear(content); + if (content->length == 0) + buf_sets(content, ENVFROM[0] ? ENVFROM : ANONADDR); + + buf_setf(line, "MAIL FROM:<%b>\r\n", content); + sock_cat(relay, line); + sock_getsmtp(relay, line); + if (!line->data[0] == '2') { + errlog(ERRORMSG, "SMTP relay does not accept mail: %b\n", line); + goto end; + } + while (buf_getline(rcpt, content) == 0) { + buf_setf(line, "RCPT TO:<%b>\r\n", content); + sock_cat(relay, line); + sock_getsmtp(relay, line); + if (bufleft(line, "421")) { + errlog(ERRORMSG, "SMTP relay error: %b\n", line); + goto end; + } + if (bufleft(line, "530")) { + errlog(ERRORMSG, "SMTP authentication required: %b\n", line); + goto end; + } + } + + buf_sets(line, "DATA\r\n"); + sock_cat(relay, line); + sock_getsmtp(relay, line); + if (!bufleft(line, "354")) { + errlog(WARNING, "SMTP relay does not accept message: %b\n", line); + goto end; + } + while (buf_getline(head, line) >= 0) { + buf_appends(line, "\r\n"); + if (bufleft(line, ".")) { + buf_setf(content, ".%b", line); + buf_move(line, content); + } + sock_cat(relay, line); + } + while (buf_getline(message, line) >= 0) { + buf_appends(line, "\r\n"); + if (bufleft(line, ".")) { + buf_setf(content, ".%b", line); + buf_move(line, content); + } + sock_cat(relay, line); + } + buf_sets(line, ".\r\n"); + sock_cat(relay, line); + sock_getsmtp(relay, line); + if (bufleft(line, "2")) + ret = 0; + else + errlog(WARNING, "SMTP relay will not send message: %b\n", line); +end: + buf_free(line); + buf_free(field); + buf_free(content); + buf_free(rcpt); +#endif /* USE_SOCK */ + return (ret); +} + +static SOCKET sendmail_state = INVALID_SOCKET; + +void sendmail_begin(void) +{ + /* begin mail sending session */ + if (sendmail_state == INVALID_SOCKET) + sendmail_state = smtp_open(); +} +void sendmail_end(void) +{ + /* end mail sending session */ + if (sendmail_state != INVALID_SOCKET) { + smtp_close(sendmail_state); + sendmail_state = INVALID_SOCKET; + } +} + +int smtpsend(BUFFER *head, BUFFER *message, char *from) +{ + SOCKET s; + int ret = -1; + + if (sendmail_state != INVALID_SOCKET) + ret = smtp_send(sendmail_state, head, message, from); + else { + s = smtp_open(); + if (s != INVALID_SOCKET) { + ret = smtp_send(s, head, message, from); + smtp_close(s); + } + } + return (ret); +} + +/* retrieve mail with POP3 **********************************************/ +#ifdef USE_SOCK +int pop3_close(SOCKET s); + +#define POP3_ANY 0 +#define POP3_APOP 1 +#define POP3_PASS 2 + +SOCKET pop3_open(char *user, char *host, char *pass, int auth) +{ + SOCKET server = INVALID_SOCKET; + BUFFER *line; + int authenticated = 0; + char md[33]; + int c; + + line = buf_new(); + server = opensocket(host, 110); + if (server == INVALID_SOCKET) + errlog(NOTICE, "Can't connect to POP3 server %s.\n", host); + else { + sock_getline(server, line); + if (!bufleft(line, "+")) { + errlog(WARNING, "No POP3 service at %s.\n", host); + closesocket(server); + server = INVALID_SOCKET; + } + } + if (server != INVALID_SOCKET) { + errlog(DEBUGINFO, "Opening POP3 connection to %s.\n", host); + do + c = buf_getc(line); + while (c != '<' && c != -1); + while (c != '>' && c != -1) { + buf_appendc(line, c); + c = buf_getc(line); + } + if (c == '>' && (auth == POP3_ANY || auth == POP3_APOP)) { + buf_appendc(line, c); + buf_appends(line, pass); + digest_md5(line, line); + id_encode(line->data, md); + buf_setf(line, "APOP %s %s\r\n", user, md); + sock_cat(server, line); + sock_getline(server, line); + if (bufleft(line, "+")) + authenticated = 1; + else { + errlog(auth == POP3_APOP ? ERRORMSG : NOTICE, + "POP3 APOP auth at %s failed: %b\n", host, line); + buf_sets(line, "QUIT\r\n"); + sock_cat(server, line); + closesocket(server); + server = pop3_open(user, host, pass, POP3_PASS); + goto end; + } + } + if (!authenticated) { + buf_setf(line, "USER %s\r\n", user); + sock_cat(server, line); + sock_getline(server, line); + if (!bufleft(line, "+")) + errlog(ERRORMSG, "POP3 USER command at %s failed: %b\n", host, line); + else { + buf_setf(line, "PASS %s\r\n", pass); + sock_cat(server, line); + sock_getline(server, line); + if (bufleft(line, "+")) + authenticated = 1; + else + errlog(ERRORMSG, "POP3 auth at %s failed: %b\n", host, line); + } + } + if (!authenticated) { + pop3_close(server); + closesocket(server); + server = INVALID_SOCKET; + } + } + end: + buf_free(line); + return (server); +} + +int pop3_close(SOCKET s) +{ + BUFFER *line; + int ret = -1; + + line = buf_new(); + buf_sets(line, "QUIT\r\n"); + sock_cat(s, line); + sock_getline(s, line); + if (bufleft(line, "+")) { + ret = 0; + errlog(DEBUGINFO, "Closing POP3 connection.\n"); + } else + errlog(ERRORMSG, "POP3 QUIT failed:\n", line->data); + buf_free(line); + return (ret); +} + +int pop3_stat(SOCKET s) +{ + BUFFER *line; + int val = -1; + + line = buf_new(); + buf_sets(line, "STAT\r\n"); + sock_cat(s, line); + sock_getline(s, line); + if (bufleft(line, "+")) + sscanf(line->data, "+%*s %d", &val); + buf_free(line); + return (val); +} + +int pop3_list(SOCKET s, int n) +{ + BUFFER *line; + int val = -1; + + line = buf_new(); + buf_setf(line, "LIST %d\r\n", n); + sock_cat(s, line); + sock_getline(s, line); + if (bufleft(line, "+")) + sscanf(line->data, "+%*s %d", &val); + buf_free(line); + return (val); +} + +int pop3_dele(SOCKET s, int n) +{ + BUFFER *line; + int ret = 0; + + line = buf_new(); + buf_setf(line, "DELE %d\r\n", n); + sock_cat(s, line); + sock_getline(s, line); + if (!bufleft(line, "+")) + ret = -1; + buf_free(line); + return (ret); +} + +int pop3_retr(SOCKET s, int n, BUFFER *msg) +{ + BUFFER *line; + int ret = -1; + + line = buf_new(); + buf_clear(msg); + buf_setf(line, "RETR %d\r\n", n); + sock_cat(s, line); + sock_getline(s, line); + if (bufleft(line, "+")) { + for (;;) { + if (sock_getline(s, line) == -1) + break; + if (bufeq(line, ".")) { + ret = 0; + break; + } else if (bufleft(line, ".")) { + buf_append(msg, line->data + 1, line->length - 1); + } else + buf_cat(msg, line); + buf_nl(msg); + } + } + buf_free(line); + return (ret); +} + +void pop3get(void) +{ + FILE *f; + char cfg[LINELEN], user[LINELEN], host[LINELEN], pass[LINELEN], auth[5]; + SOCKET server; + BUFFER *line, *msg; + int i = 0, num = 0; + + line = buf_new(); + msg = buf_new(); + f = mix_openfile(POP3CONF, "r"); + if (f != NULL) + while (fgets(cfg, sizeof(cfg), f) != NULL) { + if (cfg[0] == '#') + continue; + if (strchr(cfg, '@')) + strchr(cfg, '@')[0] = ' '; + if (sscanf(cfg, "%127s %127s %127s %4s", user, host, pass, auth) < 3) + continue; + i = POP3_ANY; + if (strileft(auth, "apop")) + i = POP3_APOP; + if (strileft(auth, "pass")) + i = POP3_PASS; + server = pop3_open(user, host, pass, i); + if (server != INVALID_SOCKET) { + num = pop3_stat(server); + if (num < 0) + errlog(WARNING, "POP3 protocol error at %s.\n", host); + else if (num == 0) + errlog(DEBUGINFO, "No mail at %s.\n", host); + else + for (i = 1; i <= num; i++) { + if (POP3SIZELIMIT > 0 && + pop3_list(server, i) > POP3SIZELIMIT * 1024) { + errlog(WARNING, "Over size message on %s.", host); + if (POP3DEL == 1) + pop3_dele(server, i); + } else { + if (pop3_retr(server, i, msg) == 0 && + pool_add(msg, "inf") == 0) + pop3_dele(server, i); + else { + errlog(WARNING, "POP3 error while getting mail from %s.", + host); + closesocket(server); + goto end; + } + } + } + pop3_close(server); + closesocket(server); + } + } + end: + if (f != NULL) + fclose(f); + buf_free(line); + buf_free(msg); +} +#endif /* USE_SOCK */ diff --git a/Src/maildir.c b/Src/maildir.c @@ -0,0 +1,323 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Maildir support routines + $Id: $ */ + + +/* Maildir support for Mixmaster 3 - see + http://www.qmail.org/man/man5/maildir.html and + http://cr.yp.to/proto/maildir.html + + Added by and (C) 2001 Doobee R. Tzeck + drt@un.bewaff.net - http://c0re.jp/ + + To test it try: + $ gcc maildir.c -DUNITTEST -o test_maildir + $ ./test_maildir + this should print a single line saying "OK" +*/ + +#include "mix3.h" + +#ifdef WIN32 +#include <io.h> +#include <direct.h> +#include <process.h> +#define S_IWUSR _S_IWRITE +#define S_IRUSR _S_IREAD +#else /* end of WIN32 */ +#include <unistd.h> +#endif /* else not WIN32 */ +#include <fcntl.h> +#include <time.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> +#include <stdarg.h> +#include <assert.h> + +#if defined(S_IFDIR) && !defined(S_ISDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif /* defined(S_IFDIR) && !defined(S_ISDIR) */ + +#ifndef SHORTNAMES + +static unsigned long namecounter = 0; + +int checkDirectory(char *dir, char *append, int create) { + char tmp[PATHMAX]; + struct stat buf; + int err; + + tmp[0] = '\0'; + strcatn(tmp, dir, PATHMAX); + if (append) + strcatn(tmp, append, PATHMAX); + + err = stat(tmp, &buf); + if (err == -1) { + if (create) { +#ifndef POSIX + err = mkdir(tmp); +#else /* end of not POSIX */ + err = mkdir(tmp, S_IRWXU); +#endif /* else if POSIX */ + if (err == 0) + errlog(NOTICE, "Creating directory %s.\n", tmp); + } else + err = 1; + } else if (!S_ISDIR(buf.st_mode)) + err = -1; + + return err; +} + +/* Write "message" to "maildir", retunr 0 on success, -1 on failure */ +#define MAX_BASENAME 113 /* actual length should be smaller than 111 bytes */ +#define MAX_SUBNAME 123 /* actual length should be smaller than 115 bytes */ +int maildirWrite(char *maildir, BUFFER *message, int create) { + int fd; + int count; + int returnValue; + char hostname[64]; + struct stat statbuf; + char basename[MAX_BASENAME]; + char tmpname[MAX_SUBNAME]; + char newname[MAX_SUBNAME]; + int messagesize; + char olddirectory[PATHMAX] = ""; + char normalizedmaildir[PATHMAX]; + + /* Declare a handler for SIGALRM so we can time out. */ + /* set_handler(SIGALRM, alarm_handler); */ + /* alarm(86400); */ + + hostname[0] = '\0'; + gethostname(hostname, 63); + hostname[63] = '\0'; + + mixfile(normalizedmaildir, maildir); + if ((checkDirectory(normalizedmaildir, NULL, create) != 0) || + (checkDirectory(normalizedmaildir, "tmp", create) != 0) || + (checkDirectory(normalizedmaildir, "cur", create) != 0) || + (checkDirectory(normalizedmaildir, "new", create) != 0)) { + returnValue = -1; + goto realend; + } + + messagesize = message->length; + + /* Step 1: chdir to maildir (and save current dir) */ + if (getcwd(olddirectory, PATHMAX) == NULL) { + returnValue = -1; + goto realend; + } + olddirectory[PATHMAX-1] = '\0'; + if(chdir(normalizedmaildir) != 0) { + returnValue = -1; + goto functionExit; + } + + /* Step 2: Stat the temporary file. Wait for ENOENT as a response. */ + for (count = 0;; count++) { + tmpname[0] = '\0'; + newname[0] = '\0'; + snprintf(basename, MAX_BASENAME, "%lu.%u_%lu.%s,S=%u", + time(NULL), getpid(), namecounter++, hostname, messagesize); + basename[MAX_BASENAME-1] = '\0'; + strcatn(tmpname, "tmp" DIRSEPSTR, MAX_SUBNAME); + strcatn(tmpname, basename, MAX_SUBNAME); + strcatn(newname, "new" DIRSEPSTR, MAX_SUBNAME); + strcatn(newname, basename, MAX_SUBNAME); + + if (stat(tmpname, &statbuf) == 0) + errno = EEXIST; + else if (errno == ENOENT) { + /* Step 4: create the file (at least try) */ + fd = open(tmpname, O_WRONLY|O_CREAT|O_EXCL, S_IWUSR|S_IRUSR); + if (fd >= 0) + break; /* we managed to open the file */ + } + + if (count > 5) { + /* Too many retries - give up */ + errlog(ERRORMSG, "Can't create message in %s\n", maildir); + returnValue = -1; + goto functionExit; + } + + /* Step 3: sleep and retry */ + sleep(2); + } + + /* Step 5: write file */ + if(write(fd, message->data, message->length) != message->length) { + returnValue = -1; + goto functionExit; + } + + /* on NFS this could fail */ +#ifndef WIN32 + if((fsync(fd) != 0) || (close(fd) != 0)) { +#else /* end of not WIN32 */ + if((_commit(fd) != 0) || (close(fd) != 0)) { +#endif /* else if WIN32 */ + returnValue = -1; + goto functionExit; + } + + /* Step 6: move message to 'cur' */ +#ifdef POSIX + for (count = 0;; count++) { + if(link(tmpname, newname) != 0) { + if (errno == EXDEV || errno == EPERM) { + /* We probably are on coda or some other filesystem that does not allow + * hardlinks. rename() the file instead of link() and unlink() + * I know, It's evil (PP). + */ + if (rename(tmpname, newname) != 0) { + returnValue = -1; + goto functionExit; + }; + break; + } else if (errno != EEXIST) { + returnValue = -1; + goto functionExit; + } + } else { + /* We successfully linked the message in new/. Now let's get + * rid of our tmp/ entry + */ + if(unlink(tmpname) != 0) { + /* unlinking failed */ + returnValue = -1; + goto functionExit; + } + break; + } + + if (count > 5) { + /* Too many retries - give up */ + errlog(ERRORMSG, "Can't move message to %s/new/\n", maildir); + returnValue = -1; + goto functionExit; + } + + sleep(2); + newname[0] = '\0'; + snprintf(basename, MAX_BASENAME, "%lu.%u_%lu.%s,S=%u", + time(NULL), getpid(), namecounter++, hostname, messagesize); + basename[MAX_BASENAME-1] = '\0'; + strcatn(newname, "new" DIRSEPSTR, MAX_SUBNAME); + strcatn(newname, basename, MAX_SUBNAME); + } +#else /* end of POSIX */ + /* On non POSIX systems we simply use rename(). Let's hope DJB + * never finds out + */ + if (rename(tmpname, newname) != 0) { + returnValue = -1; + goto functionExit; + }; +#endif /* else if not POSIX */ + + returnValue = 0; + +functionExit: + /* return to original directory */ + assert(olddirectory[0] != '\0'); + if(chdir(olddirectory) != 0) + returnValue = -1; + +realend: + + return returnValue; +} + +#else /* end of SHORTNAMES */ +int maildirWrite(char *maildir, BUFFER *message, int create) { +{ + errlog(ERRORMSG, "Maildir delivery does not work with SHORTNAMES.\n"); + return -1; +} +#endif /* else if not SHORTNAMES */ + + +#ifdef UNITTEST + +#ifdef NDEBUG +#undef NDEBUG +#endif /* NDEBUG */ + +#include <dirent.h> + +/* mock-up of errlog for unittest */ +void errlog(int type, char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +/* main for unittest */ +int main() +{ + int i, count = 23; + int fd; + DIR *d; + struct dirent *de; + BUFFER message; + char text[] = "From: nobody@un.bewaff.net\nTo: hackers@c0re.jp\nSubject: testing\n\nthis is just a test\n"; + char buf[1024]; + + /* create buffer with test data */ + message.data = text; + message.length = strlen(text); + + /* write <count> messages to maildir */ + for(i = 0; i < count; i++) + assert(maildirWrite("Maildir.test_maildir", message, 1) == 0); + + /* read them back */ + assert((d = opendir("Maildir.test_maildir/new")) != NULL); + for (i = 0; i < count + 2; i++) + { + de = readdir(d); + if(de->d_name[0] != '.') + { + buf[0] = '\0'; + strcat(buf, "Maildir.test_maildir/new/"); + strcat(buf, de->d_name); + fd = open(buf, O_RDONLY); + assert(unlink(buf) == 0); + assert(read(fd, buf, strlen(text)) == strlen(text)); + buf[strlen(text)] = '\0'; + /* check if they match the original message */ + assert(strcmp(text, buf) == 0); + close(fd); + } + } + + /* no files left in directory? */ + assert(readdir(d) == NULL); + + /* delete maildir */ + assert(rmdir("Maildir.test_maildir/tmp") == 0); + assert(rmdir("Maildir.test_maildir/new") == 0); + assert(rmdir("Maildir.test_maildir/cur") == 0); + assert(rmdir("Maildir.test_maildir") == 0); + + /* check if writing to a non existant maildir yilds an error */ + assert(maildirWrite("Maildir.test_maildir", &message, 0) == -1); + + puts("OK"); +} +#endif /* UNITTEST */ diff --git a/Src/main.c b/Src/main.c @@ -0,0 +1,820 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Command-line based frontend + $Id: main.c 937 2006-06-24 15:52:20Z colin $ */ + + +#include "mix3.h" +#include "pgp.h" +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#ifdef POSIX +#include <unistd.h> +#else /* end of POSIX */ +#include <io.h> +#endif /* else if not POSIX */ +#include <assert.h> + +static char *largopt(char *p, char *opt, char *name, int *error); +static void noarg(char *name, char p); +static int check_get_pass(int force, int never_ask_for_passphrase); + +/** main *****************************************************************/ + +/* Returns: + 0 successful operation + 1 command line error + 2 client error condition */ + +#ifdef WIN32SERVICE +int mix_main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif /* WIN32SERVICE */ +{ + int error = 0, deflt = 1, help = 0, readmail = 0, send = -1, sendpool = 0, + header = 1, maint = 0, keygen = 0, verbose = 0, sign = 0, encrypt = 0, + redirect_mail = 0, about=0, version=0; + int daemon = 0, type_list = 0, nodetach = 0; + int update_stats = 0, update_pingerlist = 0; + int never_ask_for_passphrase = 0; + +#ifdef USE_SOCK + int pop3 = 0; + +#endif /* USE_SOCK */ + char *filename = NULL; + int i; + int ret = 0; + char *p, *q; + char chain[1024] = ""; + char nym[LINELEN] = ""; + BUFFER *nymopt, *pseudonym, *attachments, *statssrc; + int numcopies = 0; /* default value set in mix.cfg */ + BUFFER *msg, *chainlist, *field, *content; + FILE *f; + char pingpath[PATHMAX]; + + /* Check if parse_yearmonthday works */ + assert(parse_yearmonthday("2003-04-01") == 1049155200); + + mix_init(NULL); + + msg = buf_new(); + chainlist = buf_new(); + nymopt = buf_new(); + pseudonym = buf_new(); + attachments = buf_new(); + field = buf_new(); + content = buf_new(); + statssrc = buf_new(); + +#ifdef USE_NCURSES + if (argc == 1) { + if (isatty(fileno(stdin))) + menu_main(); + else + menu_folder(0, NULL); + goto clientpool; + } +#endif /* USE_NCURSES */ + if (argc > 1 && strleft(argv[1], "-f")) { + menu_folder(strlen(argv[1]) > 2 ? argv[1][2] : 0, argc < 3 ? NULL : argv[2]); + goto clientpool; + } + for (i = 1; i < argc; i++) { + p = argv[i]; + if (p[0] == '-' && p[1] != '\0') { + if (p[1] == '-') { + p += 2; + if (strieq(p, "help")) + help = 1, deflt = 0; + else if (streq(p, "version")) + version = 1, deflt = 0; + else if (streq(p, "about")) + about = 1, deflt = 0; + else if (streq(p, "verbose")) + verbose = 1; + else if (streq(p, "type-list")) + type_list = 1; + else if (streq(p, "dummy")) + send = MSG_NULL, deflt = 0; + else if (streq(p, "remailer")) + maint = 1, deflt = 0; + else if (streq(p, "generate-key")) + keygen = 2, deflt = 0; + else if (streq(p, "update-keys")) + keygen = 1, deflt = 0; + else if (streq(p, "send")) + sendpool = 1, deflt = 0; + else if (streq(p, "read-mail")) + readmail = 1, deflt = 0; + else if (streq(p, "redirect")) + redirect_mail = 1, deflt = 0; + else if (streq(p, "store-mail")) + readmail = 2, deflt = 0; +#ifdef USE_SOCK + else if (streq(p, "pop-mail")) + pop3 = 1, deflt = 0; +#endif /* USE_SOCK */ + else if (streq(p, "daemon")) + daemon = 1, deflt = 0; + else if (streq(p, "no-detach")) + nodetach = 1; + else if (streq(p, "post")) + send = MSG_POST; + else if (streq(p, "mail")) + send = MSG_MAIL; + else if (streq(p, "sign")) + sign = 1; + else if (streq(p, "encrypt")) + encrypt = 1; + else if (streq(p, "no-ask-passphrase")) + never_ask_for_passphrase = 1; + else if (streq(p, "update-pinger-list")) + update_pingerlist = 1; + else if (streq(p, "update-stats")) { + buf_clear(statssrc); + f = mix_openfile(STATSSRC, "r"); + if (f != NULL) { + buf_read(statssrc, f); + fclose(f); + } + if (statssrc->length > 0) { + update_stats = 1; + } else { + deflt = 0; + fprintf(stderr, "%s: No current stats source --%s\n", argv[0], p); + } + } else if (strleft(p, "update-stats") && p[strlen("update-stats")] == '=') { + buf_clear(statssrc); + buf_appendf(statssrc, "%s", (p + strlen("update-stats") + 1)); + if (statssrc->length > 0) { + update_stats = 1; + } else { + fprintf(stderr, "%s: No stats source specified --%s\n", argv[0], p); + } + } else if ((q = largopt(p, "to", argv[0], &error)) != NULL) { + header = 0; + buf_appendf(msg, "To: %s\n", q); + } else if ((q = largopt(p, "post-to", argv[0], &error)) != NULL) { + send = MSG_POST, header = 0; + buf_appendf(msg, "Newsgroups: %s\n", q); + } else if ((q = largopt(p, "subject", argv[0], &error)) != NULL) { + buf_appendf(msg, "Subject: %s\n", q); + } else if ((q = largopt(p, "header", argv[0], &error)) != NULL) { + buf_appendf(msg, "%s\n", q); + } else if ((q = largopt(p, "chain", argv[0], &error)) != NULL) { + buf_appendf(msg, "Chain: %s\n", q); + } +#ifdef USE_PGP + else if ((q = largopt(p, "reply-chain", argv[0], &error)) != NULL) { + buf_appendf(msg, "Reply-Chain: %s\n", q); + } else if ((q = largopt(p, "latency", argv[0], &error)) != NULL) { + buf_appendf(msg, "Latency: %s\n", q); + } else if ((q = largopt(p, "attachment", argv[0], &error)) != NULL) { + buf_appendf(attachments, "%s\n", q); +#ifdef NYMSUPPORT + } else if ((q = largopt(p, "nym-config", argv[0], &error)) != NULL) { + deflt = 0; + strncpy(nym, q, sizeof(nym)); + if (i > argc && strileft(argv[i + 1], "name=")) + buf_sets(pseudonym, argv[++i] + 5); + else if (i > argc && strileft(argv[i + 1], "opt=")) + buf_appends(nymopt, argv[++i] + 5); + } else if ((q = largopt(p, "nym", argv[0], &error)) != NULL) { + buf_appendf(msg, "Nym: %s\n", q); +#endif /* NYMSUPPORT */ + } +#endif /* USE_PGP */ + else if ((q = largopt(p, "copies", argv[0], &error)) != NULL) { + sscanf(q, "%d", &numcopies); + } else if ((q = largopt(p, "config", argv[0], &error)) != NULL) { + strncpy(MIXCONF, q, PATHMAX); + MIXCONF[PATHMAX-1] = 0; + mix_config(); /* configuration file changed - reread it */ + } else if (error == 0 && mix_configline(p) == 0) { + fprintf(stderr, "%s: Invalid option %s\n", argv[0], argv[i]); + error = 1; + } + } else { + while (*++p) { + switch (*p) { + case 'd': + send = MSG_NULL, deflt = 0; + break; + case 'R': + readmail = 1, deflt = 0; + break; + case 'I': + readmail = 2, deflt = 0; + break; + case 'S': + sendpool = 1, deflt = 0; + break; + case 'M': + maint = 1, deflt = 0; + break; +#ifdef USE_SOCK + case 'P': + pop3 = 1, deflt = 0; + break; +#endif /* USE_SOCK */ + case 'D': + daemon = 1, deflt = 0; + break; + case 'G': + keygen = 2, deflt = 0; + break; + case 'K': + keygen = 1, deflt = 0; + break; + case 'L': /* backwards compatibility */ + break; + case 'v': + verbose = 1; + break; + case 'h': + help = 1, deflt = 0; + break; + case 'T': + type_list = 1; + break; + case 'V': + version = 1, deflt = 0; + break; + case 't': + if (*(p + 1) == 'o') + p++; + header = 0; + if (i < argc - 1) + buf_appendf(msg, "To: %s\n", argv[++i]); + else { + fprintf(stderr, "%s: Missing argument for option -to\n", argv[0]); + error = 1; + } + break; + case 's': + if (i < argc - 1) + buf_appendf(msg, "Subject: %s\n", argv[++i]); + else { + noarg(argv[0], *p); + error = 1; + } + break; + case 'l': + if (i < argc - 1) + buf_appendf(msg, "Chain: %s\n", argv[++i]); + else { + noarg(argv[0], *p); + error = 1; + } + break; + case 'r': + if (i < argc - 1) + buf_appendf(msg, "Reply-Chain: %s\n", argv[++i]); + else { + noarg(argv[0], *p); + error = 1; + } + break; +#ifdef USE_PGP + case 'n': + if (i < argc - 1) + buf_appendf(msg, "Nym: %s\n", argv[++i]); + else { + noarg(argv[0], *p); + error = 1; + } + break; +#endif /* USE_PGP */ + case 'c': + if (i < argc - 1) + sscanf(argv[++i], "%d", &numcopies); + else { + noarg(argv[0], *p); + error = 1; + } + break; + case 'p': + send = MSG_POST; + break; + case 'g': + if (i < argc - 1) { + send = MSG_POST, header = 0; + buf_appendf(msg, "Newsgroups: %s\n", argv[++i]); + } else { + noarg(argv[0], *p); + error = 1; + } + break; + case 'a': + if (i < argc - 1) + buf_appendf(attachments, "%s\n", argv[++i]); + else { + noarg(argv[0], *p); + error = 1; + } + break; + case 'm': + send = MSG_MAIL; + break; + default: + fprintf(stderr, "%s: Invalid option -%c\n", argv[0], *p); + error = 1; + break; + } + } + } + } else { + if (strchr(argv[i], '@')) { + header = 0; + buf_appendf(msg, "To: %s\n", argv[i]); + } else { + if (filename == NULL) + filename = argv[i]; + else { + fprintf(stderr, "%s: Error in command line: %s\n", argv[0], argv[i]); + error = 1; + } + } + } + } + + if (error) { + ret = 1; + goto end; + } + + if (type_list) { + BUFFER *type2list; + type2list = buf_new(); + if (prepare_type2list(type2list) < 0) { + fprintf(stderr, "Cannot print type2.list.\n"); + ret = 2; + } else { + printf("%s", type2list->data); + }; + buf_free(type2list); + goto end; + } + + if (version) { + printf("Mixmaster %s\n", VERSION); + ret = 0; + goto end; + } + + if (update_pingerlist) { + mixfile(pingpath, ALLPINGERSFILE); + if (verbose) printf ("downloading %s...\n", ALLPINGERSURL); + if (url_download(ALLPINGERSURL, pingpath) < 0) { + printf(" Download failed... Try again later.\n"); + errlog(ERRORMSG, "All Pingers File Download failed.\n"); + } else { + if (verbose) printf(" Done.\n"); + errlog(LOG, "All Pingers File Downloaded OK.\n"); + } + ret = 0; + goto end; + } + + if (update_stats) { + ret = download_stats(statssrc->data); + if (ret == -3) { + fprintf(stderr, "Stats source does not include all required files.\n"); + } else if (ret == -2) { + fprintf(stderr, "Could not open stats source file for writing\n"); + } else if (ret == -1) { + fprintf(stderr, "Stats source download failed.\n"); + } + ret = 0; + goto end; + } + +#ifdef USE_NCURSES +/* If we get here then it's possible we still want to use the NCURSES interface */ + if (deflt && (send == -1) && isatty(fileno(stdin))) { + menu_main(); + goto clientpool; + } +#endif /* USE_NCURSES */ + + if (help ||about ||(isatty(fileno(stdin)) && isatty(fileno(stdout)))) + fprintf(stderr, "Mixmaster %s\n", VERSION); + if (help ||about) + printf("\n\n"); + if (about) { + printf("Many people have contributed to the source code for Mixmaster.\n"); + printf("These contributors include:\n\n"); + printf("Lance Cottrell\n"); + printf("Janis Jagars\n"); + printf("Ulf Moeller\n"); + printf("Peter Palfrader\n"); + printf("Len Sassaman\n"); + printf("\nand others. For full information on copyright and license issues,\n"); + printf("read the bundled file COPYRIGHT.\n\n"); + ret = 0; + goto end; + } + + if (help) { + printf("Usage: %s [options] [user@host] [filename]\n\n", argv[0]); + printf("Options:\n\ +\n\ +-h, --help summary of command line options\n\ +-V, --version print version information\n\ + --about print authorship information\n\ +-T, --type-list list available remailers\n\ +-t, --to=user@host the recipient's address(es)\n\ +-g, --post-to=newsgroup newsgroup(s) to post to\n\ +-p, --post input is a Usenet article\n\ +-m, --mail input is a mail message\n\ +-s, --subject=subject message subject\n\ + --header='header line' arbitrary message headers\n\ +-a, --attachment=file attach a file\n" +#ifdef USE_PGP +#ifdef NYMSUPPORT + "-n, --nym=yournym use pseudonym to send the message\n" +#endif /* NYMSUPPORT */ +" --encrypt encrypt the message using the PGP format\n\ + --sign sign the message using the PGP format\n" +#endif /* USE_PGP */ + "-l, --chain=mix1,mix2,mix3,... specify a remailer chain\n\ +-c, --copies=num send num copies to increase reliability\n\ +-d, --dummy generate a dummy message\n\ +-S, --send send the message(s) in the pool\n" +#ifdef USE_PGP +#ifdef NYMSUPPORT + " --nym-config=yournym generate a new pseudonym\n\ + --latency=hours reply chain latency\n\ + --reply-chain=rem1,rem2,... reply chain for the pseudonym\n" +#endif /* NYMSUPPORT */ +#endif /* USE_PGP */ + "-v, --verbose output informational messages\n\ +-f [file] read a mail folder\n\ + --update-pinger-list Download an updated all pingers list file\n\ + --update-stats[=source] Download updated stats\n" + +#ifndef USE_NCURSES + "\n-fr, -ff, -fg [file] send reply/followup/group reply to a message\n" +#endif /* USE_NCURSES */ + "\nThe input file is expected to contain mail headers if no address is\n\ +specified in the command line.\n\ +\n\ +Remailer:\n\ +\n\ +-R, --read-mail read remailer message from stdin\n\ +-I, --store-mail read remailer msg from stdin, do not decrypt\n\ +-M, --remailer process the remailer pool\n\ +-D, --daemon remailer as background process\n\ + --no-detach do not detach from terminal as daemon\n" +#ifdef USE_SOCK + "-S, --send force sending messages from the pool\n" +#endif /* USE_SOCK */ + "-P, --pop-mail force getting messages from POP3 servers\n\ +-G, --generate-key generate a new remailer key\n\ +-K, --update-keys generate remailer keys if necessary\n\ + --config=file use alternate configuration file\n" +#ifdef WIN32SERVICE + "\n\ +WinNT service:\n\ +\n\ + --install-svc install the service\n\ + --remove-svc remove the service\n\ + --run-svc run as a service\n" +#endif /* WIN32SERVICE */ + ); + + ret = 0; + goto end; + } + + if (deflt && send == -1) + send = MSG_MAIL; + if (nym[0] != 0) + send = -1; + if ((send == MSG_MAIL || send == MSG_POST) && filename == NULL && + header == 1 && isatty(fileno(stdin))) { + /* we don't get here if USE_NCURSES is set */ + printf("Run `%s -h' to view a summary of the command line options.\n\nEnter the message, complete with headers.\n", + argv[0]); +#ifdef UNIX + printf("When done, press ^D.\n\n"); +#else + printf("When done, press ^Z.\n\n"); +#endif /* else not UNIX */ + } + if (header == 0) + buf_nl(msg); + + /* timeskew check */ + if (REMAIL == 1) + mix_check_timeskew(); + + if (readmail || redirect_mail || send == MSG_MAIL || send == MSG_POST) { + if (filename == NULL || streq(filename, "-")) + f = stdin; + else { + f = fopen(filename, "r"); + if (f == NULL) + fprintf(stderr, "Can't open %s.\n", filename); + } + + if (f && buf_read(msg, f) != -1) { + if (readmail == 1) { + check_get_pass(1, never_ask_for_passphrase); + mix_decrypt(msg); + } else if (readmail == 2) + pool_add(msg, "inf"); + if (send == MSG_MAIL || send == MSG_POST || redirect_mail) { + BUFFER *sendmsg; + int numdest = 0; + + sendmsg = buf_new(); + + while (buf_getheader(msg, field, content) == 0) { + if (bufieq(field, "nym")) { + strncpy(nym, content->data, sizeof(nym)); + } else if (bufieq(field, "chain")) + if (strchr(content->data, ';')) { + i = strchr(content->data, ';') - (char *)content->data; + strncpy(chain, content->data, i); + if (strstr(content->data + i, "copies=") != NULL) { + sscanf(strstr(content->data + i, "copies=") + + sizeof("copies=") - 1, "%d", &numcopies); + } + } else + strncpy(chain, content->data, sizeof(chain)); + else { /* line goes into message */ + if (((redirect_mail || send == MSG_MAIL) && bufieq(field, "to")) + || (send == MSG_POST && bufieq(field, "newsgroups"))) + numdest++; + if (bufieq(field, "from") && !redirect_mail) + fprintf(stderr, "Warning: The message has a From: line.\n"); + buf_appendheader(sendmsg, field, content); + } + } + buf_nl(sendmsg); + buf_rest(sendmsg, msg); + + while (buf_getline(attachments, field) != -1) + if (attachfile(sendmsg, field) == -1) { + errlog(ERRORMSG, "Can't attach %b!\n", field); + ret = 2; + goto end; + } + +#ifdef USE_PGP + if (nym[0] != 0 && strchr(nym, '@') == NULL) + strcatn(nym, "@", sizeof(nym)); + if (sign || encrypt) { + BUFFER *pass; + + pass = buf_new(); + user_pass(pass); + if (pgp_mailenc((encrypt ? PGP_ENCRYPT : 0) | + (nym[0] != 0 && sign ? PGP_SIGN : 0) | + PGP_TEXT | PGP_REMAIL, sendmsg, nym, + pass, NULL, NYMSECRING) != 0) { + fprintf(stderr, "Encryption failed: missing key!"); + ret = 2; + goto end; + } + buf_free(pass); + } + if (nym[0] != 0) { +#ifdef NYMSUPPORT + if (nym_encrypt(sendmsg, nym, send) == 0) + send = MSG_MAIL; + else +#endif /* NYMSUPPORT */ + fprintf(stderr, "Nym error, sending message anonymously.\n"); + } +#endif /* USE_PGP */ + if (numdest == 0) { + fprintf(stderr, "No destination address given!\n"); + ret = 2; + } else if (numcopies < 0 || numcopies > 10) { + fprintf(stderr, "Invalid number of copies!\n"); + ret = 2; + } else { + if ( ( redirect_mail ? + redirect_message(sendmsg, chain, numcopies, chainlist) : + mix_encrypt(send, sendmsg, chain, numcopies, chainlist) + ) == -1) { + ret = 2; + if (chainlist->length) + fprintf(stderr, "%s\n", chainlist->data); + else + fprintf(stderr, "Failed!\n"); + } else if (verbose) { + fprintf(stderr, "Chain: "); + buf_write(chainlist, stderr); + } + } + + buf_free(sendmsg); + } + if (filename != NULL) + fclose(f); + } else + ret = 2; + } + if (send == MSG_NULL) { + if (msg->length) { + while (buf_getheader(msg, field, content) == 0) { + if (bufieq(field, "chain")) + strncpy(chain, content->data, sizeof(chain)); + } + } + if (mix_encrypt(MSG_NULL, NULL, chain, numcopies, chainlist) == -1) { + ret = 2; + if (chainlist->length) + printf("%s\n", chainlist->data); + else + fprintf(stderr, "Failed!\n"); + } else if (verbose) { + printf("Chain: "); + buf_write(chainlist, stdout); + } + } +#ifdef USE_PGP +#ifdef NYMSUPPORT + if (nym[0] != 0) { + char nymserver[LINELEN] = "*"; + BUFFER *chains; + + chains = buf_new(); + if (numcopies < 1 || numcopies > 10) + numcopies = 1; + while (buf_getheader(msg, field, content) != -1) { + if (bufieq(field, "chain")) + strncpy(chain, content->data, sizeof(chain)); + else if (bufieq(field, "reply-chain")) + buf_appendf(chains, "Chain: %b\n", content); + else if (field->length) + buf_appendheader(chains, field, content); + else + buf_nl(chains); + } + if (strrchr(nym, '@')) { + strncpy(nymserver, strrchr(nym, '@'), sizeof(nymserver)); + *strrchr(nym, '@') = '\0'; + } + if (nym_config(NYM_CREATE, nym, nymserver, pseudonym, + chain, numcopies, chains, nymopt) < 0) { + ret = 2; + fprintf(stderr, "Failed!\n"); + } + user_delpass(); + buf_free(chains); + } +#endif /* NYMSUPPORT */ +#endif /* USE_PGP */ + + if (keygen) { + check_get_pass(0, never_ask_for_passphrase); + keymgt(keygen); + } + if (sendpool) + mix_send(); +#ifdef USE_SOCK + if (pop3) + pop3get(); +#endif /* USE_SOCK */ + if (maint) { + check_get_pass(1, never_ask_for_passphrase); + mix_regular(0); + } + +clientpool: + if ((REMAIL == 0) && (CLIENTAUTOFLUSH == 1)) { + SENDPOOLTIME = 0; + RATE = 100; + mix_send(); + }; + +end: + buf_free(field); + buf_free(content); + buf_free(chainlist); + buf_free(msg); + buf_free(nymopt); + buf_free(pseudonym); + buf_free(attachments); + buf_free(statssrc); + + if (daemon) { + check_get_pass(1, never_ask_for_passphrase); +#ifdef UNIX + if (! nodetach) { + int pid; + + fprintf(stderr, "Detaching.\n"); + /* Detach as suggested by the Unix Programming FAQ */ + pid = fork(); + if (pid > 0) + exit(0); + if (setsid() < 0) { + /* This should never happen. */ + fprintf(stderr, "setsid() failed.\n"); + exit(1); + }; + pid = fork(); + if (pid > 0) + exit(0); + }; + if (chdir(MIXDIR) < 0) { + if (chdir("/") < 0) { + fprintf(stderr, "Cannot chdir to mixdir or /.\n"); + exit(1); + }; + }; + if (write_pidfile(PIDFILE)) { + fprintf(stderr, "Aborting.\n"); + exit(1); + } + if (! nodetach) { + freopen ("/dev/null", "r", stdin); + freopen ("/dev/null", "w", stdout); + freopen ("/dev/null", "w", stderr); + } +#endif /* UNIX */ + mix_daemon(); +#ifdef UNIX +/* ifdef this one too, so that we do not need to export it from windows dll */ + clear_pidfile(PIDFILE); +#endif /* UNIX */ + } + mix_exit(); + return (ret); +} + +static char *largopt(char *p, char *opt, char *name, int *error) +{ + if (streq(p, opt)) { + fprintf(stderr, "%s: Missing argument for option --%s\n", name, p); + *error = 1; + } else if (strleft(p, opt) && p[strlen(opt)] == '=') { + return (p + strlen(opt) + 1); + } + return (NULL); +} + +static void noarg(char *name, char p) +{ + fprintf(stderr, "%s: Missing argument for option -%c\n", name, p); +} + +static int check_get_pass(int force, int never_ask_for_passphrase) +/* get a passphrase and check against keys + * if force != 0 passphrase must match with some key */ +{ + BUFFER *pass, *pass2, *key; + int n = 0; + + if (PASSPHRASE[0] == '\0' && isatty(fileno(stdin)) && ! never_ask_for_passphrase) { + pass = buf_new(); + pass2 = buf_new(); + key = buf_new(); + buf_sets(pass, PASSPHRASE); + while ( +#ifdef USE_PGP + pgpdb_getkey(PK_DECRYPT, PGP_ES_RSA, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, pass) < 0 && + pgpdb_getkey(PK_DECRYPT, PGP_E_ELG, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, pass) < 0 && +#endif /* USE_PGP */ + getv2seckey(NULL, key) < 0) + { + user_delpass(); + if (n) + fprintf(stderr, "re-"); + user_pass(pass); + strncpy(PASSPHRASE, pass->data, LINELEN); + PASSPHRASE[LINELEN-1] = 0; + if (!force) { + if (n && buf_eq(pass, pass2)) + break; + buf_set(pass2, pass); + } + n=1; + } + user_delpass(); + buf_free(pass); + buf_free(pass2); + buf_free(key); + + strncpy(ENTEREDPASSPHRASE, PASSPHRASE, LINELEN); + ENTEREDPASSPHRASE[LINELEN-1] = 0; + } + return 1; +} diff --git a/Src/menu.c b/Src/menu.c @@ -0,0 +1,1003 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Menu-based user interface + $Id: menu.c 934 2006-06-24 13:40:39Z rabbi $ */ + + +#include "menu.h" +#include "mix3.h" +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <fcntl.h> +#ifdef POSIX +#include <unistd.h> +#else /* end of POSIX */ +#include <io.h> +#endif /* else if not POSIX */ +#include <assert.h> + +void menu_folder(char command, char *foldername) +{ + mix_init(NULL); + if (foldername) + menu_init(); + read_folder(command, foldername, ANON); + menu_exit(); +} + +void read_folder(char command, char *foldername, char *nym) +{ +#ifdef USE_NCURSES + char path[PATHMAX] = "stdin", path_with_tilde[PATHMAX], l[LINELEN]; +#else /* end of USE_NCURSES */ + char path[PATHMAX] = "stdin", l[LINELEN]; +#endif /* else if not USE_NCURSES */ + char *h; + FILE *f; + BUFFER *folder; + BUFFER *line, *field, *content, *name; + BUFFER *index; + BUFFER *mail, *log; + int mailfolder = -1; /* -1 = unknown, 0 = no mailfolder, 1 = mailfolder */ + int num = 0; + long from = -1, subject = -1; + int folder_has_changed; +#ifdef USE_NCURSES + BUFFER *deleted_message; + BUFFER *new_folder; + BUFFER *new_index; + long length; + char sub[LINELEN], str[LINELEN], search[LINELEN] = ""; + long p; + int display, range, selected, i, redraw, c, q; + +#endif /* USE_NCURSES */ + int ispgp = 0, eof = 0; + folder_has_changed = 0; + + line = buf_new(); + field = buf_new(); + content = buf_new(); + index = buf_new(); + mail = buf_new(); + name = buf_new(); + folder = buf_new(); + log = buf_new(); + + if (foldername == NULL) + f = stdin; + else { + if (foldername[0] == '~' && (h = getenv("HOME")) != NULL) { + strncpy(path, h, PATHMAX); + strcatn(path, foldername + 1, PATHMAX); + } else + strncpy(path, foldername, PATHMAX); + f = fopen(path, "r"); + } + if (f == NULL) { +#ifdef USE_NCURSES + if (foldername) + beep(); +#endif /* USE_NCURSES */ + mix_status("Can't read %s.\n", path); + goto end; + } + for (;;) { + if (fgets(l, sizeof(l), f) == NULL) + eof = 1; + else if (mailfolder == -1) { + if (strleft(l, "From ")) + mailfolder = 1; + else if (strileft(l, "from:") || strileft(l, "path:") + || strileft(l, "xref:") || strileft(l, "return-path")) + mailfolder = 0; + else + break; + } + if (eof || (mailfolder && strleft(l, "From ")) || + (mailfolder == 0 && from != -1 && + (strileft(l, "path:") || + strileft(l, "xref:") || strileft(l,"return-path")))) { + if (num > 1) + mix_status("Reading message %d", num); +#ifdef USE_PGP + if (ispgp) +#ifdef NYMSUPPORT + switch (nym_decrypt(mail, NULL, log)) { + case 2: + from = -1, subject = -1; + while (buf_getline(mail, line) == 0) { + if (bufileft(line, "from:")) + from = folder->length + mail->ptr - line->length - 1; + if (bufileft(line, "subject:")) + subject = folder->length + mail->ptr - line->length - 1; + } + folder_has_changed = 1; + break; + case -1: + buf_clear(mail); + from = -1, subject = -1; + continue; + default: + ; + } +#else + if (!eof) { + buf_clear(mail); + from = -1, subject = -1; + continue; + } +#endif /* NYMSUPPORT */ +#endif /* USE_PGP */ + buf_cat(folder, mail); + buf_clear(mail); + ispgp = 0; + if (num > 0) { + buf_appendl(index, from); + buf_appendl(index, subject); + } + if (eof) + break; + + buf_appendl(index, folder->length); + from = subject = -1; + num++; + } + if (from == -1 && strileft(l, "from:")) + from = folder->length + mail->length; + + if (subject == -1 && strileft(l, "subject:")) + subject = folder->length + mail->length; + + buf_appends(mail, l); + if (strleft(l, begin_pgp)) + ispgp = 1; + } + + if (foldername) + fclose(f); + else { + dup2(open("/dev/tty", O_RDWR), fileno(stdin)); + menu_init(); + } + + mix_status(""); + if (folder->length == 0) { +#ifdef USE_NCURSES + clear(); + beep(); +#endif /* USE_NCURSES */ + mix_status("%s is empty.\n", path); + goto end; + } + if (mailfolder == -1) { +#ifdef USE_NCURSES + clear(); + beep(); +#endif /* USE_NCURSES */ + mix_status("%s is not a mail folder.\n", path); + goto end; + } +#ifndef USE_NCURSES + if (command == 0) { + buf_write(folder, stdout); + goto end; + } + if (num > 1) { + mix_status("Folder contains several messages."); + goto end; + } +#endif /* not USE_NCURSES */ + + if (num < 2) { + folder->ptr = 0; + mimedecode(folder); + + if (command != 0) + send_message(command, nym, folder); +#ifdef USE_NCURSES + else + read_message(folder, nym); + + clear(); +#endif /* USE_NCURSES */ + goto end; + } +#ifdef USE_NCURSES + display = selected = 0; + range = LINES - 3; + redraw = 2; + + for (;;) { + if (selected < 0) + selected = 0; + if (selected >= num) + selected = num - 1; + + if (selected < display) { + display = selected - range / 2; + redraw = 2; + } + if (selected >= display + range) { + display = selected - range / 2; + redraw = 2; + } + if (display >= num - 5) + display = num - 5; + if (display < 0) + display = 0; + + if (redraw) { + if (redraw == 2) { + clear(); + standout(); + mvprintw(0, 0, "Mixmaster %s", VERSION); + printw(" %.20s reading %.50s", nym, path); + standend(); + } + for (i = display; i < display + range; i++) { + if (i < num) { + index->ptr = 12 * i; + p = buf_getl(index); + buf_clear(name); + folder->ptr = buf_getl(index); + if (folder->ptr < 0) + folder->ptr = 0; + else { + buf_getheader(folder, field, line); + if (line->length) { + decode_header(line); + rfc822_name(line, name); + } + } + if (i == selected) + standout(); + + mvaddnstr(i - display + 2, 0, name->data, 18); + + sub[0] = '\0'; + folder->ptr = buf_getl(index); + if (folder->ptr < 0) + folder->ptr = 0; + else { + buf_getheader(folder, field, content); + if (content->length) { + decode_header(content); + strncpy(sub, content->data, sizeof(sub)); + } + } + if (sub[0] == '\0') + strcpy(sub, "(no subject)"); + mvaddnstr(i - display + 2, 20, sub, COLS - 21); + + if (i == selected) + standend(); + } + } + } + move(LINES - 1, COLS - 1); + refresh(); + redraw = 0; + + c = getch(); + switch (c) { + case '\014': + display = selected - range / 2; + redraw = 2; + break; + case 'q': + clear(); + goto end; + case '/': + echo(); + cl(LINES - 1, 0); + printw("Search: "); + refresh(); + wgetnstr(stdscr, str, LINELEN); + if (str[0] != '\0') + strncpy(search, str, LINELEN); + noecho(); + for (i = (selected < num ? selected + 1 : 0); i < num; i++) { + index->ptr = 12 * i + 4; + folder->ptr = buf_getl(index); + if (folder->ptr < 0) + folder->ptr = 0; + else { + buf_getheader(folder, field, line); + if (line->length) { + decode_header(line); + if (bufifind(line, search)) + break; + } + } + folder->ptr = buf_getl(index); + if (folder->ptr < 0) + folder->ptr = 0; + else { + buf_getheader(folder, field, line); + if (line->length) { + decode_header(line); + if (bufifind(line, search)) + break; + } + } + } + if (i < num) + selected = i; + else + beep(); + redraw = 1; + break; + case '\r': /* read message */ + case '\n': + case 'r': /* reply to message */ + case 'g': + case 'f': + case 'm': + case 'p': + case 's': + index->ptr = 12 * selected; + p = buf_getl(index); + if (selected < num - 1) { + index->ptr = 12 * (selected + 1); + q = buf_getl(index) - p; + } else + q = folder->length - p; + buf_clear(mail); + buf_append(mail, folder->data + p, q); + mimedecode(mail); + if(c == 's') + savemsg(mail); + else{ + if (c == '\r' || c == '\n') + read_message(mail, nym); + else + send_message(c, nym, mail); + } + redraw = 2; + break; + case 'd': /* delete message */ + /* Remove message from folder */ + if(num > 0){ + index->ptr = 12 * selected; + p = buf_getl(index); + if (selected < num - 1) { + index->ptr = 12 * (selected + 1); + q = buf_getl(index) - p; + } else + q = folder->length - p; + deleted_message = buf_new(); + new_folder = buf_new(); + buf_cut_out(folder, deleted_message, new_folder, p, q); + buf_free(deleted_message); + buf_free(folder); + folder = new_folder; + /* Update index file */ + new_index = buf_new(); + index->ptr = 0; + if(selected > 0) + buf_get(index, new_index, 12 * selected); + index->ptr = 12 * (selected + 1); + while((from = buf_getl(index)) != -1){ + subject = buf_getl(index); + length = buf_getl(index); + buf_appendl(new_index, from - q); + buf_appendl(new_index, subject - q); + buf_appendl(new_index, length - q); + } + buf_free(index); + index = new_index; + /* Done */ + num--; + folder_has_changed = 1; + } + redraw = 2; + break; + case KEY_UP: + selected--; + redraw = 1; + break; + case KEY_DOWN: + case 'n': /* nym ???? */ + selected++; + redraw = 1; + break; + case KEY_PPAGE: + selected -= range; + redraw = 1; + break; + case KEY_NPAGE: + selected += range; + redraw = 1; + break; + default: + beep(); + } + } +#endif /* USE_NCURSES */ + +end: +#ifdef USE_NCURSES + /* If folder has changed, ask user about saving new folder. */ + if (folder_has_changed && !streq(path, "stdin")) { + mvprintw(LINES - 2, 0, "Buffer has changed. Save [y/n]? "); + c = getch(); + cl(LINES - 2, 0); + if ((c == 'y') || (c == 'Y')){ + strncpy(path_with_tilde, path, PATHMAX-1); + strcat(path_with_tilde, "~"); + rename(path, path_with_tilde); /* Rename folder to folder~ */ + f = fopen(path, "w"); /* Write new folder */ + if (f == NULL) + mix_status("Can't write to %s.", path); + else{ + buf_write(folder, f); + mix_status("Wrote %s.", path); + fclose(f); + } + } + else{ + mix_status("%s was not saved.", path); + } + } +#endif /* USE_NCURSES */ + buf_free(folder); + buf_free(line); + buf_free(field); + buf_free(content); + buf_free(index); + buf_free(mail); + buf_free(name); + buf_free(log); +} + +#ifdef USE_NCURSES +static int sortrel(const void *a, const void *b) +{ + int na, ra, nb, rb; + + na = *(int *) a; + nb = *(int *) b; + + ra = *((int *) a + 1); + rb = *((int *) b + 1); + return rb - ra; +} + +void menu_main(void) +{ + int y, x; + int pool, n; + int c; + int space; + BUFFER *chainlist, *line; + char nym[LINELEN] = ANON; + + chainlist = buf_new(); + line = buf_new(); + + mix_init(NULL); + menu_init(); + +menu_redraw: + clear(); + for (;;) { + standout(); + mvprintw(0, 0, "Mixmaster %s", VERSION); +#if 0 + mvprintw(0, COLS - sizeof(COPYRIGHT), COPYRIGHT); +#endif + for (space = 0; space < (COLS - 10 - sizeof(VERSION)); space++) { + printw(" "); + }; + standend(); + mix_status(NULL); + +#ifdef NYMSUPPORT + mvprintw(8, 4, "n)ym: %s", nym); +#endif /* NYMSUPPORT */ + y = 12, x = 25; + mvprintw(y++, x, "m)ail"); + mvprintw(y++, x, "p)ost to Usenet"); + mvprintw(y++, x, "r)ead mail (or news article)"); + mvprintw(y++, x, "d)ummy message"); + mvprintw(y++, x, "s)end messages from pool"); + mvprintw(y++, x, "e)dit configuration file"); + mvprintw(y++, x, "u)pdate stats"); + mvprintw(y++, x, "q)uit"); + + pool = pool_read(NULL); + if (pool == 1) + mvprintw(4, 2, "%3d outgoing message in the pool. \n", pool); + else + mvprintw(4, 2, "%3d outgoing messages in the pool.\n", pool); + + move(LINES - 1, COLS - 1); + refresh(); + c = getch(); + if (c != ERR) { + mix_status(""); + switch (c) { + case '\014': + mix_status(""); + goto menu_redraw; +#ifdef NYMSUPPORT + case 'n': + menu_nym(nym); + goto menu_redraw; +#endif /* NYMSUPPORT */ + case 'm': + case 'p': + send_message(c, nym, NULL); + break; + case 'd': + mix_status("Creating message..."); + if (mix_encrypt(MSG_NULL, NULL, NULL, 1, chainlist) != 0) { + if (chainlist->length > 0) + mix_status("%s", chainlist->data); + else + mix_genericerror(); + beep(); + } else { + for (n = 0; buf_getline(chainlist, line) == 0; n++) ; + if (n > 1) + mix_status("Done (%d packets).", n); + else + mix_status("Chain: %s", chainlist->data); + } + break; + case 's': + mix_status("Mailing messages..."); + mix_send(); + mix_status("Done."); + break; + case 'r': + { + char name[LINELEN]; + + cl(LINES - 3, 0); + if (getenv("MAIL")) + printw("File name [%s]: ", getenv("MAIL")); + else + printw("File name: "); + echo(); + wgetnstr(stdscr, name, LINELEN); + noecho(); + cl(LINES - 3, 0); + if (name[0] == '\0') { + if (getenv("MAIL")) + read_folder(0, getenv("MAIL"), nym); + } else + read_folder(0, name, nym); + } + break; + case 'e': + do { + char path[PATHMAX]; + mixfile(path, MIXCONF); + menu_spawn_editor(path, 0); + mix_config(); + } while (0); + break; + case 'u': + update_stats(); + break; + case 'q': + case 'Q': + goto quit; + default: + beep(); + } + } + } + +quit: + menu_exit(); + buf_free(chainlist); + buf_free(line); +} + +void read_message(BUFFER *message, char *nym) +{ + int l = 0; + int c; + int inhdr = 1, txtbegin; + BUFFER *field, *content, *line, *hdr; + char sub[LINELEN] = "mail"; + char thisnym[LINELEN] = ""; + + field = buf_new(); + content = buf_new(); + line = buf_new(); + hdr = buf_new(); + + if (thisnym[0] == '\0') + strncpy(thisnym, nym, LINELEN); + + if (bufleft(message, "From nymserver ")) { + /* select nym if Nym: pseudo header is in the first line */ + buf_getline(message, line); + buf_getheader(message, field, content); + if (bufieq(field, "Nym")) + strncpy(thisnym, content->data, sizeof(thisnym)); + buf_rewind(message); + } + while (buf_getheader(message, field, content) == 0) { + if (bufieq(field, "received") || bufleft(field, "From ")) + continue; + if (bufieq(field, "subject")) + strncpy(sub, content->data, sizeof(sub)); + buf_appendheader(hdr, field, content); + } + if (strlen(sub) > COLS - strlen(VERSION) - strlen(thisnym) - 23) + sub[COLS - strlen(VERSION) - strlen(thisnym) - 24] = '\0'; + txtbegin = message->ptr; + +loop: + clear(); + standout(); + mvprintw(0, 0, "Mixmaster %s", VERSION); + printw(" %.20s reading %.50s\n", thisnym, sub); + standend(); + mix_status(NULL); + + while (l < LINES - 2) { + if (inhdr) { + if (buf_getline(hdr, line) == -1) + buf_clear(line), inhdr = 0; + } else { + if (buf_getline(message, line) == -1) { + standout(); + mvprintw(LINES - 1, 0, "Command"); + standend(); + refresh(); + for (;;) { + c = getch(); + switch (c) { + case 'm': + case 'p': + case 'r': + case 'g': + case 'f': + send_message(c, thisnym, message); + goto end; + case 'u': + inhdr = 0; + message->ptr = txtbegin; + l = 0; + goto loop; + case 'h': + inhdr = 1; + hdr->ptr = 0; + message->ptr = txtbegin; + l = 0; + goto loop; + case 's': + savemsg(message); + /* fallthru */ + case 'q': + case 'i': + case '\n': + case '\r': + goto end; + case '\014': + refresh(); + continue; + default: + beep(); + refresh(); + } + } + } + } + mvprintw(l + 1, 0, "%s\n", line->data); + l += (line->length - 1) / COLS + 1; + } + standout(); + mvprintw(LINES - 1, 0, "MORE"); + standend(); + refresh(); + for (;;) { + c = getch(); + switch (c) { + case 'm': + case 'p': + case 'r': + case 'g': + case 'f': + send_message(c, thisnym, message); + goto end; + case 'u': + inhdr = 0; + message->ptr = txtbegin; + l = 0; + goto loop; + case 'h': + inhdr = 1; /* show full header */ + hdr->ptr = 0; + message->ptr = txtbegin; + l = 0; + goto loop; + case 's': + savemsg(message); + /* fallthru */ + case 'q': + case 'i': + goto end; + case ' ': + case '\n': + case '\r': + l = 0; + goto loop; + case '\014': + refresh(); + continue; + default: + beep(); + refresh(); + } + } +end: + buf_free(field); + buf_free(content); + buf_free(line); + buf_free(hdr); +} + +int menu_replychain(int *d, int *l, char *mdest, char *pdest, char *psub, + char *r) +{ + int c; + char line[LINELEN]; + char reliability[9]; + +redraw: + clear(); + standout(); + printw("Create a nym reply block:"); + standend(); + mix_status(NULL); + + mvprintw(3, 0, "Type of reply block:\n"); + if (*d == MSG_MAIL) + standout(); + printw(" m)ail "); + if (*d == MSG_MAIL) + standend(); + + if (*d == MSG_POST) + standout(); + printw(" Usenet message p)ool "); + if (*d == MSG_POST) + standend(); + + if (*d == MSG_NULL) + standout(); + printw(" cover t)raffic "); + if (*d == MSG_NULL) + standend(); + + if (*d != MSG_NULL) + mvprintw(6, 0, "d)estination: %s", *d == MSG_MAIL ? mdest : pdest); + if (psub && *d == MSG_POST) + mvprintw(7, 0, "s)ubject: %s", psub); + chain_reliability(r, 1, reliability); /* chaintype 1=ek */ + mvprintw(8, 0, "c)hain: %-39s (reliability: %s)", r, reliability); + mvprintw(10, 0, "l)atency: %d hours", *l); + move(LINES - 1, COLS - 1); + + for (;;) { + refresh(); + c = getch(); + switch (c) { + case 'm': + *d = MSG_MAIL; + goto redraw; + case 'p': + *d = MSG_POST; + goto redraw; + case 't': + *d = MSG_NULL; + goto redraw; + case 'q': + return (-1); + case 'd': + cl(6, 0); + printw("d)estination: "); + echo(); + wgetnstr(stdscr, *d == MSG_MAIL ? mdest : pdest, LINELEN); + noecho(); + goto redraw; + case 'l': + cl(10, 0); + printw("l)atency: "); + echo(); + wgetnstr(stdscr, line, LINELEN); + *l = strtol(line, NULL, 10); + if (*l < 0) + *l = 0; + noecho(); + goto redraw; + case 'c': + menu_chain(r, 1, *d == MSG_POST); + goto redraw; + case '\014': + goto redraw; + case '\n': + case '\r': + return (0); + case 's': + if (*d == MSG_POST) { + cl(7, 0); + printw("s)ubject: "); + echo(); + wgetnstr(stdscr, psub, LINELEN); + noecho(); + goto redraw; + } + default: + beep(); + } + } +} + +void menu_chain(char *chainstr, int chaintype, int post) + /* chaintype 0=mix 1=ek 2=newnym */ +{ + REMAILER remailer[MAXREM]; + int badchains[MAXREM][MAXREM]; + int rlist[2 * MAXREM]; + char newchain[CHAINMAX]; + char info[LINELEN]; + int num = 0, i, middlemanlast=0, ok = 1; + int c, x, y; + int nymserv = 0; + int chain[20], chainlen = 0; + char reliability[9]; + + if (chaintype == 2) + nymserv = 1, chaintype = 1; + assert(chaintype == 0 || chaintype == 1); + + clear(); + standout(); + if (nymserv) + printw("Select nym server:\n\n"); + else + printw("Select remailer chain:\n\n"); + standend(); + + if (chaintype == 1) + num = t1_rlist(remailer, badchains); + else + num = mix2_rlist(remailer, badchains); + + if (num < 1) { + mix_status("Can't read remailer list."); + beep(); + return; + } + for (i = 0; i < num; i++) { + rlist[2 * i] = i + 1; + rlist[2 * i + 1] = remailer[i + 1].info[chaintype].reliability - + remailer[i + 1].info[chaintype].latency / 3600; + if (remailer[i + 1].info[chaintype].history[0] == '\0') + rlist[2 * i + 1] = -1; + if ((nymserv && !remailer[i + 1].flags.newnym) || + ((chaintype == 0 && !remailer[i + 1].flags.mix) || + (chaintype == 1 && !nymserv && (!remailer[i + 1].flags.ek + || !remailer[i + 1].flags.pgp)))) + rlist[2 * i] = 0, rlist[2 * i + 1] = -2; + } + qsort(rlist, num - 1, 2 * sizeof(int), sortrel); + + for (i = 0; i < num; i++) + if (rlist[2 * i + 1] == -2) { + num = i; + break; + } + if (num < 1) { + mix_status("No remailers found!"); + return; + } + if (num > 2 * (LINES - 6)) + num = 2 * (LINES - 6); + if (num > 2 * 26) + num = 2 * 26; + + for (i = 0; i < num && rlist[2 * i + 1] > -2; i++) { + y = i, x = 0; + if (y >= LINES - 6) + y -= LINES - 6, x += 40; + mvprintw(y + 2, x, "%c", i < 26 ? i + 'a' : i - 26 + 'A'); + mvprintw(y + 2, x + 2, "%s", remailer[rlist[2 * i]].name); + mvprintw(y + 2, x + 16, "%s", + remailer[rlist[2 * i]].info[chaintype].history); + sprintf(info, "%3.2f", + remailer[rlist[2 * i]].info[chaintype].reliability / 100.0); + mvprintw(y + 2, x + 29 + 6 - strlen(info), "%s%%", info); + } + y = num + 3; + if (y > LINES - 4) + y = LINES - 4; + mvprintw(y, 0, "* select at random"); + + for (;;) { + newchain[0] = '\0'; + for (i = 0; i < chainlen; i++) { + if (i) + strcatn(newchain, ",", CHAINMAX); + if (chain[i]) + strcatn(newchain, remailer[chain[i]].name, CHAINMAX); + else + strcatn(newchain, "*", CHAINMAX); + } + if (chainlen > 0) { + ok = 1; + middlemanlast = remailer[chain[chainlen - 1]].flags.middle; + if (post && !remailer[chain[chainlen - 1]].flags.post && !(chain[chainlen - 1] == 0 /*randhop*/)) + ok = 0; + } else + ok = 1; + + mvprintw(LINES - 4, 40, + middlemanlast ? + "MIDDLEMAN " : + (ok ? + " " : + "NO POSTING ")); + cl(LINES - 3, 0); + cl(LINES - 2, 0); + cl(LINES - 1, 0); + if(!nymserv){ + chain_reliability(newchain, chaintype, reliability); + mvprintw(LINES - 4, 58, "(reliability: %s)", reliability); + } + mvprintw(LINES - 3, 0, nymserv ? "Nym server: %s" : "Chain: %s", + newchain); + refresh(); + c = getch(); + if (c == '\n' || c == '\r') { + /* beep and sleep in case the user made a mistake */ + if (middlemanlast) { + beep(); + sleep(2); + } + if (ok || middlemanlast) + break; + else + beep(); + } else if (c == '*') { + if (chainlen > 19 || (nymserv && chainlen > 0)) + beep(); + else + chain[chainlen++] = 0; + } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + if (c >= 'a') + c -= 'a'; + else + c = c - 'A' + 26; + + if (chainlen > 19 || (nymserv && chainlen > 0) || c >= num) + beep(); + else + chain[chainlen++] = rlist[2 * c]; + } else if (c == killchar()) + chainlen = 0; + else if ((c == KEY_BACKSPACE || c == KEY_LEFT || c == erasechar()) + && chainlen > 0) + --chainlen; + else + beep(); + } + if (chainlen) + strncpy(chainstr, newchain, CHAINMAX); +} + +#endif /* USE_NCURSES */ diff --git a/Src/menu.h b/Src/menu.h @@ -0,0 +1,48 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Menu-based user interface + $Id: menu.h 934 2006-06-24 13:40:39Z rabbi $ */ + + +#ifndef _MENU_H +#define _MENU_H +#include "mix3.h" +#ifdef USE_NCURSES +#ifdef HAVE_NCURSES_H +#include <ncurses.h> +#else /* end of HAVE_NCURSES_H */ +#include <curses.h> +#endif /* else if not HAVE_NCURSES_H */ +#endif /* USE_NCURSES */ + +#define NONANON "non-anonymous" +#define ANON "Anonymous" + +void send_message(int type, char *nym, BUFFER *txt); +void read_folder(char command, char *foldername, char *nym); +void menu_init(void); +void menu_exit(void); + +void menu_spawn_editor(char *path, int lineno); + +#ifdef USE_NCURSES +void read_message(BUFFER *message, char *nym); +void menu_nym(char *); +void menu_chain(char *chain, int type, int post); +void cl(int y, int x); +void askfilename(char *fn); +void savemsg(BUFFER *message); +int menu_replychain(int *d, int *l, char *mdest, char *pdest, char *psub, + char *r); +void update_stats(void); + +#endif /* USE_NCURSES */ + +#define maxnym 30 + +#endif /* not _MENU_H */ diff --git a/Src/menunym.c b/Src/menunym.c @@ -0,0 +1,472 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Menu-based user interface - nym management + $Id: menunym.c 934 2006-06-24 13:40:39Z rabbi $ */ + +#ifdef NYMSUPPORT + +#include "menu.h" +#include "mix3.h" +#include <string.h> +#include <stdlib.h> +#ifdef POSIX +#include <unistd.h> +#endif /* POSIX */ + +#ifdef USE_NCURSES +void menu_nym(char *nnym) +{ + char nym[maxnym][LINELEN]; + char pending[maxnym][LINELEN]; + int c, i, num = 0, numpending = 0, select = -1; + int edit = 0; + BUFFER *nymlist; + int s; + int pass = 0; + char reliability[9]; /* When printing information about a chain, + this variable stores the reliability. */ + + nymlist = buf_new(); + + strcpy(nym[0], NONANON); + strcatn(nym[0], " (", sizeof(nym[0])); + strcatn(nym[0], NAME, sizeof(nym[0])); + strcatn(nym[0], ")", sizeof(nym[0])); + + strcpy(nym[1], ANON); + num = 2; + if (nymlist_read(nymlist) == -1) { + user_delpass(); + mix_status(""); + } else + pass = 1; + while (nymlist_get(nymlist, nym[num], NULL, NULL, NULL, NULL, NULL, &s) >= 0) { + if (s == NYM_OK) { + if (num < maxnym) + num++; + } else if (s == NYM_WAITING) { + if (numpending < maxnym) + strncpy(pending[numpending++], nym[num], LINELEN); + } + } + buf_free(nymlist); + +nymselect: + clear(); + standout(); + printw("Select nym:\n\n"); + standend(); +#ifdef USE_PGP + if (pass) + printw("c)reate new nym\ne)dit nym\nd)elete nym\n\n"); + else + printw("[nym passphrase is invalid]\n\n"); +#endif /* USE_PGP */ + for (i = 0; i < num; i++) + printw("%d) %s\n", i, nym[i]); + if (numpending > 0) { + printw("\n\nWaiting for confirmation: "); + for (i = 0; i < numpending; i++) + printw("%s ", pending[i]); + printw("\n"); + } +select: + if (select != -1) + printw("\r%d", select); + else + printw("\r \r"); + refresh(); + c = getch(); + if (c == erasechar()) + c = KEY_BACKSPACE; + if (c >= '0' && c <= '9') { + if (select == -1) + select = c - '0'; + else + select = 10 * select + c - '0'; + if (edit ? select == 0 || select >= num + numpending - 1 : select >= num) { + beep(); + select = -1; + } + refresh(); + goto select; + } else + switch (c) { + case KEY_BACKSPACE: + select /= 10; + if (select < 1) + select = -1; + goto select; + case 'q': + if (edit) { + edit = 0; + select = -1; + goto nymselect; + } + break; +#ifdef USE_PGP + case 'e': + if (pass) { + if (edit || num + numpending < 3) { + edit = 0; + select = -1; + goto nymselect; + } else { + clear(); + standout(); + printw("Edit nym:\n\n"); + standend(); + for (i = 2; i < num + numpending; i++) + printw("%d) %s\n", i - 1, i < num ? nym[i] : pending[i - num]); + printw("\n"); + select = -1; + edit = NYM_MODIFY; + goto select; + } + } + break; + case 'd': + if (pass) { + if (edit || num + numpending < 3) { + edit = 0; + select = -1; + goto nymselect; + } else { + clear(); + standout(); + printw("Delete nym:\n\n"); + standend(); + for (i = 2; i < num + numpending; i++) + printw("%d) %s\n", i - 1, i < num ? nym[i] : pending[i - num]); + printw("\n"); + select = -1; + edit = NYM_DELETE; + goto select; + } + } + break; + case '\r': + case '\n': + if (select == -1 || (edit && select == 0)) { + beep(); + edit = 0; + select = -1; + goto nymselect; + } + if (!edit) { + strncpy(nnym, nym[select], LINELEN); + return; + } + /* fallthru */ + case 'c': + if (pass) { + char nymserv[LINELEN] = "*"; + char replyblock[5][CHAINMAX], dest[10][LINELEN]; + int latent[5], desttype[5]; + char mdest[LINELEN], pdest[LINELEN] = "alt.anonymous.messages", + psub[LINELEN] = ""; + int deflatent = 0, defdesttype = MSG_MAIL; + char alias[LINELEN] = ""; + BUFFER *name, *opt; + char sendchain[CHAINMAX]; + int sendnumcopies = 1, rnum = 1; + int i; + char line[LINELEN]; + int acksend = 0, signsend = 0, fixedsize = 0, disable = 0, + fingerkey = 1; + + name = buf_new(); + opt = buf_new(); + strncpy(sendchain, CHAIN, CHAINMAX); + strncpy(mdest, ADDRESS, LINELEN); + if (edit) + strncpy(alias, select + 1 < num ? nym[select + 1] : + pending[select + 1 - num], LINELEN); + if (edit == NYM_MODIFY) { + nymlist_getnym(alias, NULL, NULL, opt, name, NULL); + acksend = bufifind(opt, "+acksend"); + signsend = bufifind(opt, "+signsend"); + fixedsize = bufifind(opt, "+fixedsize"); + disable = bufifind(opt, "+disable"); + fingerkey = bufifind(opt, "+fingerkey"); + rnum = -1; + } + newnym: + if (!edit) { + clear(); + standout(); + printw("Create a nym:"); + standend(); + + mvprintw(3, 0, "Alias address: "); + echo(); + wgetnstr(stdscr, alias, LINELEN); + noecho(); + if (alias[0] == '\0') + goto end; + for (i = 0; alias[i] > ' ' && alias[i] != '@'; i++) ; + alias[i] = '\0'; + if (i == 0) + goto newnym; + mvprintw(4, 0, "Pseudonym: "); + echo(); + wgetnstr(stdscr, line, LINELEN); + noecho(); + buf_sets(name, line); + menu_chain(nymserv, 2, 0); + } + if (edit != NYM_DELETE) { + for (i = 0; i < 5; i++) { + desttype[i] = defdesttype; + latent[i] = deflatent; + dest[i][0] = '\0'; + strcpy(replyblock[i], "*,*,*,*"); + } + if (rnum != -1) { + menu_replychain(&defdesttype, &deflatent, mdest, pdest, psub, + replyblock[0]); + desttype[0] = defdesttype; + latent[0] = deflatent; + strncpy(dest[0], desttype[0] == MSG_POST ? pdest : mdest, + LINELEN); + } + } + redraw: + clear(); + standout(); + switch (edit) { + case NYM_DELETE: + printw("Delete nym:"); + break; + case NYM_MODIFY: + printw("Edit nym:"); + break; + default: + printw("Create a nym:"); + break; + } + standend(); + loop: + { + if (!edit) { + cl(2, 0); + printw("Nym: a)lias address: %s", alias); + cl(3, 0); + printw(" nym s)erver: %s", nymserv); + } + if (edit != NYM_DELETE) { + cl(4, 0); + printw(" p)seudonym: %s", name->data); + if (edit) + mvprintw(6, 0, "Nym modification:"); + else + mvprintw(6, 0, "Nym creation:"); + } + cl(7, 0); + chain_reliability(sendchain, 0, reliability); /* chaintype 0=mix */ + printw(" c)hain to nym server: %-30s (reliability: %s)", sendchain, reliability); + cl(8, 0); + printw(" n)umber of redundant copies: %d", sendnumcopies); + if (edit != NYM_DELETE) { + mvprintw(10, 0, "Configuration:\n"); + printw(" A)cknowledge sending: %s\n", acksend ? "yes" : "no"); + printw(" S)erver signatures: %s\n", signsend ? "yes" : "no"); + printw(" F)ixed size replies: %s\n", fixedsize ? "yes" : + "no"); + printw(" D)isable: %s\n", disable ? "yes" : "no"); + printw(" Finger K)ey: %s\n", fingerkey ? "yes" : "no"); + mvprintw(17, 0, "Reply chains:"); + cl(18, 0); + if (rnum == -1) + printw(" create new r)eply block"); + else { + printw(" number of r)eply chains: %2d reliability", rnum); + for (i = 0; i < rnum; i++) { + cl(i + 19, 0); + chain_reliability(replyblock[i], 1, reliability); /* 1=ek */ + printw(" %d) %30s %-31s [%s]", i + 1, + desttype[i] == MSG_NULL ? + "(cover traffic)" : dest[i], replyblock[i], + reliability); + } + } + } + move(LINES - 1, COLS - 1); + refresh(); + c = getch(); + if (edit != NYM_DELETE && c >= '1' && c <= '9' && c - '1' < rnum) { + menu_replychain(&defdesttype, &deflatent, mdest, pdest, psub, + replyblock[c - '1']); + desttype[c - '1'] = defdesttype; + latent[c - '1'] = deflatent; + strncpy(dest[c - '1'], + desttype[c - '1'] == MSG_POST ? pdest : mdest, LINELEN); + goto redraw; + } + switch (c) { + case 'A': + acksend = !acksend; + goto redraw; + case 'S': + signsend = !signsend; + goto redraw; + case 'F': + fixedsize = !fixedsize; + goto redraw; + case 'D': + disable = !disable; + goto redraw; + case 'K': + fingerkey = !fingerkey; + goto redraw; + case 'q': + edit = 0; + select = -1; + goto nymselect; + case '\014': + goto redraw; + case 'a': + cl(2, 0); + printw("Nym: a)lias address: "); + echo(); + wgetnstr(stdscr, alias, LINELEN); + noecho(); + for (i = 0; alias[i] > ' ' && alias[i] != '@'; i++) ; + alias[i] = '\0'; + if (i == 0) + goto nymselect; + goto redraw; + case 'p': + cl(4, 0); + printw(" p)seudonym: "); + echo(); + wgetnstr(stdscr, line, LINELEN); + noecho(); + if (line[0] != '\0') + buf_sets(name, line); + goto redraw; + case 'c': + menu_chain(sendchain, 0, 0); + goto redraw; + case 'n': + cl(8, 0); + printw(" n)umber of redundant copies: "); + echo(); + wgetnstr(stdscr, line, LINELEN); + noecho(); + sendnumcopies = strtol(line, NULL, 10); + if (sendnumcopies < 1 || sendnumcopies > 10) + sendnumcopies = 1; + goto redraw; + case 'r': + cl(18, 0); + printw(" number of r)eply chains: "); + echo(); + wgetnstr(stdscr, line, LINELEN); + noecho(); + i = rnum; + rnum = strtol(line, NULL, 10); + if (rnum < 1) + rnum = 1; + if (rnum > 5) + rnum = 5; + for (; i < rnum; i++) + if (dest[i][0] == '\0') { + desttype[i] = defdesttype; + latent[i] = deflatent; + strncpy(dest[i], defdesttype == MSG_POST ? pdest : + mdest, LINELEN); + } + goto redraw; + case 's': + menu_chain(nymserv, 2, 0); + goto redraw; + case '\n': + case '\r': + { + BUFFER *chains; + int err; + + if (rnum == -1) + chains = NULL; + else { + chains = buf_new(); + for (i = 0; i < rnum; i++) + if (replyblock[i][0] != '\0') { + if (desttype[i] == MSG_POST) + buf_appendf(chains, "Subject: %s\n", psub); + if (desttype[i] == MSG_MAIL) + buf_appends(chains, "To: "); + else if (desttype[i] == MSG_POST) + buf_appends(chains, "Newsgroups: "); + else + buf_appends(chains, "Null:"); + buf_appendf(chains, "%s\n", dest[i]); + buf_appendf(chains, "Chain: %s\n", replyblock[i]); + buf_appendf(chains, "Latency: %d\n\n", latent[i]); + } + } + create: + clear(); + buf_setf(opt, + " %cacksend %csignsend +cryptrecv %cfixedsize %cdisable %cfingerkey", + acksend ? '+' : '-', + signsend ? '+' : '-', + fixedsize ? '+' : '-', + disable ? '+' : '-', + fingerkey ? '+' : '-'); + if (edit) { + mix_status("Preparing nymserver configuration message..."); + err = nym_config(edit, alias, NULL, + name, sendchain, sendnumcopies, + chains, opt); + } else { + mix_status("Preparing nym creation request..."); + err = nym_config(edit, alias, nymserv, name, + sendchain, sendnumcopies, chains, + opt); + } + if (err == -3) { + beep(); + mix_status("Bad passphrase!"); + getch(); + goto create; + } + if (err != 0) { + mix_genericerror(); + beep(); + refresh(); + } else { + if (edit) + mix_status("Nymserver configuration message completed."); + else + mix_status("Nym creation request completed."); + } + if (chains) + buf_free(chains); + goto end; + } + default: + beep(); + goto loop; + } + } + end: + buf_free(name); + buf_free(opt); + return; + } +#endif /* USE_PGP */ + default: + beep(); + goto select; + } +} + +#endif /* USE_NCURSES */ +#endif /* NYMSUPPORT */ diff --git a/Src/menusend.c b/Src/menusend.c @@ -0,0 +1,556 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Menu-based user interface -- send message + $Id: menusend.c 934 2006-06-24 13:40:39Z rabbi $ */ + + +#include "menu.h" +#include "mix3.h" +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#ifdef POSIX +#include <unistd.h> +#else /* end of POSIX */ +#include <io.h> +#endif /* else if not POSIX */ + +void send_message(int type, char *nym, BUFFER *in) +{ + char dest[LINELEN] = "", subject[LINELEN] = ""; + char chain[CHAINMAX], thisnym[LINELEN], path[PATHMAX]; + BUFFER *chainlist, *msg, *txt, *tmp, *field, *content, *cc, *cite; + int numcopies; + int hdr = 0; /* txt buffer contains header lines */ + FILE *f; + int n, err; + +#ifdef USE_PGP + int sign = 0, encrypt = 0, key = 0; + +#endif /* USE_PGP */ +#ifdef USE_NCURSES + char reliability[9]; + int c; + char line[LINELEN]; + +#endif /* USE_NCURSES */ + msg = buf_new(); + tmp = buf_new(); + txt = buf_new(); + field = buf_new(); + content = buf_new(); + chainlist = buf_new(); + cc = buf_new(); + cite = buf_new(); + strncpy(chain, CHAIN, CHAINMAX); + numcopies = NUMCOPIES; + + mix_status(""); + strncpy(thisnym, nym, sizeof(thisnym)); + + if (in != NULL) + buf_set(txt, in); + + if (bufileft(txt, "From ")) + buf_getline(txt, field); /* ignore envelope From */ + + if (type == 'p' || type == 'm') { +#ifndef USE_NCURSES + mix_status("Invalid option to -f"); + mix_exit(); + exit(1); +#else /* end of not USE_NCURSES */ + clear(); + echo(); + if (in != NULL) + mvprintw(1, 0, "%s forwarding message...", thisnym); + if (type == 'p') + mvprintw(3, 0, "Newsgroups: "); + else + mvprintw(3, 0, "Send message to: "); + refresh(); + wgetnstr(stdscr, dest, LINELEN); + if (dest[0] == '\0') { + noecho(); + cl(3, 0); + goto quit; + } + if (txt->length == 0) { + mvprintw(4, 0, "Subject: "); + refresh(); + wgetnstr(stdscr, subject, LINELEN); + } else { + strcpy(subject, "Forwarded message"); + while (buf_getheader(txt, field, content) == 0) { + if (bufieq(field, "subject")) { + strncpy(subject, content->data, sizeof(subject)); + strcatn(subject, " (fwd)", sizeof(subject)); + } + if (bufieq(field, "from") || bufieq(field, "subject") || + bufieq(field, "date")) + buf_appendheader(tmp, field, content); + } + buf_nl(tmp); + buf_rest(tmp, txt); + buf_move(txt, tmp); + } + noecho(); +#endif /* else if USE_NCURSES */ + } else { + strcpy(subject, "Re: your mail"); + while (buf_getheader(txt, field, content) == 0) { + if (bufieq(field, "subject")) { + if (bufileft(content, "Re:")) + subject[0] = '\0'; + else + strcpy(subject, "Re: "); + strcatn(subject, content->data, sizeof(subject)); + } + if (bufieq(field, "from")) + buf_set(cite, content); + if (type == 'p' || type == 'f') { + if (dest[0] == '\0' && bufieq(field, "newsgroups")) + strncpy(dest, content->data, sizeof(dest)); + if (bufieq(field, "followup-to") && !bufieq(content, "poster")) + strncpy(dest, content->data, sizeof(dest)); + if (bufieq(field, "message-id")) + buf_appendf(tmp, "References: %b\n", content); + } else { + if (dest[0] == '\0' && bufieq(field, "from")) + strncpy(dest, content->data, sizeof(dest)); + if (bufieq(field, "reply-to")) + strncpy(dest, content->data, sizeof(dest)); + if (type == 'g' && (bufieq(field, "to") || bufieq(field, "cc"))) { + if (cc->length) + buf_appends(cc, ", "); + buf_cat(cc, content); + } + if (bufieq(field, "message-id")) + buf_appendf(tmp, "In-Reply-To: %b\n", content); + } + } + if (cc->length) + buf_appendf(tmp, "Cc: %b\n", cc); + if (tmp->length > 0) + hdr = 1; + if (hdr) + buf_nl(tmp); + + if ((type == 'f' || type == 'g') && cite->length) { + buf_appendf(tmp, "%b wrote:\n\n", cite); + } + if (type == 'r') + buf_appends(tmp, "You wrote:\n\n"); + + while (buf_getline(txt, content) != -1) + buf_appendf(tmp, "> %b\n", content); + buf_set(txt, tmp); + if (dest[0] == '\0') { +#ifdef USE_NCURSES + beep(); + mix_status("No recipient address found."); +#endif /* USE_NCURSES */ + goto quit; + } + goto edit; + } + +#ifdef USE_NCURSES +redraw: + clear(); + + for (;;) { + standout(); + mvprintw(0, 0, "Mixmaster %s - ", VERSION); + printw(type == 'p' || type == 'f' ? "posting to Usenet" : "sending mail"); + standend(); + mix_status(NULL); + cl(2, 0); +#ifdef NYMSUPPORT + printw("n)ym: %s", thisnym); +#endif /* NYMSUPPORT */ + if (!strleft(thisnym, NONANON)) { + chain_reliability(chain, 0, reliability); /* chaintype 0=mix */ + cl(4, 0); + printw("c)hain: %-35s (reliability: %s)", chain, reliability); + cl(5, 0); + printw("r)edundancy: %3d copies ", numcopies); + } + cl(7, 0); + printw("d)estination: %s", dest); + cl(8, 0); + printw("s)ubject: %s", subject); +#ifdef USE_PGP + if (type != 'p' && type != 'f') { + cl(10, 0); + printw("pgp encry)ption: "); + if (encrypt) + printw("yes"); + else + printw("no"); + } + if (!streq(thisnym, ANON)) { + cl(11, 0); + printw("p)gp signature: "); + if (sign) + printw("yes"); + else + printw("no"); + cl(12, 0); + if (key == 0) + printw("attach pgp k)ey: no"); + } +#endif /* USE_PGP */ + + if (txt->length == 0) + mvprintw(LINES - 3, 18, + "e)dit message f)ile q)uit w/o sending"); + else + mvprintw(LINES - 3, 0, + "m)ail message e)dit message f)ile q)uit w/o sending"); + move(LINES - 1, COLS - 1); + refresh(); + c = getch(); + if (c != ERR) { + mix_status(""); + if (c == '\r' || c == '\n') { /* default action is edit or mail */ + if (txt->length == 0) + c = 'e'; + else + c = 'm'; + } + switch (c) { +#ifdef NYMSUPPORT + case 'n': + menu_nym(thisnym); + goto redraw; +#endif /* NYMSUPPORT */ + case '\014': + goto redraw; + case 'd': + echo(); + cl(LINES - 3, 20); + cl(7, 14); + wgetnstr(stdscr, dest, LINELEN); + noecho(); + break; + case 's': + echo(); + cl(LINES - 3, 20); + cl(8, 10); + wgetnstr(stdscr, subject, LINELEN); + noecho(); + break; + case 'c': + menu_chain(chain, 0, (type == 'p' || type == 'f') + && streq(thisnym, ANON)); + goto redraw; + case 'r': + echo(); + cl(LINES - 5, 20); + cl(5, 13); + wgetnstr(stdscr, line, LINELEN); + numcopies = strtol(line, NULL, 10); + if (numcopies < 1 || numcopies > 10) + numcopies = 1; + noecho(); + break; + case 'f': + cl(LINES - 3, 0); + askfilename(path); + cl(LINES - 3, 0); + if (txt->length) { + buf_sets(tmp, path); + buf_clear(msg); + if (!hdr) + buf_nl(msg); + buf_cat(msg, txt); + if (attachfile(msg, tmp) == -1) + beep(); + else { + buf_move(txt, msg); + hdr = 1; + } + } else { + if ((f = fopen(path, "r")) != NULL) { + buf_clear(txt); + buf_read(txt, f); + fclose(f); + } else + beep(); + } + break; + case 'e': +#endif /* USE_NCURSES */ + { + int linecount; + + edit: + linecount = 1; + sprintf(path, "%s%cx%02x%02x%02x%02x.txt", POOLDIR, DIRSEP, + rnd_byte(), rnd_byte(), rnd_byte(), rnd_byte()); + f = fopen(path, "w"); + if (f == NULL) { +#ifdef USE_NCURSES + beep(); +#endif /* USE_NCURSES */ + } else { + if (type == 'f' || type == 'p') + fprintf(f, "Newsgroups: %s\n", dest); + if (type == 'r' || type == 'g' || type == 'm') + fprintf(f, "To: %s\n", dest); + fprintf(f, "Subject: %s\n", subject); + linecount += 2; + if (hdr) + while (buf_getline(txt, NULL) == 0) linecount++; + else + fprintf(f, "\n"); + linecount++; + if (txt->length == 0) + fprintf(f, "\n"); + + buf_write(txt, f); + fclose(f); + } + + menu_spawn_editor(path, linecount); + + f = fopen(path, "r"); + if (f == NULL) { +#ifdef USE_NCURSES + clear(); + beep(); + continue; +#else /* end of USE_NCURSES */ + goto quit; +#endif /* else if not USE_NCURSES */ + } + buf_reset(txt); + hdr = 0; + + buf_reset(tmp); + buf_read(tmp, f); + while (buf_getheader(tmp, field, content) == 0) { + if (bufieq(field, "subject")) + strncpy(subject, content->data, + sizeof(subject)); + else if ((type == 'p' || type == 'f') && + bufieq(field, "newsgroups")) + strncpy(dest, content->data, sizeof(dest)); + else if (bufieq(field, "to")) + strncpy(dest, content->data, sizeof(dest)); + else { + buf_appendheader(txt, field, content); + hdr = 1; + } + } + if (hdr) + buf_nl(txt); + buf_rest(txt, tmp); + + fclose(f); + unlink(path); + strcatn(path, "~", PATHMAX); + unlink(path); +#ifndef USE_NCURSES + { + char line[4]; + + fprintf(stderr, "Send message [y/n]? "); + scanf("%3s", line); + if (!strleft(line, "y")) + goto quit; + } +#else /* end of not USE_NCURSES */ + goto redraw; + } + break; + case 'm': + if (txt->length == 0) + beep(); + else if (dest[0] == '\0') { + mix_status("No destination given."); + goto redraw; + } else { + mix_status("Creating message..."); +#endif /* else if USE_NCURSES */ + buf_reset(msg); + + if (type == 'p' || type == 'f') + buf_appends(msg, "Newsgroups: "); + else + buf_appends(msg, "To: "); + buf_appends(msg, dest); + buf_nl(msg); + buf_appends(msg, "Subject: "); + if (subject[0] == '\0') + buf_appends(msg, "(no subject)"); + else + buf_appends(msg, subject); + buf_nl(msg); + if (!hdr) + buf_nl(msg); + buf_cat(msg, txt); +#ifdef USE_PGP + { + BUFFER *p; + + p = buf_new(); + if (streq(thisnym, ANON)) + sign = 0; + if (sign || (key && !strileft(thisnym, NONANON))) + user_pass(p); + + if (encrypt || sign) { + if (pgp_mailenc((encrypt ? PGP_ENCRYPT : 0) + | (sign ? PGP_SIGN : 0) | PGP_TEXT + | (strleft(thisnym, NONANON) ? 0 : PGP_REMAIL), + msg, strleft(thisnym, NONANON) ? + ADDRESS : thisnym, p, PGPPUBRING, + strleft(thisnym, NONANON) ? + PGPSECRING : NYMSECRING) == -1) { + mix_genericerror(); +#ifdef USE_NCURSES + beep(); + goto redraw; +#endif /* USE_NCURSES */ + } + } + buf_free(p); + } +#endif /* USE_PGP */ + + if (strleft(thisnym, NONANON)) { + FILE *f = NULL; + + if (type == 'p' || type == 'f') { + if (strchr(NEWS, '@')) { + /* NOT_IMPLEMENTED; */ + } else + f = openpipe(NEWS); + } else { + if (NAME[0]) { + buf_sets(tmp, NAME); + buf_appends(tmp, " <"); + buf_appends(tmp, ADDRESS); + buf_appends(tmp, ">"); + } else + buf_sets(tmp, ADDRESS); + mail_encode(msg, 0); + if (sendmail(msg, tmp->data, NULL) != 0) { +#ifdef USE_NCURSES + clear(); +#endif /* USE_NCURSES */ + mix_status("Error sending message."); +#ifdef USE_NCURSES + goto redraw; +#else /* end of USE_NCURSES */ + goto quit; +#endif /* else if not USE_NCURSES */ + } + } +#ifdef USE_NCURSES + clear(); +#endif /* USE_NCURSES */ + mix_status("Message sent non-anonymously."); + goto quit; + } else { +#ifdef USE_PGP +#ifdef NYMSUPPORT + if (!streq(thisnym, ANON)) { + if (nym_encrypt(msg, thisnym, (type == 'p' || type == 'f') ? + MSG_POST : MSG_MAIL) == 0) + type = 'm'; + } +#endif /* NYMSUPPORT */ +#endif /* USE_PGP */ + err = mix_encrypt((type == 'p' || type == 'f') ? + MSG_POST : MSG_MAIL, + msg, chain, numcopies, chainlist); + if (err == 0) { +#ifdef USE_NCURSES + clear(); +#endif /* USE_NCURSES */ + for (n = 0; buf_getline(chainlist, tmp) == 0; n++) ; + if (n > 1) + mix_status("Done. (%d packets)", n); + else + mix_status("Chain: %s", chainlist->data); + goto quit; + } else { +#ifdef USE_NCURSES + beep(); +#endif /* USE_NCURSES */ + if (chainlist->length) + mix_status("%s", chainlist->data); + else + mix_genericerror(); + } + } + } +#ifdef USE_NCURSES + break; + case 'q': + case 'Q': + clear(); + goto quit; +#ifdef USE_PGP + case 'p': + if (!streq(thisnym, ANON)) + sign = !sign; + break; + case 'y': + encrypt = !encrypt; + break; + case 'k': + if (!streq(thisnym, ANON)) { + BUFFER *p, *keytxt, *uid; + + key = 1; + p = buf_new(); + keytxt = buf_new(); + uid = buf_new(); + + buf_appendf(uid, "<%s>", strleft(thisnym, NONANON) ? ADDRESS : + thisnym); + user_pass(p); + pgp_pubkeycert(uid, strleft(thisnym, NONANON) ? + PGPSECRING : NYMSECRING, p, keytxt, PGP_ARMOR_NYMKEY); + + buf_clear(msg); + if (!hdr) + buf_nl(msg); + buf_cat(msg, txt); + buf_sets(p, "application/pgp-keys"); + mime_attach(msg, keytxt, p); + hdr = 1; + buf_move(txt, msg); + + buf_free(p); + buf_free(keytxt); + buf_free(uid); + } + break; +#endif /* USE_PGP */ + default: + beep(); + } + } + } +#endif /* USE_NCURSES */ +quit: + buf_free(cc); + buf_free(cite); + buf_free(msg); + buf_free(txt); + buf_free(field); + buf_free(content); + buf_free(chainlist); + buf_free(tmp); +} diff --git a/Src/menustats.c b/Src/menustats.c @@ -0,0 +1,445 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Menu-based user interface + $Id: menustats.c 934 2006-06-24 13:40:39Z rabbi $ */ + + +#if 0 +void errlog(int type, char *format, ...) { }; +char MIXDIR[512]; +#include "util.c" +#include "buffers.c" +int menu_getuserpass(BUFFER *p, int mode) { return 0; }; +#endif + +#include <string.h> +#include <assert.h> +#include <stdlib.h> + +#include "menu.h" +#ifdef WIN32 +#include <urlmon.h> +#pragma comment(lib,"urlmon.lib") +#else +#include <sys/wait.h> +#endif /* WIN32 */ + + +int url_download(char *url, char *dest) { + int err; +#ifdef WIN32 + err = URLDownloadToFile(NULL, url, dest, BINDF_GETNEWESTVERSION, NULL); + + if (err != S_OK) + return -1; + else + return 0; +#else + char s[PATHMAX]; + snprintf(s, PATHMAX, "%s -q %s -O %s", WGET, url, dest); + err = system(s); + + if (err != -1) { + if (WIFEXITED(err) == 0) + return -1; /* abnormal child exit */ + else + return 0; + } + else + return -1; +#endif /* WIN32 */ +} + +/** read the allpingers file into the buffer */ +void read_allpingers(BUFFER *allpingers) { + FILE *f; + + f = mix_openfile(ALLPINGERSFILE, "r"); + if (f != NULL) { + buf_clear(allpingers); + buf_read(allpingers, f); + fclose(f); + } +} + +/* Get all sections from inifile. + * + * They are put into the sections buffer, separated by newlines + */ +void get_sections(BUFFER *inifile, BUFFER *sections) { + BUFFER *line; + int err; + + line = buf_new(); + + buf_rewind(inifile); + buf_reset(sections); + + while ((err = buf_getline(inifile, line)) != -1) { + if (bufileft (line, "[") && + bufiright(line, "]")) { + line->data[line->length-1] = '\0'; + buf_appends(sections, line->data+1); + buf_nl(sections); + }; + } + buf_free (line); +} + +/* Get value of an attribute + * + * returns -1 if it isn't found. + */ +int get_attribute(BUFFER *inifile, char *section, char *attribute, BUFFER *value) { + BUFFER *line; + int err = -1; + int insection = 0; + + line = buf_new(); + + buf_rewind(inifile); + buf_reset(value); + + while (buf_getline(inifile, line) != -1) { + if (bufileft (line, "[") && + bufiright(line, "]")) { + if (insection) + break; + + line->data[line->length-1] = '\0'; + if (strcasecmp(section, line->data+1) == 0) { + insection = 1; + } + } else if (insection && bufileft(line, attribute)) { + /* we are in the right section and this attribute name + * at least starts with what we want */ + char *ptr = line->data + strlen(attribute); + /* eat up whitespace */ + while ((*ptr == ' ') || (*ptr == '\t')) + ptr++; + if (*ptr != '=') + continue; + ptr++; + while ((*ptr == ' ') || (*ptr == '\t')) + ptr++; + buf_appends(value, ptr); + err = 0; + break; + } + } + buf_free (line); + return (err); +} + + + + + +static char *files[] = { "mlist", "rlist", "mixring", "pgpring"}; +#define NUMFILES sizeof(files)/sizeof(*files) + +/* Download all the needed files from the specified source */ +/* returns -1 on error */ +int stats_download(BUFFER *allpingers, char *sourcename, int curses) { + char *localfiles[] = { TYPE2REL, TYPE1LIST, PUBRING, PGPREMPUBASC }; + char path[PATHMAX]; + char path_t[PATHMAX]; + BUFFER *value; + int ret = 0; + int err; + int i; + + value = buf_new(); + + if (curses) { + clear(); + } + + err = get_attribute(allpingers, sourcename, "base", value); + if (err == 0) { + if (curses) { + standout(); + printw("%s", value->data); + standend(); + } + else + printf("%s\n\r", value->data); + } + +/* Loop to get each file in turn to a temp file */ + + for (i=0; i<NUMFILES; i++) { + err = get_attribute(allpingers, sourcename, files[i], value); + if (err < 0) { + /* the attribute vanished under us */ + ret = -1; + break; + } + mixfile(path, localfiles[i]); + if (curses) { + mvprintw(i+3, 0, "downloading %s from %s...", localfiles[i], value->data); + refresh(); + } + else + printf("downloading %s from %s...", localfiles[i], value->data); + err = url_download(value->data, strcat(path, ".t")); + if (err < 0) { + if (curses) + printw("failed to download.\n\rTry using another stats source."); + else + printf("failed to download.\n\rTry using another stats source.\n\r"); + ret = -1; + break; + } + if (curses) + printw("done"); + else + printf("done\n\r"); + } + +/* We got them all ok - so rename them to their correct names */ + + for (i=0; i<NUMFILES; i++) { + mixfile(path, localfiles[i]); + mixfile(path_t, localfiles[i]); + strcat(path_t, ".t"); + rename(path_t, path); + } + + if (curses) { + printw("\n\n\n\n\rPress any key to continue"); + getch(); + clear(); + } + buf_free(value); + return ret; +} +/* Checks whether the stats source has all the required files. + * + * 1 if it has, + * 0 otherwise + */ +int good_stats_source (BUFFER *allpingers, char *sourcename) { + BUFFER *value; + int i; + int res = 1; + int err; + + value = buf_new(); + + for (i = 0; i < NUMFILES; i++) { + err = get_attribute(allpingers, sourcename, files[i], value); + if (err < 0) { + res = 0; + break; + } + } + + buf_free (value); + return (res); +} + +/* Do a stats download update and report status */ +/* 0 on success */ +/* -1 File download failed */ +/* -2 Error writing file */ +/* -3 Stats source incomplete */ + +int download_stats(char *sourcename) { + BUFFER *inifile; + FILE *f; + int ret; + inifile = buf_new(); + read_allpingers(inifile); + if (good_stats_source(inifile, sourcename) == 1) { + if (stats_download(inifile, sourcename, 0) == 0) { + f = mix_openfile(STATSSRC, "w+"); + if (f != NULL) { + fprintf(f, "%s", sourcename); + fclose(f); + ret = 0; + } else { + ret = -2; + errlog(ERRORMSG, "Could not open stats source file for writing.\n"); + } + } else { + ret = -1; + errlog(ERRORMSG, "Stats source download failed.\n"); + } + } else { + ret = -3; + errlog(ERRORMSG, "Stats source does not include all required files.\n"); + } + + buf_free(inifile); + return (ret); +} + + +/* Download allpingers.txt */ +/* -1 on error */ +static int download_list() { + char path[PATHMAX]; + + mixfile(path, ALLPINGERSFILE); + + clear(); + standout(); + printw(ALLPINGERSURL); + standend(); + + mvprintw(3,0,"downloading %s...", ALLPINGERSURL); + refresh(); + if (url_download(ALLPINGERSURL, path) < 0) { + printw("failed to download.\n\rTry again later."); + printw("\n\n\rPress any key to continue"); + getch(); + return -1; + } + return 0; +} + +/* Displays the choice of stats sources */ +#define MAXPING (26 * 2) +void update_stats() { + char c; + BUFFER *inifile; + BUFFER *pingernames; + BUFFER *goodpingers; + BUFFER *line; + BUFFER *statssrc; + FILE *f; + int num; + int err; + int x, y; + int i; + + inifile = buf_new(); + pingernames = buf_new(); + goodpingers = buf_new(); + line = buf_new(); + statssrc = buf_new(); + + while (1) { + clear(); + standout(); + buf_clear(statssrc); + f = mix_openfile(STATSSRC, "r"); + if (f != NULL) { + buf_read(statssrc, f); + fclose(f); + } + printw("Select stats source:"); + standend(); + if (statssrc->length > 0) + printw(" Current: %s (Enter to download)", statssrc->data); + printw("\n\n"); + + read_allpingers(inifile); + get_sections (inifile, pingernames); + + num = 0; + buf_reset(goodpingers); + buf_rewind(pingernames); + while ((buf_getline(pingernames, line) != -1) && num < MAXPING) { + if (good_stats_source (inifile, line->data)) { + buf_cat(goodpingers, line); + buf_nl(goodpingers); + num++; + } + } + + x = 0; + buf_rewind(goodpingers); + for (i=0; i<num; i++) { + err = buf_getline(goodpingers, line); + assert (err != -1); + y = i; + if (y >= LINES - 6) + y -= LINES - 6, x = 40; + mvprintw(y + 2, x, "%c", i < 26 ? i + 'a' : i - 26 + 'A'); + mvprintw(y + 2, x + 2, "%s", line->data); + } + y = i + 3; + if (y > LINES - 4) + y = LINES - 4; + mvprintw(y, 0, "* update list of pingers from web = edit list <space> to exit"); + c = getch(); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + if (c >= 'a') + c -= 'a'; + else + c = c - 'A' + 26; + if (c < num) { + buf_rewind(goodpingers); + while (c >= 0) { + err = buf_getline(goodpingers, line); + assert (err != -1); + c--; + } + if (stats_download(inifile, line->data, 1) == 0) { + f = mix_openfile(STATSSRC, "w+"); + if (f != NULL) { + fprintf(f, "%s", line->data); + fclose(f); + } else + fprintf(stderr, "Could not open stats source file for writing\n"); + break; + } + } + } + else if (c == '*') { + download_list(); + } + else if (c == '=') { + char path[PATHMAX]; + mixfile(path, ALLPINGERSFILE); + menu_spawn_editor(path, 0); + } + else if ((c == '\r') && statssrc->length) { + stats_download(inifile, statssrc->data, 1); + break; + } + else if (c == ' ') { + break; + } + } + clear(); + + buf_free(statssrc); + buf_free(inifile); + buf_free(line); + buf_free(pingernames); + buf_free(goodpingers); +} + +#if 0 +int main() { + strcpy(MIXDIR,"./"); + + BUFFER *allpingers; + BUFFER *pingernames; + BUFFER *value; + + allpingers = buf_new(); + pingernames = buf_new(); + value = buf_new(); + + read_allpingers (allpingers); + get_sections (allpingers, pingernames); + + printf("%s", pingernames->data); + + get_attribute (allpingers, "noreply", "rlist", value); + printf("%s\n", value->data); + + + exit(0); +} + +#endif diff --git a/Src/menuutil.c b/Src/menuutil.c @@ -0,0 +1,154 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Menu-based user interface - utility functions + $Id: menuutil.c 934 2006-06-24 13:40:39Z rabbi $ */ + + +#include "menu.h" +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> + +int menu_initialized = 0; + +#ifdef USE_NCURSES +void cl(int y, int x) +{ + move(y, x); + hline(' ', COLS - x); +} +#endif /* USE_NCURSES */ + +void menu_init(void) +{ +#ifdef USE_NCURSES + initscr(); + cbreak(); + noecho(); + nonl(); + intrflush(stdscr, FALSE); + keypad(stdscr, TRUE); + menu_initialized = 1; +#endif /* USE_NCURSES */ +} + +void menu_exit(void) +{ + user_delpass(); +#ifdef USE_NCURSES + endwin(); +#endif /* USE_NCURSES */ +} + +#ifdef USE_NCURSES +void askfilename(char *path) +{ + char line[PATHMAX]; + + printw("\rFile name: "); + echo(); + wgetnstr(stdscr, path, PATHMAX); + noecho(); + printw("\r"); + if (path[0] == '~') { + char *h; + + if ((h = getenv("HOME")) != NULL) { + strncpy(line, h, PATHMAX); + strcatn(line, "/", PATHMAX); + strcatn(line, path + 1, PATHMAX); + strncpy(path, line, PATHMAX); + } + } +} + +void savemsg(BUFFER *message) +{ + char savename[PATHMAX]; + FILE *f; + + askfilename(savename); + f = fopen(savename, "a"); + if (f != NULL) { + buf_write(message, f); + fclose(f); + } +} + +#endif /* USE_NCURSES */ + +void menu_spawn_editor(char *path, int lineno) { +#ifdef WIN32 + SHELLEXECUTEINFO sei; + ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO)); + sei.cbSize = sizeof(SHELLEXECUTEINFO); + sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_DDEWAIT; + sei.hwnd = NULL; + sei.lpVerb = "open"; + sei.lpFile = path; + sei.lpParameters = NULL; + sei.nShow = SW_SHOWNORMAL; + + if (ShellExecuteEx(&sei) == TRUE) { + WaitForSingleObject(sei.hProcess, INFINITE); + CloseHandle(sei.hProcess); + } +#else /* WIN32 */ + char *editor; + char s[PATHMAX]; + +/* Command line option +nn to position the cursor? */ +#define cursorpos (strfind(editor, "emacs") || streq(editor, "vi") || \ + streq(editor, "joe")) + + editor = getenv("EDITOR"); + if (editor == NULL) + editor = "vi"; + + if (lineno > 1 && cursorpos) + snprintf(s, PATHMAX, "%s +%d %s", editor, lineno, path); + else + snprintf(s, PATHMAX, "%s %s", editor, path); + +#ifdef USE_NCURSES + clear(); + refresh(); + endwin(); +#endif /* USE_NCURSES */ + system(s); +#ifdef USE_NCURSES + refresh(); +#endif /* USE_NCURSES */ + +#endif /* WIN32 */ +} + +int menu_getuserpass(BUFFER *b, int mode) +{ +#ifdef USE_NCURSES + char p[LINELEN]; + + if (menu_initialized) { + cl(LINES - 1, 10); + if (mode == 0) + printw("enter passphrase: "); + else + printw("re-enter passphrase: "); + wgetnstr(stdscr, p, LINELEN); + cl(LINES - 1, 10); + refresh(); + if (mode == 0) + buf_appends(b, p); + else + return (bufeq(b, p)); + return (0); + } +#endif /* USE_NCURSES */ + return (-1); +} diff --git a/Src/mime.c b/Src/mime.c @@ -0,0 +1,814 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + MIME functions + $Id: mime.c 934 2006-06-24 13:40:39Z rabbi $ */ + + +#include "mix3.h" +#include <ctype.h> +#include <string.h> + +#define hex(i) (isdigit(i) ? (i) - '0' : tolower(i) - 'a' + 10) + +#define hexdigit(i) ((byte)(i < 10 ? i + '0' : i - 10 + 'A')) + +static void encode_word(BUFFER *in) +{ + BUFFER *out; + int i; + + out = buf_new(); + for (i = 0; i < in->length; i++) + if (in->data[i] < 32 || in->data[i] >= 127 || in->data[i] == '=' + || in->data[i] == '?' || in->data[i] == '_') { + buf_appendc(out, '='); + buf_appendc(out, hexdigit(in->data[i] / 16)); + buf_appendc(out, hexdigit(in->data[i] % 16)); + } else if (in->data[i] == ' ') + buf_appendc(out, '_'); + else + buf_appendc(out, in->data[i]); + buf_move(in, out); + buf_free(out); +} + +void body_encode(BUFFER *in, int transport, BUFFER *hdr) +{ + BUFFER *out; + int c, l=0, encoding = 0; + out = buf_new(); + + buf_clear(hdr); + + l = in->ptr; + while ((c = buf_getc(in)) != -1 && encoding != 2) { + if (c >= 160) + encoding = 1; + else if (c == ' ') { + if (buf_getc(in) == '\n') + encoding = 1; + buf_ungetc(in); + } else if ((c < 32 && c != ' ' && c != '\n' && c != '\t') || + (c >= 127 && c < 160)) { + encoding = 2; + } + } + in->ptr = l; + + if (encoding == 2) { + buf_sets(hdr, "Content-Transfer-Encoding: base64\n"); + encode(in, 76); + } else { + +#if 0 + if (encoding == 0) + buf_sets(hdr, "Content-Transfer-Encoding: 7bit\n"); +#endif + if (encoding != 0 && transport == MIME_8BIT) + buf_sets(hdr, "Content-Transfer-Encoding: 8bit\n"); + if (encoding == 0 || transport == MIME_8BIT) { + buf_rest(out, in); /* transparent */ + buf_move(in, out); + } else { + buf_sets(hdr, "Content-Transfer-Encoding: quoted-printable\n"); + l = 0; + while ((c = buf_getc(in)) != -1) { + if (c == '\n') { + buf_nl(out); + l = 0; + } + else if (c < 32 || c >= 127 || c == '=') { + if (l > 73) { + buf_appends(out, "=\n"); + l = 0; + } + buf_appendc(out, '='); + buf_appendc(out, hexdigit(c / 16)); + buf_appendc(out, hexdigit(c % 16)); + l += 3; + } else if (c == ' ') { + if (buf_getc(in) == '\n') { + buf_appendc(out, '='); + buf_appendc(out, hexdigit(c / 16)); + buf_appendc(out, hexdigit(c % 16)); + buf_nl(out); + l = 0; + } else { + buf_appendc(out, c); + buf_ungetc(in); + l++; + } + } else { + buf_appendc(out, c); + l++; + } + } + buf_move(in, out); + } + } + buf_free(out); +} + +int mail_encode(BUFFER *in, int encoding) +{ + BUFFER *out, *line, *tmp; + + out = buf_new(); + line = buf_new(); + tmp = buf_new(); + + while (buf_getline(in, line) == 0) { + hdr_encode(line, 255); + buf_cat(out, line); + buf_nl(out); + } + if (in->ptr < in->length) { + /* no newline if only encoding header lines */ + if (encoding == 0) { + buf_nl(out); + buf_rest(out, in); + } + else { + body_encode(in, encoding, line); + buf_cat(out, line); + buf_nl(out); + buf_cat(out, in); + } + } + buf_move(in, out); + buf_free(line); + buf_free(tmp); + buf_free(out); + return (0); +} + +int hdr_encode(BUFFER *in, int n) +{ + int i; + int encodeword = 0, encode = 0; + BUFFER *out, *word, *space; + + out = buf_new(); + word = buf_new(); + space = buf_new(); + for (i = 0; i <= in->length; i++) { + if (isspace(in->data[i]) || in->data[i] == '\0') { + if (word->length) { + if (encodeword) { + if (encode == 0) { + buf_cat(out, space); + buf_clear(space); + buf_appends(out, "=?"); + buf_appends(out, MIMECHARSET); + buf_appends(out, "?Q?"); + encode = 1; + } else { + buf_cat(space, word); + buf_move(word, space); + } + encode_word(word); + } + if (encode && !encodeword) { + encode = 0; + buf_appends(out, "?="); + } + buf_cat(out, space); + buf_cat(out, word); + encodeword = 0; + buf_clear(space); + buf_clear(word); + } + buf_appendc(space, in->data[i]); + } else { + if (in->data[i] < 32 || in->data[i] >= 127) + encodeword = 1; + buf_appendc(word, in->data[i]); + } + } + if (encode) + buf_appends(out, "?="); + + buf_move(in, out); + while (n > 0 && in->length - in->ptr > n) { + for (i = 1; i < in->length - in->ptr; i++) + if (isspace(in->data[in->length - i])) + break; + buf_get(in, out, in->length - i); + buf_appends(out, "\n\t"); + } + buf_rest(out, in); + buf_move(in, out); + buf_free(out); + buf_free(space); + buf_free(word); + return (0); +} + +void addprintable(BUFFER *out, int c) +{ + if (c == '\n') + buf_appendc(out, (char) c); + else if (c == '\t') + buf_appends(out, " "); + else if (c == '\014') + buf_appends(out, "^L"); + else if (c == '\r') ; + else if (c <= 31 || (c >= 128 && c <= 128 + 31)) + buf_appendc(out, '?'); + else + buf_appendc(out, (char) c); +} + +void addprintablebuf(BUFFER *out, BUFFER *in) +{ + int c; + + while ((c = buf_getc(in)) != -1) + addprintable(out, c); +} + +int decode_line(BUFFER *line) +{ + BUFFER *out; + unsigned int i; + int c, softbreak = 0; + + out = buf_new(); + for (i = 0; line->data[i] != '\0'; i++) { + if (line->data[i] == '=') { + if (isxdigit(line->data[i + 1]) && isxdigit(line->data[i + 2])) { + c = hex(line->data[i + 1]) * 16 + hex(line->data[i + 2]); + i += 2; + addprintable(out, c); + } else if (line->data[i + 1] == '\0') { + softbreak = 1; + break; + } + } else + addprintable(out, line->data[i]); + } + + buf_move(line, out); + buf_free(out); + return (softbreak); +} + +int decode_header(BUFFER *in) +{ + int encoded = 0; + int c; + int err = 0; + int last = 0; + BUFFER *out; + + out = buf_new(); + for (in->ptr = 0; in->data[in->ptr] != '\0'; in->ptr++) { + if (encoded == 'q' && in->data[in->ptr] == '=' && + isxdigit(in->data[in->ptr + 1]) + && isxdigit(in->data[in->ptr + 2])) { + c = hex(in->data[in->ptr + 1]) * 16 + hex(in->data[in->ptr + 2]); + in->ptr += 2; + addprintable(out, c); + } else if (encoded == 'q' && in->data[in->ptr] == '_') + buf_appendc(out, ' '); + else if (in->data[in->ptr] == '=' && in->data[in->ptr + 1] == '?' && + in->data[in->ptr + 2] != '\0') { + if (last > 0 && out->length > last) { + out->data[last] = '\0'; + out->length = last; + } + in->ptr++; + while (in->data[++in->ptr] != '?') + if (in->data[in->ptr] == 0) { + err = -1; + goto end; + } + if (in->data[in->ptr + 1] != '\0' && in->data[in->ptr + 2] == '?') { + encoded = tolower(in->data[in->ptr + 1]); + in->ptr += 2; + if (encoded == 'b') { + BUFFER *tmp; + + tmp = buf_new(); + decode(in, tmp); + addprintablebuf(out, tmp); + last = out->length; + buf_free(tmp); + } else if (encoded != 'q') + err = 1; + } else { + err = -1; + goto end; + } + } else if (encoded && in->data[in->ptr] == '?' && + in->data[in->ptr + 1] == '=') { + in->ptr++; + last = out->length; + encoded = 0; + } else { + addprintable(out, in->data[in->ptr]); + if (!encoded || !isspace(in->data[in->ptr])) + last = out->length; + } + } +end: + if (err == -1) + buf_set(out, in); + + buf_move(in, out); + buf_free(out); + return (err); +} + +#define delimclose 2 + +int boundary(BUFFER *line, BUFFER *boundary) +{ + int c; + + if (boundary->length == 0 || !bufleft(line, "--") || + !strleft(line->data + 2, boundary->data)) + return (0); + line->ptr = boundary->length + 2; + for (;;) { + c = buf_getc(line); + if (c == -1) + return (1); + if (c == '-' && buf_getc(line) == '-') + return (delimclose); + if (!isspace(c)) + return (0); + } +} + +#define pgpenc 1 +#define pgpsig 2 + +/* + * decodes non-multipart quoted printable message + */ +int qp_decode_message(BUFFER *msg) +{ + BUFFER *out, *line, *field, *content; + out = buf_new(); + line = buf_new(); + field = buf_new(); + content = buf_new(); + + buf_rewind(msg); + + /* copy over headers without decoding */ + while (buf_getheader(msg, field, content) == 0) { + if (bufieq(field, "content-transfer-encoding") + && bufieq(content, "quoted-printable")) { + continue; /* no longer quoted-printable */ + } else { + buf_appendheader(out, field, content); + } + } + + buf_nl(out); + + /* copy over body, quoted-printable decoding as we go */ + while (buf_getline(msg, line) != -1) { + int softbreak; + softbreak = decode_line(line); + buf_cat(out, line); + if (!softbreak) + buf_nl(out); + } + buf_move(msg, out); + buf_free(out); + buf_free(line); + buf_free(field); + buf_free(content); + return 0; +} + + +int entity_decode(BUFFER *msg, int message, int mptype, BUFFER *data) +{ + BUFFER *out, *line, *field, *content, *type, *subtype, *disposition, + *mboundary, *part, *sigdata; + int ret = 0, ptype = 0, partno = 0; + int p, encoded = 0; + + out = buf_new(); + line = buf_new(); + field = buf_new(); + content = buf_new(); + type = buf_new(); + subtype = buf_new(); + disposition = buf_new(); + mboundary = buf_new(); + part = buf_new(); + sigdata = buf_new(); + + if (message && bufileft(msg, "From ")) { + buf_getline(msg, out); /* envelope from */ + buf_nl(out); + } + + while (buf_getheader(msg, field, content) == 0) { + if (bufieq(field, "content-transfer-encoding") && + bufieq(content, "quoted-printable")) + encoded = 'q'; + if (bufieq(field, "content-type")) { + get_type(content, type, subtype); + if (bufieq(type, "multipart")) + get_parameter(content, "boundary", mboundary); + if (bufieq(type, "multipart") && bufieq(subtype, "encrypted")) { + get_parameter(content, "protocol", line); + if (bufieq(line, "application/pgp-encrypted")) + ptype = pgpenc; + } + if (bufieq(type, "multipart") && bufieq(subtype, "signed")) { + get_parameter(content, "protocol", line); + if (bufieq(line, "application/pgp-signature")) + ptype = pgpsig; + } + } + if (bufieq(field, "content-disposition")) + buf_set(disposition, content); + if (message) { + decode_header(content); + buf_appendheader(out, field, content); + } + } + + if (message) + buf_nl(out); + + if (bufifind(disposition, "attachment")) { + buf_appendf(out, "[-- %b attachment", type); + get_parameter(disposition, "filename", line); + if (line->length) + buf_appendf(out, " (%b)", line); + buf_appends(out, " --]\n"); + } + + if (mboundary->length) { + /* multipart */ + while (buf_getline(msg, line) > -1 && !boundary(line, mboundary)) + ; /* ignore preamble */ + while (buf_getline(msg, line) != -1) { + p = boundary(line, mboundary); + if (p) { + if (part->data[part->length - 1] == '\n') + part->data[--(part->length)] = '\0'; + partno++; + if (ptype == pgpsig && partno == 1) + buf_set(sigdata, part); + ret += entity_decode(part, 0, ptype, sigdata); + buf_cat(out, part); + buf_clear(part); + if (p == delimclose) + break; + if (bufieq(subtype, "alternative") && ret > 0) + break; + if (bufieq(subtype, "mixed")) + buf_appends(out, + "[-------------------------------------------------------------------------]\n"); + } else { + buf_cat(part, line); + buf_nl(part); + } + } + } else if (mptype == pgpenc && bufieq(type, "application") && + bufieq(subtype, "pgp-encrypted")) { + /* application/pgp-encrypted part of multipart/encrypted */ + ; /* skip */ + } else if (mptype == pgpenc && bufieq(type, "application") && + bufieq(subtype, "octet-stream")) { + /* application/octet-stream part of multipart/encrypted */ + int ok = 0; + buf_getline(msg, line); + if (bufleft(line, info_beginpgp)) { + if (buffind(line, "(SIGNED)")) { + buf_getline(msg, line); + buf_appends(out, "[-- OpenPGP message with signature --]\n"); + if (bufleft(line, info_pgpsig)) + buf_appendf(out, "[%s]\n", + line->data + sizeof(info_pgpsig) - 1); + else + buf_appends(out, "[Signature invalid]\n"); + } else + buf_appends(out, "[-- OpenPGP message --]\n"); + while (buf_getline(msg, line) != -1) { + if (bufleft(line, info_endpgp)) { + ret += entity_decode(part, 0, 0, NULL); + buf_cat(out, part); + buf_appends(out, "[-- End OpenPGP message --]\n"); + ok = 1, ret++; + break; + } + buf_cat(part, line); + buf_nl(part); + } + } + if (!ok) { + buf_appends(out, "[-- Bad OpenPGP message --]\n"); + buf_cat(out, msg); + } + } else if (mptype == pgpsig && bufeq(type, "application") && + bufieq(subtype, "pgp-signature")) { + buf_rest(part, msg); +#ifdef USE_PGP + if (pgp_decrypt(part, NULL, data, PGPPUBRING, NULL) == PGP_SIGOK) + buf_appendf(out, "[-- OpenPGP signature from:\n %b --]\n", data); + else + buf_appends(out, "[-- Invalid OpenPGP signature --]\n"); +#else /* USE_PGP */ + buf_appends(out, "[-- No OpenPGP support --]\n"); +#endif /* !USE_PGP */ + } else if (type->length == 0 || bufieq(type, "text")) { + while (buf_getline(msg, line) != -1) { + int softbreak; + softbreak = encoded ? decode_line(line) : 0; + buf_cat(out, line); + if (!softbreak) + buf_nl(out); + } + ret++; + } else { + buf_appendf(out, "[-- %b/%b message part --]\n", type, subtype); + buf_cat(out, msg); + } + + buf_move(msg, out); + buf_free(line); + buf_free(out); + buf_free(field); + buf_free(content); + buf_free(type); + buf_free(subtype); + buf_free(disposition); + buf_free(mboundary); + buf_free(part); + buf_free(sigdata); + return (0); +} + +void mimedecode(BUFFER *msg) +{ + entity_decode(msg, 1, 0, NULL); +} + +int attachfile(BUFFER *message, BUFFER *filename) +{ + BUFFER *type, *attachment; + FILE *f; + int ret = -1; + + type = buf_new(); + attachment = buf_new(); + + if ((bufiright(filename, ".txt") || !bufifind(filename, ".")) &&(strlen(DEFLTENTITY) != 0)) + buf_sets(type, DEFLTENTITY); + else if (bufiright(filename, ".htm") || bufiright(filename, ".html")) + buf_sets(type, "text/html"); + else if (bufiright(filename, ".jpeg")) + buf_sets(type, "image/jpeg"); + else if (bufiright(filename, ".gif")) + buf_sets(type, "image/gif"); + else if (bufiright(filename, ".pcm")) + buf_sets(type, "audio/basic"); + else if (bufiright(filename, ".mpg") || bufiright(filename, ".mpeg")) + buf_sets(type, "video/mpeg"); + else if (bufiright(filename, ".ps")) + buf_sets(type, "application/postscript"); + else + buf_sets(type, "application/octet-stream"); + + f = fopen(filename->data, "r"); + if (f) { + buf_read(attachment, f); + fclose(f); + ret = mime_attach(message, attachment, type); + } + + buf_free(attachment); + buf_free(type); + return(ret); +} + +int mime_attach(BUFFER *message, BUFFER *attachment, BUFFER *attachtype) +{ + BUFFER *out, *part, *line, *type, *subtype, *mboundary, *field, *content; + int mimeheader = 0, multipart = 0, versionheader = 0; + + out = buf_new(); + line = buf_new(); + part = buf_new(); + type = buf_new(); + subtype = buf_new(); + mboundary = buf_new(); + field = buf_new(); + content = buf_new(); + + buf_rewind(message); + while (buf_getheader(message, field, content) == 0) { + if (bufieq(field, "mime-version")) + versionheader = 1; + if (bufieq(field, "content-type")) { + get_type(content, type, subtype); + if (bufieq(type, "multipart") && bufieq(subtype, "mixed")) { + multipart = 1; + get_parameter(content, "boundary", mboundary); + } + } + if (bufileft(field, "content-")) + mimeheader = 1; + } + + if (mimeheader && !multipart) { + buf_rewind(message); + while (buf_getheader(message, field, content) == 0) { + if (bufileft(field, "content-")) + buf_appendheader(part, field, content); + else + buf_appendheader(out, field, content); + } + } else { + buf_ungetc(message); + buf_append(out, message->data, message->ptr); + buf_getc(message); + } + + if (!versionheader) + buf_appends(out, "MIME-Version: 1.0\n"); + + if (!multipart) { + buf_setrnd(mboundary, 18); + encode(mboundary, 0); + buf_appendf(out, "Content-Type: multipart/mixed; boundary=\"%b\"\n", + mboundary); + } + buf_nl(out); + + if (multipart) { + while (buf_getline(message, line) != -1) { + if (boundary(line, mboundary) == delimclose) + break; + buf_cat(out, line); + buf_nl(out); + } + } else { + buf_appendf(out, "--%b\n", mboundary); + if (part->length) { + buf_cat(out, part); /* body part header */ + } + else { + if (strlen(DEFLTENTITY)) + buf_appendf(out, "Content-Type: %s\n", DEFLTENTITY); + } + + buf_nl(out); + buf_cat(out, message); + buf_nl(out); + } + + buf_appendf(out, "--%b\n", mboundary); + buf_appendf(out, "Content-Type: %b\n", attachtype); + + body_encode(attachment, MIME_8BIT, line); + buf_cat(out, line); + buf_nl(out); + buf_cat(out, attachment); + buf_appendf(out, "\n--%b--\n", mboundary); + + buf_move(message, out); + + buf_free(out); + buf_free(line); + buf_free(part); + buf_free(type); + buf_free(subtype); + buf_free(mboundary); + buf_free(field); + buf_free(content); + return (1); +} + +static int entity_encode(BUFFER *message, BUFFER *out, BUFFER *messagehdr, + int encoding) +{ + BUFFER *field, *content, *mboundary, *part, *line, *line2, *tmp; + + field = buf_new(); + content = buf_new(); + mboundary = buf_new(); + part = buf_new(); + line = buf_new(); + line2 = buf_new(); + tmp = buf_new(); + + buf_rewind(message); + buf_clear(out); + buf_clear(messagehdr); + + while (buf_getheader(message, field, content) == 0) { + if (bufileft(field, "content-")) + buf_appendheader(out, field, content); + else if (messagehdr) + buf_appendheader(messagehdr, field, content); + + if (bufieq(field, "content-type")) { + get_type(content, line, tmp); + if (bufieq(line, "multipart")) + get_parameter(content, "boundary", mboundary); + } + } + + buf_nl(out); + if (mboundary->length) { + while (buf_getline(message, line) != -1) { + buf_cat(out, line); + buf_nl(out); + if (boundary(line, mboundary)) + break; + } + while (buf_getline(message, line) != -1) { + if (boundary(line, mboundary)) { + entity_encode(part, tmp, line2, encoding); + buf_cat(out, line2); + buf_cat(out, tmp); + buf_cat(out, line); + buf_nl(out); + buf_clear(part); + if (boundary(line, mboundary) == delimclose) + break; + } else { + buf_cat(part, line); + buf_nl(part); + } + } + } else + buf_rest(out, message); + buf_rewind(out); + mail_encode(out, encoding); + + buf_free(field); + buf_free(content); + buf_free(mboundary); + buf_free(part); + buf_free(line); + buf_free(line2); + buf_free(tmp); + return (1); +} + +int pgpmime_sign(BUFFER *message, BUFFER *uid, BUFFER *pass, char *secring) +{ +#ifndef USE_PGP + return (-1) +#else /* end of not USE_PGP */ + BUFFER *out, *body, *mboundary, *algo; + int err; + + out = buf_new(); + body = buf_new(); + mboundary = buf_new(); + algo = buf_new(); + + pgp_signhashalgo(algo, uid, secring, pass); + + entity_encode(message, body, out, MIME_7BIT); + + buf_setrnd(mboundary, 18); + encode(mboundary, 0); + buf_appendf(out, "Content-Type: multipart/signed; boundary=\"%b\";\n", + mboundary); + buf_appendf(out, + "\tmicalg=pgp-%b; protocol=\"application/pgp-signature\"\n", + algo); + buf_nl(out); + + buf_appendf(out, "--%b\n", mboundary); + buf_cat(out, body); + buf_nl(out); + buf_appendf(out, "--%b\n", mboundary); + + err = pgp_encrypt(PGP_SIGN | PGP_TEXT | PGP_DETACHEDSIG, body, NULL, + uid, pass, NULL, secring); + + buf_appends(out, "Content-Type: application/pgp-signature\n"); + buf_nl(out); + buf_cat(out, body); + buf_nl(out); + buf_appendf(out, "--%b--\n", mboundary); + if (err == 0) + buf_move(message, out); + + buf_free(out); + buf_free(body); + buf_free(mboundary); + buf_free(algo); + return (err); +#endif /* else if USE_PGP */ +} diff --git a/Src/mix.c b/Src/mix.c @@ -0,0 +1,1262 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the file COPYRIGHT for + details. + + Mixmaster initialization, configuration + $Id: mix.c 962 2007-11-19 13:42:41Z zax $ */ + + +#include "mix3.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <ctype.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#ifdef POSIX +#include <signal.h> +#include <unistd.h> +#include <pwd.h> +#include <sys/utsname.h> +#else /* end of POSIX */ +#include <io.h> +#include <direct.h> +#endif /* else if not POSIX */ +#ifdef WIN32 +#include <windows.h> +#include <shlobj.h> +#include <shlobj.h> +#endif /* WIN32 */ +#include <assert.h> +#include "menu.h" + +int buf_vappendf(BUFFER *b, char *fmt, va_list args); + +/** filenames ************************************************************/ +char MIXCONF[PATHMAX] = DEFAULT_MIXCONF; +char DISCLAIMFILE[PATHMAX]; +char FROMDSCLFILE[PATHMAX]; +char MSGFOOTERFILE[PATHMAX]; +char POP3CONF[PATHMAX]; +char HELPFILE[PATHMAX]; +char REQUESTDIR[PATHMAX]; +char ABUSEFILE[PATHMAX]; +char REPLYFILE[PATHMAX]; +char USAGEFILE[PATHMAX]; +char USAGELOG[PATHMAX]; +char BLOCKFILE[PATHMAX]; +char ADMKEYFILE[PATHMAX]; +char KEYFILE[PATHMAX]; +char PGPKEY[PATHMAX]; +char DSAPARAMS[PATHMAX]; +char DHPARAMS[PATHMAX]; +char MIXRAND[PATHMAX]; +char SECRING[PATHMAX]; +char PUBRING[PATHMAX]; +char IDLOG[PATHMAX]; +char STATS[PATHMAX]; +char PGPMAXCOUNT[PATHMAX]; +char DESTBLOCK[PATHMAX]; +char DESTALLOW[PATHMAX]; +char DESTALLOW2[PATHMAX]; +char SOURCEBLOCK[PATHMAX]; +char HDRFILTER[PATHMAX]; +char REGULAR[PATHMAX]; +char POOL[PATHMAX]; +char TYPE1LIST[PATHMAX]; +char TYPE2REL[PATHMAX]; +char PIDFILE[PATHMAX]; + +char PGPREMPUBRING[PATHMAX]; +char PGPREMPUBASC[PATHMAX]; +char PGPREMSECRING[PATHMAX]; +char NYMSECRING[PATHMAX]; +char NYMDB[PATHMAX]; +char STAREX[PATHMAX]; + +/** config ***************************************************************/ + +char MIXDIR[PATHMAX]; +char POOLDIR[PATHMAX]; + +/* programs */ +char SENDMAIL[LINELEN]; +char SENDANONMAIL[LINELEN]; +char NEWS[LINELEN]; +char TYPE1[LINELEN]; + +/* addresses */ +char MAILtoNEWS[LINELEN]; +char REMAILERNAME[LINELEN]; +char ANONNAME[LINELEN]; +char REMAILERADDR[LINELEN]; +char ANONADDR[LINELEN]; +char COMPLAINTS[LINELEN]; +int AUTOREPLY; +char SMTPRELAY[LINELEN]; +char SMTPUSERNAME[LINELEN]; +char SMTPPASSWORD[LINELEN]; + +#ifdef USE_SOCK +char HELONAME[LINELEN]; +char ENVFROM[LINELEN]; +int POP3DEL; +int POP3SIZELIMIT; +long POP3TIME; + +#endif /* USE_SOCK */ + +char SHORTNAME[LINELEN]; +char ALLPINGERSURL[BUFSIZE]; +char ALLPINGERSFILE[PATHMAX]; +char WGET[PATHMAX]; +char STATSSRC[PATHMAX]; +int STATSAUTOUPDATE; +long STATSINTERVAL; + + +/* remailer configuration */ +int REMAIL; +int MIX; +int PGP; +int UNENCRYPTED; +int REMIX; +int REPGP; +char EXTFLAGS[LINELEN]; /* user-defined capstring flags */ + +char PRECEDENCE[LINELEN]; /* default Precedence: header for outgoing mail */ +int POOLSIZE; +int RATE; +int INDUMMYP; +int OUTDUMMYP; +int INDUMMYMAXP; +int OUTDUMMYMAXP; +int MIDDLEMAN; +int AUTOBLOCK; +int STATSDETAILS; +char FORWARDTO[LINELEN]; +int SIZELIMIT; /* maximal size of remailed messages */ +int INFLATEMAX; /* maximal size of Inflate: padding */ +int MAXRANDHOPS; +int BINFILTER; /* filter binary attachments? */ +int LISTSUPPORTED; /* list supported remailers in remailer-conf reply? */ +long PACKETEXP; /* Expiration time for old packets */ +long IDEXP; /* 0 = no ID log !! */ +long SENDPOOLTIME; /* frequency for sending pool messages */ +long MAILINTIME; /* frequency for processing MAILIN mail */ + +long KEYLIFETIME; +long KEYOVERLAPPERIOD; +long KEYGRACEPERIOD; + +char ERRLOG[LINELEN]; +char ADDRESS[LINELEN]; +char NAME[LINELEN]; + +char ORGANIZATION[LINELEN]; +char MID[LINELEN]; + +/* client config */ +int NUMCOPIES; +char CHAIN[LINELEN]; +int VERBOSE; +int DISTANCE; +int MINREL; +int RELFINAL; +long MAXLAT; +long MINLAT; +char PGPPUBRING[PATHMAX]; +char PGPSECRING[PATHMAX]; +char PASSPHRASE[LINELEN]; +char MAILIN[PATHMAX]; +char MAILBOX[PATHMAX]; +char MAILABUSE[PATHMAX]; +char MAILBLOCK[PATHMAX]; +char MAILUSAGE[PATHMAX]; +char MAILANON[PATHMAX]; +char MAILERROR[PATHMAX]; +char MAILBOUNCE[PATHMAX]; + +int CLIENTAUTOFLUSH; +int MAXRECIPIENTS; + +long TIMESKEW_FORWARD; +long TIMESKEW_BACK; +int TEMP_FAIL; + +char ENTEREDPASSPHRASE[LINELEN] = ""; + +static int rereadconfig = 0; +static int terminatedaemon = 0; + +#if defined(S_IFDIR) && !defined(S_ISDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif /* defined(S_IFDIR) && !defined(S_ISDIR) */ + +static int mixdir(char *d, int create) +{ + int err; + struct stat buf; + + if (d != MIXDIR) + strncpy(MIXDIR, d, PATHMAX); + if (MIXDIR[strlen(MIXDIR) - 1] == DIRSEP) + MIXDIR[strlen(MIXDIR) - 1] = '\0'; + err = stat(MIXDIR, &buf); + if (err == -1) { + if (create) { +#ifndef POSIX + err = mkdir(MIXDIR); +#else /* end of not POSIX */ + err = mkdir(MIXDIR, S_IRWXU); +#endif /* else if POSIX */ + if (err == 0) + errlog(NOTICE, "Creating directory %s.\n", MIXDIR); + } else + err = 1; + } else if (!S_ISDIR(buf.st_mode)) + err = -1; + if (err == 0) + strcatn(MIXDIR, DIRSEPSTR, PATHMAX); + return (err); +} + +void whoami(char *addr, char *defaultname) +{ + char *p = NULL; + +#if defined(HAVE_GETDOMAINNAME) || (defined(HAVE_GETHOSTNAME) && ! defined(HAVE_UNAME)) + char line[LINELEN]; + +#endif /* defined(HAVE_GETDOMAINNAME) || [...] */ +#ifdef HAVE_UNAME + struct utsname uts; + +#endif /* HAVE_UNAME */ +#ifdef POSIX + p = getlogin(); +#endif /* POSIX */ + if (p == NULL) + strcpy(addr, defaultname); + else + strncpy(addr, p, LINELEN); + + strcatn(addr, "@", LINELEN); +#ifdef HAVE_UNAME + if (uname(&uts) != -1) + strcatn(addr, uts.nodename, LINELEN); +#elif defined(HAVE_GETHOSTNAME) /* end of HAVE_UNAME */ + if (gethostname(line, LINELEN) == 0) + strcatn(addr, line, LINELEN); +#endif /* defined(HAVE_GETHOSTNAME) */ + if (addr[strlen(addr) - 1] == '@') + strcatn(addr, SHORTNAME, LINELEN); + + if (strchr(strchr(addr, '@'), '.') == NULL) { +#ifdef HAVE_GETDOMAINNAME + if (getdomainname(line, LINELEN) == 0 && !streq(line, "(none)")) { + strcatn(addr, ".", LINELEN); + strcatn(addr, line, LINELEN); + } +#endif /* HAVE_GETDOMAINNAME */ + } +} + +#define read_conf(t) readconfline(line, #t, sizeof(#t)-1, t) + +static int readconfline(char *line, char *name, int namelen, char *var) +{ + if (strncmp(line, name, namelen) == 0 && + (isspace(line[namelen]) || line[namelen] == '=')) { + line += namelen; + if (*line == '=') + line++; + while (isspace(*line)) + line++; + if (line[0] == '\n' || line[0] == '\0') /* leave default */ + return (1); + strncpy(var, line, LINELEN); + if (var[strlen(var) - 1] == '\n') + var[strlen(var) - 1] = '\0'; + return (1); + } else + return (0); +} + +#define read_conf_i(t) readiconfline(line, #t, sizeof(#t)-1, &t) + +static int readiconfline(char *line, char *name, int namelen, int *var) +{ + if (strncmp(line, name, namelen) == 0 && + (isspace(line[namelen]) || line[namelen] == '=')) { + line += namelen; + if (*line == '=') + line++; + while (isspace(*line)) + line++; + if (line[0] == '\n' || line[0] == '\0') /* leave default */ + return (1); + switch (tolower(line[0])) { + case 'n': + *var = 0; + break; + case 'y': + *var = 1; + break; + case 'x': + *var = 2; + break; + default: + sscanf(line, "%d", var); + } + return (1); + } else + return (0); +} + +#define read_conf_t(t) readtconfline(line, #t, sizeof(#t)-1, &t) + +static int readtconfline(char *line, char *name, int namelen, long *var) +{ + char *linenext; + int mod = 0; + long l = 0; + long n; + + if (strncmp(line, name, namelen) == 0 && + (isspace(line[namelen]) || line[namelen] == '=')) { + line += namelen; + if (*line == '=') + line++; + for (;; line++) { + n = strtol(line, &linenext, 10); + if (linenext == line) + break; + line = linenext; + mod = 1; + assert(line != NULL); + while (isspace(*line)) + line++; + switch (tolower(*line)) { + case 'y': /* years */ + l += 365 * 24 * 60 * 60 * n; + break; + case 'b': /* months */ + l += 30 * 24 * 60 * 60 * n; + break; + case 'w': /* weeks */ + l += 7 * 24 * 60 * 60 * n; + break; + case 'd': /* days */ + l += 24 * 60 * 60 * n; + break; + case 's': /* seconds */ + l += n; + break; + case 'm': /* minutes */ + l += 60 * n; + break; + case 'h': /* hours - default */ + default: + l += 60 * 60 * n; + break; + } + } + if (mod) + *var = l; + return (1); + } else + return (0); +} + +static void mix_setdefaults() +{ +#define strnncpy(a,b) strncpy(a, b, sizeof(a)); a[sizeof(a)-1] = '\0' + + strnncpy(DISCLAIMFILE , DEFAULT_DISCLAIMFILE); + strnncpy(FROMDSCLFILE , DEFAULT_FROMDSCLFILE); + strnncpy(MSGFOOTERFILE, DEFAULT_MSGFOOTERFILE); + strnncpy(POP3CONF , DEFAULT_POP3CONF); + strnncpy(HELPFILE , DEFAULT_HELPFILE); + strnncpy(REQUESTDIR , DEFAULT_REQUESTDIR); + strnncpy(ABUSEFILE , DEFAULT_ABUSEFILE); + strnncpy(REPLYFILE , DEFAULT_REPLYFILE); + strnncpy(USAGEFILE , DEFAULT_USAGEFILE); + strnncpy(USAGELOG , DEFAULT_USAGELOG); + strnncpy(BLOCKFILE , DEFAULT_BLOCKFILE); + strnncpy(ADMKEYFILE , DEFAULT_ADMKEYFILE); + strnncpy(KEYFILE , DEFAULT_KEYFILE); + strnncpy(PGPKEY , DEFAULT_PGPKEY); + strnncpy(DSAPARAMS , DEFAULT_DSAPARAMS); + strnncpy(DHPARAMS , DEFAULT_DHPARAMS); + strnncpy(MIXRAND , DEFAULT_MIXRAND); + strnncpy(SECRING , DEFAULT_SECRING); + strnncpy(PUBRING , DEFAULT_PUBRING); + strnncpy(IDLOG , DEFAULT_IDLOG); + strnncpy(STATS , DEFAULT_STATS); + strnncpy(PGPMAXCOUNT , DEFAULT_PGPMAXCOUNT); + strnncpy(DESTBLOCK , DEFAULT_DESTBLOCK); + strnncpy(DESTALLOW , DEFAULT_DESTALLOW); + strnncpy(DESTALLOW2 , DEFAULT_DESTALLOW2); + strnncpy(SOURCEBLOCK , DEFAULT_SOURCEBLOCK); + strnncpy(HDRFILTER , DEFAULT_HDRFILTER); + strnncpy(REGULAR , DEFAULT_REGULAR); + strnncpy(POOL , DEFAULT_POOL); + strnncpy(TYPE1LIST , DEFAULT_TYPE1LIST); + strnncpy(TYPE2REL , DEFAULT_TYPE2REL); + strnncpy(PIDFILE , DEFAULT_PIDFILE); + + strnncpy(PGPREMPUBRING, DEFAULT_PGPREMPUBRING); + strnncpy(PGPREMPUBASC , DEFAULT_PGPREMPUBASC); + strnncpy(PGPREMSECRING, DEFAULT_PGPREMSECRING); + strnncpy(NYMSECRING , DEFAULT_NYMSECRING); + strnncpy(NYMDB , DEFAULT_NYMDB); + strnncpy(STAREX , DEFAULT_STAREX); + strnncpy(ALLPINGERSURL, DEFAULT_ALLPINGERSURL); + strnncpy(ALLPINGERSFILE, DEFAULT_ALLPINGERSFILE); + strnncpy(WGET , DEFAULT_WGET); + strnncpy(STATSSRC , DEFAULT_STATSSRC); + + strnncpy(MIXDIR , ""); + strnncpy(POOLDIR , ""); + +/* programs */ +#ifdef WIN32 + strnncpy(SENDMAIL , "outfile"); +#else /* end of WIN32 */ + strnncpy(SENDMAIL , "/usr/lib/sendmail -t"); +#endif /* else if not WIN32 */ + strnncpy(SENDANONMAIL , ""); + strnncpy(NEWS , ""); + strnncpy(TYPE1 , ""); + +/* addresses */ + strnncpy(MAILtoNEWS , "mail2news@dizum.com,mail2news@m2n.mixmin.net"); + strnncpy(REMAILERNAME , "Anonymous Remailer"); + strnncpy(ANONNAME , "Anonymous"); + strnncpy(REMAILERADDR , ""); + strnncpy(ANONADDR , ""); + strnncpy(COMPLAINTS , ""); + strnncpy(SMTPRELAY , ""); + AUTOREPLY = 0; + +#ifdef USE_SOCK + strnncpy(HELONAME , ""); + strnncpy(ENVFROM , ""); + POP3DEL = 0; + POP3SIZELIMIT = 0; + POP3TIME = 60 * 60; + +#endif /* USE_SOCK */ + + strnncpy(SHORTNAME , ""); + +/* configuration */ + REMAIL = 0; + MIX = 1; + PGP = 1; + UNENCRYPTED = 0; + REMIX = 1; + REPGP = 1; + STATSAUTOUPDATE = 0; + STATSINTERVAL = 8 * 60 * 60; + strnncpy(EXTFLAGS, ""); + + strnncpy(PRECEDENCE, ""); + POOLSIZE = 0; + RATE = 100; + INDUMMYP = 3; /* add dummy messages with probability p for each message added to the pool */ + OUTDUMMYP = 10; /* add dummy messages with probability p each time we send from the pool */ + INDUMMYMAXP = 84; /* for both of the above: while (rnd < p) { senddummy(); } */ + OUTDUMMYMAXP = 96; /* set max INDUMMYP and OUTDUMMYP such that 24 and 5.25 dummy messages will */ + MIDDLEMAN = 0; /* be generated on average. More than this is insane. */ + AUTOBLOCK = 1; + STATSDETAILS = 1; + strnncpy(FORWARDTO, "*"); + SIZELIMIT = 0; /* maximal size of remailed messages */ + INFLATEMAX = 50; /* maximal size of Inflate: padding */ + MAXRANDHOPS = 5; + BINFILTER = 0; /* filter binary attachments? */ + LISTSUPPORTED = 1; /* list supported remailers in remailer-conf reply? */ + PACKETEXP = 7 * SECONDSPERDAY; /* Expiration time for old packets */ + IDEXP = 7 * SECONDSPERDAY; /* 0 = no ID log !! */ + SENDPOOLTIME = 0; /* frequency for sending pool messages */ + MAILINTIME = 5 * 60; /* frequency for processing MAILIN mail */ + + KEYLIFETIME = 13 * 30 * 24 * 60 * 60; /* validity period for keys. */ + KEYOVERLAPPERIOD = 1 * 30 * 24 * 60 * 60; /* when keys have this amount of time */ + /* left before expiration, create */ + /* new ones when ./mix -K is run.*/ + KEYGRACEPERIOD = 7 * 24 * 60 * 60; /* accept mail to the old key for this */ + /* amount of time after it has expired. */ + + + strnncpy(ERRLOG , ""); + strnncpy(ADDRESS , ""); + strnncpy(NAME , ""); + + strnncpy(ORGANIZATION, "Anonymous Posting Service"); + strnncpy(MID , "y"); + +/* client config */ + NUMCOPIES = 1; + strnncpy(CHAIN, "*,*,*,*"); + VERBOSE = 2; + DISTANCE = 2; + MINREL = 98; + RELFINAL = 99; + MAXLAT = 36 * 60 * 60; + MINLAT = 5 * 60; + strnncpy(PGPPUBRING, ""); + strnncpy(PGPSECRING, ""); +#ifdef COMPILEDPASS + strnncpy(PASSPHRASE, COMPILEDPASS); +#else /* end of COMPILEDPASS */ + strnncpy(PASSPHRASE, ""); +#endif /* else if not COMPILEDPASS */ + strnncpy(MAILIN , ""); + strnncpy(MAILBOX , "mbox"); + strnncpy(MAILABUSE , ""); + strnncpy(MAILBLOCK , ""); +#ifdef WIN32 + strnncpy(MAILUSAGE , "nul:"); + strnncpy(MAILANON , "nul:"); + strnncpy(MAILERROR , "nul:"); +#else /* end of WIN32 */ + strnncpy(MAILUSAGE , "/dev/null"); + strnncpy(MAILANON , "/dev/null"); + strnncpy(MAILERROR , "/dev/null"); +#endif /* else if not WIN32 */ + strnncpy(MAILBOUNCE, ""); + + CLIENTAUTOFLUSH = 1; + MAXRECIPIENTS = 5; + + TIMESKEW_FORWARD = 2*7*24*60*60; + TIMESKEW_BACK = 12*60*60; + TEMP_FAIL = 75; +} + +int mix_configline(char *line) +{ + return (read_conf(ADDRESS) || read_conf(NAME) || + read_conf(SHORTNAME) || read_conf(REMAILERADDR) || + read_conf(ANONADDR) || read_conf(REMAILERNAME) || + read_conf(ANONNAME) || read_conf(COMPLAINTS) || + read_conf_i(AUTOREPLY) || read_conf(SMTPRELAY) || + read_conf(SMTPUSERNAME) || read_conf(SMTPPASSWORD) || +#ifdef USE_SOCK + read_conf(HELONAME) || read_conf(ENVFROM) || +#endif /* USE_SOCK */ + read_conf(SENDMAIL) || read_conf(SENDANONMAIL) || + read_conf(PRECEDENCE) || + read_conf_i(REMAIL) || read_conf_i(MIX) || + read_conf_i(PGP) || read_conf_i(UNENCRYPTED) || + read_conf_i(REMIX) || read_conf(NEWS) || + read_conf_i(REPGP) || read_conf(EXTFLAGS) || + read_conf(MAILtoNEWS) || read_conf(ERRLOG) || + read_conf(ORGANIZATION) || read_conf(MID) || + read_conf(TYPE1) || read_conf_i(POOLSIZE) || + read_conf_i(RATE) || read_conf_i(MIDDLEMAN) || + read_conf_i(INDUMMYP) || + read_conf_i(OUTDUMMYP) || + read_conf_i(AUTOBLOCK) || read_conf(FORWARDTO) || + read_conf_i(STATSDETAILS) || + read_conf_i(SIZELIMIT) || read_conf_i(INFLATEMAX) || + read_conf_i(MAXRANDHOPS) || read_conf_i(BINFILTER) || + read_conf_i(LISTSUPPORTED) || + read_conf_t(PACKETEXP) || read_conf_t(IDEXP) || + read_conf_t(SENDPOOLTIME) || read_conf_i(NUMCOPIES) || + read_conf_t(MAILINTIME) || + read_conf(CHAIN) || read_conf_i(VERBOSE) || + read_conf_i(DISTANCE) || read_conf_i(MINREL) || + read_conf_i(RELFINAL) || read_conf_t(MAXLAT) || + read_conf_t(MINLAT) || + read_conf(PGPPUBRING) || read_conf(PGPSECRING) || + read_conf(PASSPHRASE) || read_conf_t(KEYLIFETIME) || + read_conf_t(KEYGRACEPERIOD) || read_conf_t(KEYOVERLAPPERIOD) || +#ifdef USE_SOCK + read_conf_i(POP3DEL) || read_conf_i(POP3SIZELIMIT) || + read_conf_t(POP3TIME) || +#endif /* USE_SOCK */ + read_conf(MAILBOX) || read_conf(MAILABUSE) || + read_conf(MAILBLOCK) || read_conf(MAILUSAGE) || + read_conf(MAILANON) || read_conf(MAILERROR) || + read_conf(MAILBOUNCE) || read_conf(MAILIN) || + + read_conf(DISCLAIMFILE) || read_conf(FROMDSCLFILE) || + read_conf(MSGFOOTERFILE) || + read_conf(POP3CONF) || read_conf(HELPFILE) || + read_conf(REQUESTDIR) || + read_conf(ABUSEFILE) || read_conf(REPLYFILE) || + read_conf(USAGEFILE) || read_conf(USAGELOG) || + read_conf(BLOCKFILE) || read_conf(ADMKEYFILE) || + read_conf(KEYFILE) || read_conf(PGPKEY) || + read_conf(DSAPARAMS) || read_conf(DHPARAMS) || + read_conf(MIXRAND) || read_conf(SECRING) || + read_conf(PUBRING) || read_conf(IDLOG) || + read_conf(STATS) || read_conf(DESTBLOCK) || + read_conf(PGPMAXCOUNT) || + read_conf(DESTALLOW) || read_conf(DESTALLOW2) || + read_conf(SOURCEBLOCK) || + read_conf(STAREX) || read_conf(ALLPINGERSURL) || + read_conf(ALLPINGERSFILE) || + read_conf(HDRFILTER) || read_conf(REGULAR) || + read_conf(POOL) || read_conf(TYPE1LIST) || + read_conf(TYPE2REL) || + read_conf(PGPREMPUBRING) || read_conf(PGPREMPUBASC) || + read_conf(PGPREMSECRING) || read_conf(NYMSECRING) || + read_conf(NYMDB) || read_conf(PIDFILE) || + read_conf(WGET) || read_conf(STATSSRC) || + read_conf_i(STATSAUTOUPDATE) || read_conf_t(STATSINTERVAL) || + + read_conf_i(CLIENTAUTOFLUSH) || + read_conf_i(MAXRECIPIENTS) || + + read_conf_t(TIMESKEW_FORWARD) || + read_conf_t(TIMESKEW_BACK) || + read_conf_i(TEMP_FAIL) ); +} + +int mix_config(void) +{ + char *d; + FILE *f; + char line[PATHMAX]; + int err = -1; +#ifdef POSIX + struct passwd *pw; +#endif /* POSIX */ + struct stat buf; +#ifdef HAVE_UNAME + struct utsname uts; +#endif /* HAVE_UNAME */ +#ifdef WIN32 + HKEY regsw, reg, regpgp; + DWORD type, len; + int rkey = 0; +#endif /* WIN32 */ + + mix_setdefaults(); + +#ifdef POSIX + pw = getpwuid(getuid()); +#endif /* POSIX */ + + /* find our base directory + * + * first match wins. + * + * - what the MIXPATH environment variable points to, if it is set. + * - On WIN32, HKEY_CURRENT_USER\Software\Mixmaster\MixDir, if it exists + * - whatever is compiled in with -DSPOOL + * - On Win32 %APPDATA%\Mixmaster + * - on POSIX, ~/Mix (or ~/<HOMEMIXDIR>) + * - the current working directory + */ + + if (err == -1 && (d = getenv("MIXPATH")) != NULL) + err = mixdir(d, 1); + +#ifdef WIN32 + RegOpenKeyEx(HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, &regsw); + len=sizeof(line); + if (err == -1 && + RegOpenKeyEx(regsw, "Mixmaster", 0, KEY_QUERY_VALUE, &reg) == 0) { + if (RegQueryValueEx(reg, "MixDir", 0, &type, line, &len) == 0) + err = mixdir(line, 1); + RegCloseKey(reg); + } +#endif /* WIN32 */ + +#ifdef SPOOL + if (err == -1 && strlen(SPOOL) > 0) + err = mixdir(SPOOL, 0); +#endif /* SPOOL */ + +#ifdef WIN32 + if (err == -1) { + LPMALLOC lpmalloc; + ITEMIDLIST *itemidlist; + if (SUCCEEDED(SHGetMalloc(&lpmalloc))) + { + SHGetSpecialFolderLocation(0,CSIDL_APPDATA,&itemidlist); + SHGetPathFromIDList(itemidlist,line); + lpmalloc->lpVtbl->Free(lpmalloc,&itemidlist); + lpmalloc->lpVtbl->Release(lpmalloc); + + strcatn(line, "\\Mixmaster", PATHMAX); + err = mixdir(line, 1); + + } + } +#endif /* WIN32 */ + +#ifdef POSIX + if (err == -1 && pw != NULL) { + strncpy(line, pw->pw_dir, PATHMAX); + line[PATHMAX-1] = '\0'; + if (line[strlen(line) - 1] != DIRSEP) + strcatn(line, DIRSEPSTR, PATHMAX); + strcatn(line, HOMEMIXDIR, PATHMAX); + err = mixdir(line, 1); + } +#endif /* POSIX */ + + if (err == -1) { + getcwd(MIXDIR, PATHMAX); + mixdir(MIXDIR, 0); + } + +#ifdef GLOBALMIXCONF + f = mix_openfile(GLOBALMIXCONF, "r"); + if (f != NULL) { + while (fgets(line, LINELEN, f) != NULL) + if (line[0] > ' ' && line[0] != '#') + mix_configline(line); + fclose(f); + } +#endif /* GLOBALMIXCONF */ + f = mix_openfile(MIXCONF, "r"); + if (f != NULL) { + while (fgets(line, LINELEN, f) != NULL) + if (line[0] > ' ' && line[0] != '#') + mix_configline(line); + fclose(f); + } + + mixfile(POOLDIR, POOL); /* set POOLDIR after reading POOL from cfg file */ + if (POOLDIR[strlen(POOLDIR) - 1] == DIRSEP) + POOLDIR[strlen(POOLDIR) - 1] = '\0'; + if (stat(POOLDIR, &buf) != 0) + if +#ifndef POSIX + (mkdir(POOLDIR) != 0) +#else /* end of not POSIX */ + (mkdir(POOLDIR, S_IRWXU) == -1) +#endif /* else if POSIX */ + strncpy(POOLDIR, MIXDIR, PATHMAX); + + if (IDEXP > 0 && IDEXP < 5 * SECONDSPERDAY) + IDEXP = 5 * SECONDSPERDAY; + if (MAXRANDHOPS > 20) + MAXRANDHOPS = 20; + if (INDUMMYP > INDUMMYMAXP) + INDUMMYP = INDUMMYMAXP; + if (OUTDUMMYP > OUTDUMMYMAXP) + OUTDUMMYP = OUTDUMMYMAXP; + + if (strchr(SHORTNAME, '.')) + *strchr(SHORTNAME, '.') = '\0'; + if (strchr(SHORTNAME, ' ')) + *strchr(SHORTNAME, ' ') = '\0'; +#ifdef HAVE_UNAME + if (SHORTNAME[0] == '\0' && uname(&uts) != -1) + strncpy(SHORTNAME, uts.nodename, LINELEN); +#elif defined(HAVE_GETHOSTNAME) /* end of HAVE_UNAME */ + if (SHORTNAME[0] == '\0') + gethostname(SHORTNAME, LINELEN); +#endif /* defined(HAVE_GETHOSTNAME) */ + if (SHORTNAME[0] == '\0') + strcpy(SHORTNAME, "unknown"); + + if (ADDRESS[0] == '\0') + whoami(ADDRESS, "user"); + +#ifdef HAVE_GECOS + if (NAME[0] == '\0' && pw != NULL) + strcatn(NAME, pw->pw_gecos, sizeof(NAME)); +#endif /* HAVE_GECOS */ + + if (REMAILERADDR[0] == '\0') + strncpy(REMAILERADDR, ADDRESS, LINELEN); + + if (COMPLAINTS[0] == '\0') + strncpy(COMPLAINTS, REMAILERADDR, LINELEN); + + if (strchr(REMAILERNAME, '@') == NULL) { + strcatn(REMAILERNAME, " <", LINELEN); + strcatn(REMAILERNAME, REMAILERADDR, LINELEN); + strcatn(REMAILERNAME, ">", LINELEN); + } + if (strchr(ANONNAME, '@') == NULL && ANONADDR[0] != '\0') { + strcatn(ANONNAME, " <", LINELEN); + strcatn(ANONNAME, ANONADDR, LINELEN); + strcatn(ANONNAME, ">", LINELEN); + } + if (strchr(ANONNAME, '@') == NULL) { + strcatn(ANONNAME, " <", LINELEN); + strcatn(ANONNAME, REMAILERADDR, LINELEN); + strcatn(ANONNAME, ">", LINELEN); + } +#ifndef USE_PGP + if (TYPE1[0] == '\0') + PGP = 0; +#endif /* not USE_PGP */ + +#ifdef WIN32 + if (RegOpenKeyEx(regsw, "PGP", 0, KEY_ALL_ACCESS, &regpgp) == 0) + rkey++; + if (rkey && RegOpenKeyEx(regpgp, "PGPlib", 0, KEY_QUERY_VALUE, &reg) == 0) + rkey++; + if (PGPPUBRING[0] == '\0' && rkey == 2) { + len = PATHMAX; + RegQueryValueEx(reg, "PubRing", 0, &type, PGPPUBRING, &len); + } + if (PGPSECRING[0] == '\0' && rkey == 2) { + len = PATHMAX; + RegQueryValueEx(reg, "SecRing", 0, &type, PGPSECRING, &len); + } + if (rkey == 2) + RegCloseKey(reg); + if (rkey) + RegCloseKey(regpgp); + RegCloseKey(regsw); +#endif /* WIN32 */ + + if (PGPPUBRING[0] == '\0') { + char *d; + + if ((d = getenv("HOME")) != NULL) { + strcpy(PGPPUBRING, d); + strcatn(PGPPUBRING, "/.pgp/", PATHMAX); + } + strcatn(PGPPUBRING, "pubring.pkr", PATHMAX); + if (stat(PGPPUBRING, &buf) == -1) + strcpy(strrchr(PGPPUBRING, '.'), ".pgp"); + } + if (PGPSECRING[0] == '\0') { + char *d; + + if ((d = getenv("HOME")) != NULL) { + strcpy(PGPSECRING, d); + strcatn(PGPSECRING, "/.pgp/", PATHMAX); + } + strcatn(PGPSECRING, "secring.skr", PATHMAX); + if (stat(PGPSECRING, &buf) == -1) + strcpy(strrchr(PGPSECRING, '.'), ".pgp"); + } + if (streq(NEWS, "mail-to-news")) + strncpy(NEWS, MAILtoNEWS, sizeof(NEWS)); + + if (f == NULL) { +#ifndef GLOBALMIXCONF + /* Only write the config file in non systemwide installation */ + f = mix_openfile(MIXCONF, "w"); + if (f == NULL) + errlog(WARNING, "Can't open %s%s!\n", MIXDIR, MIXCONF); + else { + fprintf(f, "# mix.cfg - mixmaster configuration file\n"); + fprintf(f, "NAME %s\n", NAME); + fprintf(f, "ADDRESS %s\n", ADDRESS); + fprintf(f, "\n# edit to set up a remailer:\n"); + fprintf(f, "REMAIL n\n"); + fprintf(f, "SHORTNAME %s\n", SHORTNAME); + fprintf(f, "REMAILERADDR %s\n", REMAILERADDR); + fprintf(f, "COMPLAINTS %s\n", COMPLAINTS); + fclose(f); + } +#endif /* not GLOBALMIXCONF */ + REMAIL = 0; + } + + if (ENTEREDPASSPHRASE[0] != '\0') { + strncpy(PASSPHRASE, ENTEREDPASSPHRASE, LINELEN); + PASSPHRASE[LINELEN-1] = 0; + }; + + return (0); +} + +/** Library initialization: ******************************************/ + +static int initialized = 0; + +void mix_check_timeskew() { + FILE *f; + long now, tpool = 0, tpop3 = 0, tdaily = 0, tmailin = 0, latest = 0; + + f = mix_openfile(REGULAR, "r+"); + if (f != NULL) { + lock(f); + fscanf(f, "%ld %ld %ld %ld", &tpool, &tpop3, &tdaily, &tmailin); + latest = tpool; + latest = latest > tpop3 ? latest : tpop3; + latest = latest > tdaily ? latest : tdaily; + latest = latest > tmailin ? latest : tmailin; + now = time(NULL); + + + if (( (TIMESKEW_BACK != 0) && (now < latest - TIMESKEW_BACK )) || + ( (TIMESKEW_FORWARD != 0) && (now > latest + TIMESKEW_FORWARD)) ) { + /* Possible timeskew */ + errlog(ERRORMSG, "Possible timeskew detected. Check clock and rm %s\n", REGULAR); + exit(TEMP_FAIL); + } + fclose(f); + } else { + /* shrug */ + } +} + +int mix_init(char *mixdir) +{ + if (!initialized) { + if (mixdir) + strncpy(MIXDIR, mixdir, LINELEN); + mix_config(); +#if defined(USE_SOCK) && defined(WIN32) + sock_init(); +#endif /* defined(USE_SOCK) && defined(WIN32) */ + /* atexit (mix_exit); */ + initialized = 1; + } + + if (rnd_init() == -1) + rnd_seed(); + return(0); +} + +void mix_exit(void) +{ + if (!initialized) + return; + rnd_final(); +#if defined(USE_SOCK) && defined(WIN32) + sock_exit(); +#endif /* defined(USE_SOCK) && defined(WIN32) */ + initialized=0; +} + +void mix_upd_stats(void) +{ + FILE *f; + BUFFER *statssrc; + statssrc = buf_new(); + buf_clear(statssrc); + f = mix_openfile(STATSSRC, "r"); + if (f != NULL) { + buf_read(statssrc, f); + fclose(f); + } + if (statssrc->length > 0) + download_stats(statssrc->data); + buf_free(statssrc); +} + +int mix_regular(int force) +{ + FILE *f; + long now, tpool = 0, tpop3 = 0, tdaily = 0, tmailin = 0, tstats = 0; + int ret = 0; + + mix_init(NULL); + now = time(NULL); + + f = mix_openfile(REGULAR, "r+"); + if (f != NULL) { + lock(f); + fscanf(f, "%ld %ld %ld %ld %ld", &tpool, &tpop3, &tdaily, &tmailin, &tstats); + if (now - tpool >= SENDPOOLTIME) + force |= FORCE_POOL | FORCE_MAILIN; +#ifdef USE_SOCK + if (now - tpop3 >= POP3TIME) + force |= FORCE_POP3 | FORCE_MAILIN; +#endif /* USE_SOCK */ + if (now - tdaily >= SECONDSPERDAY) + force |= FORCE_DAILY; + if (now - tmailin >= MAILINTIME) + force |= FORCE_MAILIN; + if (now - tstats >= STATSINTERVAL) + force |= FORCE_STATS; + if (force & FORCE_POOL) + tpool = now; + if (force & FORCE_POP3) + tpop3 = now; + if (force & FORCE_DAILY) + tdaily = now; + if (force & FORCE_MAILIN) + tmailin = now; + if (force & FORCE_STATS) + tstats = now; + rewind(f); + fprintf(f, "%ld %ld %ld %ld %ld\n", tpool, tpop3, tdaily, tmailin, tstats); + unlock(f); + fclose(f); + } else { + force = FORCE_POOL | FORCE_POP3 | FORCE_DAILY | FORCE_MAILIN | FORCE_STATS; + f = mix_openfile(REGULAR, "w+"); + if (f != NULL) { + lock(f); + fprintf(f, "%ld %ld %ld %ld %ld\n", now, now, now, now, now); + unlock(f); + fclose(f); + } else + errlog(ERRORMSG, "Can't create %s!\n", REGULAR); + } + + if (force & FORCE_DAILY) + mix_daily(), ret = 1; +#ifdef USE_SOCK + if (force & FORCE_POP3) + pop3get(); +#endif /* USE_SOCK */ + if (force & FORCE_MAILIN) + ret = process_mailin(); + if (force & FORCE_POOL) + ret = pool_send(); + if ((force & FORCE_STATS) && (STATSAUTOUPDATE != 0)) + mix_upd_stats(); + + return (ret); +} + +int mix_daily(void) +{ + idexp(); + pgpmaxexp(); + pool_packetexp(); + stats(NULL); + keymgt(0); + return (0); +} + +/** Handle signals SIGHUP, SIGINT, and SIGTERM + This signal handler gets called if the daemon + process receives one of SIGHUP, SIGINT, or SIGTERM. + It then sets either rereadconfig of terminatedaemon + to true depending on the signal received. + + @author PP + @return nothing + */ +#ifdef POSIX +void sighandler(int signal) { + if (signal == SIGHUP) + rereadconfig = 1; + else if (signal == SIGINT || signal == SIGTERM) + terminatedaemon = 1; +}; +#endif /* POSIX */ + +/** Set the signal handler for SIGHUP, SIGINT and SIGTERM + This function registers signal handlers so that + we can react on signals send by the user in daemon + mode. SIGHUP will instruct mixmaster to reload its + configuration while SIGINT and SIGTERM will instruct + it to shut down. Mixmaster will finish the current + pool run before it terminates. + + @param restart Whether or not system calls should be + restarted. Usually we want this, the + only excetion is the sleep() in the + daemon mail loop. + @author PP + @return -1 if calling sigaction failed, 0 on + no error + */ +int setsignalhandler(int restart) +{ +#ifdef POSIX + struct sigaction hdl; + int err = 0; + + memset(&hdl, 0, sizeof(hdl)); + hdl.sa_handler = sighandler; + hdl.sa_flags = restart ? SA_RESTART : 0; + + if (sigaction(SIGHUP, &hdl, NULL)) + err = -1; + if (sigaction(SIGINT, &hdl, NULL)) + err = -1; + if (sigaction(SIGTERM, &hdl, NULL)) + err = -1; + return (err); +#else /* POSIX */ + return(0); +#endif /* POSIX */ +} + +#ifdef WIN32 +/* Try to detect if we are the service or not... + seems there is no easy reliable way */ +int is_nt_service(void) +{ + static int issvc = -1; +#ifdef WIN32SERVICE + STARTUPINFO StartupInfo; + OSVERSIONINFO VersionInfo; + DWORD dwsize; + + if (issvc != -1) /* do it only once */ + return issvc; + + VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo); + if (GetVersionEx(&VersionInfo)) + if (VersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT) + return issvc = 0; /* not NT - not the service */ + + GetStartupInfo(&StartupInfo); + if (StartupInfo.lpDesktop[0] == 0) + return issvc = 1; /* have no desktop - we are the service probably */ +#endif /* WIN32SERVICE */ + + return issvc = 0; /* assume not the service */ +} /* is_nt_service */ + +HANDLE hMustTerminate = NULL; +void set_nt_exit_event(HANDLE h_svc_exit_event) +{ + hMustTerminate = h_svc_exit_event; +} /* set_nt_exit_event */ + +#endif /* WIN32 */ + +int mix_daemon(void) +{ + long t, slept; + t = SENDPOOLTIME; + if (MAILINTIME < t && (MAILIN != NULL && MAILIN[0] != '\0')) + t = MAILINTIME; +#ifdef USE_SOCK + if (POP3TIME < t) + t = POP3TIME; +#endif /* USE_SOCK */ + if (t < 5) + t = 5; /* Some kind of safety for broken systems */ + slept = t; + + setsignalhandler(1); /* set signal handlers and restart any interrupted system calls */ + for(;;) { + if (terminatedaemon) + break; + if (rereadconfig) { + rereadconfig = 0; + mix_config(); + t = SENDPOOLTIME; + if (MAILINTIME < t && (MAILIN != NULL && MAILIN[0] != '\0')) + t = MAILINTIME; +#ifdef USE_SOCK + if (POP3TIME < t) + t = POP3TIME; + if (t < 5) + t = 5; /* Some kind of safety for broken systems */ +#endif /* USE_SOCK */ + } + if (slept >= t) { + mix_regular(0); + slept = 0; + } + +#ifdef WIN32SERVICE + if (hMustTerminate) { + if (WaitForSingleObject(hMustTerminate, t * 1000) == WAIT_OBJECT_0) { + CloseHandle(hMustTerminate); + terminatedaemon = 1; + } + } +#endif /* WIN32SERVICE */ + + if (!terminatedaemon && !rereadconfig) { + setsignalhandler(0); /* set signal handlers; don't restart system calls */ +#ifdef WIN32 + sleep(t); /* how to get the real number of seconds slept? */ + slept = t; +#else /* end of WIN32 */ + slept += (t - slept) - sleep(t - slept); +#endif /* else if not WIN32 */ + setsignalhandler(1); /* set signal handlers and restart any interrupted system calls */ + } + } + return (0); +} + +/** error ***************************************************************/ + +void errlog(int type, char *fmt,...) +{ + va_list args; + BUFFER *msg; + FILE *e = NULL; + time_t t; + struct tm *tc; + char line[LINELEN]; + int p; + char err[6][8] = + {"", "Error", "Warning", "Notice", "Info", "Info"}; + + if ((VERBOSE == 0 && type != ERRORMSG) || (type == LOG && VERBOSE < 2) + || (type == DEBUGINFO && VERBOSE < 3)) + return; + + t = time(NULL); + tc = localtime(&t); + strftime(line, LINELEN, "[%Y-%m-%d %H:%M:%S] ", tc); + + msg = buf_new(); + buf_appends(msg, line); + p = msg->length; + buf_appendf(msg, "%s: ", err[type]); + va_start(args, fmt); + buf_vappendf(msg, fmt, args); + va_end(args); + + if (streq(ERRLOG, "stdout")) + e = stdout; + else if (streq(ERRLOG, "stderr")) + e = stderr; + + if (e == NULL && (ERRLOG[0] == '\0' || + (e = mix_openfile(ERRLOG, "a")) == NULL)) + mix_status("%s", msg->data + p); + else { + buf_write(msg, e); + if (e != stderr && e != stdout) { + fclose(e); + /* duplicate the error message on screen */ + mix_status("%s", msg->data + p); + } + } + buf_free(msg); +} + +static char statusline[BUFSIZE] = ""; + +void mix_status(char *fmt,...) +{ + va_list args; + + if (fmt != NULL) { + va_start(args, fmt); +#ifdef _MSC + _vsnprintf(statusline, sizeof(statusline) - 1, fmt, args); +#else /* end of _MSC */ + vsnprintf(statusline, sizeof(statusline) - 1, fmt, args); +#endif /* else if not _MSC */ + va_end(args); + } +#ifdef USE_NCURSES + if (menu_initialized) { + cl(LINES - 2, 10); + printw("%s", statusline); + refresh(); + } else +#endif /* USE_NCURSES */ + { + fprintf(stderr, "%s", statusline); + } +} + +void mix_genericerror(void) +{ + if (streq(statusline, "") || strfind(statusline, "...") || + strifind(statusline, "generating")) + mix_status("Failed!"); + else + mix_status(NULL); +} diff --git a/Src/mix.h b/Src/mix.h @@ -0,0 +1,917 @@ +/* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. + + Mixmaster may be redistributed and modified under certain conditions. + This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF