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