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