aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--INSTALL28
-rw-r--r--Makefile.in35
-rw-r--r--NEWS9
-rw-r--r--acconfig.h3
-rw-r--r--conf.c8
-rw-r--r--configure.in21
-rw-r--r--driver.c15
-rw-r--r--etrn.c2
-rw-r--r--fetchmail-features.html9
-rw-r--r--fetchmail.c16
-rw-r--r--fetchmail.h10
-rwxr-xr-xfetchmailconf34
-rw-r--r--imap.c2
-rw-r--r--options.c31
-rw-r--r--pop2.c4
-rw-r--r--pop3.c2
-rw-r--r--rcfile_l.l3
-rw-r--r--rcfile_y.y8
-rw-r--r--sample.rcfile8
-rw-r--r--socket.c290
20 files changed, 505 insertions, 33 deletions
diff --git a/INSTALL b/INSTALL
index 2d980995..79ff0486 100644
--- a/INSTALL
+++ b/INSTALL
@@ -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:
diff --git a/NEWS b/NEWS
index f62eab93..dd9d6884 100644
--- a/NEWS
+++ b/NEWS
@@ -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.
diff --git a/acconfig.h b/acconfig.h
index 15440a3b..3214bdd1 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -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
diff --git a/conf.c b/conf.c
index ea7e3bad..499e141d 100644
--- a/conf.c
+++ b/conf.c
@@ -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:
diff --git a/driver.c b/driver.c
index 950830f0..38475016 100644
--- a/driver.c
+++ b/driver.c
@@ -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)
{
diff --git a/etrn.c b/etrn.c
index 1d540c2b..1588ac34 100644
--- a/etrn.c
+++ b/etrn.c
@@ -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">&lt;esr@snark.thyrsus.com&gt;</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)
diff --git a/imap.c b/imap.c
index 19defbc9..0dc78689 100644
--- a/imap.c
+++ b/imap.c
@@ -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 */
diff --git a/options.c b/options.c
index 8db1840f..fc8056ba 100644
--- a/options.c
+++ b/options.c
@@ -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"));
diff --git a/pop2.c b/pop2.c
index a17a4e22..8d6f2f3f 100644
--- a/pop2.c
+++ b/pop2.c
@@ -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 */
diff --git a/pop3.c b/pop3.c
index d3ab0dc0..53fcb1fe 100644
--- a/pop3.c
+++ b/pop3.c
@@ -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 */
diff --git a/rcfile_l.l b/rcfile_l.l
index 8355346f..d37f0a7a 100644
--- a/rcfile_l.l
+++ b/rcfile_l.l
@@ -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; }
diff --git a/rcfile_y.y b/rcfile_y.y
index 57e47444..9bea1636 100644
--- a/rcfile_y.y
+++ b/rcfile_y.y
@@ -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
diff --git a/socket.c b/socket.c
index db0d7000..6d82c428 100644
--- a/socket.c
+++ b/socket.c
@@ -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));
}