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