Use p_new instead of xmalloc()
[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 #ifdef USE_GNUTLS
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 <lib-ui/curses.h>
22 #include <lib-ui/menu.h>
23
24 #include "mutt.h"
25 #include "mutt_socket.h"
26 #include "mutt_ssl.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, ssize_t len);
35 static int tls_socket_write (CONNECTION * conn, const char *buf, ssize_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_ssl_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, ssize_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, ssize_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_ssl_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 = p_new(tlssockdata, 1);
155   conn->sockdata = data;
156   err = gnutls_certificate_allocate_credentials (&data->xcred);
157   if (err < 0) {
158     p_delete(&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)(intptr_t)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   p_delete(&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     p_delete(&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 = p_new(unsigned char, 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   m_fclose(&fd1);
314
315   do {
316     ret = gnutls_pem_base64_decode_alloc (NULL, &b64_data, &cert);
317     if (ret != 0) {
318       p_delete(&b64_data_data);
319       return 0;
320     }
321
322     ptr = (unsigned char *) strstr ((char*) b64_data.data, CERT_SEP) + 1;
323     ptr = (unsigned char *) strstr ((char*) 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         p_delete(&b64_data_data);
333         return 1;
334       }
335     }
336
337     gnutls_free (cert.data);
338   } while (ptr != NULL);
339
340   /* no match found */
341   p_delete(&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   char md[36];
349   ssize_t n;
350   int j;
351
352   n = 36;
353
354   if (gnutls_fingerprint(algo, data, md, (size_t *)&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, ssize_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     m_strcpy(s, len, _("[invalid date]"));
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   ssize_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 (m_strcmp(linestr + pmatch[1].rm_so, hostname) == 0 &&
412               m_strcmp(linestr + pmatch[2].rm_so, buf) == 0) {
413             regfree (&preg);
414             p_delete(&linestr);
415             m_fclose(&fp);
416             return 1;
417           }
418         }
419       }
420     }
421
422     regfree (&preg);
423     m_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[STRING];
435   char buf[STRING];
436   char fpbuf[STRING];
437   ssize_t buflen;
438   char dn_common_name[STRING];
439   char dn_email[STRING];
440   char dn_organization[STRING];
441   char dn_organizational_unit[STRING];
442   char dn_locality[STRING];
443   char dn_province[STRING];
444   char dn_country[STRING];
445   MUTTMENU *menu;
446   int done, row, i, ret;
447   FILE *fp;
448   time_t t;
449   const gnutls_datum *cert_list;
450   unsigned int cert_list_size = 0;
451   gnutls_certificate_status_t certstat;
452   char datestr[30];
453   gnutls_x509_crt cert;
454   gnutls_datum pemdata;
455   int certerr_expired = 0;
456   int certerr_notyetvalid = 0;
457   int certerr_hostname = 0;
458   int certerr_nottrusted = 0;
459   int certerr_revoked = 0;
460   int certerr_signernotca = 0;
461
462   if (gnutls_auth_get_type (state) != GNUTLS_CRD_CERTIFICATE) {
463     mutt_error (_("Unable to get certificate from peer"));
464     mutt_sleep (2);
465     return 0;
466   }
467
468   if (gnutls_certificate_verify_peers2(state, &certstat) < 0) {
469       mutt_error (_("Certificate verification error (%s)"),
470                   gnutls_strerror(certstat));
471       mutt_sleep (2);
472       return 0;
473   }
474
475   /* We only support X.509 certificates (not OpenPGP) at the moment */
476   if (gnutls_certificate_type_get (state) != GNUTLS_CRT_X509) {
477     mutt_error (_("Certificate is not X.509"));
478     mutt_sleep (2);
479     return 0;
480   }
481
482   if (gnutls_x509_crt_init (&cert) < 0) {
483     mutt_error (_("Error initialising gnutls certificate data"));
484     mutt_sleep (2);
485     return 0;
486   }
487
488   cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
489   if (!cert_list) {
490     mutt_error (_("Unable to get certificate from peer"));
491     mutt_sleep (2);
492     return 0;
493   }
494
495   /* FIXME: Currently only check first certificate in chain. */
496   if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
497     mutt_error (_("Error processing certificate data"));
498     mutt_sleep (2);
499     return 0;
500   }
501
502   if (gnutls_x509_crt_get_expiration_time (cert) < time (NULL)) {
503     certerr_expired = 1;
504   }
505
506   if (gnutls_x509_crt_get_activation_time (cert) > time (NULL)) {
507     certerr_notyetvalid = 1;
508   }
509
510   if (!gnutls_x509_crt_check_hostname (cert, conn->account.host) &&
511       !tls_check_stored_hostname (&cert_list[0], conn->account.host)) {
512     certerr_hostname = 1;
513   }
514
515   /* see whether certificate is in our cache (certificates file) */
516   if (tls_compare_certificates (&cert_list[0])) {
517     if (certstat & GNUTLS_CERT_INVALID) {
518       /* doesn't matter - have decided is valid because server
519          certificate is in our trusted cache */
520       certstat ^= GNUTLS_CERT_INVALID;
521     }
522
523     if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
524       /* doesn't matter that we haven't found the signer, since
525          certificate is in our trusted cache */
526       certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
527     }
528
529     if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
530       /* Hmm. Not really sure how to handle this, but let's say
531          that we don't care if the CA certificate hasn't got the
532          correct X.509 basic constraints if server certificate is
533          in our cache. */
534       certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
535     }
536
537   }
538
539   if (certstat & GNUTLS_CERT_REVOKED) {
540     certerr_revoked = 1;
541     certstat ^= GNUTLS_CERT_REVOKED;
542   }
543
544   if (certstat & GNUTLS_CERT_INVALID) {
545     certerr_nottrusted = 1;
546     certstat ^= GNUTLS_CERT_INVALID;
547   }
548
549   if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
550     /* NB: already cleared if cert in cache */
551     certerr_nottrusted = 1;
552     certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
553   }
554
555   if (certstat & GNUTLS_CERT_SIGNER_NOT_CA) {
556     /* NB: already cleared if cert in cache */
557     certerr_signernotca = 1;
558     certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
559   }
560
561   /* OK if signed by (or is) a trusted certificate */
562   /* we've been zeroing the interesting bits in certstat - 
563      don't return OK if there are any unhandled bits we don't
564      understand */
565   if (!(certerr_expired || certerr_notyetvalid ||
566         certerr_hostname || certerr_nottrusted) && certstat == 0) {
567     gnutls_x509_crt_deinit (cert);
568     return 1;
569   }
570
571
572   /* interactive check from user */
573   menu = mutt_new_menu ();
574   menu->max = 25;
575   menu->dialog = p_new(char*, menu->max);
576   for (i = 0; i < menu->max; i++)
577     menu->dialog[i] = p_new(char, STRING);
578
579   row = 0;
580   m_strcpy(menu->dialog[row], STRING,
581            _("This certificate belongs to:"));
582   row++;
583
584   buflen = sizeof (dn_common_name);
585   if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
586                                      dn_common_name, (size_t *)&buflen) != 0)
587     dn_common_name[0] = '\0';
588   buflen = sizeof (dn_email);
589   if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
590                                      dn_email, (size_t *)&buflen) != 0)
591     dn_email[0] = '\0';
592   buflen = sizeof (dn_organization);
593   if (gnutls_x509_crt_get_dn_by_oid
594       (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
595        (size_t *)&buflen) != 0)
596     dn_organization[0] = '\0';
597   buflen = sizeof (dn_organizational_unit);
598   if (gnutls_x509_crt_get_dn_by_oid
599       (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
600        dn_organizational_unit, (size_t *)&buflen) != 0)
601     dn_organizational_unit[0] = '\0';
602   buflen = sizeof (dn_locality);
603   if (gnutls_x509_crt_get_dn_by_oid
604       (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, (size_t *)&buflen) != 0)
605     dn_locality[0] = '\0';
606   buflen = sizeof (dn_province);
607   if (gnutls_x509_crt_get_dn_by_oid
608       (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
609        (size_t *)&buflen) != 0)
610     dn_province[0] = '\0';
611   buflen = sizeof (dn_country);
612   if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
613                                      dn_country, (size_t *)&buflen) != 0)
614     dn_country[0] = '\0';
615
616   snprintf (menu->dialog[row++], STRING, "   %s  %s", dn_common_name,
617             dn_email);
618   snprintf (menu->dialog[row++], STRING, "   %s", dn_organization);
619   snprintf (menu->dialog[row++], STRING, "   %s",
620             dn_organizational_unit);
621   snprintf (menu->dialog[row++], STRING, "   %s  %s  %s", dn_locality,
622             dn_province, dn_country);
623   row++;
624
625   m_strcpy(menu->dialog[row], STRING,
626            _("This certificate was issued by:"));
627   row++;
628
629   buflen = sizeof (dn_common_name);
630   if (gnutls_x509_crt_get_issuer_dn_by_oid
631       (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, (size_t *)&buflen) != 0)
632     dn_common_name[0] = '\0';
633   buflen = sizeof (dn_email);
634   if (gnutls_x509_crt_get_issuer_dn_by_oid
635       (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, (size_t *)&buflen) != 0)
636     dn_email[0] = '\0';
637   buflen = sizeof (dn_organization);
638   if (gnutls_x509_crt_get_issuer_dn_by_oid
639       (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization,
640        (size_t *)&buflen) != 0)
641     dn_organization[0] = '\0';
642   buflen = sizeof (dn_organizational_unit);
643   if (gnutls_x509_crt_get_issuer_dn_by_oid
644       (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
645        dn_organizational_unit, (size_t *)&buflen) != 0)
646     dn_organizational_unit[0] = '\0';
647   buflen = sizeof (dn_locality);
648   if (gnutls_x509_crt_get_issuer_dn_by_oid
649       (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, (size_t *)&buflen) != 0)
650     dn_locality[0] = '\0';
651   buflen = sizeof (dn_province);
652   if (gnutls_x509_crt_get_issuer_dn_by_oid
653       (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province,
654        (size_t *)&buflen) != 0)
655     dn_province[0] = '\0';
656   buflen = sizeof (dn_country);
657   if (gnutls_x509_crt_get_issuer_dn_by_oid
658       (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, (size_t *)&buflen) != 0)
659     dn_country[0] = '\0';
660
661   snprintf (menu->dialog[row++], STRING, "   %s  %s", dn_common_name,
662             dn_email);
663   snprintf (menu->dialog[row++], STRING, "   %s", dn_organization);
664   snprintf (menu->dialog[row++], STRING, "   %s",
665             dn_organizational_unit);
666   snprintf (menu->dialog[row++], STRING, "   %s  %s  %s", dn_locality,
667             dn_province, dn_country);
668   row++;
669
670   snprintf (menu->dialog[row++], STRING,
671             _("This certificate is valid"));
672
673   t = gnutls_x509_crt_get_activation_time (cert);
674   snprintf (menu->dialog[row++], STRING, _("   from %s"),
675             tls_make_date (t, datestr, 30));
676
677   t = gnutls_x509_crt_get_expiration_time (cert);
678   snprintf (menu->dialog[row++], STRING, _("     to %s"),
679             tls_make_date (t, datestr, 30));
680
681   fpbuf[0] = '\0';
682   tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]);
683   snprintf (menu->dialog[row++], STRING, _("SHA1 Fingerprint: %s"),
684             fpbuf);
685   fpbuf[0] = '\0';
686   tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]);
687   snprintf (menu->dialog[row++], STRING, _("MD5 Fingerprint: %s"),
688             fpbuf);
689
690   if (certerr_notyetvalid) {
691     row++;
692     m_strcpy(menu->dialog[row], STRING,
693              _("WARNING: Server certificate is not yet valid"));
694   }
695   if (certerr_expired) {
696     row++;
697     m_strcpy(menu->dialog[row], STRING,
698              _("WARNING: Server certificate has expired"));
699   }
700   if (certerr_revoked) {
701     row++;
702     m_strcpy(menu->dialog[row], STRING,
703              _("WARNING: Server certificate has been revoked"));
704   }
705   if (certerr_hostname) {
706     row++;
707     m_strcpy(menu->dialog[row], STRING,
708              _("WARNING: Server hostname does not match certificate"));
709   }
710   if (certerr_signernotca) {
711     row++;
712     m_strcpy(menu->dialog[row], STRING,
713              _("WARNING: Signer of server certificate is not a CA"));
714   }
715
716   menu->title = _("TLS/SSL Certificate check");
717   /* certificates with bad dates, or that are revoked, must be
718      accepted manually each and every time */
719   if (SslCertFile && !certerr_expired && !certerr_notyetvalid
720       && !certerr_revoked) {
721     menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
722     menu->keys = _("roa");
723   }
724   else {
725     menu->prompt = _("(r)eject, accept (o)nce");
726     menu->keys = _("ro");
727   }
728
729   helpstr[0] = '\0';
730   mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
731   strncat (helpstr, buf, sizeof (helpstr));
732   mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
733   strncat (helpstr, buf, sizeof (helpstr));
734   menu->help = helpstr;
735
736   done = 0;
737   set_option (OPTUNBUFFEREDINPUT);
738   while (!done) {
739     switch (mutt_menuLoop (menu)) {
740     case -1:                   /* abort */
741     case OP_MAX + 1:           /* reject */
742     case OP_EXIT:
743       done = 1;
744       break;
745     case OP_MAX + 3:           /* accept always */
746       done = 0;
747       if ((fp = fopen (SslCertFile, "a"))) {
748         /* save hostname if necessary */
749         if (certerr_hostname) {
750           fprintf (fp, "#H %s %s\n", conn->account.host, fpbuf);
751           done = 1;
752         }
753         if (certerr_nottrusted) {
754           done = 0;
755           ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", &cert_list[0],
756                                                 &pemdata);
757           if (ret == 0) {
758             if (fwrite (pemdata.data, pemdata.size, 1, fp) == 1) {
759               done = 1;
760             }
761             gnutls_free (pemdata.data);
762           }
763         }
764         m_fclose(&fp);
765       }
766       if (!done) {
767         mutt_error (_("Warning: Couldn't save certificate"));
768         mutt_sleep (2);
769       }
770       else {
771         mutt_message (_("Certificate saved"));
772         mutt_sleep (0);
773       }
774       /* fall through */
775     case OP_MAX + 2:           /* accept once */
776       done = 2;
777       break;
778     }
779   }
780   unset_option (OPTUNBUFFEREDINPUT);
781   mutt_menuDestroy (&menu);
782   gnutls_x509_crt_deinit (cert);
783   return (done == 2);
784 }
785
786 #endif /* USE_GNUTLS */