diff -ruN smap.old/LICENSE.MPL smap/LICENSE.MPL --- smap.old/LICENSE.MPL Wed Dec 31 18:00:00 1969 +++ smap/LICENSE.MPL Wed Sep 23 06:45:24 1998 @@ -0,0 +1,360 @@ + MOZILLA PUBLIC LICENSE + Version 1.0 + + ---------------- + +1. Definitions. + + 1.1. ``Contributor'' means each entity that creates or contributes to + the creation of Modifications. + + 1.2. ``Contributor Version'' means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. ``Covered Code'' means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. ``Electronic Distribution Mechanism'' means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. ``Executable'' means Covered Code in any form other than Source + Code. + + 1.6. ``Initial Developer'' means the individual or entity identified as + the Initial Developer in the Source Code notice required by Exhibit A. + + 1.7. ``Larger Work'' means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. ``License'' means this document. + + 1.9. ``Modifications'' means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. ``Original Code'' means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this License + is not already Covered Code governed by this License. + + 1.11. ``Source Code'' means the preferred form of the Covered Code 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, or a list of source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. ``You'' means an individual or a legal entity exercising rights + under, and complying with all of the terms of, this License or a future + version of this License issued under Section 6.1. For legal entities, + ``You'' includes any entity which controls, is controlled by, or is + under common control with You. For purposes of this definition, + ``control'' means (a) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (b) ownership of fifty percent (50%) or more of the + outstanding shares or beneficial ownership of such entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + + (a) to use, reproduce, modify, display, perform, sublicense and + distribute the Original Code (or portions thereof) with or without + Modifications, or as part of a Larger Work; and + + (b) under patents now or hereafter owned or controlled by Initial + Developer, to make, have made, use and sell (``Utilize'') the + Original Code (or portions thereof), but solely to the extent that + any such patent is reasonably necessary to enable You to Utilize + the Original Code (or portions thereof) and not to any greater + extent that may be necessary to Utilize further Modifications or + combinations. + + 2.2. Contributor Grant. + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + + (a) to use, reproduce, modify, display, perform, sublicense and + distribute the Modifications created by such Contributor (or + portions thereof) either on an unmodified basis, with other + Modifications, as Covered Code or as part of a Larger Work; and + + (b) under patents now or hereafter owned or controlled by + Contributor, to Utilize the Contributor Version (or portions + thereof), but solely to the extent that any such patent is + reasonably necessary to enable You to Utilize the Contributor + Version (or portions thereof), and not to any greater extent that + may be necessary to Utilize further Modifications or combinations. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be distributed + only under the terms of this License or a future version of this + License released under Section 6.1, and You must include a copy of this + License with every copy of the Source Code You distribute. You may not + offer or impose any terms on any Source Code version that alters or + restricts the applicable version of this License or the recipients' + rights hereunder. However, You may include an additional document + offering the additional rights described in Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which you contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that the + Modification is derived, directly or indirectly, from Original Code + provided by the Initial Developer and including the name of the Initial + Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + + (a) Third Party Claims. + If You have knowledge that a party claims an intellectual property + right in particular functionality or code (or its utilization + under this License), you must include a text file with the source + code distribution titled ``LEGAL'' which describes the claim and + the party making the claim in sufficient detail that a recipient + will know whom to contact. If you obtain such knowledge after You + make Your Modification available as described in Section 3.2, You + shall promptly modify the LEGAL file in all copies You make + available thereafter and shall take other steps (such as notifying + appropriate mailing lists or newsgroups) reasonably calculated to + inform those who received the Covered Code that new knowledge has + been obtained. + + (b) Contributor APIs. + If Your Modification is an application programming interface and + You own or control patents which are reasonably necessary to + implement that API, you must also include this information in the + LEGAL file. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code, and this License in any documentation for the Source Code, where + You describe recipients' rights relating to Covered Code. If You + created one or more Modification(s), You may add your name as a + Contributor to the notice described in Exhibit A. If it is not possible + to put such notice in a particular Source Code file due to its + structure, then you must include such notice in a location (such as a + relevant directory file) where a user would be likely to look for such + a notice. You may choose to offer, and to charge a fee for, warranty, + support, indemnity or liability obligations to one or more recipients + of Covered Code. However, You may do so only on Your own behalf, and + not on behalf of the Initial Developer or any Contributor. You must + make it absolutely clear than any such warranty, support, indemnity or + liability obligation is offered by You alone, and You hereby agree to + indemnify the Initial Developer and every Contributor for any liability + incurred by the Initial Developer or such Contributor as a result of + warranty, support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of the + Covered Code is available under the terms of this License, including a + description of how and where You have fulfilled the obligations of + Section 3.2. The notice must be conspicuously included in any notice in + an Executable version, related documentation or collateral in which You + describe recipients' rights relating to the Covered Code. You may + distribute the Executable version of Covered Code under a license of + Your choice, which may contain terms different from this License, + provided that You are in compliance with the terms of this License and + that the license for the Executable version does not attempt to limit + or alter the recipient's rights in the Source Code version from the + rights set forth in this License. If You distribute the Executable + version under a different license You must make it absolutely clear + that any terms which differ from this License are offered by You alone, + not by the Initial Developer or any Contributor. You hereby agree to + indemnify the Initial Developer and every Contributor for any liability + incurred by the Initial Developer or such Contributor as a result of + any such terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to statute + or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and + the code they affect. Such description must be included in the LEGAL + file described in Section 3.4 and must be included with all + distributions of the Source Code. Except to the extent prohibited by + statute or regulation, such description must be sufficiently detailed + for a recipient of ordinary skill to be able to understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A, and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation (``Netscape'') may publish revised + and/or new versions of the License from time to time. Each version will + be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If you create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), you must (a) rename Your license so that the + phrases ``Mozilla'', ``MOZILLAPL'', ``MOZPL'', ``Netscape'', ``NPL'' or + any confusingly similar phrase do not appear anywhere in your license + and (b) otherwise make it clear that your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial Developer, + Original Code or Contributor in the notice described in Exhibit A shall + not of themselves be deemed to be modifications of this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN ``AS IS'' BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL 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 + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + 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. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a ``commercial item,'' as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of ``commercial computer + software'' and ``commercial computer software documentation,'' as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if any, + provides otherwise), excluding its conflict-of-law provisions. With + respect to disputes in which at least one party is a citizen of, or an + entity chartered or registered to do business in, the United States of + America: (a) unless otherwise agreed in writing, all disputes relating + to this License (excepting any dispute relating to intellectual + property rights) shall be subject to final and binding arbitration, + with the losing party paying all costs of arbitration; (b) any + arbitration relating to this Agreement shall be held in Santa Clara + County, California, under the auspices of JAMS/EndDispute; and (c) any + litigation relating to this Agreement shall be subject to the + jurisdiction of the Federal Courts of the Northern District of + California, with venue lying in Santa Clara County, California, with + the losing party responsible for costs, including without limitation, + court costs and reasonable attorneys fees and expenses. 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. + +12. RESPONSIBILITY FOR CLAIMS. + + Except in cases where another Contributor has failed to comply with + Section 3.4, You are responsible for damages arising, directly or + indirectly, out of Your utilization of rights under this License, based + on the number of copies of Covered Code you made available, the + revenues you received from utilizing such rights, and other relevant + factors. You agree to work with affected parties to distribute + responsibility on an equitable basis. + +EXHIBIT A. + + ``The contents of this file are subject to the Mozilla Public License + Version 1.0 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________.'' diff -ruN smap.old/Makefile smap/Makefile --- smap.old/Makefile Tue Mar 26 15:05:05 1996 +++ smap/Makefile Mon Nov 30 00:16:16 1998 @@ -15,13 +15,16 @@ CFLAGS= -I.. $(COPT) -all: smap +all: smap mkspamdb -smap: smap.o arpadate.o ../libfwall.a - $(CC) $(LDFL) -o $@ smap.o arpadate.o ../libfwall.a $(AUXLIB) +smap: smap.o arpadate.o base64.o md5.o nospam.o ../libfwall.a + $(CC) $(LDFL) -o $@ smap.o arpadate.o base64.o md5.o nospam.o ../libfwall.a $(AUXLIB) $(DBMLIB) + +mkspamdb: mkspamdb.o + $(CC) $(LDFL) -o $@ mkspamdb.o $(DBMLIB) clean: - rm -f smap smap.o arpadate.o core + rm -f smap smap.o arpadate.o base64.o nospam.o mkspamdb.o mkspamdb core install: all if [ -f $(DEST)/smap ]; then \ @@ -29,3 +32,9 @@ fi $(CP) smap $(DEST) chmod 755 $(DEST)/smap + + if [ -f $(DEST)/mkspamdb ]; then \ + mv $(DEST)/mkspamdb $(DEST)/mkspamdb.old; \ + fi + $(CP) mkspamdb $(DEST) + chmod 755 $(DEST)/mkspamdb diff -ruN smap.old/base64.c smap/base64.c --- smap.old/base64.c Wed Dec 31 18:00:00 1969 +++ smap/base64.c Wed Sep 23 06:45:25 1998 @@ -0,0 +1,189 @@ +/* $Id: base64.c,v 1.10 1998/01/27 01:20:03 aas Exp $ + +Copyright (c) 1997,1998 Gisle Aas + +The tables and some of the code is borrowed from metamail, which comes +with this message: + + Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore) + + Permission to use, copy, modify, and distribute this material + for any purpose and without fee is hereby granted, provided + that the above copyright notice and this permission notice + appear in all copies, and that the name of Bellcore not be + used in advertising or publicity pertaining to this + material without the specific, prior written permission + of an authorized representative of Bellcore. BELLCORE + MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY + OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS", + WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. + +*/ + + +#include + +static char basis_64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +#define XX 255 /* illegal base64 char */ +#define EQ 254 /* padding */ +static unsigned char index_64[256] = { + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63, + 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,EQ,XX,XX, + XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX, + XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX, + + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, +}; + +#define MAX_LINE 76 +#define MMGETC(str,len) (len > 0 ? len--,*str++ : EOF) +#define INVALID_B64(c) (index_64[(unsigned char)c] == XX) + +char *decode_base64(istr) + char *istr; +{ + char *ostr, *obuf; + int len, rlen; + int c1, c2, c3, c4; + + len = strlen(istr); + rlen = len * 3 / 4; + if((ostr = obuf = (char *)malloc(rlen ? rlen + 1: 1)) == (char *)0) + return (char *)0; + + while ((c1 = MMGETC(istr, len)) != EOF) { + if (INVALID_B64(c1)) + continue; + do { + c2 = MMGETC(istr, len); + } while (c2 != EOF && INVALID_B64(c2)); + do { + c3 = MMGETC(istr, len); + } while (c3 != EOF && INVALID_B64(c3)); + do { + c4 = MMGETC(istr, len); + } while (c4 != EOF && INVALID_B64(c4)); + + if (c2 == EOF || c3 == EOF || c4 == EOF) { + free(obuf); + return (char *)0; + } else if (c1 == '=' || c2 == '=') { + free(obuf); + return (char *)0; + } + + c1 = index_64[c1]; + c2 = index_64[c2]; + *ostr++ = (c1<<2) | ((c2&0x30)>>4); + + if (c3 == '=') { + break; + } else { + c3 = index_64[c3]; + *ostr++ = ((c2&0XF) << 4) | ((c3&0x3C) >> 2); + } + if (c4 == '=') { + break; + } else { + c4 = index_64[c4]; + *ostr++ = ((c3&0x03) <<6) | c4; + } + } + *ostr = '\0'; + return obuf; +} + +char *encode_base64(istr) + char *istr; +{ + unsigned char c1, c2, c3; + int chunk, eollen; + char *ostr, *obuf, *eol; + int len, rlen; + + len = strlen(istr); + eol = "\r\n"; + eollen = 2; + + /* calculate the length of the result */ + rlen = (len+2) / 3 * 4; /* encoded bytes */ + + if (rlen) { + /* add space for EOL */ + rlen += ((rlen-1) / MAX_LINE + 1) * eollen; + } + + if((ostr = obuf = (char *)malloc(rlen ? rlen + 1: 1)) == (char *)0) + return (char *)0; + + /* encode */ + for (chunk=0; len > 0; len -= 3, chunk++) { + if (chunk == (MAX_LINE/4)) { + char *c = eol; + char *e = eol + eollen; + while (c < e) + *ostr++ = *c++; + chunk = 0; + } + c1 = *istr++; + c2 = *istr++; + *ostr++ = basis_64[c1>>2]; + *ostr++ = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)]; + if (len > 2) { + c3 = *istr++; + *ostr++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; + *ostr++ = basis_64[c3 & 0x3F]; + } else if (len == 2) { + *ostr++ = basis_64[(c2 & 0xF) << 2]; + *ostr++ = '='; + } else { /* len == 1 */ + *ostr++ = '='; + *ostr++ = '='; + } + } + + if (rlen) { + /* append eol to the result string */ + char *c = eol; + char *e = eol + eollen; + while (c < e) + *ostr++ = *c++; + } + + *ostr = '\0'; + return obuf; +} + +#if 0 +void main(argc, argv) + int argc; + char *argv[]; +{ + char *str; + + if(argc != 2) + return; + + str = decode_base64(argv[1]); + + if(str) { + printf("Decoded: %s\n", str); + free(str); + } else { + printf("Bad MIME string detected\n"); + } +} +#endif diff -ruN smap.old/md5.c smap/md5.c --- smap.old/md5.c Wed Dec 31 18:00:00 1969 +++ smap/md5.c Mon Nov 30 00:49:29 1998 @@ -0,0 +1,497 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm +*/ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ +#include +#include +#include "md5global.h" +#include "md5.h" + +/* Constants for MD5Transform routine. +*/ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); +static void Encode PROTO_LIST + ((unsigned char *, UINT4 *, unsigned int)); +static void Decode PROTO_LIST + ((UINT4 *, unsigned char *, unsigned int)); +static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); +static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + + */ +#ifdef I +/* This might be defined via NANA */ +#undef I +#endif + +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + + */ + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. +*/ + +#define FF(a, b, c, d, x, s, ac) { (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define GG(a, b, c, d, x, s, ac) { (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define HH(a, b, c, d, x, s, ac) { (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define II(a, b, c, d, x, s, ac) { (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. +*/ + +void MD5Init (context) +MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + + /* Load magic initialization constants. + +*/ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the context. +*/ + +void MD5Update (context, input, inputLen) +MD5_CTX *context; /* context */ +unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. + +*/ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)input, partLen); MD5Transform + (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)&input[i], + inputLen-i); + +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + + */ + +void MD5Final (digest, context) +unsigned char digest[16]; /* message digest */ +MD5_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. + +*/ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. + +*/ + MD5_memset ((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + + */ + +static void MD5Transform (state, block) +UINT4 state[4]; +unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + */ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + + */ + +static void Encode (output, input, len) +unsigned char *output; +UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + + */ + +static void Decode (output, input, len) +UINT4 *output; +unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) + | (((UINT4)input[j+3]) << 24); +} + +/* Note: Replace "for loop" with standard memcpy if possible. + + */ + +static void MD5_memcpy (output, input, len) +POINTER output; +POINTER input; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. +*/ + +static void MD5_memset (output, value, len) +POINTER output; +int value; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} + + +void hmac_md5(text, text_len, key, key_len, digest) + +unsigned char* text; /* pointer to data stream */ +int text_len; /* length of data stream */ +unsigned char* key; /* pointer to authentication key */ +int key_len; /* length of authentication key */ +caddr_t digest; /* caller digest to be filled in */ +{ + MD5_CTX context; + + unsigned char k_ipad[65]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[65]; /* outer padding - + * key XORd with opad + */ + unsigned char tk[16]; + int i; + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + + MD5_CTX tctx; + + MD5Init(&tctx); + MD5Update(&tctx, key, key_len); + MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + bzero( k_ipad, sizeof k_ipad); + bzero( k_opad, sizeof k_opad); + bcopy( key, k_ipad, key_len); + bcopy( key, k_opad, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + /* + * perform inner MD5 + */ + + MD5Init(&context); /* init context for 1st + * pass */ + MD5Update(&context, k_ipad, 64); /* start with inner pad */ + MD5Update(&context, text, text_len); /* then text of datagram */ + MD5Final(digest, &context); /* finish up 1st pass */ + + /* + * perform outer MD5 + */ + MD5Init(&context); /* init context for 2nd + * pass */ + MD5Update(&context, k_opad, 64); /* start with outer pad */ + MD5Update(&context, digest, 16); /* then results of 1st + * hash */ + MD5Final(digest, &context); /* finish up 2nd pass */ + +} + +/* + * Copyright 1998 by the University of Washington + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appears in all copies and that both the + * above copyright notice and this permission notice appear in supporting + * documentation, and that the name of the University of Washington not be + * used in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. This software is made available + * "as is", and + * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, + * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN + * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ +/* + * RFC 2104 HMAC hashing + * Accepts: text to hash + * text length + * key + * key length + * Returns: hash as text, always + */ + +#define DIGLEN 16 +#define PADLEN 64 + +char *md5_hash (char *text,unsigned long tl,char *key,unsigned long kl) +{ + int i,j; + static char hshbuf[2*DIGLEN + 1]; + char *s; + MD5_CTX ctx; + char *hex = "0123456789abcdef"; + unsigned char digest[DIGLEN],k_ipad[PADLEN+1],k_opad[PADLEN+1],tk[DIGLEN]; + if (kl > PADLEN) { /* key longer than pad length? */ + MD5Init (&ctx); /* yes, set key as MD5(key) */ + MD5Update (&ctx,(unsigned char *) key,kl); + MD5Final (tk,&ctx); + key = (char *) tk; + kl = DIGLEN; + } + /* store key in pads */ + memcpy (k_opad,memcpy (memset (k_ipad,0,PADLEN+1),key,kl),PADLEN+1); + /* XOR key with ipad and opad values */ + for (i = 0; i < PADLEN; i++) {/* for each byte of pad */ + k_ipad[i] ^= 0x36; /* XOR key with ipad */ + k_opad[i] ^= 0x5c; /* and opad values */ + } + MD5Init (&ctx); /* inner MD5: hash ipad and text */ + MD5Update (&ctx,k_ipad,PADLEN); + MD5Update (&ctx,(unsigned char *) text,tl); + MD5Final (digest,&ctx); + MD5Init (&ctx); /* outer MD5: hash opad and inner results */ + MD5Update (&ctx,k_opad,PADLEN); + MD5Update (&ctx,digest,DIGLEN); + MD5Final (digest,&ctx); + /* convert to printable hex */ + for (i = 0, s = hshbuf; i < DIGLEN; i++) { + *s++ = hex[(j = digest[i]) >> 4]; + *s++ = hex[j & 0xf]; + } + *s = '\0'; /* tie off hash text */ + return hshbuf; +} diff -ruN smap.old/md5.h smap/md5.h --- smap.old/md5.h Wed Dec 31 18:00:00 1969 +++ smap/md5.h Mon Nov 30 00:17:03 1998 @@ -0,0 +1,37 @@ +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init PROTO_LIST ((MD5_CTX *)); +void MD5Update PROTO_LIST + ((MD5_CTX *, unsigned char *, unsigned int)); +void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); + +void hmac_md5 PROTO_LIST ((unsigned char *, int, unsigned char *, int, caddr_t)); Binary files smap.old/md5.o and smap/md5.o differ diff -ruN smap.old/md5global.h smap/md5global.h --- smap.old/md5global.h Wed Dec 31 18:00:00 1969 +++ smap/md5global.h Mon Nov 30 00:17:03 1998 @@ -0,0 +1,31 @@ +/* GLOBAL.H - RSAREF types and constants + */ + +/* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. +The following makes PROTOTYPES default to 0 if it has not already + been defined with C compiler flags. + */ +#ifndef PROTOTYPES +#define PROTOTYPES 0 +#endif + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +typedef unsigned long int UINT4; + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. +If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif + diff -ruN smap.old/mkspamdb.c smap/mkspamdb.c --- smap.old/mkspamdb.c Wed Dec 31 18:00:00 1969 +++ smap/mkspamdb.c Wed Sep 23 06:45:25 1998 @@ -0,0 +1,155 @@ +/*--------------------------------------------------------------------------- + * SaberNet.net + *--------------------------------------------------------------------------- + * + * @(#)mkspamdb.c 1.12 12/02/97 + * + * Creates a DB from a spam list. Requires ndbm(3). + * + * There is no warranty or other guarantee of fitness of this software. + * It is provided solely "as is". The author(s) disclaim(s) all + * responsibility and liability with respect to this software's usage + * or its effect upon hardware or computer systems. + * + * Author: Jason R. Rhoads - 26 August 1997 + * + *--------------------------------------------------------------------------- + * Data should be made up of a single column of data. Fields may be IP + * addresses, hostnames or email addresses. + * + * Example: + * + * 205.199.193 + * 205.218.84 + * 205.218.85 + * 205.218.86 + * awgroup.com + * babchurch.org + * bealeader.com + * @becsplace.com + * betterways.net + * bhoman.com + * csource@mercury.af2.com + * + * Comments may me placed to the right of the data when separated by + * whitespace. + * + * Successive runs will add new entries to an existing DB. Remove the .dir + * and .pag files to rebuild a DB from scratch. + * + * For an up to date spam list, send email to spamlist@us.net + *---------------------------------------------------------------------------*/ +#include +#include +#include +#include + +static const char cUseage[] = + "usage: mkspamdb [-v|-l|-r] -f -d \n"; + +static void lowercase(char *s) +{ + while ((*s = tolower(*s)) !=0) s++; +} + +int main(int argc, char **argv) +{ + DBM *db; + FILE *fp; + datum key; + datum val = { "", 1 }; + char buffer[80]; + int status; + int opt; + + int verbose = 0; + int insert = 1; + int listKeys = 0; + char *spamList = NULL; + char *dbName = NULL; + + while ((opt = getopt(argc, argv, "vlrf:d:")) != EOF) + switch (opt) + { + case 'r' : insert = 0; + break; + case 'v' : verbose = 1; + break; + case 'l' : listKeys = 1; + break; + case 'f' : spamList = optarg; + break; + case 'd' : dbName = optarg; + break; + } + + if ( (!listKeys && spamList == NULL) || (dbName == NULL) ) + { + fprintf(stderr, cUseage); + return(2); + } + + if ( (db = dbm_open(dbName, O_RDWR | O_CREAT, 0660)) == (DBM *)0) + { + fprintf(stderr, "Unable to open db: %s\n", dbName); + return(1); + } + + if (listKeys) + { + key = dbm_firstkey(db); + while (key.dptr != NULL) + { + printf("%s\n", key.dptr); + key = dbm_nextkey(db); + } + } + else + { + if ( (fp = fopen(spamList, "r")) == (FILE *)0 ) + { + fprintf(stderr, "Unable to open input file: %s\n", spamList); + exit(1); + } + + while ( (fgets(buffer, 256, fp) != (char *)0) && (!feof(fp)) ) + { + if (buffer != (char *)0) + { + char *ktmp = (char *)strtok(buffer, " \t\n"); + + if (ktmp != (char *)0) + { + if (ktmp[0] == '@') ktmp++; + lowercase(ktmp); + key.dptr = ktmp; + key.dsize = strlen(ktmp) + 1; + if (insert) + { + status = dbm_store(db, key, val, DBM_INSERT); + } + else + { + status = dbm_delete(db, key); + } + + if (verbose) + { + if (insert) + { + fprintf(stdout, "%40s %s\n", ktmp, + (status ? "skipped - already exists" : "added") ); + } + else + { + fprintf(stdout, "%40s %s\n", ktmp, + (status ? "skipped - does not exist" : "removed") ); + } + } + } + } + } + } + dbm_close(db); + return(0); +} diff -ruN smap.old/nospam.c smap/nospam.c --- smap.old/nospam.c Wed Dec 31 18:00:00 1969 +++ smap/nospam.c Wed Sep 23 06:45:25 1998 @@ -0,0 +1,561 @@ +/*--------------------------------------------------------------------------- + * SaberNet.net + *--------------------------------------------------------------------------- + * + * @(#)nospam.c 1.27 05/28/98 + * + * Verifies the sender's addresses and mailrelay against a DB + * of known spammers. Requires ndbm(3). + * + * There is no warranty or other guarantee of fitness of this software. + * It is provided solely "as is". The author(s) disclaim(s) all + * responsibility and liability with respect to this software's usage + * or its effect upon hardware or computer systems. + * + * Author: Jason R. Rhoads - 26 August 1997 + * + * + * Revisions: + * + * 07-May-98 JRR Added support for wildcards in relay-domain + * 05-May-98 JRR Added more comments/cleaned up code. + * 28-Mar-98 JRR Added inRelayDomain(). Allow empty sender from internal + * hosts. + * 05-Mar-98 JRR Added check for empty sender. + * 25-Mar-98 ALB Made empty sender check optional (comply with RFC821) + * 25-Feb-98 JRR Added error message for mailFromSpammer() + * 02-Dec-97 JRR Added support for Paul Vixie's MAPS RBL + * 10-Nov-97 JRR Added support for multiple relay-domain lines. + * 07-Nov-97 JRR Fixed core dump when the relay-domain is not specified. + * 05-Nov-97 JRR Fixed domain choping for top level domains. + * 02-Oct-97 JRR Added DNS query to verify sender's email address. + * 01-Oct-97 JRR Added support for multiple relay-domain entries + * (http://maps.vix.com/rbl/usage.html) + * 18-Sep-97 JRR Added relay blocker + * + *---------------------------------------------------------------------------*/ +#include "firewall.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __STDC__ +extern void smap_printf(const char *fmt, ...); +#else +extern void smap_printf(); +#endif + +/*--------------------------------[static data]-------------------------------*/ +#define RBL_DOM ".rbl.maps.vix.com" + + +/*------------------------------[local functions]-----------------------------*/ +static int option_selected(const char *pri, const char *sec, Cfg *configTable); +static char *strmcpy(const char* s); +static char* rblcpy(const char* addr); +static int cntchr(char *s, char c); +static char *chop_domain(char *s); +static int spam_db_key_exists(char *s, DBM *db); + + +/*--------------------------------[init_resolv]-------------------------------- + * Establishes a static TCP connection to a nameserver. + * + * Paramaters: + * hostname Current hostname + * + * Note: + * Must be called before chroot(1M) + *----------------------------------------------------------------------------*/ +void init_resolv(char *hostname) +{ + union + { + HEADER hdr; /* defined in resolv.h */ + u_char buf[PACKETSZ]; /* defined in arpa/nameserver.h */ + } response; + + res_init(); + _res.options |=RES_USEVC|RES_STAYOPEN; + res_query(hostname, C_IN, T_NS, (u_char *)&response, sizeof(response)); +} + +/*------------------------------[matches_spam_db]------------------------------ + * Checks the sender's IP and email addresses against the spam database + * + * Paramaters: + * sender Sender's email address from the rcpt to: command + * hostname Mail client's hostname + * addr Mail client's IP address + * config Pointer to the configuration table + * + * Returns: + * 1 Match + * 0 No match or spam-database not configured + * + *----------------------------------------------------------------------------*/ +int matches_spam_db(char *sender, char *hostname, char *addr, Cfg *config) +{ + Cfg *cf; + DBM *db; + datum key, val; + char *senderCopy; + char *senderDomain; + char *dbPath = (char *)0; + int returnValue = 0; + + /* Handle empty from: lines */ + if (option_selected("nospam", "RFC821", config) == 0) + { + if ( (strlen(sender) == 0) || (strcmp(sender, "<>") == 0) + || (strcmp(sender, "<@>") == 0) ) + { + smap_printf("550 sender required\r\n"); + return(1); + } + } + + if ((cf = cfg_get("spam-database", config)) != (Cfg *)0) + { + if (cf->argc != 1) + { + syslog(LLEV, "spam-database must have one parameter, line %d", cf->ln); + return(0); + } + dbPath = cf->argv[0]; + } + + /* Did we get a path? */ + if (dbPath == (char *)0) + { + return(0); + } + + /* Malloc a copy of the sender */ + if ((senderCopy = strmcpy(sender)) == (char *)0) + { + syslog(LLEV, "fwtksyserr: out of memory: %m"); + return(0); + } + + /* Open the database */ + if ((db = dbm_open(dbPath, O_RDONLY, 0440)) == (DBM *)0) + { + syslog(LLEV, "error opening spam-database: %s", dbPath); + return(0); + } + + senderDomain = chop_domain(senderCopy); + + if ( ((senderDomain != (char *)0) && + (spam_db_key_exists(senderDomain, db))) || + (spam_db_key_exists(senderCopy, db)) ) + { + smap_printf("550 sender ignored: %s matches spam database entry\r\n", + sender); + returnValue = 1; + } + else if ( (spam_db_key_exists(chop_domain(hostname), db)) || + (spam_db_key_exists(hostname, db)) || + (spam_db_key_exists(addr, db)) ) + { + smap_printf("550 sender ignored: %s/%s matches spam database entry\r\n", + hostname, addr); + returnValue = 1; + } + + dbm_close(db); + free(senderCopy); + return (returnValue); +} + +/*------------------------------[valid_recipient]------------------------------ + * External clients may only address mail to local destinations. Local + * clients may send mail to both internal and external sites. + * + * Paramaters: + * rcpt Recipient's email address + * addr Mail client's IP address + * config Pointer to the configuration table + * + * Returns: + * 1 Valid or relay-domain not configured + * 0 Invalid + *----------------------------------------------------------------------------*/ +int valid_recipient(char *rcpt, char *addr, Cfg *config) +{ + Cfg *cf; + char *rcptCopy; + char *rcptDomain; + int status = 1; + + /* Check for relay-domain option */ + if ((cf = cfg_get("relay-domain", config)) == (Cfg *)0) + { + return(1); + } + + /* Check if client matches a relay-domain */ + if (relay_domain(addr, config) == 1) + { + return(1); + } + + /* Malloc a copy of rcpt */ + if ((rcptCopy = strmcpy(rcpt)) == (char *)0) + { + syslog(LLEV, "fwtksyserr: out of memory: %m"); + return(1); + } + + rcptDomain = chop_domain(rcptCopy); + + /* Check if the mail is destined for a relay-domain */ + if (relay_domain(rcptDomain, config) == 0) + { + smap_printf("571 %s... we do not relay\r\n", rcpt); + status = 0; + } + free(rcptCopy); + + return(status); +} + +/*-------------------------------[valid_sender]------------------------------- + * Verifies that the mail client has a valid NS record. + * + * Paramaters: + * sender Mail client's email address + * config Pointer to the configuration table + * + * Returns: + * 0 No NS record found + * 1 NS record found or ns-required not configured + *----------------------------------------------------------------------------*/ +int valid_sender(char *sender, Cfg *configTable) +{ + char *sp; + char *senderCopy; + int returnValue = 1; + int cnt; + union + { + HEADER hdr; /* defined in resolv.h */ + u_char buf[PACKETSZ]; /* defined in arpa/nameserver.h */ + } response; + + if ( (option_selected("nospam", "ns-required", configTable) == 0) || + (sender == (char *)0) ) + { + return(1); + } + + if ((senderCopy = strmcpy(sender)) == (char *)0) + { + syslog(LLEV, "fwtksyserr: out of memory: %m"); + return(1); + } + + sp = chop_domain(senderCopy); + + if (res_query(sp, C_IN, T_NS, (u_char *)&response, sizeof(response)) < 0) + { + if (h_errno == HOST_NOT_FOUND) + { + smap_printf("550 sender ignored: %s does not have a name server record\r\n", sp); + returnValue = 0; + } + } + + free(senderCopy); + return(returnValue); +} + + +/*-----------------------------[matches_maps_rbl]----------------------------- + * Checks the mail client's IP address agains Paul Vixie's MAPS RBL + * + * Paramaters: + * addr Mail client's IP address + * config Pointer to the configuration table + * + * Returns: + * 0 No match or maps-rbl not configured + * 1 Match + *----------------------------------------------------------------------------*/ +int matches_maps_rbl(char *addr, Cfg *configTable) +{ + Cfg *cf; + char *raddr; + int returnValue = 0; + int x; + + if ( (option_selected("nospam", "maps-rbl", configTable) == 0) || + (addr == (char *)0) ) + return(0); + + if ((raddr = rblcpy(addr)) == (char *)0) + { + syslog(LLEV, "fwtksyserr: out of memory: %m"); + return(0); + } + + if (gethostbyname(raddr) != NULL) + { + smap_printf("550 sender ignored: %s was found in the MAPS RBL\r\n", + addr); + returnValue = 1; + } + + free(raddr); + return(returnValue); +} + +/*-------------------------------[relay_domain]------------------------------- + * Attempts to match an IP address with the relay_domain patterns + * + * Paramaters: + * addr IP address to match + * config Pointer to the configuration table + * + * Returns: + * 1 Match + * 0 No match + *----------------------------------------------------------------------------*/ +int relay_domain(char *addr, Cfg *config) +{ + char *local = NULL; + char *rval; + int cnt; + Cfg *cf = cfg_get("relay-domain", config); + + for (; cf != (Cfg *)0; cf = cfg_get("relay-domain",(Cfg *)0)) + { + if (cf->argc > 0) + { + for (cnt = 0; cnt < cf->argc; cnt++) + { + local=cf->argv[cnt]; + if (hostmatch(local, addr) == 1) + { + return(1); + } + } + } + } + return(0); +} + + + +/*------------------------------[option_selected]------------------------------ + * Checks for nospam options + * + * Paramaters: + * section Section to check in (nospam) + * option Option to look for + * config Pointer to the configuration table + * + * Returns: + * 0 Not selected + * 1 Selected + *----------------------------------------------------------------------------*/ +static int option_selected(const char *section, const char *option, Cfg *config) +{ + int cnt; + Cfg *cf = cfg_get(section, config); + + for (; cf != (Cfg *)0; cf = cfg_get(section, (Cfg *)0)) + { + if (cf->argc > 0) + { + for (cnt = 0; cnt < cf->argc; cnt++) + { + if (strcasecmp(cf->argv[cnt], option) == 0) + return(1); + } + } + } + + return(0); +} + +/*----------------------------------[strmcpy]---------------------------------- + * Malloc and copy a string + * + * Paramaters: + * s String to copy + * + * Returns: + * char* New string or NULL if malloc failed + * + * Note: + * User is responsible for cleanup. + *----------------------------------------------------------------------------*/ +static char *strmcpy(const char* s) +{ + int len = strlen(s); + char *newString = malloc((len+1)*sizeof(char)); + int i, j = 0; + + if (newString) + { + for (i=0; i < len; i++) + { + if (s[i] != '<' && s[i] != '>') + { + newString[j++] = tolower(s[i]); + } + } + newString[j] = 0; + } + + return(newString); +} + +/*----------------------------------[rblcpy]---------------------------------- + * Creates a new string by reversing addr and appending the rbl domain + * Example: a.b.c.d => d.c.b.a.rbl.maps.vix.com + * + * Paramater: + * addr Mail client's IP address + * + * Returns: + * char* New string or NULL if malloc failed + * + * Note: + * User is responsible for clean up. + *----------------------------------------------------------------------------*/ +static char* rblcpy(const char* addr) +{ + int addrlen = strlen(addr); + int len = addrlen + strlen(RBL_DOM); + char *newString = malloc((len+1)*sizeof(char)); + int i; + int j = 0; + int k = addrlen; + + if (newString) + { + for (i=addrlen - 1; i >= 0; i--) + { + if ((addr[i] == '.') || (i == 0)) + { + int l = (i ? i + 1 : i); + + for (;l < k; l++) newString[j++] = addr[l]; + newString[j++] = '.'; + k = i; + } + } + newString[strlen(addr)] = 0; + strcat(newString, RBL_DOM); + } + + return(newString); +} + +/*----------------------------------[cntchr]---------------------------------- + * Returns the number of times c appears in s + *----------------------------------------------------------------------------*/ +static int cntchr(char *s, char c) +{ + char *sp = s; + int cnt = 0; + + if (sp == (char *)NULL) return(0); + + while (*sp != '\0') { if ( *sp == c) cnt ++; sp++; } + + return(cnt); +} + +/*--------------------------------[chop_domain]-------------------------------- + * Chops US top level domains to x.x and others to x.x.x + * + * Paramater: + * s String to chop + * + * Returns: + * s Modified string or NULL on error + *----------------------------------------------------------------------------*/ +static char *chop_domain(char *s) +{ + int dots; + char *rval = s; + + if (s == (char *)0) return(s); + + rval = strrchr(s, '@'); + if (rval == (char *)NULL) { rval = s; } else { rval++; } + + dots = cntchr(rval, '.'); + while ( (rval != (char *)NULL) && (dots-- > 2) ) + { + if ((rval = strchr(rval, '.')) != (char *)NULL) rval++; + } + + if ( (rval != (char *)NULL) && (dots) ) + { + char *last = strrchr(s, '.'); + if (last != (char *)NULL) + { + last++; + if ( (strcmp(last, "com") == 0) || + (strcmp(last, "edu") == 0) || + (strcmp(last, "net") == 0) || + (strcmp(last, "org") == 0) || + (strcmp(last, "gov") == 0) || + (strcmp(last, "mil") == 0) || + (strcmp(last, "firm") == 0) || + (strcmp(last, "store") == 0) || + (strcmp(last, "web") == 0) || + (strcmp(last, "arts") == 0) || + (strcmp(last, "rec") == 0) || + (strcmp(last, "info") == 0) || + (strcmp(last, "nom") == 0) ) + { + if ((rval = strchr(rval, '.')) != (char *)NULL) rval++; + } + } + } + + if (rval != (char *)NULL) return(rval); + + return(s); +} + +/*----------------------------[spam_db_key_exists]---------------------------- + * Check for key in the spam database + * + * Parmaters: + * s Key to check for + * db Database handle + * + * Returns: + * 0 Key not found + * 1 Key found + *----------------------------------------------------------------------------*/ +static int spam_db_key_exists(char *s, DBM *db) +{ + datum key, val; + static char* matchingKey; + + key.dptr = s; + key.dsize = strlen(s) + 1; + val = dbm_fetch(db, key); + + matchingKey = val.dptr; + + return(val.dptr != NULL); +} + + diff -ruN smap.old/smap.c smap/smap.c --- smap.old/smap.c Sat Jan 18 14:40:57 1997 +++ smap/smap.c Thu Dec 3 15:25:23 1998 @@ -1,4 +1,4 @@ -/* +/*-*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * Copyright (c) 1993, 1997 Trusted Information Systems, Incorporated * All rights reserved. * @@ -8,8 +8,16 @@ /* * Author: Marcus J. Ranum, Trusted Information Systems, Inc. + * SSL Support by: Habeeb J. Dihu (MacGyver) + * SASL Support by: Habeeb J. Dihu (MacGyver) + * SASL and SSL patches are Copyright(C) 1997, 1998 + * Habeeb J. Dihu (MacGyver) , All Rights Reserved. + * + * The SSL and SASL patches are distributed under the Mozilla + * Public License, v1.0. Read the file LICENSE.MPL in the smap + * directory for details, or http://www.mozilla.org/MPL/. */ -static char RcsId[] = "$Header: /usr/home/rick/fwtk2.0/fwtk/smap/RCS/smap.c,v 1.19 1997/01/18 20:40:53 rick Exp $"; +static char RcsId[] = "$Header: /usr/home/rick/fwtk2.0/fwtk/smap/RCS/smap.c,v 1.20 1997/03/17 22:29:44 rick Exp $"; #include @@ -25,9 +33,33 @@ #include #include #include +#include + +#ifdef SMAP_AUTH_SHADOW +#include +#elif defined(SMAP_AUTH_PAM) +#include +#else +#include +#include +#endif + +#ifdef SMAP_AUTH_SKEY +#include "skey.h" +#endif /* SMAP_AUTH_SKEY */ + +#ifdef SMAP_SSL +#include "ssl.h" +#endif /* SMAP_SSL */ #include "firewall.h" +#if __STDC__ +#include +#else +#include +#endif + /* smap - sendmail wrapper. @@ -47,7 +79,17 @@ mjr. 1993 */ +extern int matches_spam_db(char *sender, char *hostname, char *addr, Cfg *config); +extern int valid_recipient(char *rcpt, char *addr, Cfg *config); +extern int valid_sender(char *sender, Cfg *configTable); +extern int matches_maps_rbl(char *addr, Cfg *configTable); +extern int relay_domain(char *addr, Cfg *config); + +extern char *decode_base64(); +extern char *encode_base64(); + extern char *strtok(); +extern char *strpbrk(); extern char *optarg; @@ -71,8 +113,23 @@ static int curbytes = 0; static int currecip = 0; static int timeout = PROXY_TIMEOUT; +static int port = 25; static int livetim = 0; static int msgsize = 0; +static int authorized = 0; +static int authsock = -1; +static pid_t authpid = -1; +#ifdef SMAP_SSL +static SSL_CTX *sslcontext = (SSL_CTX *)0; +static SSL *sslconn = (SSL *)0; +static int sslenabled = 0; +static int sslverify = SSL_VERIFY_NONE; +#endif /* SMAP_SSL */ + +#ifdef SMAP_AUTH_CRAM_MD5 +static char *md5file = (char *)0; +extern char *md5_hash(); +#endif /* SMAP_AUTH_CRAM_MD5 */ static FILE *smapopen(); static int crlfgets(); @@ -85,6 +142,109 @@ char *tmpfile; } *filelist = (struct file_list *) 0; +#if defined(SMAP_AUTH_LOGIN) || defined(SMAP_AUTH_SHADOW) || defined(SMAP_AUTH_PAM) +#define SM_AUTH_LOGIN 0x001 +#define SM_AUTH_LOGIN_GETUSER 10 +#define SM_AUTH_LOGIN_GETPASS 20 +#endif /* SMAP_AUTH_LOGIN || SMAP_AUTH_SHADOW || SMAP_AUTH_PAM */ + +#ifdef SMAP_AUTH_SKEY +#define SM_AUTH_SKEY 0x002 +#define SM_AUTH_SKEY_GETUSER 100 +#define SM_AUTH_SKEY_GETCHAL 200 +#endif /* SMAP_AUTH_SKEY */ + +#ifdef SMAP_AUTH_CRAM_MD5 +#define SM_AUTH_CRAM_MD5 0x004 +#define SM_AUTH_CRAM_MD5_CHAL 1000 +#endif /* SMAP_AUTH_CRAM_MD5 */ + +#ifdef SMAP_AUTH_PAM +static char *pam_user = (char *)0; +static char *pam_pass = (char *)0; +static int pam_conv_error = 0; + +static int pam_exchange(num_msg, msg, resp, appdata_ptr) + int num_msg; + struct pam_message **msg; + struct pam_response **resp; + void *appdata_ptr; +{ + int i; + struct pam_response *response = NULL; + + response = malloc(sizeof(struct pam_response) * num_msg); + if(response == (struct pam_response *)0) { + syslog(LLEV,"fwtksyserr: out of memory: %m"); + close(authsock); + exit(1); + } + + for(i = 0; i < num_msg; i++) { + response[i].resp_retcode = PAM_SUCCESS; + + switch(msg[i]->msg_style) { + case PAM_PROMPT_ECHO_ON: + response[i].resp = pam_user; + /* PAM frees resp */ + break; + + case PAM_PROMPT_ECHO_OFF: + response[i].resp = pam_pass; + /* PAM frees resp */ + break; + + case PAM_TEXT_INFO: + case PAM_ERROR_MSG: + /* ignore it, but pam still wants a NULL response... */ + response[i].resp = NULL; + break; + + default: + /* Must be an error of some sort... */ + free(response); + pam_conv_error = 1; + return PAM_CONV_ERR; + } + } + + *resp = response; + return PAM_SUCCESS; +} + +static struct pam_conv pam_conv = { + &pam_exchange, + NULL +}; +#endif /* SMAP_AUTH_PAM */ + +void +#if __STDC__ +smap_printf(const char *fmt, ...) +#else +smap_printf(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + char buf[BUFSIZ * 4]; + va_list ap; + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); +#ifdef SMAP_SSL + if(sslenabled) + SSL_write(sslconn, buf, strlen(buf)); + else +#endif /* SMAP_SSL */ + { printf(buf); fflush(stdout); } +} + static void ringring() { @@ -112,19 +272,61 @@ char opt; FILE *vout = (FILE *)0; int gotdata = 0; + int authbits = 0, authstate = 0; + char *authuser = (char *)0, *authpass = (char *)0, *dp; struct hostent *hp; +#ifdef SMAP_AUTH_SHADOW + struct spwd *authp; +#elif defined(SMAP_AUTH_PAM) + int pam_error; + pam_handle_t *pamh = NULL; +#else + struct pwd *authp; +#endif + +#ifdef SMAP_AUTH_SKEY + struct skey skey; + char skeybuf[80]; + int skeystatus = -1, skeyverstat = -1; +#endif /* SMAP_AUTH_SKEY */ + +#ifdef SMAP_SSL + char *ssl_cert_file = NULL, *ssl_key_file = NULL; +#endif /* SMAP_SSL */ + #ifndef LOG_DAEMON openlog("smap",LOG_PID); #else openlog("smap",LOG_PID|LOG_NDELAY,LFAC); #endif +#ifdef SMAP_SSL + if(!strcmp(av[0], "smap-ssl")) + sslenabled = 1; +#endif /* SMAP_SSL */ + if (ac > 1 && !strcmp(av[1], "-daemon")) { int sock, sockl; struct sockaddr_in sa; int pid; + if (ac > 2) { + if(isalldigits(av[2])) + port = atoi(av[2]); + else { + struct servent *sp; + + sp = getservbyname(av[2], "tcp"); + if(sp != (struct servent *)0) + port = ntohs(sp->s_port); + else { + syslog(LLEV, "fwtksyserr: Unknown port/service: %s",av[2]); + exit(1); + } + } + } + sa.sin_family = AF_INET; bzero((char *)&sa.sin_addr, sizeof(sa.sin_addr)); sa.sin_port = htons(25); @@ -134,7 +336,7 @@ exit(1); } if (bind(sock, (struct sockaddr *)&sa, sizeof(sa))) { - syslog(LLEV,"fwtksyserr: Failed to bind port 25: %m"); + syslog(LLEV,"fwtksyserr: Failed to bind port %d: %m", port); exit(1); } if (listen(sock, 5) < 0) { @@ -170,7 +372,7 @@ close(sockl); close(sock); } - + if(peername(0,rladdr,riaddr,sizeof(riaddr))) { syslog(LLEV,"cannot get remote host"); exit(1); @@ -182,10 +384,12 @@ strcpy(myhostname,"amnesiac"); else if ((hp = gethostbyname(myhostname)) != 0) strcpy(myhostname,hp->h_name); - + if((cfp = cfg_read("smap")) == (Cfg *)-1) exit(1); - + + init_resolv(myhostname); + if((cf = cfg_get("groupid",cfp)) != (Cfg *)0) { if(cf->argc != 1) { syslog(LLEV,"fwtkcfgerr: groupid must have one parameter, line %d",cf->ln); @@ -235,6 +439,76 @@ maxrecip = atoi(cf->argv[0]); } +#ifdef SMAP_AUTH_CRAM_MD5 + if((cf = cfg_get("md5file",cfp)) != (Cfg *)0) { + if(cf->argc != 1) { + syslog(LLEV,"fwtkcfgerr: md5file must have one parameter, line %d",cf->ln); + exit(1); + } + + md5file = cf->argv[0]; + } +#endif /* SMAP_AUTH_CRAM_MD5 */ + + if((cf = cfg_get("auth",cfp)) != (Cfg *)0) { + for(x = 0; x < cf->argc; x++) { + if(0) { +#if defined(SMAP_AUTH_LOGIN) || defined(SMAP_AUTH_SHADOW) || defined(SMAP_AUTH_PAM) + } else if(!strcasecmp(cf->argv[x], "login")) { + authbits |= SM_AUTH_LOGIN; +#endif /* SMAP_AUTH_LOGIN || SMAP_AUTH_SHADOW || SMAP_AUTH_PAM */ +#ifdef SMAP_AUTH_SKEY + } else if(!strcasecmp(cf->argv[x], "skey")) { + authbits |= SM_AUTH_SKEY; +#endif /* SMAP_AUTH_SKEY */ +#ifdef SMAP_AUTH_CRAM_MD5 + } else if(!strcasecmp(cf->argv[x], "cram-md5")) { + authbits |= SM_AUTH_CRAM_MD5; +#endif /* SMAP_AUTH_CRAM_MD5 */ + } else { + syslog(LLEV,"fwtkcfgerr: unrecognized auth specified, line %d",cf->ln); + exit(1); + } + } + } else { + /* Default to the 'original' behavior and relay all SMTP traffic. + */ + authorized = 1; + } + +#ifdef SMAP_SSL + if((cf = cfg_get("ssl-auth",cfp)) != (Cfg *)0) { + if(cf->argc != 1) { + syslog(LLEV,"fwtkcfgerr: ssl-auth must have one parameter, line %d",cf->ln); + exit(1); + } + + if(!strcasecmp(cf->argv[0],"none") || !strcasecmp(cf->argv[0],"false")) + sslverify = SSL_VERIFY_NONE; + else if(!strcasecmp(cf->argv[0],"optional")) + sslverify = SSL_VERIFY_PEER; + else if(!strcasecmp(cf->argv[0],"require")) + sslverify = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + else { + syslog(LLEV,"fwtkcfgerr: unrecognized ssl-auth specified, line %d",cf->ln); + exit(1); + } + } + + if((cf = cfg_get("ssl",cfp)) != (Cfg *)0) { + if(cf->argc != 1 && cf->argc != 2) { + syslog(LLEV,"fwtkcfgerr: ssl must have between one or two parameters, line %d",cf->ln); + exit(1); + } + + ssl_cert_file = ssl_key_file = cf->argv[0]; + + if(cf->argc == 2) + ssl_key_file = cf->argv[1]; + } else { + sslenabled = 0; + } +#endif /* SMAP_SSL */ if((cf = cfg_get("timeout",cfp)) != (Cfg *)0) { if(cf->argc != 1) { @@ -248,11 +522,211 @@ alarm(timeout); } + if(authbits != 0) { + int authpipe[2]; + + if(socketpair(AF_UNIX, SOCK_DGRAM, 0, authpipe) < 0) + return; + + authpid = fork(); + if (authpid < 0) { + syslog(LLEV,"fwtksyserr: Fork failed: %m"); + close(authpipe[0]); + close(authpipe[1]); + exit(1); + } + + if (authpid == 0) { + signal(SIGCHLD, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + close(authpipe[0]); + close(0); + close(1); + authsock = authpipe[1]; + + for(;;) { +#ifdef SMAP_AUTH_SHADOW + struct spwd *authp; +#else + struct passwd *authp; +#endif + char authbuf[BUFSIZ + 1], *p; + int len, authval; + +#ifdef SMAP_AUTH_CRAM_MD5 + char md5buf[BUFSIZ + 1], *md5p, *md5pass; + FILE *md5fp; +#endif /* SMAP_AUTH_CRAM_MD5 */ + + authval = 0; + if((len = read(authsock, &authstate, sizeof(authstate))) == 0) + break; + if(len < 0) { + if(errno == EINTR) { + errno = 0; + continue; + } + syslog(LLEV,"fwtksyserr: Read failed: %m"); + exit(1); + } + + if(authstate == 0) { + close(authsock); + exit(0); + } + + if((len = read(authsock, authbuf, BUFSIZ)) == 0) + break; + if(len < 0) { + if(errno == EINTR) { + errno = 0; + continue; + } + syslog(LLEV,"fwtksyserr: Read failed: %m"); + close(authsock); + exit(1); + } + + switch(authstate) { +#if defined(SMAP_AUTH_LOGIN) || defined(SMAP_AUTH_SHADOW) || defined(SMAP_AUTH_PAM) + case SM_AUTH_LOGIN_GETPASS: + authbuf[len] = '\0'; + if((p = (char *)strchr(authbuf, ':')) != (char *)0) + *p++ = '\0'; + if(!p || !*p) + goto authquit; + +#ifdef SMAP_AUTH_SHADOW + if((authp = getspnam(authbuf)) != (struct spwd *)0) { + if(!strcmp(authp->sp_pwdp,crypt(p,authp->sp_pwdp))) { + authval = 1; + } + } +#elif defined(SMAP_AUTH_PAM) + pam_user = malloc(strlen(authbuf) + 1); + if(pam_user == (char *)0) { + syslog(LLEV,"fwtksyserr: out of memory: %m"); + close(authsock); + exit(1); + } + strcpy(pam_user, authbuf); + + pam_pass = malloc(strlen(p) + 1); + if(pam_user == (char *)0) { + syslog(LLEV,"fwtksyserr: out of memory: %m"); + close(authsock); + exit(1); + } + strcpy(pam_pass, p); + + pam_error = pam_start("smap", pam_user, &pam_conv, &pamh); + if(pam_error != PAM_SUCCESS) { + syslog(LLEV,"fwtksyserr: %s",pam_strerror(pamh,pam_error)); + goto authquit; + } + + pam_error = pam_authenticate(pamh, PAM_SILENT); + if(pam_error == PAM_SUCCESS && !pam_conv_error && + (pam_error = pam_acct_mgmt(pamh, PAM_SILENT) == PAM_SUCCESS)) { + authval = 1; + } + + pam_end(pamh, pam_error); + pamh = (pam_handle_t *)0; +#else + if((authp = getpwnam(authbuf)) != (struct passwd *)0) { + if(!strcmp(authp->pw_passwd,crypt(p,authp->pw_passwd))) { + authval = 1; + } + } +#endif /* SMAP_AUTH_SHADOW */ + + memset(authbuf, 0, sizeof(authbuf)); + + authquit: + write(authsock, &authval, sizeof(authval)); + if(authval == 1) { + close(authsock); + exit(0); + } + continue; + +#endif /* SMAP_AUTH_LOGIN || SMAP_AUTH_SHADOW || SMAP_AUTH_PAM */ +#ifdef SMAP_AUTH_SKEY + case SM_AUTH_SKEY_GETUSER: + skeystatus = skeychallenge(&skey,authbuf,skeybuf); + write(authsock,&skeystatus,sizeof(skeystatus)); + write(authsock,skeybuf,sizeof(skeybuf)); + continue; + + case SM_AUTH_SKEY_GETCHAL: + skeyverstat = skeyverify(&skey,authbuf); + write(authsock,&skeyverstat,sizeof(skeyverstat)); + if(!skeystatus && !skeyverstat) { + close(authsock); + exit(0); + } + continue; +#endif /* SMAP_AUTH_SKEY */ +#ifdef SMAP_AUTH_CRAM_MD5 + case SM_AUTH_CRAM_MD5_CHAL: + if(md5file == (char *)0) + goto authquit; + + authbuf[len] = '\0'; + authuser = p = authbuf; + if((authuser = (char *)strchr(authbuf, ' ')) != (char *)0) + *authuser++ = '\0'; + if(!authuser || !*authuser) + goto authquit; + if((p = (char *)strchr(authuser, ' ')) != (char *)0) + *p++ = '\0'; + if(!p || !*p) + goto authquit; + + if((md5fp = fopen(md5file, "r")) == (FILE *)0) + goto authquit; + + while (fgets(md5buf, sizeof(md5buf) - 1, md5fp)) { + md5p = md5buf; + while(md5p && *md5p && isspace(*md5p)) + md5p++; + + if(*md5p == '#') + continue; + if((md5pass = (char *)strchr(md5p, '\t')) == (char *)0) + continue; + *md5pass++ = '\0'; + if(strcmp(authuser,md5p)) + continue; + if(dp = (char *)strpbrk(md5pass,"\r\n")) + *dp = '\0'; + if(!strcmp(p,md5_hash(authbuf,strlen(authbuf), + md5pass,strlen(md5pass)))) + authval = 1; + fclose(md5fp); + goto authquit; + } + continue; +#endif /* SMAP_AUTH_CRAM_MD5 */ + default: + close(authsock); + exit(0); + } + } + close(authsock); + exit(0); + } else { + close(authpipe[1]); + authsock = authpipe[0]; + } + } + if(rootdir != (char *)0) { chdir("/"); if((chdir(rootdir) || chroot(rootdir))) { syslog(LLEV,"fwtksyserr: cannot chroot to %.512s: %m",rootdir); - exit(1); + smap_exit(1); } chdir("/"); } @@ -260,16 +734,58 @@ if(rungid != -1 && setgid(rungid)) { syslog(LLEV,"fwtksyserr: cannot set gid to %d: %m",rungid); - exit(1); + smap_exit(1); } if(runuid != -1 && setuid(runuid)) { syslog(LLEV,"fwtksyserr: cannot set uid to %d: %m",runuid); - exit(1); + smap_exit(1); } - printf("220 %s SMTP/smap Ready.\r\n",myhostname); - fflush(stdout); +#ifdef SMAP_SSL + if(sslenabled) { + SSL_load_error_strings(); + SSLeay_add_ssl_algorithms(); + + sslcontext = SSL_CTX_new(SSLv23_server_method()); + + SSL_CTX_set_options(sslcontext, SSL_OP_ALL); + + if (SSL_CTX_need_tmp_RSA(sslcontext)) { + RSA *rsa; + + rsa = RSA_generate_key(512, RSA_F4, NULL, NULL); + + if(!SSL_CTX_set_tmp_rsa(sslcontext, rsa)) { + syslog(LLEV,"Unable to assign temporary (512 bit) RSA key."); + smap_exit(1); + } + + RSA_free(rsa); + } + + sslconn = SSL_new(sslcontext); + SSL_set_fd(sslconn, fileno(stdin)); + SSL_set_verify(sslconn, sslverify, NULL); + + if(!SSL_use_certificate_file(sslconn, ssl_cert_file, X509_FILETYPE_PEM)) { + syslog(LLEV,"Error loading certificate '%s': %s", ssl_cert_file, ERR_error_string(ERR_get_error(), NULL)); + smap_exit(1); + } + + if (!SSL_use_RSAPrivateKey_file(sslconn, ssl_key_file, X509_FILETYPE_PEM)) { + syslog(LLEV,"Error loading private key '%s': %s", ssl_cert_file, ERR_error_string(ERR_get_error(), NULL)); + smap_exit(1); + } + + if(SSL_accept(sslconn) == -1) { + syslog(LLEV,"Error accepting SSL connection: %s", ERR_error_string(ERR_get_error(), NULL)); + smap_exit(1); + } + } +#endif /* SMAP_SSL */ + + smap_printf("220 %s SMTP/smap Ready.\r\n",myhostname); #ifdef SO_KEEPALIVE @@ -282,10 +798,155 @@ syslog(LLEV,"peer dropped connection: %m"); break; } - + + if(authstate != 0) { + if(!strcasecmp(buf,"*")) { + smap_printf("504 Authentication cancelled\r\n"); + resetauth: + authstate = 0; + if(authuser != (char *)0) { + memset(authuser,0,strlen(authuser)); + free(authuser); + authuser = (char *)0; + } + if(authpass != (char *)0) { + memset(authpass,0,strlen(authpass)); + free(authpass); + authpass = (char *)0; + } + continue; + } + + if((p = strtok(buf," \r\t\n")) == (char *)0) { + smap_printf("501 Syntax error\r\n"); + goto resetauth; + } + + if((dp = decode_base64(p)) == (char *)0) { + smap_printf("501 Garbled base64 data\r\n"); + goto resetauth; + } + + switch(authstate) { +#if defined(SMAP_AUTH_LOGIN) || defined(SMAP_AUTH_SHADOW) || defined(SMAP_AUTH_PAM) + case SM_AUTH_LOGIN_GETUSER: + authuser = dp; + smap_printf("334 UGFzc3dvcmQ6\r\n"); + authstate = SM_AUTH_LOGIN_GETPASS; + continue; + + case SM_AUTH_LOGIN_GETPASS: + authpass = dp; + + /* We have the username and password, now authenticate 'em. + */ + p = malloc(strlen(authuser) + strlen(authpass) + 2); + if(p == (char *)0) + goto outofmemory; + sprintf(p,"%s:%s",authuser,authpass); + write(authsock,&authstate,sizeof(authstate)); + write(authsock,p,strlen(authuser) + strlen(authpass) + 2); + memset(authpass,0,strlen(authpass)); + free(authpass); + authpass = (char *)0; + memset(p,0,strlen(p)); + free(p); + read(authsock,&authorized,sizeof(authorized)); + if(authorized == 1) { + authstate = 0; + write(authsock, &authstate, sizeof(authstate)); + close(authsock); + authsock = -1; + smap_printf("235 LOGIN authentication successful\r\n"); + } else { + authorized = 0; + syslog(LLEV,"failed to authenticate user '%s'",authuser); + smap_printf("535 LOGIN authentication failed\r\n"); + } + memset(authuser,0,strlen(authuser)); + free(authuser); + authuser = (char *)0; + authstate = 0; + continue; +#endif /* SMAP_AUTH_LOGIN || SMAP_AUTH_SHADOW || SMAP_AUTH_PAM */ +#ifdef SMAP_AUTH_SKEY + case SM_AUTH_SKEY_GETUSER: + authuser = dp; + write(authsock,&authstate,sizeof(authstate)); + write(authsock,authuser,strlen(authuser) + 1); + read(authsock,&skeystatus,sizeof(skeystatus)); + read(authsock,skeybuf,sizeof(skeybuf)); + p = encode_base64(skeybuf); + if(p == (char *)0) + goto outofmemory; + smap_printf("334 %s",p); + free(p); + authstate = SM_AUTH_SKEY_GETCHAL; + continue; + + case SM_AUTH_SKEY_GETCHAL: + authpass = dp; + write(authsock,&authstate,sizeof(authstate)); + write(authsock,authpass,strlen(authpass) + 1); + read(authsock,&skeyverstat,sizeof(skeyverstat)); + + if(!skeystatus && !skeyverstat) { + authorized = 1; + smap_printf("235 SKEY authentication successful\r\n"); + } else { + smap_printf("235 SKEY authentication failed\r\n"); + } + memset(authuser,0,strlen(authuser)); + memset(authpass,0,strlen(authpass)); + free(authuser); + free(authpass); + authuser = authpass = (char *)0; + authstate = 0; + continue; +#endif /* SMAP_AUTH_SKEY */ +#ifdef SMAP_AUTH_CRAM_MD5 + case SM_AUTH_CRAM_MD5_CHAL: + authpass = dp; + dp = malloc(strlen(authpass) + strlen(authuser) + 5); + sprintf(dp, "%s %s", authuser, authpass); + write(authsock,&authstate,sizeof(authstate)); + write(authsock,dp,strlen(dp) + 1); + memset(authpass,0,strlen(authpass)); + memset(dp,0,strlen(dp)); + free(dp); + free(authpass); + authpass = dp = (char *)0; + read(authsock,&authorized,sizeof(authorized)); + if(authorized == 1) { + authstate = 0; + write(authsock, &authstate, sizeof(authstate)); + close(authsock); + authsock = -1; + smap_printf("235 CRAM-MD5 authentication successful\r\n"); + } else { + authorized = 0; + syslog(LLEV,"failed to authenticate user '%s'",authuser); + smap_printf("535 CRAM-MD5 authentication failed\r\n"); + } + + authstate = 0; + if(authuser != (char *)0) { + memset(authuser,0,strlen(authuser)); + free(authuser); + authuser = (char *)0; + } + continue; +#endif /* SMAP_AUTH_CRAM_MD5 */ + default: + smap_printf("535 Authentication failure\r\n"); + syslog(LLEV,"exiting - authentication protocol failure"); + unlink(tempfile); + smap_exit(1); + } + } + if((p = strtok(buf," \r\t\n")) == (char *)0) { - printf("500 Command unrecognized\r\n"); - fflush(stdout); + smap_printf("500 Command unrecognized\r\n"); continue; } @@ -293,41 +954,58 @@ if(!strcasecmp(p,"MAIL")) { char *q; - if((q = strtok((char *)0,"\r\n")) == (char *)0) { - printf("501 Syntax error\r\n"); - fflush(stdout); + smap_printf("501 Syntax error\r\n"); continue; } if(strncasecmp(q,"From:",5)) { - printf("501 Syntax error\r\n"); - fflush(stdout); + smap_printf("501 Syntax error\r\n"); continue; } /* block escape in names */ if(strchr(q, '`')) { - printf("501 Syntax error\r\n"); - fflush(stdout); + smap_printf("501 Syntax error\r\n"); continue; } q += 5; while(isspace(*q)) q++; + + /* Run spam checks on external SMTP clients */ + if(!authorized && relay_domain(riaddr, cfp) == 0) + { + if(matches_maps_rbl(riaddr, cfp)) { + syslog(LLEV,"spam ignored address=%s host=%.512s/%.20s (MAPS RBL)", + q,rladdr,riaddr); + continue; + } + + if(matches_spam_db(q, rladdr, riaddr, cfp)) { + syslog(LLEV,"spam ignored address=%s host=%.512s/%.20s (DB)", + q,rladdr,riaddr); + continue; + } + + if(!valid_sender(q, cfp)) { + syslog(LLEV,"sender's domain does not have an NS record: from=%.512s host=%.512s/%.20s", q, rladdr,riaddr); + continue; + } + } + if(ruser != (char *)0) free(ruser); ruser = malloc(strlen(q) + 1); if(ruser == (char *)0) { - printf("410 out of memory\r\n"); - fflush(stdout); + outofmemory: + smap_printf("410 out of memory\r\n"); syslog(LLEV,"fwtksyserr: out of memory: %m"); unlink(tempfile); smap_exit(1); } strcpy(ruser,q); - printf("250 %s... Sender Ok\r\n",ruser); - fflush(stdout); + smap_printf("250 %s... Sender Ok\r\n",ruser); continue; } @@ -336,22 +1014,18 @@ struct towho *nr; char *q; - if(ruser == (char *)0) { - printf("503 need MAIL From: first.\r\n"); - fflush(stdout); + smap_printf("503 need MAIL From: first.\r\n"); continue; } if((q = strtok((char *)0,"\r\n")) == (char *)0) { - printf("501 Syntax error\r\n"); - fflush(stdout); + smap_printf("501 Syntax error\r\n"); continue; } if(strncasecmp(q,"To:",3)) { - printf("501 Syntax error\r\n"); - fflush(stdout); + smap_printf("501 Syntax error\r\n"); continue; } @@ -361,14 +1035,22 @@ #ifdef SPECIALDOMAIN if(!checkvalid(q)) { syslog(LLEV,"securityalert: rejecting recip %.512s",q); - fflush(stdout); continue; } #endif + + if(authneeded(q, cfp)) { + smap_printf("505 Authentication required\r\n"); + continue; + } + if(!authorized && !valid_recipient(q, riaddr, cfp)) { + syslog(LLEV,"relay denied: to=%.512s from=%.512s host=%.512s/%.20s", q, ruser, rladdr,riaddr); + continue; + } + if(maxrecip > 0 && currecip++ > maxrecip) { - printf("550 too many recipients\r\n"); - fflush(stdout); + smap_printf("550 too many recipients\r\n"); syslog(LLEV,"exiting - too many recipients"); unlink(tempfile); smap_exit(1); @@ -376,16 +1058,14 @@ nr = (struct towho *)malloc(sizeof(struct towho)); if(nr == (struct towho *)0) { - printf("410 out of memory\r\n"); - fflush(stdout); + smap_printf("410 out of memory\r\n"); syslog(LLEV,"fwtksyserr: out of memory: %m"); unlink(tempfile); smap_exit(1); } nr->who = malloc(strlen(q) + 1); if(nr->who == (char *)0) { - printf("410 out of memory\r\n"); - fflush(stdout); + smap_printf("410 out of memory\r\n"); syslog(LLEV,"fwtksyserr: out of memory: %m"); unlink(tempfile); smap_exit(1); @@ -393,8 +1073,7 @@ strcpy(nr->who,q); nr->nxt = recip; recip = nr; - printf("250 %s OK\r\n",q); - fflush(stdout); + smap_printf("250 %s OK\r\n",q); continue; } @@ -406,8 +1085,7 @@ curbytes = 0; currecip = 0; if(recip == (struct towho *)0) { - printf("503 need RCPT first.\r\n"); - fflush(stdout); + smap_printf("503 need RCPT first.\r\n"); continue; } if((vout = smapopen()) == (FILE *)0) { @@ -415,35 +1093,31 @@ smap_exit(1); } - printf("354 Enter mail, end with \".\" on a line by itself\r\n"); - fflush(stdout); + smap_printf("354 Enter mail, end with \".\" on a line by itself\r\n"); fprintf(vout,"FROM %s\n",ruser); for(tp = recip; tp != (struct towho *)0; tp = tp->nxt) fprintf(vout,"RCPT %s\n",tp->who); fprintf(vout,"BODY\n"); time(&now); - fprintf(vout,"Received: from %s(%s) by %s via smap (%s)\n\tid %s; %s\n", - rladdr,riaddr,myhostname, - FWTK_VERSION_MINOR,tempfile, - arpadate((char *)0)); - + fprintf(vout,"Received: from %s (%s [%s]) by %s via smap (%s)\n\tid %s; %s\n", + ruser,rladdr,riaddr,myhostname, + FWTK_VERSION_MINOR,tempfile, + arpadate((char *)0)); + msgsize = 0; gotdata = 0; switch(crunchbody(stdin,vout)) { case 1: - printf("550 you could say goodbye\r\n"); - fflush(stdout); + smap_printf("550 you could say goodbye\r\n"); break; case 2: - printf("552 Too much data\r\n"); - fflush(stdout); + smap_printf("552 Too much data\r\n"); syslog(LLEV,"exiting too much data"); unlink(tempfile); smap_exit(1); case 3: - printf("450 System problem\r\n"); - fflush(stdout); + smap_printf("450 System problem\r\n"); continue; default: gotdata++; @@ -464,8 +1138,7 @@ } add_file_list(tempfile); chmod(tempfile,0700); - printf("250 Mail accepted\r\n"); - fflush(stdout); + smap_printf("250 Mail accepted\r\n"); syslog(LLEV,"exiting host=%.512s/%.20s bytes=%d",rladdr,riaddr,msgsize); vout = (FILE *)0; tempfile = (char *)0; @@ -477,34 +1150,31 @@ char *q; if((q = strtok((char *)0," \t\r\n")) == (char *)0) - printf("550 User Unknown\r\n"); + smap_printf("550 User Unknown\r\n"); else - printf("250 <%s>\r\n",q); + smap_printf("250 <%s>\r\n",q); syslog(LLEV,"%.100s %.100s (%.512s/%.20s)",p,q?q:"",rladdr,riaddr); - fflush(stdout); continue; } if(!strcasecmp(p,"HELP")) { - printf("214-Commands\r\n"); - printf("214-HELO MAIL RCPT DATA RSET\r\n"); - printf("214 NOOP QUIT HELP VRFY EXPN\r\n"); - fflush(stdout); + smap_printf("214-Commands\r\n"); + smap_printf("214-HELO MAIL RCPT DATA RSET\r\n"); + smap_printf("214-NOOP QUIT HELP VRFY EXPN\r\n"); + smap_printf("214 EHLO\r\n"); continue; } if(!strcasecmp(p,"NOOP")) { - printf("220 OK\r\n"); - fflush(stdout); + smap_printf("220 OK\r\n"); continue; } if(!strcasecmp(p,"QUIT")) { - printf("221 Closing connection\r\n"); - fflush(stdout); + smap_printf("221 Closing connection\r\n"); break; } @@ -520,8 +1190,7 @@ smap_exit(1); } } - printf("250 Reset state\r\n"); - fflush(stdout); + smap_printf("250 Reset state\r\n"); continue; } @@ -530,40 +1199,191 @@ char *q; if((q = strtok((char *)0,"\r\n")) == (char *)0) { - printf("250 Charmed, I'm sure.\r\n"); + smap_printf("250 Charmed, I'm sure.\r\n"); rhost = "??"; } else { - printf("250 (%s) pleased to meet you.\r\n",q); + smap_printf("250 (%s) pleased to meet you.\r\n",q); rhost = malloc(strlen(q) + 1); strcpy(rhost,q); } - fflush(stdout); continue; } + if(!strcasecmp(p,"EHLO")) { + char *q, ebuf[BUFSIZ], *e = ebuf; + int a = 0; + + if((q = strtok((char *)0,"\r\n")) == (char *)0) { + strcpy(e,"250-Charmed, I'm sure.\r\n"); + e = ebuf + strlen(ebuf); + rhost = "??"; + } else { + sprintf(e,"250-(%.100s) pleased to meet you.\r\n",q); + e = ebuf + strlen(ebuf); + + rhost = malloc(strlen(q) + 1); + strcpy(rhost,q); + } + + if(authbits) { + strcpy(e,"250-AUTH="); + e = ebuf + strlen(ebuf); + } + +#if defined(SMAP_AUTH_LOGIN) || defined(SMAP_AUTH_SHADOW) || defined(SMAP_AUTH_PAM) + if(authbits & SM_AUTH_LOGIN) { + strcpy(e,"LOGIN"); + e = ebuf + strlen(ebuf); + a++; + } +#endif /* SMAP_AUTH_LOGIN || SMAP_AUTH_SHADOW || SMAP_AUTH_PAM */ +#ifdef SMAP_AUTH_SKEY + if(authbits & SM_AUTH_SKEY) { + sprintf(e,"%sSKEY", a++ ? " " : ""); + e = ebuf + strlen(ebuf); + } +#endif /* SMAP_AUTH_SKEY */ +#ifdef SMAP_AUTH_CRAM_MD5 + if(authbits & SM_AUTH_CRAM_MD5) { + sprintf(e,"%sCRAM-MD5", a++ ? " " : ""); + e = ebuf + strlen(ebuf); + } +#endif /* SMAP_AUTH_CRAM_MD5 */ + + strcpy(e,"\r\n250 HELP\r\n"); + smap_printf(ebuf); + continue; + } + + if(!strcasecmp(p,"AUTH")) { + char *q, *r; + + /* Once a client is authorized, no more AUTHs are allowed this + * session. + */ + if(authorized) { + smap_printf("503 You may only authenticate once!\r\n"); + continue; + } + + if((q = strtok((char *)0," \t\r\n")) == (char *)0) { + smap_printf("501 Syntax error\r\n"); + continue; + } + + if((r = strtok((char *)0," \t\r\n")) != (char *)0) { + if((r = decode_base64(r)) == (char *)0) { + smap_printf("501 Garbled base64 data\r\n"); + continue; + } + } + +#if defined(SMAP_AUTH_LOGIN) || defined(SMAP_AUTH_SHADOW) || defined(SMAP_AUTH_PAM) + if(!strncasecmp(q,"LOGIN",5)) { + if(!(authbits & SM_AUTH_LOGIN)) + goto authbomb; + + if(r == (char *)0) { + smap_printf("334 VXNlcm5hbWU6\r\n"); + authstate = SM_AUTH_LOGIN_GETUSER; + } else { + authuser = malloc(strlen(r) + 1); + if(authuser == (char *)0) + goto outofmemory; + strcpy(authuser,r); + memset(r,0,strlen(r)); + free(r); + smap_printf("334 UGFzc3dvcmQ6\r\n"); + authstate = SM_AUTH_LOGIN_GETPASS; + } + continue; + } +#endif /* SMAP_AUTH_LOGIN || SMAP_AUTH_SHADOW || SMAP_AUTH_PAM */ +#ifdef SMAP_AUTH_SKEY + if(!strncasecmp(q,"SKEY",5)) { + if(!(authbits & SM_AUTH_SKEY)) + goto authbomb; + + if(r == (char *)0) { + smap_printf("334 VXNlcm5hbWU6\r\n"); + } else { + authstate = SM_AUTH_SKEY_GETUSER; + authuser = malloc(strlen(r) + 1); + if(authuser == (char *)0) + goto outofmemory; + strcpy(authuser,r); + memset(r,0,strlen(r)); + free(r); + + write(authsock,&authstate,sizeof(authstate)); + write(authsock,authuser,strlen(authuser) + 1); + read(authsock,&skeystatus,sizeof(skeystatus)); + read(authsock,skeybuf,sizeof(skeybuf)); + + p = encode_base64(skeybuf); + if(p == (char *)0) + goto outofmemory; + smap_printf("334 %s",p); + free(p); + authstate = SM_AUTH_SKEY_GETCHAL; + } + continue; + } +#endif /* SMAP_AUTH_SKEY */ +#ifdef SMAP_AUTH_CRAM_MD5 + if(!strncasecmp(q,"CRAM-MD5",8)) { + if(!(authbits & SM_AUTH_CRAM_MD5)) + goto authbomb; + + if(r != (char *)0) { + memset(r,0,strlen(r)); + free(r); + } + + authuser = malloc(1024); + if(authuser == (char *)0) + goto outofmemory; + sprintf(authuser, "<%lu.%lu@%s>", getpid(), time((time_t *)0), + myhostname); + p = encode_base64(authuser); + if(p == (char *)0) + goto outofmemory; + smap_printf("334 %s",p); + memset(p,0,strlen(p)); + free(p); + p = (char *)0; + authstate = SM_AUTH_CRAM_MD5_CHAL; + continue; + } +#endif /* SMAP_AUTH_CRAM_MD5 */ + + /* Unknown authentication scheme or other error. + */ + authbomb: + smap_printf("504 Unrecognized authentication type\r\n"); + continue; + } + + if(!strcasecmp(p,"VERB")) { - printf("200 Verbose mode\r\n"); - fflush(stdout); + smap_printf("200 Verbose mode\r\n"); continue; } if(!strcasecmp(p,"ONEX")) { - printf("200 Only one transaction\r\n"); - fflush(stdout); + smap_printf("200 Only one transaction\r\n"); continue; } if(!strcasecmp(p,"DEBUG")) { - printf("200 Debug set -NOT!\r\n"); - fflush(stdout); + smap_printf("200 Debug set -NOT!\r\n"); continue; } - printf("500 Command unrecognized\r\n"); - fflush(stdout); + smap_printf("500 Command unrecognized\r\n"); } smap_exit(0); } @@ -619,6 +1439,8 @@ drop = 1; } } + if(buf[0] == '.' && buf[1] == '\0') + break; if(buf[0] == '.' && buf[1] == '.') x = fprintf(out,"%s\n",&buf[1]); else @@ -658,8 +1480,15 @@ if(ferror(fp) || feof(fp)) return(-1); - + while(--siz) { +#ifdef SMAP_SSL + if(fp == stdin && sslenabled) { + unsigned char sc; + SSL_read(sslconn, &sc, 1); + x = (int)sc; + } else +#endif /* SMAP_SSL */ if((x = fgetc(fp)) == EOF) { if(s == buf) return(-1); @@ -731,6 +1560,12 @@ { struct file_list *fl; + if(authsock != -1) { + int i = 0; + write(authsock, &i, sizeof(i)); + close(authsock); + } + while (filelist != (struct file_list *)0) { fl = filelist->next; unlink(filelist->tmpfile); @@ -747,9 +1582,9 @@ int cstat; #ifdef SYSV - while (wait3(&cstat, WNOHANG, (struct rusage *)0) > 0) -#else while (waitpid(-1, &cstat, WNOHANG) > 0) +#else + while (wait3(&cstat, WNOHANG, (struct rusage *)0) > 0) #endif ; } @@ -808,9 +1643,78 @@ free(chsavp); return(1); bomb: - printf(bad); + smap_printf(bad); free(chsavp); return(0); } #endif +#ifndef SYSV +extern char *index(); +extern char *rindex(); +#endif + +authneeded(r, cfp) +char *r; +Cfg *cfp; +{ + Cfg *cf; + char *atp; + char *jxp; + char *chop; + char *chsavp; + int x, i; + + if(authorized == 1) + return(0); + + if((chop = malloc((x = strlen(r)) + 1)) == (char *)0) { + unlink(tempfile); + syslog(LLEV,"fwtksyserr: of memory: %m"); + smap_exit(1); + } + chsavp = chop; + strcpy(chop,r); + + if(r[0] == '<') { + if(chop[x - 1] == '>') + chop[x - 1] = '\0'; + chop++; + } + + if((atp = rindex(chop,'@')) != (char *)0) { + atp++; + + if((cf = cfg_get("relay-domain", cfp)) == (Cfg *)0) { + goto bomb; + } + + for(; cf != (Cfg *)0; cf = cfg_get("relay-domain",(Cfg *)0)) { + for(i = 0; i < cf->argc; i++) { + /* check if it ends in a valid relay-domain */ + if(!strcasecmp(atp,cf->argv[i])) + goto breakout; + } + } + + if(cf == (Cfg *)0) + goto bomb; + breakout: + /* now make sure there are no other routing chars */ + atp--; + *atp = '\0'; + if((jxp = strpbrk(chop,"%@:[]!")) != (char *)0) { + goto bomb; + } + } + + if((jxp = strpbrk(chop,"%@:[]!")) != (char *)0) + goto bomb; + + free(chsavp); + return(0); + +bomb: + free(chsavp); + return(1); +}