diff options
author | Eric S. Raymond <esr@thyrsus.com> | 1999-10-27 00:15:14 +0000 |
---|---|---|
committer | Eric S. Raymond <esr@thyrsus.com> | 1999-10-27 00:15:14 +0000 |
commit | 0d1e69ae10af5eba7cd3426e802d40d17b6db218 (patch) | |
tree | d838573a667ed2ba75e04797f87eb9f45f1df05f | |
parent | 8d293a3b12a5e2c4ecc6f70bcd59b862294458c8 (diff) | |
download | fetchmail-0d1e69ae10af5eba7cd3426e802d40d17b6db218.tar.gz fetchmail-0d1e69ae10af5eba7cd3426e802d40d17b6db218.tar.bz2 fetchmail-0d1e69ae10af5eba7cd3426e802d40d17b6db218.zip |
This preliminary SSL patch goes to Mike.
svn path=/trunk/; revision=2643
-rw-r--r-- | INSTALL | 28 | ||||
-rw-r--r-- | Makefile.in | 35 | ||||
-rw-r--r-- | NEWS | 9 | ||||
-rw-r--r-- | acconfig.h | 3 | ||||
-rw-r--r-- | conf.c | 8 | ||||
-rw-r--r-- | configure.in | 21 | ||||
-rw-r--r-- | driver.c | 15 | ||||
-rw-r--r-- | etrn.c | 2 | ||||
-rw-r--r-- | fetchmail-features.html | 9 | ||||
-rw-r--r-- | fetchmail.c | 16 | ||||
-rw-r--r-- | fetchmail.h | 10 | ||||
-rwxr-xr-x | fetchmailconf | 34 | ||||
-rw-r--r-- | imap.c | 2 | ||||
-rw-r--r-- | options.c | 31 | ||||
-rw-r--r-- | pop2.c | 4 | ||||
-rw-r--r-- | pop3.c | 2 | ||||
-rw-r--r-- | rcfile_l.l | 3 | ||||
-rw-r--r-- | rcfile_y.y | 8 | ||||
-rw-r--r-- | sample.rcfile | 8 | ||||
-rw-r--r-- | socket.c | 290 |
20 files changed, 505 insertions, 33 deletions
@@ -26,7 +26,8 @@ you should install OPIE. You need version 2.32 or better. The OPIE library sources are available at ftp://ftp.inner.net/pub/opie. You can also find OPIE and IPV6-capable servers there. -Building in IPv6 support or the IPsec patches REQUIRES that Craig +Building in IPv6 support or the IPsec patches *requires* + that Craig Metz's inet6-apps kit be installed; the IPsec patches require that the kit be built with network security API support enabled. The kit can be gotten from ftp.ipv6.inner.net:/pub/ipv6 (via IPv6) or ftp.inner.net @@ -56,12 +57,23 @@ Support for CompuServe's RPA authentication method (rather similar to APOP) is available but also not included in the standard build. You can compile it in with `configure --enable-RPA'. +Support for Microsoft's NTLM authentication method is also available +but also not included in the standard build. You can compile it in +with `configure --enable-NTLM'. + Support for authentication using RFC1731 GSSAPI is available but also not included by default. You can compile it in with `configure --with-gssapi', which looks for GSSAPI support in standard locations (/usr, /usr/local). If you set --with-GSSAPI=DIR you can direct the build to look for GSSAPI support under DIR. +Hooks for the OpenSSL library (see http://www.openssl.org/) are +included in the distribution. To enable these, configure with +--with-ssl; they are not included in the standard build. Note that +due to U.S. crypto export regulations (which we hope will soon be +overturned on Constitutional grounds), no actual cryptography code is +included in the distribution. + If you want to build for debugging, CFLAGS=-g LDFLAGS=" " ./configure @@ -135,7 +147,7 @@ If you're upgrading from popclient, see question F4 in the FAQ file. 6. TEST -I strongly recommend that your first fetchmail run use the -v and -k +I strongly recommend that your first fetchmail run use the -v, -a and -k options, in case there is something not quite right with your server, your local delivery configuration or your port 25 listener. Also, beware of aliases that direct your local mail back to the server host! @@ -143,10 +155,14 @@ beware of aliases that direct your local mail back to the server host! This software is known to work with the qpop/popper series of freeware POP3 servers; also with the IMAP2bis and IMAP4 servers that are distributed with Pine from the University of Washington; also with the -Cyrus IMAP server from CMU. This covers all the servers normally -hosted on Linux and *BSD systems. It also works with Microsoft Exchange, -despite the fact that Microsoft Exchange is extremely broken (returns -incorrect message lengths in LIST responses). +Cyrus IMAP server from CMU. This covers all the servers commonly +hosted on Linux and *BSD systems. It also works with the IMAP service +of Microsoft Exchange, despite the fact that Microsoft Exchange is +extremely broken (returns incorrect message lengths in LIST +responses). + +See the FAQ, section S, for detailed advice on running with various +servers. 7. REPORTING BUGS diff --git a/Makefile.in b/Makefile.in index 6033ae03..e1753c23 100644 --- a/Makefile.in +++ b/Makefile.in @@ -185,20 +185,27 @@ realclean: distclean mostlyclean: clean -config.status: configure Makefile.in - $(srcdir)/configure - -config.h: config.status config.h.in - sh config.status; touch config.h - -Makefile: config.status - sh config.status - -configure: configure.in - autoconf $(ACFLAGS) - -config.h.in: acconfig.h configure.in - autoheader $(ACFLAGS); touch config.h.in +# These magic rules are copied from the autoconf documentation + +${srcdir}/configure: configure.in aclocal.m4 + cd ${srcdir} && autoconf + +# autoheader might not change config.h.in, so touch a stamp file. +${srcdir}/config.h.in: stamp-h.in + +${srcdir}/stamp-h.in: configure.in aclocal.m4 acconfig.h # config.h.top config.h.bot + cd ${srcdir} && autoheader + echo timestamp> ${srcdir}/stamp-h.in + +config.h: stamp-h +stamp-h: config.h.in config.status + ./config.status + +Makefile: Makefile.in config.status + ./config.status + +config.status: configure + ./config.status --recheck # This tells versions [3.59,3.63) of GNU make not to export all variables. .NOEXPORT: @@ -1,3 +1,10 @@ + Things to do: + +In the SSL support, we eed to add server certificate validation (In +other words, does the certificate match the system we are trying to +contact?). Also, add authentication of Certifying Authority (Is this +a Certifying Authority we recognize?). + Release Notes: (The `lines' figures total .c, .h, .l, and .y files under version control.) @@ -8,6 +15,8 @@ * Backed out the 5.1.0 change to quote usernames with embedded spaces. It actually breaks things. * Added to fetchmailconf a warning about Imail IMAP servers. +* SSL patches by Michael Warfield merged in. Distribution still contains + no crypto code. fetchmail-5.1.2 (Thu Oct 7 09:46:07 EDT 1999), 17906 lines: * Joe Loughry <loughry@uswest.net> sent a patch to handle multihomed machines. @@ -100,6 +100,9 @@ /* Define if you want SDPS support compiled in */ #undef SDPS_ENABLE +/* Define if you want SSL support compiled in */ +#undef SSL_ENABLE + /* Define if you want OPIE support compiled in */ #undef OPIE @@ -169,6 +169,9 @@ void dump_config(struct runctl *runp, struct query *querylist) #ifdef ETRN_ENABLE printf("'etrn',"); #endif /* ETRN_ENABLE */ +#ifdef SSL_ENABLE + printf("'ssl',"); +#endif /* SSL_ENABLE */ #if OPIE printf("'opie',"); #endif /* OPIE */ @@ -342,6 +345,11 @@ void dump_config(struct runctl *runp, struct query *querylist) numdump("warnings", ctl->warnings); numdump("fetchlimit", ctl->fetchlimit); numdump("batchlimit", ctl->batchlimit); +#ifdef SSL_ENABLE + booldump("ssl", ctl->use_ssl); + stringdump("sslkey", ctl->sslkey); + stringdump("sslcert", ctl->sslcert); +#endif /* SSL_ENABLE */ numdump("expunge", ctl->expunge); stringdump("properties", ctl->properties); listdump("smtphunt", ctl->smtphunt); diff --git a/configure.in b/configure.in index f146305e..d1c91d2c 100644 --- a/configure.in +++ b/configure.in @@ -346,6 +346,24 @@ else fi fi +### use option --with-ssl to compile in the SSL support +AC_ARG_WITH(ssl, + [ --with-ssl[=DIR] enable SSL and point its top directory]) + +if test -n "$with_ssl" +then + if test "$with_ssl" = "yes" + then + # Let's just define the standard location for the SSLeay root + with_ssl="/usr/local/ssl" + fi + includedir=$with_ssl/include + AC_DEFINE(SSL_ENABLE) + CEFLAGS="$CPPFLAGS -I$includedir" + LDEFLAGS="$LDEFLAGS -L$with_ssl/lib" + LIBS="$LIBS -lssl -lcrypto" +fi + ### use option --with-kerberos=DIR to point at a Kerberos directory AC_ARG_WITH(kerberos, [ --with-kerberos=DIR point fetchmail compilation at a Kerberos directory]) @@ -488,7 +506,8 @@ AC_OUTPUT([Makefile intl/Makefile po/Makefile.in], [ # The reason for this odd makedepend line is that we want # to have all dependencies evaluated relative to the source directory # and let VPATH do all the dirty work when we build remotely - echo "You can ignore any makedepend error messages"; + echo "You can ignore any makedepend error messages:"; + echo timestamp > stamp-h; (cd $srcdir; makedepend -f - *.c) >>Makefile]) dnl Local Variables: @@ -1556,7 +1556,11 @@ const int maxfetch; /* maximum number of messages to fetch */ phase = OPEN_WAIT; set_timeout(mytimeout); #if !INET6 +#ifdef SSL_ENABLE + port = ctl->server.port ? ctl->server.port : ( ctl->use_ssl ? protocol->sslport : protocol->port ); +#else port = ctl->server.port ? ctl->server.port : protocol->port; +#endif #endif /* !INET6 */ realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname; @@ -1615,6 +1619,17 @@ const int maxfetch; /* maximum number of messages to fetch */ set_timeout(0); phase = oldphase; +#ifdef SSL_ENABLE + /* perform initial SSL handshake on open connection */ + /* Note: We pass the realhost name over for certificate + verification. We may want to make this configurable */ + if (ctl->use_ssl && SSLOpen(mailserver_socket,ctl->sslkey,ctl->sslcert,realhost) == -1) + { + report(stderr, "SSL connection failed."); + goto closeUp; + } +#endif + #ifdef KERBEROS_V4 if (ctl->server.preauthenticate == A_KERBEROS_V4) { @@ -119,8 +119,10 @@ const static struct method etrn = "ETRN", /* ESMTP ETRN extension */ #if INET6 "smtp", /* standard SMTP port */ + "smtps", /* ssl SMTP port */ #else /* INET6 */ 25, /* standard SMTP port */ + 465, /* ssl SMTP port */ #endif /* INET6 */ FALSE, /* this is not a tagged protocol */ FALSE, /* this does not use a message delimiter */ diff --git a/fetchmail-features.html b/fetchmail-features.html index 1cc54ab2..81d8ac19 100644 --- a/fetchmail-features.html +++ b/fetchmail-features.html @@ -10,7 +10,7 @@ <table width="100%" cellpadding=0><tr> <td width="30%">Back to <a href="index.html">Fetchmail Home Page</a> <td width="30%" align=center>To <a href="/~esr/sitemap.html">Site Map</a> -<td width="30%" align=right>$Date: 1999/09/14 07:38:00 $ +<td width="30%" align=right>$Date: 1999/10/27 00:15:09 $ </table> <HR> @@ -18,6 +18,11 @@ <H2>Since 5.0:</H2> <UL> +<LI>Following recent court decisions and changes in U.S. federal +regulatory policy, hooks for Secure Sockets Layer (SSL) are now part +of the main fetchmail distribution. The distribution still contains +no actual cryptographic code. + <LI>NTLM support, so fetchmail can query Microsoft Exchange servers. <LI>Expunge option can now be used to break POP3 retrieval into subsessions. @@ -177,7 +182,7 @@ get-mail, gwpop, pimp-1.0, pop-perl5-1.2, popc, popmail-1.6 and upop.<P> <table width="100%" cellpadding=0><tr> <td width="30%">Back to <a href="index.html">Fetchmail Home Page</a> <td width="30%" align=center>To <a href="/~esr/sitemap.html">Site Map</a> -<td width="30%" align=right>$Date: 1999/09/14 07:38:00 $ +<td width="30%" align=right>$Date: 1999/10/27 00:15:09 $ </table> <P><ADDRESS>Eric S. Raymond <A HREF="mailto:esr@thyrsus.com"><esr@snark.thyrsus.com></A></ADDRESS> diff --git a/fetchmail.c b/fetchmail.c index 0905be27..5477d032 100644 --- a/fetchmail.c +++ b/fetchmail.c @@ -220,6 +220,9 @@ int main(int argc, char **argv) #ifndef ETRN_ENABLE printf("-ETRN"); #endif /* ETRN_ENABLE */ +#ifdef SSL_ENABLE + printf("+SSL"); +#endif #if OPIE printf("+OPIE"); #endif /* OPIE */ @@ -856,6 +859,7 @@ static void optmerge(struct query *h2, struct query *h1, int force) FLAG_MERGE(server.plugin); FLAG_MERGE(server.plugout); + FLAG_MERGE(wildcard); FLAG_MERGE(remotename); FLAG_MERGE(password); @@ -879,6 +883,11 @@ static void optmerge(struct query *h2, struct query *h1, int force) FLAG_MERGE(warnings); FLAG_MERGE(fetchlimit); FLAG_MERGE(batchlimit); +#ifdef SSL_ENABLE + FLAG_MERGE(use_ssl); + FLAG_MERGE(sslkey); + FLAG_MERGE(sslcert); +#endif FLAG_MERGE(expunge); FLAG_MERGE(properties); @@ -1003,6 +1012,9 @@ static int load_params(int argc, char **argv, int optind) DEFAULT(ctl->mimedecode, FALSE); DEFAULT(ctl->server.dns, TRUE); DEFAULT(ctl->server.uidl, FALSE); +#ifdef SSL_ENABLE + DEFAULT(ctl->use_ssl, FALSE); +#endif DEFAULT(ctl->server.checkalias, FALSE); #undef DEFAULT @@ -1476,6 +1488,10 @@ static void dump_params (struct runctl *runp, printf(_(" Kerberos V4 preauthentication enabled.\n")); if (ctl->server.preauthenticate == A_KERBEROS_V5) printf(_(" Kerberos V5 preauthentication enabled.\n")); +#ifdef SSL_ENABLE + if (ctl->use_ssl) + printf(" SSL encrypted sessions enabled.\n"); +#endif if (ctl->server.timeout > 0) printf(_(" Server nonresponse timeout is %d seconds"), ctl->server.timeout); if (ctl->server.timeout == CLIENT_TIMEOUT) diff --git a/fetchmail.h b/fetchmail.h index 161d14af..6694cf79 100644 --- a/fetchmail.h +++ b/fetchmail.h @@ -26,6 +26,11 @@ #define KPOP_PORT 1109 #endif /* INET6 */ +#ifdef SSL_ENABLE +#define SIMAP_PORT 993 +#define SPOP3_PORT 995 +#endif + /* preauthentication types */ #define A_PASSWORD 0 /* password or inline authentication */ #define A_KERBEROS_V4 1 /* preauthenticate w/ Kerberos V4 */ @@ -141,8 +146,10 @@ struct method /* describe methods for protocol state machine */ const char *name; /* protocol name */ #if INET6 const char *service; + const char *sslservice; #else /* INET6 */ int port; /* service port */ + int sslport; /* service port for ssl */ #endif /* INET6 */ flag tagged; /* if true, generate & expect command tags */ flag delimited; /* if true, accept "." message delimiter */ @@ -257,6 +264,9 @@ struct query int fetchlimit; /* max # msgs to get in single poll */ int batchlimit; /* max # msgs to pass in single SMTP session */ int expunge; /* max # msgs to pass between expunges */ + flag use_ssl; /* use SSL encrypted session */ + char *sslkey; /* optional SSL private key file */ + char *sslcert; /* optional SSL certificate file */ char *properties; /* passthrough properties for extensions */ /* internal use -- per-poll state */ diff --git a/fetchmailconf b/fetchmailconf index 2e334c06..05a961a1 100755 --- a/fetchmailconf +++ b/fetchmailconf @@ -4,7 +4,7 @@ # by Eric S. Raymond, <esr@snark.thyrsus.com>. # Requires Python with Tkinter, and the following OS-dependent services: # posix, posixpath, socket -version = "1.17" +version = "1.18" from Tkinter import * from Dialog import * @@ -221,6 +221,9 @@ class User: self.fetchlimit = 0 # Max messages fetched per batch self.batchlimit = 0 # Max message forwarded per batch self.expunge = 0 # Interval between expunges (IMAP) + self.ssl = 0 # Enable Seccure Socket Layer + self.sslkey = None # SSL key filename + self.sslcert = None # SSL certificate filename self.properties = None # Extension properties User.typemap = ( ('remote', 'String'), @@ -248,7 +251,10 @@ class User: ('fetchlimit', 'Int'), ('batchlimit', 'Int'), ('expunge', 'Int'), - ('properties', 'String')) + ('ssl', 'Boolean'), + ('sslkey', 'String'), + ('sslcert', 'String'), + ('properties', 'String')) def __repr__(self): str = " " @@ -296,6 +302,12 @@ class User: str = str + " fetchlimit " + `self.fetchlimit` if self.batchlimit != UserDefaults.batchlimit: str = str + " batchlimit " + `self.batchlimit` + if self.ssl != UserDefaults.ssl: + str = str + flag2str(self.ssl, 'ssl') + if self.sslkey != UserDefaults.sslkey: + str = str + " sslkey " + `self.sslkey` + if self.sslcert != UserDefaults.sslcert: + str = str + " ssl " + `self.sslcert` if self.expunge != UserDefaults.expunge: str = str + " expunge " + `self.expunge` str = str + "\n" @@ -855,6 +867,10 @@ The `interface' and `monitor' options are available only for Linux and freeBSD systems. See the fetchmail manual page for details on these. +The ssl option enables SSL communication with a maolserver +supporting Secure Sockets Layer. The sslkey and sslcert options +declare key and certificate files for use with SSL. + The `netsec' option will be configurable only if fetchmail was compiled with IPV6 support. If you need to use it, you probably know what to do. @@ -1338,6 +1354,16 @@ class UserEdit(Frame, MyWidget): self.password, '12').pack(side=TOP, fill=X) secwin.pack(fill=X, anchor=N) + if 'ssl' in feature_options or 'ssl' in dictmembers: + sslwin = Frame(leftwin, relief=RAISED, bd=5) + Checkbutton(sslwin, text="Use SSL?", + variable=self.ssl).pack(side=TOP, fill=X) + LabeledEntry(sslwin, 'SSL key:', + self.sslkey, '14').pack(side=TOP, fill=X) + LabeledEntry(sslwin, 'SSL certificate:', + self.sslcert, '14').pack(side=TOP, fill=X) + sslwin.pack(fill=X, anchor=N) + names = Frame(leftwin, relief=RAISED, bd=5) Label(names, text="Local names").pack(side=TOP) ListEdit("New name: ", @@ -1602,7 +1628,7 @@ def copy_instance(toclass, fromdict): # The `optional' fields are the ones we can ignore for purposes of # conformability checking; they'll still get copied if they are # present in the dictionary. - optional = ('interface', 'monitor', 'netsec') + optional = ('interface', 'monitor', 'netsec', 'ssl', 'sslkey', 'sslcert') class_sig = setdiff(toclass.__dict__.keys(), optional) class_sig.sort() dict_keys = setdiff(fromdict.keys(), optional) @@ -1739,7 +1765,7 @@ gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7 # `Configuration' is the top level of the object tree we're going to mung. # The dictmembers list is used to track the set of fields the dictionary # contains; in particular, we can use it to tell whether things like the - # monitor, interface, and netsec fields are present. + # monitor, interface, netsec, ssl, sslkey, or sslcert fields are present. dictmembers = [] Fetchmailrc = Configuration() copy_instance(Fetchmailrc, fetchmailrc) @@ -1318,8 +1318,10 @@ const static struct method imap = "IMAP", /* Internet Message Access Protocol */ #if INET6 "imap", + "imaps", #else /* INET6 */ 143, /* standard IMAP2bis/IMAP4 port */ + 993, /* ssl IMAP2bis/IMAP4 port */ #endif /* INET6 */ TRUE, /* this is a tagged protocol */ FALSE, /* no message delimiter */ @@ -71,6 +71,12 @@ #define LA_CONFIGDUMP 47 #define LA_YYDEBUG 48 +#ifdef SSL_ENABLE +#define LA_SSL 49 +#define LA_SSLKEY 50 +#define LA_SSLCERT 51 +#endif + /* options still left: CDgGhHjJoORwWxXYz */ static const char *shortoptions = "?Vcsvd:NqL:f:i:p:UP:A:t:E:Q:u:akKFnl:r:S:Z:b:B:e:m:T:I:M:yw:"; @@ -130,6 +136,12 @@ static const struct option longoptions[] = { {"netsec", required_argument, (int *) 0, LA_NETSEC }, #endif /* INET6 */ +#ifdef SSL_ENABLE + {"ssl", no_argument, (int *) 0, LA_SSL }, + {"sslkey", required_argument, (int *) 0, LA_SSLKEY }, + {"sslcert", required_argument, (int *) 0, LA_SSLCERT }, +#endif + #if (defined(linux) && !INET6) || defined(__FreeBSD__) {"interface", required_argument, (int *) 0, LA_INTERFACE }, {"monitor", required_argument, (int *) 0, LA_MONITOR }, @@ -523,6 +535,20 @@ struct query *ctl; /* option record to be initialized */ ctl->server.plugout = xstrdup(optarg); break; +#ifdef SSL_ENABLE + case LA_SSL: + ctl->use_ssl = FLAG_TRUE; + break; + + case LA_SSLKEY: + ctl->sslkey = xstrdup(optarg); + break; + + case LA_SSLCERT: + ctl->sslcert = xstrdup(optarg); + break; +#endif + case 'y': case LA_YYDEBUG: yydebug = TRUE; @@ -578,6 +604,11 @@ struct query *ctl; /* option record to be initialized */ P(_(" -I, --interface interface required specification\n")); P(_(" -M, --monitor monitor interface for activity\n")); #endif +#if defined( SSL_ENABLE ) + P(_(" --ssl enable ssl encrypted session\n")); + P(_(" --sslkey ssl private key file\n")); + P(_(" --sslcert ssl client certificate\n")); +#endif P(_(" --plugin specify external command to open connection\n")); P(_(" --plugout specify external command to open smtp connection\n")); @@ -125,9 +125,11 @@ const static struct method pop2 = { "POP2", /* Post Office Protocol v2 */ #if INET6 - "pop2", /* standard POP3 port */ + "pop2", /* standard POP2 port */ + "pop2", /* ssl POP2 port */ #else /* INET6 */ 109, /* standard POP2 port */ + 109, /* ssl POP2 port - not */ #endif /* INET6 */ FALSE, /* this is not a tagged protocol */ FALSE, /* does not use message delimiter */ @@ -645,8 +645,10 @@ const static struct method pop3 = "POP3", /* Post Office Protocol v3 */ #if INET6 "pop3", /* standard POP3 port */ + "pop3s", /* ssl POP3 port */ #else /* INET6 */ 110, /* standard POP3 port */ + 995, /* ssl POP3 port */ #endif /* INET6 */ FALSE, /* this is not a tagged protocol */ TRUE, /* this uses a message delimiter */ @@ -147,6 +147,9 @@ dropstatus { return DROPSTATUS; } mimedec(ode)? { return MIMEDECODE; } dns { return DNS; } uidl { return UIDL; } +ssl { return SSL; } +sslkey { return SSLKEY; } +sslcert { return SSLCERT; } checkalias { return CHECKALIAS; } limit { return LIMIT; } @@ -69,7 +69,7 @@ extern char * yytext; %token <sval> STRING %token <number> NUMBER %token NO KEEP FLUSH FETCHALL REWRITE FORCECR STRIPCR PASS8BITS DROPSTATUS -%token DNS SERVICE PORT UIDL INTERVAL MIMEDECODE CHECKALIAS +%token DNS SERVICE PORT UIDL INTERVAL MIMEDECODE CHECKALIAS SSL SSLKEY SSLCERT %% @@ -317,6 +317,10 @@ user_option : TO localnames HERE | DROPSTATUS {current.dropstatus = FLAG_TRUE;} | MIMEDECODE {current.mimedecode = FLAG_TRUE;} + | SSL {current.use_ssl = FLAG_TRUE;} + | SSLKEY STRING {current.sslkey = xstrdup($2);} + | SSLCERT STRING {current.sslcert = xstrdup($2);} + | NO KEEP {current.keep = FLAG_FALSE;} | NO FLUSH {current.flush = FLAG_FALSE;} | NO FETCHALL {current.fetchall = FLAG_FALSE;} @@ -327,6 +331,8 @@ user_option : TO localnames HERE | NO DROPSTATUS {current.dropstatus = FLAG_FALSE;} | NO MIMEDECODE {current.mimedecode = FLAG_FALSE;} + | NO SSL {current.use_ssl = FLAG_FALSE;} + | LIMIT NUMBER {current.limit = NUM_VALUE_IN($2);} | WARNINGS NUMBER {current.warnings = NUM_VALUE_IN($2);} | FETCHLIMIT NUMBER {current.fetchlimit = NUM_VALUE_IN($2);} diff --git a/sample.rcfile b/sample.rcfile index 3e49f83c..b08af78f 100644 --- a/sample.rcfile +++ b/sample.rcfile @@ -27,6 +27,9 @@ # uidl # no uidl # port -- must be followed by a TCP/IP port number +# ssl +# sslkeyfile -- must be followed by path to private key file +# sslcertfile -- must be followed by path to certificate file # authenticate (or auth) -- must be followed by an authentication type # timeout -- must be followed by a numeric timeout value # envelope -- must be followed by an envelope header name @@ -91,6 +94,11 @@ # kpop (or KPOP) # etrn (or ETRN) # +# Add ssl for protocols operating over an SSL connection (POP3 and IMAP) +# Default port for IMAPS (IMAP over SSL) is 993 +# Default port for POP3S (POP3 over SSL) is 995 +# Taken from assigned numbers and compatible with netscape/outlook usage. +# # Legal authentication types are # login # kerberos @@ -299,13 +299,35 @@ va_dcl { } +#ifdef SSL_ENABLE +#include "openssl/ssl.h" +#include "openssl/err.h" +#include "openssl/pem.h" +#include "openssl/x509.h" + +static SSL_CTX *_ctx = NULL; +static SSL *_ssl_context[FD_SETSIZE]; + +SSL *SSLGetContext( int ); +#endif /* SSL_ENABLE */ + int SockWrite(int sock, char *buf, int len) { int n, wrlen = 0; +#ifdef SSL_ENABLE + SSL *ssl; +#endif while (len) { +#ifdef SSL_ENABLE + if( NULL != ( ssl = SSLGetContext( sock ) ) ) + n = SSL_write(ssl, buf, len); + else + n = write(sock, buf, len); +#else n = write(sock, buf, len); +#endif if (n <= 0) return -1; len -= n; @@ -319,6 +341,9 @@ int SockRead(int sock, char *buf, int len) { char *newline, *bp = buf; int n; +#ifdef SSL_ENABLE + SSL *ssl; +#endif if (--len < 1) return(-1); @@ -329,12 +354,66 @@ int SockRead(int sock, char *buf, int len) * (2) to return the true length of data read, even if the * data coming in has embedded NULS. */ +#ifdef SSL_ENABLE + if( NULL != ( ssl = SSLGetContext( sock ) ) ) { + /* Hack alert! */ + /* OK... SSL_peek works a little different from MSG_PEEK + Problem is that SSL_peek can return 0 if there + is no data currently available. If, on the other + hand, we loose the socket, we also get a zero, but + the SSL_read then SEGFAULTS! To deal with this, + we'll check the error code any time we get a return + of zero from SSL_peek. If we have an error, we bail. + If we don't, we read one character in SSL_read and + loop. This should continue to work even if they + later change the behavior of SSL_peek + to "fix" this problem... :-( */ + if ((n = SSL_peek(ssl, bp, len)) < 0) { + return(-1); + } + if( 0 == n ) { + /* SSL_peek says no data... Does he mean no data + or did the connection blow up? If we got an error + then bail! */ + if( 0 != ( n = ERR_get_error() ) ) { + return -1; + } + /* We didn't get an error so read at least one + character at this point and loop */ + n = 1; + /* Make sure newline start out NULL! + * We don't have a string to pass through + * the strchr at this point yet */ + newline = NULL; + } else if ((newline = memchr(bp, '\n', n)) != NULL) + n = newline - bp + 1; + if ((n = SSL_read(ssl, bp, n)) == -1) { + return(-1); + } + /* Check for case where our single character turned out to + * be a newline... (It wasn't going to get caught by + * the strchr above if it came from the hack... ). */ + if( NULL == newline && 1 == n && '\n' == *bp ) { + /* Got our newline - this will break + out of the loop now */ + newline = bp; + } + } else { + if ((n = recv(sock, bp, len, MSG_PEEK)) <= 0) + return(-1); + if ((newline = memchr(bp, '\n', n)) != NULL) + n = newline - bp + 1; + if ((n = read(sock, bp, n)) == -1) + return(-1); + } +#else if ((n = recv(sock, bp, len, MSG_PEEK)) <= 0) return(-1); if ((newline = memchr(bp, '\n', n)) != NULL) n = newline - bp + 1; if ((n = read(sock, bp, n)) == -1) return(-1); +#endif bp += n; len -= n; } while @@ -348,16 +427,219 @@ int SockPeek(int sock) { int n; char ch; +#ifdef SSL_ENABLE + SSL *ssl; +#endif - if ((n = recv(sock, &ch, 1, MSG_PEEK)) == -1) - return -1; - else - return(ch); +#ifdef SSL_ENABLE + if( NULL != ( ssl = SSLGetContext( sock ) ) ) { + n = SSL_peek(ssl, &ch, 1); + if( 0 == n ) { + /* This code really needs to implement a "hold back" + * to simulate a functioning SSL_peek()... sigh... + * Has to be coordinated with the read code above. + * Next on the list todo... */ + + /* SSL_peek says no data... Does he mean no data + or did the connection blow up? If we got an error + then bail! */ + if( 0 != ( n = ERR_get_error() ) ) { + return -1; + } + + /* Haven't seen this case actually occur, but... + if the problem in SockRead can occur, this should + be possible... Just not sure what to do here. + This should be a safe "punt" the "peek" but don't + "punt" the "session"... */ + + return 0; /* Give him a '\0' character */ + } + } else { + n = recv(sock, &ch, 1, MSG_PEEK); + } +#else + n = recv(sock, &ch, 1, MSG_PEEK); +#endif + if (n == -1) + return -1; + else + return(ch); } +#ifdef SSL_ENABLE + +static char *_ssl_server_cname = NULL; + +SSL *SSLGetContext( int sock ) +{ + /* If SSLOpen has never initialized - just return NULL */ + if( NULL == _ctx ) + return NULL; + + if( sock < 0 || sock > FD_SETSIZE ) + return NULL; + return _ssl_context[sock]; +} + + +int SSL_verify_callback( int ok_return, X509_STORE_CTX *ctx ) +{ + char buf[260]; + char cbuf[260]; + char ibuf[260]; + char *str_ptr; + X509 *x509_cert; + int err, depth; + + x509_cert = X509_STORE_CTX_get_current_cert(ctx); + err = X509_STORE_CTX_get_error(ctx); + depth = X509_STORE_CTX_get_error_depth(ctx); + + X509_NAME_oneline(X509_get_subject_name(x509_cert), buf, 256); + X509_NAME_oneline(X509_get_issuer_name(x509_cert), ibuf, 256); + + /* Just to be sure those buffers are terminated... I think the + X509 libraries do, but... */ + buf[256] = ibuf[256] = '\0'; + + if (depth == 0) { + if( ( str_ptr = strstr( ibuf, "/O=" ) ) ) { + str_ptr += 3; + strcpy( cbuf, str_ptr ); + if( ( str_ptr = strchr(cbuf, '/' ) ) ) { + *str_ptr = '\0'; + } + if (outlevel == O_VERBOSE) + report(stdout, "Issuer Organization: %s", cbuf ); + } else { + if (outlevel == O_VERBOSE) + report(stdout, "Unknown Organization", cbuf ); + } + if( ( str_ptr = strstr( ibuf, "/CN=" ) ) ) { + str_ptr += 4; + strcpy( cbuf, str_ptr ); + if( ( str_ptr = strchr(cbuf, '/' ) ) ) { + *str_ptr = '\0'; + } + if (outlevel == O_VERBOSE) + report(stdout, "Issuer CommonName: %s", cbuf ); + } else { + if (outlevel == O_VERBOSE) + report(stdout, "Unknown Issuer CommonName", cbuf ); + } + if( ( str_ptr = strstr( buf, "/CN=" ) ) ) { + str_ptr += 4; + strcpy( cbuf, str_ptr ); + if( ( str_ptr = strchr(cbuf, '/' ) ) ) { + *str_ptr = '\0'; + } + if (outlevel == O_VERBOSE) + report(stdout, "Server CommonName: %s", cbuf ); + /* Should we have some wildcarding here? */ + if ( NULL != _ssl_server_cname && 0 != strcmp( cbuf, _ssl_server_cname ) ) { + report(stdout, "Server CommonName mismatch: %s != %s", cbuf, _ssl_server_cname ); + } + } else { + if (outlevel == O_VERBOSE) + report(stdout, "Unknown Server CommonName", cbuf ); + } + } + + switch (ctx->error) { + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256); + report(stdout, "unknown issuer= %s", buf); + break; + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + report(stderr, "Server Certificate not yet valid"); + break; + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + report(stderr, "Server Certificate expired"); + break; + } + /* We are not requiring or validating server or issuer id's as yet */ + /* Always return OK from here */ + ok_return = 1; + return( ok_return ); +} + + +/* performs initial SSL handshake over the connected socket + * uses SSL *ssl global variable, which is currently defined + * in this file + */ +int SSLOpen(int sock, char *mycert, char *mykey, char *servercname ) +{ + SSL_load_error_strings(); + SSLeay_add_ssl_algorithms(); + + if( sock < 0 || sock > FD_SETSIZE ) { + report(stderr, "File descriptor out of range for SSL" ); + return( -1 ); + } + + if( ! _ctx ) { + /* Be picky and make sure the memory is cleared */ + memset( _ssl_context, 0, sizeof( _ssl_context ) ); + _ctx = SSL_CTX_new(SSLv23_client_method()); + if(_ctx == NULL) { + ERR_print_errors_fp(stderr); + return(-1); + } + } + + _ssl_context[sock] = SSL_new(_ctx); + + if(_ssl_context[sock] == NULL) { + ERR_print_errors_fp(stderr); + return(-1); + } + + /* This static is for the verify callback */ + _ssl_server_cname = servercname; + + SSL_CTX_set_verify(_ctx, SSL_VERIFY_PEER, SSL_verify_callback); + + if( mycert || mykey ) { + + /* Ok... He has a certificate file defined, so lets declare it. If + * he does NOT have a separate certificate and private key file then + * assume that it's a combined key and certificate file. + */ + if( !mykey ) + mykey = mycert; + if( !mycert ) + mycert = mykey; + SSL_use_certificate_file(_ssl_context[sock], mycert, SSL_FILETYPE_PEM); + SSL_use_RSAPrivateKey_file(_ssl_context[sock], mykey, SSL_FILETYPE_PEM); + } + + SSL_set_fd(_ssl_context[sock], sock); + + if(SSL_connect(_ssl_context[sock]) == -1) { + ERR_print_errors_fp(stderr); + return(-1); + } + + return(0); +} +#endif + int SockClose(int sock) /* close a socket (someday we may do other cleanup here) */ { +#ifdef SSL_ENABLE + SSL *ssl; + + if( NULL != ( ssl = SSLGetContext( sock ) ) ) { + /* Clean up the SSL stack */ + SSL_free( _ssl_context[sock] ); + _ssl_context[sock] = NULL; + } +#endif return(close(sock)); } |