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