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