summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Roessler <roessler@does-not-exist.org>2000-10-04 18:33:27 +0000
committerThomas Roessler <roessler@does-not-exist.org>2000-10-04 18:33:27 +0000
commit47bb2c52234acfdc4ccc066baed8541df8acc3d0 (patch)
treee269f516f0d343710a399b39d339f9106b390db4
parentbc3d0e280048a2d283ca2491e93f1a5c5e5a8fa6 (diff)
Adding support for Netscape's (pardon, Mozilla's) SSL
implementation. From Michael Elkins.
-rw-r--r--Makefile.am5
-rw-r--r--acconfig.h3
-rw-r--r--configure.in26
-rw-r--r--init.c2
-rw-r--r--init.h8
-rw-r--r--mutt.h2
-rw-r--r--mutt_socket.c2
-rw-r--r--mutt_ssl_nss.c390
-rw-r--r--pop.c26
9 files changed, 454 insertions, 10 deletions
diff --git a/Makefile.am b/Makefile.am
index c674929e..652e60f6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -69,11 +69,12 @@ non_us_sources = pgp.c pgpinvoke.c pgpkey.c pgplib.c sha1.c \
doc/language.txt doc/language50.txt OPS.PGP doc/PGP-Notes.txt \
OPS.MIX remailer.c remailer.h pgpewrap \
contrib/pgp2.rc contrib/pgp5.rc contrib/gpg.rc \
- mutt_ssl.c mutt_ssl.h README.SSL
+ mutt_ssl.c mutt_ssl.h README.SSL mutt_ssl_nss.c
EXTRA_mutt_SOURCES = account.c md5c.c mutt_sasl.c mutt_socket.c mutt_ssl.c \
pop.c pgp.c pgpinvoke.c pgpkey.c pgplib.c sha1.c gnupgparse.c \
- resize.c dotlock.c remailer.c browser.h mbyte.h remailer.h url.h
+ resize.c dotlock.c remailer.c browser.h mbyte.h remailer.h url.h \
+ mutt_ssl_nss.c
EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP TODO configure acconfig.h account.h \
attach.h buffy.h charset.h copy.h dotlock.h functions.h gen_defs \
diff --git a/acconfig.h b/acconfig.h
index 1f50cc6f..3dad2c55 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -59,6 +59,9 @@
/* Do you want support for SSL? (--with-ssl) */
#undef USE_SSL
+/* Do you want support for SSL via the NSS library? (--with-nss) */
+#undef USE_NSS
+
/* Avoid SSL routines which used patent-encumbered RC5 algorithms */
#undef NO_RC5
diff --git a/configure.in b/configure.in
index 78e78f66..e5bd60b4 100644
--- a/configure.in
+++ b/configure.in
@@ -554,8 +554,8 @@ dnl -- end socket dependencies --
if test "$need_socket" = "yes"
then
- AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt))
AC_CHECK_FUNC(gethostent, , AC_CHECK_LIB(nsl, gethostent))
+ AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt))
AC_CHECK_FUNCS(getaddrinfo)
AC_DEFINE(USE_SOCKET)
MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS account.o mutt_socket.o"
@@ -659,6 +659,30 @@ AC_ARG_WITH(ssl, [ --with-ssl[=PFX] Compile in SSL socket support for
])
AM_CONDITIONAL(USE_SSL, test x$need_ssl = xyes)
+dnl SSL support via NSS
+AC_ARG_WITH(nss, [ --with-nss[=PFX] Compile in SSL socket support for POP/IMAP via NSS],
+[ if test "$with_nss" != no
+ then
+ if test "$need_socket" != "yes"; then
+ AC_MSG_ERROR([SSL support is only useful with POP or IMAP support])
+ fi
+
+ if test "$with_nss" != "yes"
+ then
+ LDFLAGS="$LDFLAGS -L$withval/lib"
+ CPPFLAGS="$CPPFLAGS -I$withval/include -I$withval/public/security"
+ fi
+
+ AC_DEFINE(USE_NSS)
+ MUTTLIBS="$MUTTLIBS -lssl -lnss -lcertdb -lcerthi -lcryptohi"
+ MUTTLIBS="$MUTTLIBS -lpk11wrap -lsoftoken -lsecutil -ldbm -lplds4 -lplc4 -lfreebl"
+ MUTTLIBS="$MUTTLIBS -lnspr4"
+
+ MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS mutt_ssl_nss.o"
+ need_ssl=yes
+ fi
+])
+
dnl -- end imap dependencies --
AC_ARG_WITH(sasl, [ --with-sasl[=DIR] Use Cyrus SASL library for POP/IMAP authentication],
diff --git a/init.c b/init.c
index 8390441a..9c1cfd32 100644
--- a/init.c
+++ b/init.c
@@ -31,7 +31,7 @@
#endif
-#ifdef USE_SSL
+#if defined(USE_SSL) || defined(USE_NSS)
#include "mutt_ssl.h"
#endif
diff --git a/init.h b/init.h
index e7ee8cf8..fbb5902a 100644
--- a/init.h
+++ b/init.h
@@ -1326,7 +1326,7 @@ struct option_t MuttVars[] = {
*/
#endif /* HAVE_PGP */
-#ifdef USE_SSL
+#if defined(USE_SSL)||defined(USE_NSS)
{ "certificate_file", DT_PATH, R_NONE, UL &SslCertFile, 0 },
/*
** .pp
@@ -1412,10 +1412,14 @@ struct option_t MuttVars[] = {
** .pp
** The name or address of your POP3 server.
*/
- { "pop_port", DT_NUM, R_NONE, UL &PopPort, 110 },
+ { "pop_port", DT_NUM, R_NONE, UL &PopPort, -1 },
/*
** .pp
** This variable specifies which port your POP server is listening on.
+ ** If you specify a value less than zero, Mutt will auto-select the port
+ ** based upon whether your are doing normal pop3 (110) or pop3s (995).
+ ** If your POP3 server listens on a non-standard port, you will have to
+ ** set this to the correct value for your environment.
*/
{ "pop_last", DT_BOOL, R_NONE, OPTPOPLAST, 0 },
/*
diff --git a/mutt.h b/mutt.h
index c07f4d59..44a462a9 100644
--- a/mutt.h
+++ b/mutt.h
@@ -318,7 +318,7 @@ enum
OPTIMAPFORCESSL,
# endif
#endif
-#ifdef USE_SSL
+#if defined(USE_SSL) || defined(USE_NSS)
OPTSSLV2,
OPTSSLV3,
OPTTLSV1,
diff --git a/mutt_socket.c b/mutt_socket.c
index d513081e..052a7579 100644
--- a/mutt_socket.c
+++ b/mutt_socket.c
@@ -195,6 +195,8 @@ CONNECTION* mutt_conn_find (const CONNECTION* start, const ACCOUNT* account)
{
#ifdef USE_SSL
ssl_socket_setup (conn);
+#elif USE_NSS
+ mutt_nss_socket_setup (conn);
#else
mutt_error _("SSL is unavailable.");
sleep (2);
diff --git a/mutt_ssl_nss.c b/mutt_ssl_nss.c
new file mode 100644
index 00000000..d30a1535
--- /dev/null
+++ b/mutt_ssl_nss.c
@@ -0,0 +1,390 @@
+/* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ */
+
+#include <prinit.h>
+#include <pk11func.h>
+#include <prtypes.h>
+#include <prio.h>
+#include <prnetdb.h>
+#include "nss.h"
+#include "ssl.h"
+#include "sechash.h"
+#include "cert.h"
+#include "cdbhdl.h"
+#include "mutt.h"
+#include "mutt_socket.h"
+#include "mutt_curses.h"
+
+static int MuttNssInitialized = 0;
+
+char *SslCertFile = 0;
+char *SslEntropyFile = 0; /* unused, required to link */
+
+/* internal data struct we use with the CONNECTION. this is where NSS-specific
+ * data gets stuffed so that the main mutt_socket.h doesn't have to be
+ * modified.
+ */
+typedef struct
+{
+ PRFileDesc *fd;
+ CERTCertDBHandle *db;
+}
+mutt_nss_t;
+
+/* nss callback to grab the user's password. */
+static char *
+mutt_nss_password_func (PK11SlotInfo * slot, PRBool retry, void *arg)
+{
+ return NULL;
+}
+
+static int
+mutt_nss_error (const char *call)
+{
+ mutt_error ("%s failed (error %d)", call, PR_GetError ());
+ return -1;
+}
+
+/* initialize the NSS library for use. must be called prior to any other
+ * functions in this module.
+ */
+static int
+mutt_nss_init (void)
+{
+ if (!MuttNssInitialized)
+ {
+ PK11_SetPasswordFunc (mutt_nss_password_func);
+ if (NSS_Init (SslCertFile) == SECFailure)
+ return mutt_nss_error ("NSS_Init");
+
+ /* always use strong crypto. */
+ if (NSS_SetDomesticPolicy () == SECFailure)
+ return mutt_nss_error ("NSS_SetDomesticPolicy");
+
+ /* intialize the session cache */
+ SSL_ClearSessionCache ();
+
+ MuttNssInitialized = 1;
+ }
+ return 0;
+}
+
+/* convert from int64 to a readable string and print on the screen */
+static void
+mutt_nss_pretty_time (int64 usecs)
+{
+ struct tm t;
+ PRExplodedTime ex;
+ char timebuf[128];
+
+ PR_ExplodeTime (usecs, PR_LocalTimeParameters, &ex);
+
+ t.tm_sec = ex.tm_sec;
+ t.tm_min = ex.tm_min;
+ t.tm_hour = ex.tm_hour;
+ t.tm_mday = ex.tm_mday;
+ t.tm_mon = ex.tm_month;
+ t.tm_year = ex.tm_year - 1900; /* PRExplodedTime uses the absolute year */
+ t.tm_wday = ex.tm_wday;
+ t.tm_yday = ex.tm_yday;
+
+ strfcpy (timebuf, asctime (&t), sizeof (timebuf));
+ timebuf[strlen (timebuf) - 1] = 0;
+
+ addstr (timebuf);
+}
+
+/* this function is called by the default hook CERT_AuthCertificate when it
+ * can't verify a cert based upon the contents of the user's certificate
+ * database. we are given the option to override the decision and accept
+ * it anyway.
+ */
+static SECStatus
+mutt_nss_bad_cert (void *arg, PRFileDesc * fd)
+{
+ PRErrorCode err;
+ CERTCertificate *cert, *issuer;
+ unsigned char hash[16];
+ int i;
+ CERTCertTrust trust;
+ int64 not_before, not_after;
+ event_t ch;
+ char status[256];
+
+ /* first lets see why this certificate failed. we only want to override
+ * in the case where the cert was not found.
+ */
+ err = PR_GetError ();
+ mutt_error (_("SSL_AuthCertificate failed (error %d)"), err);
+
+ /* fetch the cert in question */
+ cert = SSL_PeerCertificate (fd);
+
+ move (LINES - 8, 0);
+ clrtoeol ();
+ move (LINES - 7, 0);
+ clrtoeol ();
+ addstr ("Issuer: ");
+ addstr (CERT_NameToAscii (&cert->issuer));
+ move (LINES - 6, 0);
+ clrtoeol ();
+ addstr ("Subject: ");
+ addstr (CERT_NameToAscii (&cert->subject));
+
+ move (LINES - 5, 0);
+ clrtoeol ();
+ addstr ("Valid: ");
+ CERT_GetCertTimes (cert, &not_before, &not_after);
+ mutt_nss_pretty_time (not_before);
+ addstr (" to ");
+ mutt_nss_pretty_time (not_after);
+
+ move (LINES - 4, 0);
+ clrtoeol ();
+ addstr ("Fingerprint: ");
+
+ /* calculate the MD5 hash of the raw certificate */
+ HASH_HashBuf (HASH_AlgMD5, hash, cert->derCert.data, cert->derCert.len);
+ for (i = 0; i < 16; i++)
+ {
+ printw ("%0x", hash[i]);
+ if (i != 15)
+ addch (':');
+ }
+
+ mvaddstr (LINES - 3, 0, "Signature: ");
+ clrtoeol ();
+
+ /* find the cert which signed this cert */
+ issuer = CERT_FindCertByName ((CERTCertDBHandle *) arg, &cert->derIssuer);
+
+ /* verify the sig (only) if we have the issuer cert handy */
+ if (issuer &&
+ CERT_VerifySignedData (&cert->signatureWrap, issuer, PR_Now (), NULL)
+ == SECSuccess)
+ addstr ("GOOD");
+ else
+ addstr ("BAD");
+
+ move (LINES - 2, 0);
+ SETCOLOR (MT_COLOR_STATUS);
+ memset (status, ' ', sizeof (status) - 1);
+ if (COLS < sizeof (status))
+ status[COLS - 1] = 0;
+ else
+ status[sizeof (status) - 1] = 0;
+ memcpy (status, "--- SSL Certificate Check",
+ sizeof ("--- SSL Certificate Check") - 1);
+ addstr (status);
+ clrtoeol ();
+ SETCOLOR (MT_COLOR_NORMAL);
+
+ for (;;)
+ {
+ mvaddstr (LINES - 1, 0, "(r)eject, accept (o)nce, (a)lways accept?");
+ clrtoeol ();
+ ch = mutt_getch ();
+ if (ch.ch == -1)
+ {
+ i = SECFailure;
+ break;
+ }
+ else if (tolower (ch.ch) == 'r')
+ {
+ i = SECFailure;
+ break;
+ }
+ else if (tolower (ch.ch) == 'o')
+ {
+ i = SECSuccess;
+ break;
+ }
+ else if (tolower (ch.ch) == 'a')
+ {
+ /* push this certificate onto the user's certificate store so it
+ * automatically becomes valid next time we see it
+ */
+
+ /* set this certificate as a valid peer for SSL-auth ONLY. */
+ CERT_DecodeTrustString (&trust, "P,,");
+
+ CERT_AddTempCertToPerm (cert, NULL, &trust);
+ i = SECSuccess;
+ break;
+ }
+ BEEP ();
+ }
+
+ /* SSL_PeerCertificate() returns a copy with an updated ref count, so
+ * we have to destroy our copy here.
+ */
+ CERT_DestroyCertificate (cert);
+
+ return i;
+}
+
+/* TODO: this currently doesn't support the `Preconnect', it should be
+ * moved out of raw_socket_open() into a generic function instead.
+ */
+static int
+mutt_nss_socket_open (CONNECTION * con)
+{
+ mutt_nss_t *sockdata;
+ PRNetAddr addr;
+ struct hostent *he;
+
+ memset (&addr, 0, sizeof (addr));
+ addr.inet.family = AF_INET;
+ addr.inet.port = PR_htons (con->account.port);
+ he = gethostbyname (con->account.host);
+ if (!he)
+ {
+ mutt_error (_("Unable to find ip for host %s"), con->account.host);
+ return -1;
+ }
+ addr.inet.ip = *((int *) he->h_addr_list[0]);
+
+ sockdata = safe_calloc (1, sizeof (mutt_nss_t));
+
+ do
+ {
+ sockdata->fd = PR_NewTCPSocket ();
+ if (sockdata->fd == NULL)
+ {
+ mutt_error (_("PR_NewTCPSocket failed."));
+ break;
+ }
+ /* make this a SSL socket */
+ sockdata->fd = SSL_ImportFD (NULL, sockdata->fd);
+
+ /* set SSL version options based upon user's preferences */
+ if (!option (OPTTLSV1))
+ {
+ SSL_OptionSet (sockdata->fd, SSL_ENABLE_TLS, PR_FALSE);
+ }
+ if (!option (OPTSSLV2))
+ {
+ SSL_OptionSet (sockdata->fd, SSL_ENABLE_SSL2, PR_FALSE);
+ }
+ if (!option (OPTSSLV3))
+ {
+ SSL_OptionSet (sockdata->fd, SSL_ENABLE_SSL3, PR_FALSE);
+ }
+
+ /* set the host we were attempting to connect to in order to verify
+ * the name in the certificate we get back.
+ */
+ if (SSL_SetURL (sockdata->fd, con->account.host))
+ {
+ mutt_nss_error ("SSL_SetURL");
+ break;
+ }
+
+ /* we don't need no stinking pin. we don't authenticate ourself
+ * via SSL.
+ */
+ SSL_SetPKCS11PinArg (sockdata->fd, 0);
+
+ sockdata->db = CERT_GetDefaultCertDB ();
+
+ /* use the default supplied hook. it takes an argument to our
+ * certificate database. the manual lies, you can't really specify
+ * NULL for the callback to get the default!
+ */
+ SSL_AuthCertificateHook (sockdata->fd, SSL_AuthCertificate,
+ sockdata->db);
+ /* set the callback to be used when SSL_AuthCertificate() fails. this
+ * allows us to override and insert the cert back into the db
+ */
+ SSL_BadCertHook (sockdata->fd, mutt_nss_bad_cert, sockdata->db);
+
+ if (PR_Connect (sockdata->fd, &addr, PR_INTERVAL_NO_TIMEOUT) ==
+ PR_FAILURE)
+ {
+ mutt_error (_("Unable to connect to host %s"), con->account.host);
+ break;
+ }
+
+ /* store the extra info in the CONNECTION struct for later use. */
+ con->sockdata = sockdata;
+
+ /* HACK. some of the higher level calls in mutt_socket.c depend on this
+ * being >0 when we are in the connected state. we just set this to
+ * an arbitrary value to avoid hitting that bug, since we neve have the
+ * real fd.
+ */
+ con->fd = 42;
+
+ /* success */
+ return 0;
+ }
+ while (0);
+
+ /* we get here when we had an oops. clean up the mess. */
+
+ if (sockdata)
+ {
+ if (sockdata->fd)
+ PR_Close (sockdata->fd);
+ if (sockdata->db)
+ CERT_ClosePermCertDB (sockdata->db);
+ safe_free ((void **) &sockdata);
+ }
+ return -1;
+}
+
+static int
+mutt_nss_socket_close (CONNECTION * con)
+{
+ mutt_nss_t *sockdata = (mutt_nss_t *) con->sockdata;
+
+ if (PR_Close (sockdata->fd) == PR_FAILURE)
+ {
+ return -1;
+ }
+ if (sockdata->db)
+ CERT_ClosePermCertDB (sockdata->db);
+ /* free up the memory we used for this connection specific to NSS. */
+ safe_free ((void **) &con->sockdata);
+ return 0;
+}
+
+static int
+mutt_nss_socket_read (CONNECTION * con)
+{
+ return PR_Read (((mutt_nss_t *) con->sockdata)->fd, con->inbuf,
+ LONG_STRING);
+}
+
+static int
+mutt_nss_socket_write (CONNECTION * con, const char *buf, size_t count)
+{
+ return PR_Write (((mutt_nss_t *) con->sockdata)->fd, buf, count);
+}
+
+/* initialize a new connection for use with NSS */
+int
+mutt_nss_socket_setup (CONNECTION * con)
+{
+ if (mutt_nss_init ())
+ return -1;
+ con->open = mutt_nss_socket_open;
+ con->read = mutt_nss_socket_read;
+ con->write = mutt_nss_socket_write;
+ con->close = mutt_nss_socket_close;
+ return 0;
+}
diff --git a/pop.c b/pop.c
index 6e640202..0080fd80 100644
--- a/pop.c
+++ b/pop.c
@@ -120,7 +120,7 @@ int pop_authenticate (CONNECTION *conn)
void mutt_fetchPopMail (void)
{
char buffer[LONG_STRING];
- char msgbuf[SHORT_STRING];
+ char msgbuf[SHORT_STRING], *p;
int i, delanswer, last = 0, msgs, bytes, err = 0;
CONTEXT ctx;
MESSAGE *msg = NULL;
@@ -133,10 +133,30 @@ void mutt_fetchPopMail (void)
return;
}
+ /* if the host is specified as `my.com/ssl', use ssl to connect to the
+ * given port. this is kinda silly (looks like nobody was using this
+ * as of 9-29-2000), but it's easier to test pop+ssl than imap+ssl for
+ * the new NSS support. -me
+ */
+ account.flags = 0;
strfcpy (account.host, PopHost, sizeof (account.host));
- account.port = PopPort;
+ p = strchr(account.host, '/');
+ if (p) {
+ if (!strcmp (p+1, "ssl")) {
+ *p = 0;
+ account.flags |= M_ACCT_SSL;
+ }
+ }
account.type = M_ACCT_TYPE_POP;
- account.flags = 0;
+ if (PopPort < 0) {
+ /* auto-select the port based upon what we are attempting to do */
+ if (account.flags & M_ACCT_SSL)
+ account.port = 995; /* pop3s (pop3 + ssl) */
+ else
+ account.port = 110; /* pop3 */
+ }
+ else
+ account.port = PopPort;
conn = mutt_conn_find (NULL, &account);
if (mutt_socket_open (conn) < 0)
return;