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