1 /* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
29 #include "mutt_socket.h"
30 #include "mutt_curses.h"
32 static int MuttNssInitialized = 0;
34 /* internal data struct we use with the CONNECTION. this is where NSS-specific
35 * data gets stuffed so that the main mutt_socket.h doesn't have to be
45 /* nss callback to grab the user's password. */
47 mutt_nss_password_func (PK11SlotInfo * slot, PRBool retry, void *arg)
53 mutt_nss_error (const char *call)
55 mutt_error (_("%s failed (error %d)"), call, PR_GetError ());
59 /* initialize the NSS library for use. must be called prior to any other
60 * functions in this module.
65 if (!MuttNssInitialized)
67 PK11_SetPasswordFunc (mutt_nss_password_func);
68 if (NSS_Init (SslCertFile) == SECFailure)
69 return mutt_nss_error ("NSS_Init");
71 /* always use strong crypto. */
72 if (NSS_SetDomesticPolicy () == SECFailure)
73 return mutt_nss_error ("NSS_SetDomesticPolicy");
75 /* intialize the session cache */
76 SSL_ClearSessionCache ();
78 MuttNssInitialized = 1;
83 /* convert from int64 to a readable string and print on the screen */
85 mutt_nss_pretty_time (int64 usecs)
91 PR_ExplodeTime (usecs, PR_LocalTimeParameters, &ex);
95 t.tm_hour = ex.tm_hour;
96 t.tm_mday = ex.tm_mday;
97 t.tm_mon = ex.tm_month;
98 t.tm_year = ex.tm_year - 1900; /* PRExplodedTime uses the absolute year */
99 t.tm_wday = ex.tm_wday;
100 t.tm_yday = ex.tm_yday;
102 strfcpy (timebuf, asctime (&t), sizeof (timebuf));
103 timebuf[strlen (timebuf) - 1] = 0;
108 /* this function is called by the default hook CERT_AuthCertificate when it
109 * can't verify a cert based upon the contents of the user's certificate
110 * database. we are given the option to override the decision and accept
114 mutt_nss_bad_cert (void *arg, PRFileDesc * fd)
117 CERTCertificate *cert, *issuer;
118 unsigned char hash[16];
121 int64 not_before, not_after;
125 /* first lets see why this certificate failed. we only want to override
126 * in the case where the cert was not found.
128 err = PR_GetError ();
129 mutt_error (_("SSL_AuthCertificate failed (error %d)"), err);
131 /* fetch the cert in question */
132 cert = SSL_PeerCertificate (fd);
139 addstr (CERT_NameToAscii (&cert->issuer));
142 addstr ("Subject: ");
143 addstr (CERT_NameToAscii (&cert->subject));
148 CERT_GetCertTimes (cert, ¬_before, ¬_after);
149 mutt_nss_pretty_time (not_before);
151 mutt_nss_pretty_time (not_after);
155 addstr ("Fingerprint: ");
157 /* calculate the MD5 hash of the raw certificate */
158 HASH_HashBuf (HASH_AlgMD5, hash, cert->derCert.data, cert->derCert.len);
159 for (i = 0; i < 16; i++)
161 printw ("%0x", hash[i]);
166 mvaddstr (LINES - 3, 0, "Signature: ");
169 /* find the cert which signed this cert */
170 issuer = CERT_FindCertByName ((CERTCertDBHandle *) arg, &cert->derIssuer);
172 /* verify the sig (only) if we have the issuer cert handy */
174 CERT_VerifySignedData (&cert->signatureWrap, issuer, PR_Now (), NULL)
181 SETCOLOR (MT_COLOR_STATUS);
182 memset (status, ' ', sizeof (status) - 1);
183 if (COLS < sizeof (status))
184 status[COLS - 1] = 0;
186 status[sizeof (status) - 1] = 0;
187 memcpy (status, "--- SSL Certificate Check",
188 sizeof ("--- SSL Certificate Check") - 1);
191 SETCOLOR (MT_COLOR_NORMAL);
195 mvaddstr (LINES - 1, 0, "(r)eject, accept (o)nce, (a)lways accept?");
203 else if (ascii_tolower (ch.ch) == 'r')
208 else if (ascii_tolower (ch.ch) == 'o')
213 else if (ascii_tolower (ch.ch) == 'a')
215 /* push this certificate onto the user's certificate store so it
216 * automatically becomes valid next time we see it
219 /* set this certificate as a valid peer for SSL-auth ONLY. */
220 CERT_DecodeTrustString (&trust, "P,,");
222 CERT_AddTempCertToPerm (cert, NULL, &trust);
229 /* SSL_PeerCertificate() returns a copy with an updated ref count, so
230 * we have to destroy our copy here.
232 CERT_DestroyCertificate (cert);
238 mutt_nss_socket_open (CONNECTION * con)
240 mutt_nss_t *sockdata;
244 memset (&addr, 0, sizeof (addr));
245 addr.inet.family = AF_INET;
246 addr.inet.port = PR_htons (con->account.port);
247 he = gethostbyname (con->account.host);
250 mutt_error (_("Unable to find ip for host %s"), con->account.host);
253 addr.inet.ip = *((int *) he->h_addr_list[0]);
255 sockdata = safe_calloc (1, sizeof (mutt_nss_t));
259 sockdata->fd = PR_NewTCPSocket ();
260 if (sockdata->fd == NULL)
262 mutt_error (_("PR_NewTCPSocket failed."));
265 /* make this a SSL socket */
266 sockdata->fd = SSL_ImportFD (NULL, sockdata->fd);
268 /* set SSL version options based upon user's preferences */
269 if (!option (OPTTLSV1))
270 SSL_OptionSet (sockdata->fd, SSL_ENABLE_TLS, PR_FALSE);
272 if (!option (OPTSSLV2))
273 SSL_OptionSet (sockdata->fd, SSL_ENABLE_SSL2, PR_FALSE);
275 if (!option (OPTSSLV3))
276 SSL_OptionSet (sockdata->fd, SSL_ENABLE_SSL3, PR_FALSE);
278 /* set the host we were attempting to connect to in order to verify
279 * the name in the certificate we get back.
281 if (SSL_SetURL (sockdata->fd, con->account.host))
283 mutt_nss_error ("SSL_SetURL");
287 /* we don't need no stinking pin. we don't authenticate ourself
290 SSL_SetPKCS11PinArg (sockdata->fd, 0);
292 sockdata->db = CERT_GetDefaultCertDB ();
294 /* use the default supplied hook. it takes an argument to our
295 * certificate database. the manual lies, you can't really specify
296 * NULL for the callback to get the default!
298 SSL_AuthCertificateHook (sockdata->fd, SSL_AuthCertificate,
300 /* set the callback to be used when SSL_AuthCertificate() fails. this
301 * allows us to override and insert the cert back into the db
303 SSL_BadCertHook (sockdata->fd, mutt_nss_bad_cert, sockdata->db);
305 if (PR_Connect (sockdata->fd, &addr, PR_INTERVAL_NO_TIMEOUT) ==
308 mutt_error (_("Unable to connect to host %s"), con->account.host);
312 /* store the extra info in the CONNECTION struct for later use. */
313 con->sockdata = sockdata;
315 /* HACK. some of the higher level calls in mutt_socket.c depend on this
316 * being >0 when we are in the connected state. we just set this to
317 * an arbitrary value to avoid hitting that bug, since we neve have the
327 /* we get here when we had an oops. clean up the mess. */
332 PR_Close (sockdata->fd);
334 CERT_ClosePermCertDB (sockdata->db);
341 mutt_nss_socket_close (CONNECTION * con)
343 mutt_nss_t *sockdata = (mutt_nss_t *) con->sockdata;
345 if (PR_Close (sockdata->fd) == PR_FAILURE)
349 CERT_ClosePermCertDB (sockdata->db);
350 /* free up the memory we used for this connection specific to NSS. */
351 FREE (&con->sockdata);
356 mutt_nss_socket_read (CONNECTION* conn, char* buf, size_t len)
358 return PR_Read (((mutt_nss_t*) conn->sockdata)->fd, buf, len);
362 mutt_nss_socket_write (CONNECTION * con, const char *buf, size_t count)
364 return PR_Write (((mutt_nss_t *) con->sockdata)->fd, buf, count);
367 /* initialize a new connection for use with NSS */
369 mutt_nss_socket_setup (CONNECTION * con)
371 if (mutt_nss_init ())
373 con->open = mutt_nss_socket_open;
374 con->read = mutt_nss_socket_read;
375 con->write = mutt_nss_socket_write;
376 con->close = mutt_nss_socket_close;