d2663581e1e51bd96553a69dfd7611c8fcb7fcf7
[apps/madmutt.git] / lib-sys / mutt_ssl_gnutls.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 2001 Marco d'Itri <md@linux.it>
4  * Copyright (C) 2001-2004 Andrew McDonald <andrew@mcdonald.org.uk>
5  *
6  * This file is part of mutt-ng, see http://www.muttng.org/.
7  * It's licensed under the GNU General Public License,
8  * please see the file GPL in the top level source directory.
9  */
10
11 #include <lib-lib/lib-lib.h>
12
13 #include <gnutls/gnutls.h>
14 #include <gnutls/x509.h>
15 #ifdef HAVE_GNUTLS_OPENSSL_H
16 #include <gnutls/openssl.h>
17 #endif
18
19 #include <lib-ui/curses.h>
20 #include <lib-ui/menu.h>
21
22 #include "mutt.h"
23 #include "mutt_socket.h"
24
25 typedef struct _tlssockdata {
26   gnutls_session state;
27   gnutls_certificate_credentials xcred;
28 } tlssockdata;
29
30 /* local prototypes */
31 static int tls_socket_read (CONNECTION * conn, char *buf, ssize_t len);
32 static int tls_socket_write (CONNECTION * conn, const char *buf, ssize_t len);
33 static int tls_socket_open (CONNECTION * conn);
34 static int tls_socket_close (CONNECTION * conn);
35 static int tls_starttls_close (CONNECTION * conn);
36
37 static int tls_init (void);
38 static int tls_negotiate (CONNECTION * conn);
39 static int tls_check_certificate (CONNECTION * conn);
40
41
42 static int tls_init (void)
43 {
44   static unsigned char init_complete = 0;
45   int err;
46
47   if (init_complete)
48     return 0;
49
50   err = gnutls_global_init ();
51   if (err < 0) {
52     mutt_error (_("gnutls_global_init: %s"), gnutls_strerror (err));
53     mutt_sleep (2);
54     return -1;
55   }
56
57   init_complete = 1;
58   return 0;
59 }
60
61 int mutt_ssl_socket_setup (CONNECTION * conn)
62 {
63   if (tls_init () < 0)
64     return -1;
65
66   conn->conn_open = tls_socket_open;
67   conn->conn_read = tls_socket_read;
68   conn->conn_write = tls_socket_write;
69   conn->conn_close = tls_socket_close;
70
71   return 0;
72 }
73
74 static int tls_socket_read (CONNECTION * conn, char *buf, ssize_t len)
75 {
76   tlssockdata *data = conn->sockdata;
77   int ret;
78
79   if (!data) {
80     mutt_error (_("Error: no TLS socket open"));
81     mutt_sleep (2);
82     return -1;
83   }
84
85   ret = gnutls_record_recv (data->state, buf, len);
86   if (gnutls_error_is_fatal (ret) == 1) {
87     mutt_error (_("tls_socket_read (%s)"), gnutls_strerror (ret));
88     mutt_sleep (4);
89     return -1;
90   }
91   return ret;
92 }
93
94 static int tls_socket_write (CONNECTION * conn, const char *buf, ssize_t len)
95 {
96   tlssockdata *data = conn->sockdata;
97   int ret;
98
99   if (!data) {
100     mutt_error (_("Error: no TLS socket open"));
101     mutt_sleep (2);
102     return -1;
103   }
104
105   ret = gnutls_record_send (data->state, buf, len);
106   if (gnutls_error_is_fatal (ret) == 1) {
107     mutt_error (_("tls_socket_write (%s)"), gnutls_strerror (ret));
108     mutt_sleep (4);
109     return -1;
110   }
111   return ret;
112 }
113
114 static int tls_socket_open (CONNECTION * conn)
115 {
116   if (raw_socket_open (conn) < 0)
117     return -1;
118
119   if (tls_negotiate (conn) < 0) {
120     tls_socket_close (conn);
121     return -1;
122   }
123
124   return 0;
125 }
126
127 int mutt_ssl_starttls (CONNECTION * conn)
128 {
129   if (tls_init () < 0)
130     return -1;
131
132   if (tls_negotiate (conn) < 0)
133     return -1;
134
135   conn->conn_read = tls_socket_read;
136   conn->conn_write = tls_socket_write;
137   conn->conn_close = tls_starttls_close;
138
139   return 0;
140 }
141
142 static int protocol_priority[] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
143
144 /* tls_negotiate: After TLS state has been initialised, attempt to negotiate
145  *   TLS over the wire, including certificate checks. */
146 static int tls_negotiate (CONNECTION * conn)
147 {
148   tlssockdata *data;
149   int err;
150
151   data = p_new(tlssockdata, 1);
152   conn->sockdata = data;
153   err = gnutls_certificate_allocate_credentials (&data->xcred);
154   if (err < 0) {
155     p_delete(&conn->sockdata);
156     mutt_error (_("gnutls_certificate_allocate_credentials: %s"),
157                 gnutls_strerror (err));
158     mutt_sleep (2);
159     return -1;
160   }
161
162   gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
163                                           GNUTLS_X509_FMT_PEM);
164   /* ignore errors, maybe file doesn't exist yet */
165
166   if (SslCACertFile) {
167     gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
168                                             GNUTLS_X509_FMT_PEM);
169   }
170
171 /*
172   gnutls_set_x509_client_key (data->xcred, "", "");
173   gnutls_set_x509_cert_callback (data->xcred, cert_callback);
174 */
175
176   gnutls_init (&data->state, GNUTLS_CLIENT);
177
178   /* set socket */
179   gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr)(intptr_t)conn->fd);
180
181   /* disable TLS/SSL protocols as needed */
182   if (!option (OPTTLSV1) && !option (OPTSSLV3)) {
183     mutt_error (_("All available protocols for TLS/SSL connection disabled"));
184     goto fail;
185   }
186   else if (!option (OPTTLSV1)) {
187     protocol_priority[0] = GNUTLS_SSL3;
188     protocol_priority[1] = 0;
189   }
190   else if (!option (OPTSSLV3)) {
191     protocol_priority[0] = GNUTLS_TLS1;
192     protocol_priority[1] = 0;
193   }
194   /*
195      else
196      use the list set above
197    */
198
199   /* We use default priorities (see gnutls documentation),
200      except for protocol version */
201   gnutls_set_default_priority (data->state);
202   gnutls_protocol_set_priority (data->state, protocol_priority);
203
204   if (SslDHPrimeBits > 0) {
205     gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
206   }
207
208 /*
209   gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
210 */
211
212   gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
213
214   err = gnutls_handshake (data->state);
215
216   while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) {
217     err = gnutls_handshake (data->state);
218   }
219   if (err < 0) {
220     if (err == GNUTLS_E_FATAL_ALERT_RECEIVED) {
221       mutt_error (_("gnutls_handshake: %s(%s)"), gnutls_strerror (err),
222                   gnutls_alert_get_name (gnutls_alert_get (data->state)));
223     }
224     else {
225       mutt_error (_("gnutls_handshake: %s"), gnutls_strerror (err));
226     }
227     mutt_sleep (2);
228     goto fail;
229   }
230
231   if (!tls_check_certificate (conn))
232     goto fail;
233
234   /* set Security Strength Factor (SSF) for SASL */
235   /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
236   conn->ssf =
237     gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
238
239   mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
240                 gnutls_protocol_get_name (gnutls_protocol_get_version
241                                           (data->state)),
242                 gnutls_kx_get_name (gnutls_kx_get (data->state)),
243                 gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
244                 gnutls_mac_get_name (gnutls_mac_get (data->state)));
245   mutt_sleep (0);
246
247   return 0;
248
249 fail:
250   gnutls_certificate_free_credentials (data->xcred);
251   gnutls_deinit (data->state);
252   p_delete(&conn->sockdata);
253   return -1;
254 }
255
256 static int tls_socket_close (CONNECTION * conn)
257 {
258   tlssockdata *data = conn->sockdata;
259
260   if (data) {
261     gnutls_bye (data->state, GNUTLS_SHUT_RDWR);
262
263     gnutls_certificate_free_credentials (data->xcred);
264     gnutls_deinit (data->state);
265     p_delete(&conn->sockdata);
266   }
267
268   return raw_socket_close (conn);
269 }
270
271 static int tls_starttls_close (CONNECTION * conn)
272 {
273   int rc;
274
275   rc = tls_socket_close (conn);
276   conn->conn_read = raw_socket_read;
277   conn->conn_write = raw_socket_write;
278   conn->conn_close = raw_socket_close;
279
280   return rc;
281 }
282
283 #define CERT_SEP "-----BEGIN"
284
285 /* this bit is based on read_ca_file() in gnutls */
286 static int tls_compare_certificates (const gnutls_datum * peercert)
287 {
288   gnutls_datum cert;
289   unsigned char *ptr;
290   FILE *fd1;
291   int ret;
292   gnutls_datum b64_data;
293   unsigned char *b64_data_data;
294   struct stat filestat;
295
296   if (stat (SslCertFile, &filestat) == -1)
297     return 0;
298
299   b64_data.size = filestat.st_size + 1;
300   b64_data_data = p_new(unsigned char, b64_data.size);
301   b64_data_data[b64_data.size - 1] = '\0';
302   b64_data.data = b64_data_data;
303
304   fd1 = fopen (SslCertFile, "r");
305   if (fd1 == NULL) {
306     return 0;
307   }
308
309   b64_data.size = fread (b64_data.data, 1, b64_data.size, fd1);
310   m_fclose(&fd1);
311
312   do {
313     ret = gnutls_pem_base64_decode_alloc (NULL, &b64_data, &cert);
314     if (ret != 0) {
315       p_delete(&b64_data_data);
316       return 0;
317     }
318
319     ptr = (unsigned char *) strstr ((char*) b64_data.data, CERT_SEP) + 1;
320     ptr = (unsigned char *) strstr ((char*) ptr, CERT_SEP);
321
322     b64_data.size = b64_data.size - (ptr - b64_data.data);
323     b64_data.data = ptr;
324
325     if (cert.size == peercert->size) {
326       if (memcmp (cert.data, peercert->data, cert.size) == 0) {
327         /* match found */
328         gnutls_free (cert.data);
329         p_delete(&b64_data_data);
330         return 1;
331       }
332     }
333
334     gnutls_free (cert.data);
335   } while (ptr != NULL);
336
337   /* no match found */
338   p_delete(&b64_data_data);
339   return 0;
340 }
341
342 static void tls_fingerprint (gnutls_digest_algorithm algo,
343                              char *s, int l, const gnutls_datum * data)
344 {
345   char md[36];
346   ssize_t n;
347   int j;
348
349   n = 36;
350
351   if (gnutls_fingerprint(algo, data, md, (size_t *)&n) < 0) {
352     snprintf (s, l, _("[unable to calculate]"));
353   }
354   else {
355     for (j = 0; j < (int) n; j++) {
356       char ch[8];
357
358       snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
359       strncat (s, ch, l);
360     }
361     s[2 * n + n / 2 - 1] = '\0';        /* don't want trailing space */
362   }
363 }
364
365 static char *tls_make_date (time_t t, char *s, ssize_t len)
366 {
367   struct tm *l = gmtime (&t);
368
369   if (l)
370     snprintf (s, len, "%s, %d %s %d %02d:%02d:%02d UTC",
371               Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
372               l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
373   else
374     m_strcpy(s, len, _("[invalid date]"));
375
376   return (s);
377 }
378
379 static int tls_check_stored_hostname (const gnutls_datum * cert,
380                                       const char *hostname)
381 {
382   char buf[80];
383   FILE *fp;
384   char *linestr = NULL;
385   ssize_t linestrsize;
386   int linenum = 0;
387   regex_t preg;
388   regmatch_t pmatch[3];
389
390   /* try checking against names stored in stored certs file */
391   if ((fp = fopen (SslCertFile, "r"))) {
392     if (regcomp
393         (&preg,
394          "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$",
395          REG_ICASE | REG_EXTENDED) != 0) {
396       regfree (&preg);
397       return 0;
398     }
399
400     buf[0] = '\0';
401     tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
402     while ((linestr =
403             mutt_read_line (linestr, &linestrsize, fp, &linenum)) != NULL) {
404       if (linestr[0] == '#' && linestr[1] == 'H') {
405         if (regexec (&preg, linestr, 3, pmatch, 0) == 0) {
406           linestr[pmatch[1].rm_eo] = '\0';
407           linestr[pmatch[2].rm_eo] = '\0';
408           if (m_strcmp(linestr + pmatch[1].rm_so, hostname) == 0 &&
409               m_strcmp(linestr + pmatch[2].rm_so, buf) == 0) {
410             regfree (&preg);
411             p_delete(&linestr);
412             m_fclose(&fp);
413             return 1;
414           }
415         }
416       }
417     }
418
419     regfree (&preg);
420     m_fclose(&fp);
421   }
422
423   /* not found a matching name */
424   return 0;
425 }
426
427 static int tls_check_certificate (CONNECTION * conn)
428 {
429   tlssockdata *data = conn->sockdata;
430   gnutls_session state = data->state;
431   char helpstr[STRING];
432   char buf[STRING];
433   char fpbuf[STRING];
434   ssize_t buflen;
435   char dn_common_name[STRING];
436   char dn_email[STRING];
437   char dn_organization[STRING];
438   char dn_organizational_unit[STRING];
439   char dn_locality[STRING];
440   char dn_province[STRING];
441   char dn_country[STRING];
442   MUTTMENU *menu;
443   int done, row, i, ret;
444   FILE *fp;
445   time_t t;
446   const gnutls_datum *cert_list;
447   unsigned int cert_list_size = 0;
448   gnutls_certificate_status_t certstat;
449   char datestr[30];
450   gnutls_x509_crt cert;
451   gnutls_datum pemdata;
452   int certerr_expired = 0;
453   int certerr_notyetvalid = 0;
454   int certerr_hostname = 0;
455   int certerr_nottrusted = 0;
456   int certerr_revoked = 0;
457   int certerr_signernotca = 0;
458
459   if (gnutls_auth_get_type (state) != GNUTLS_CRD_CERTIFICATE) {
460     mutt_error (_("Unable to get certificate from peer"));
461     mutt_sleep (2);
462     return 0;
463   }
464
465   if (gnutls_certificate_verify_peers2(state, &certstat) < 0) {
466       mutt_error (_("Certificate verification error (%s)"),
467                   gnutls_strerror(certstat));
468       mutt_sleep (2);
469       return 0;
470   }
471
472   /* We only support X.509 certificates (not OpenPGP) at the moment */
473   if (gnutls_certificate_type_get (state) != GNUTLS_CRT_X509) {
474     mutt_error (_("Certificate is not X.509"));
475     mutt_sleep (2);
476     return 0;
477   }
478
479   if (gnutls_x509_crt_init (&cert) < 0) {
480     mutt_error (_("Error initialising gnutls certificate data"));
481     mutt_sleep (2);
482     return 0;
483   }
484
485   cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
486   if (!cert_list) {
487     mutt_error (_("Unable to get certificate from peer"));
488     mutt_sleep (2);
489     return 0;
490   }
491
492   /* FIXME: Currently only check first certificate in chain. */
493   if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
494     mutt_error (_("Error processing certificate data"));
495     mutt_sleep (2);
496     return 0;
497   }
498
499   if (gnutls_x509_crt_get_expiration_time (cert) < time (NULL)) {
500     certerr_expired = 1;
501   }
502
503   if (gnutls_x509_crt_get_activation_time (cert) > time (NULL)) {
504     certerr_notyetvalid = 1;
505   }
506
507   if (!gnutls_x509_crt_check_hostname (cert, conn->account.host) &&
508       !tls_check_stored_hostname (&cert_list[0], conn->account.host)) {
509     certerr_hostname = 1;
510   }
511
512   /* see whether certificate is in our cache (certificates file) */
513   if (tls_compare_certificates (&cert_list[0])) {
514     if (certstat & GNUTLS_CERT_INVALID) {
515       /* doesn't matter - have decided is valid because server
516          certificate is in our trusted cache */
517       certstat ^= GNUTLS_CERT_INVALID;
518     }
519
520     if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
521       /* doesn't matter that we haven't found the signer, since
522          certificate is in our trusted cache */
523       certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
524     }
525
526     if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
527       /* Hmm. Not really sure how to handle this, but let's say
528          that we don't care if the CA certificate hasn't got the
529          correct X.509 basic constraints if server certificate is
530          in our cache. */
531       certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
532     }
533
534   }
535
536   if (certstat & GNUTLS_CERT_REVOKED) {
537     certerr_revoked = 1;
538     certstat ^= GNUTLS_CERT_REVOKED;
539   }
540
541   if (certstat & GNUTLS_CERT_INVALID) {
542     certerr_nottrusted = 1;
543     certstat ^= GNUTLS_CERT_INVALID;
544   }
545
546   if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
547     /* NB: already cleared if cert in cache */
548     certerr_nottrusted = 1;
549     certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
550   }
551
552   if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
553     /* NB: already cleared if cert in cache */
554     certerr_signernotca = 1;
555     certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
556   }
557
558   /* OK if signed by (or is) a trusted certificate */
559   /* we've been zeroing the interesting bits in certstat - 
560      don't return OK if there are any unhandled bits we don't
561      understand */
562   if (!(certerr_expired || certerr_notyetvalid ||
563         certerr_hostname || certerr_nottrusted) && certstat == 0) {
564     gnutls_x509_crt_deinit (cert);
565     return 1;
566   }
567
568
569   /* interactive check from user */
570   menu = mutt_new_menu ();
571   menu->max = 25;
572   menu->dialog = p_new(char*, menu->max);
573   for (i = 0; i < menu->max; i++)
574     menu->dialog[i] = p_new(char, STRING);
575
576   row = 0;
577   m_strcpy(menu->dialog[row], STRING,
578            _("This certificate belongs to:"));
579   row++;
580
581   buflen = sizeof (dn_common_name);
582   if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
583                                      dn_common_name, (size_t *)&buflen) != 0)
584     dn_common_name[0] = '\0';
585   buflen = sizeof (dn_email);
586   if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
587                                      dn_email, (size_t *)&buflen) != 0)
588     dn_email[0] = '\0';
589   buflen = sizeof (dn_organization);
590   if (gnutls_x509_crt_get_dn_by_oid
591       (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
592        (size_t *)&buflen) != 0)
593     dn_organization[0] = '\0';
594   buflen = sizeof (dn_organizational_unit);
595   if (gnutls_x509_crt_get_dn_by_oid
596       (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
597        dn_organizational_unit, (size_t *)&buflen) != 0)
598     dn_organizational_unit[0] = '\0';
599   buflen = sizeof (dn_locality);
600   if (gnutls_x509_crt_get_dn_by_oid
601       (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, (size_t *)&buflen) != 0)
602     dn_locality[0] = '\0';
603   buflen = sizeof (dn_province);
604   if (gnutls_x509_crt_get_dn_by_oid
605       (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
606        (size_t *)&buflen) != 0)
607     dn_province[0] = '\0';
608   buflen = sizeof (dn_country);
609   if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
610                                      dn_country, (size_t *)&buflen) != 0)
611     dn_country[0] = '\0';
612
613   snprintf (menu->dialog[row++], STRING, "   %s  %s", dn_common_name,
614             dn_email);
615   snprintf (menu->dialog[row++], STRING, "   %s", dn_organization);
616   snprintf (menu->dialog[row++], STRING, "   %s",
617             dn_organizational_unit);
618   snprintf (menu->dialog[row++], STRING, "   %s  %s  %s", dn_locality,
619             dn_province, dn_country);
620   row++;
621
622   m_strcpy(menu->dialog[row], STRING,
623            _("This certificate was issued by:"));
624   row++;
625
626   buflen = sizeof (dn_common_name);
627   if (gnutls_x509_crt_get_issuer_dn_by_oid
628       (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, (size_t *)&buflen) != 0)
629     dn_common_name[0] = '\0';
630   buflen = sizeof (dn_email);
631   if (gnutls_x509_crt_get_issuer_dn_by_oid
632       (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, (size_t *)&buflen) != 0)
633     dn_email[0] = '\0';
634   buflen = sizeof (dn_organization);
635   if (gnutls_x509_crt_get_issuer_dn_by_oid
636       (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
637        (size_t *)&buflen) != 0)
638     dn_organization[0] = '\0';
639   buflen = sizeof (dn_organizational_unit);
640   if (gnutls_x509_crt_get_issuer_dn_by_oid
641       (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
642        dn_organizational_unit, (size_t *)&buflen) != 0)
643     dn_organizational_unit[0] = '\0';
644   buflen = sizeof (dn_locality);
645   if (gnutls_x509_crt_get_issuer_dn_by_oid
646       (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, (size_t *)&buflen) != 0)
647     dn_locality[0] = '\0';
648   buflen = sizeof (dn_province);
649   if (gnutls_x509_crt_get_issuer_dn_by_oid
650       (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
651        (size_t *)&buflen) != 0)
652     dn_province[0] = '\0';
653   buflen = sizeof (dn_country);
654   if (gnutls_x509_crt_get_issuer_dn_by_oid
655       (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, (size_t *)&buflen) != 0)
656     dn_country[0] = '\0';
657
658   snprintf (menu->dialog[row++], STRING, "   %s  %s", dn_common_name,
659             dn_email);
660   snprintf (menu->dialog[row++], STRING, "   %s", dn_organization);
661   snprintf (menu->dialog[row++], STRING, "   %s",
662             dn_organizational_unit);
663   snprintf (menu->dialog[row++], STRING, "   %s  %s  %s", dn_locality,
664             dn_province, dn_country);
665   row++;
666
667   snprintf (menu->dialog[row++], STRING,
668             _("This certificate is valid"));
669
670   t = gnutls_x509_crt_get_activation_time (cert);
671   snprintf (menu->dialog[row++], STRING, _("   from %s"),
672             tls_make_date (t, datestr, 30));
673
674   t = gnutls_x509_crt_get_expiration_time (cert);
675   snprintf (menu->dialog[row++], STRING, _("     to %s"),
676             tls_make_date (t, datestr, 30));
677
678   fpbuf[0] = '\0';
679   tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]);
680   snprintf (menu->dialog[row++], STRING, _("SHA1 Fingerprint: %s"),
681             fpbuf);
682   fpbuf[0] = '\0';
683   tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]);
684   snprintf (menu->dialog[row++], STRING, _("MD5 Fingerprint: %s"),
685             fpbuf);
686
687   if (certerr_notyetvalid) {
688     row++;
689     m_strcpy(menu->dialog[row], STRING,
690              _("WARNING: Server certificate is not yet valid"));
691   }
692   if (certerr_expired) {
693     row++;
694     m_strcpy(menu->dialog[row], STRING,
695              _("WARNING: Server certificate has expired"));
696   }
697   if (certerr_revoked) {
698     row++;
699     m_strcpy(menu->dialog[row], STRING,
700              _("WARNING: Server certificate has been revoked"));
701   }
702   if (certerr_hostname) {
703     row++;
704     m_strcpy(menu->dialog[row], STRING,
705              _("WARNING: Server hostname does not match certificate"));
706   }
707   if (certerr_signernotca) {
708     row++;
709     m_strcpy(menu->dialog[row], STRING,
710              _("WARNING: Signer of server certificate is not a CA"));
711   }
712
713   menu->title = _("TLS/SSL Certificate check");
714   /* certificates with bad dates, or that are revoked, must be
715      accepted manually each and every time */
716   if (SslCertFile && !certerr_expired && !certerr_notyetvalid
717       && !certerr_revoked) {
718     menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
719     menu->keys = _("roa");
720   }
721   else {
722     menu->prompt = _("(r)eject, accept (o)nce");
723     menu->keys = _("ro");
724   }
725
726   helpstr[0] = '\0';
727   mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
728   strncat (helpstr, buf, sizeof (helpstr));
729   mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
730   strncat (helpstr, buf, sizeof (helpstr));
731   menu->help = helpstr;
732
733   done = 0;
734   set_option (OPTUNBUFFEREDINPUT);
735   while (!done) {
736     switch (mutt_menuLoop (menu)) {
737     case -1:                   /* abort */
738     case OP_MAX + 1:           /* reject */
739     case OP_EXIT:
740       done = 1;
741       break;
742     case OP_MAX + 3:           /* accept always */
743       done = 0;
744       if ((fp = fopen (SslCertFile, "a"))) {
745         /* save hostname if necessary */
746         if (certerr_hostname) {
747           fprintf (fp, "#H %s %s\n", conn->account.host, fpbuf);
748           done = 1;
749         }
750         if (certerr_nottrusted) {
751           done = 0;
752           ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", &cert_list[0],
753                                                 &pemdata);
754           if (ret == 0) {
755             if (fwrite (pemdata.data, pemdata.size, 1, fp) == 1) {
756               done = 1;
757             }
758             gnutls_free (pemdata.data);
759           }
760         }
761         m_fclose(&fp);
762       }
763       if (!done) {
764         mutt_error (_("Warning: Couldn't save certificate"));
765         mutt_sleep (2);
766       }
767       else {
768         mutt_message (_("Certificate saved"));
769         mutt_sleep (0);
770       }
771       /* fall through */
772     case OP_MAX + 2:           /* accept once */
773       done = 2;
774       break;
775     }
776   }
777   unset_option (OPTUNBUFFEREDINPUT);
778   mutt_menuDestroy (&menu);
779   gnutls_x509_crt_deinit (cert);
780   return (done == 2);
781 }