]> sjero.net Git - wget/blob - src/gnutls.c
Fix timeout option when used with SSL
[wget] / src / gnutls.c
1 /* SSL support via GnuTLS library.
2    Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software
3    Foundation, Inc.
4
5 This file is part of GNU Wget.
6
7 GNU Wget is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 GNU Wget is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Wget.  If not, see <http://www.gnu.org/licenses/>.
19
20 Additional permission under GNU GPL version 3 section 7
21
22 If you modify this program, or any covered work, by linking or
23 combining it with the OpenSSL project's OpenSSL library (or a
24 modified version of that library), containing parts covered by the
25 terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
26 grants you additional permission to convey the resulting work.
27 Corresponding Source for a non-source form of such a combination
28 shall include the source code for the parts of OpenSSL used as well
29 as that of the covered work.  */
30
31 #include "wget.h"
32
33 #include <assert.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <stdio.h>
38 #include <dirent.h>
39 #include <stdlib.h>
40
41 #include <gnutls/gnutls.h>
42 #include <gnutls/x509.h>
43 #include <sys/ioctl.h>
44
45 #include "utils.h"
46 #include "connect.h"
47 #include "url.h"
48 #include "ptimer.h"
49 #include "ssl.h"
50
51 #include <sys/fcntl.h>
52
53 #ifdef WIN32
54 # include "w32sock.h"
55 #endif
56
57 #include "host.h"
58
59 static int
60 key_type_to_gnutls_type (enum keyfile_type type)
61 {
62   switch (type)
63     {
64     case keyfile_pem:
65       return GNUTLS_X509_FMT_PEM;
66     case keyfile_asn1:
67       return GNUTLS_X509_FMT_DER;
68     default:
69       abort ();
70     }
71 }
72
73 /* Note: some of the functions private to this file have names that
74    begin with "wgnutls_" (e.g. wgnutls_read) so that they wouldn't be
75    confused with actual gnutls functions -- such as the gnutls_read
76    preprocessor macro.  */
77
78 static gnutls_certificate_credentials_t credentials;
79 bool
80 ssl_init (void)
81 {
82   /* Becomes true if GnuTLS is initialized. */
83   static bool ssl_initialized = false;
84
85   /* GnuTLS should be initialized only once. */
86   if (ssl_initialized)
87     return true;
88
89   const char *ca_directory;
90   DIR *dir;
91
92   gnutls_global_init ();
93   gnutls_certificate_allocate_credentials (&credentials);
94   gnutls_certificate_set_verify_flags(credentials,
95                                       GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
96
97   ca_directory = opt.ca_directory ? opt.ca_directory : "/etc/ssl/certs";
98
99   dir = opendir (ca_directory);
100   if (dir == NULL)
101     {
102       if (opt.ca_directory && *opt.ca_directory)
103         logprintf (LOG_NOTQUIET, _("ERROR: Cannot open directory %s.\n"),
104                    opt.ca_directory);
105     }
106   else
107     {
108       struct dirent *dent;
109       while ((dent = readdir (dir)) != NULL)
110         {
111           struct stat st;
112           char *ca_file;
113           asprintf (&ca_file, "%s/%s", ca_directory, dent->d_name);
114
115           stat (ca_file, &st);
116
117           if (S_ISREG (st.st_mode))
118             gnutls_certificate_set_x509_trust_file (credentials, ca_file,
119                                                     GNUTLS_X509_FMT_PEM);
120
121           free (ca_file);
122         }
123
124       closedir (dir);
125     }
126
127   /* Use the private key from the cert file unless otherwise specified. */
128   if (opt.cert_file && !opt.private_key)
129     {
130       opt.private_key = opt.cert_file;
131       opt.private_key_type = opt.cert_type;
132     }
133   /* Use the cert from the private key file unless otherwise specified. */
134   if (!opt.cert_file && opt.private_key)
135     {
136       opt.cert_file = opt.private_key;
137       opt.cert_type = opt.private_key_type;
138     }
139
140   if (opt.cert_file && opt.private_key)
141     {
142       int type;
143       if (opt.private_key_type != opt.cert_type)
144         {
145           /* GnuTLS can't handle this */
146           logprintf (LOG_NOTQUIET, _("ERROR: GnuTLS requires the key and the \
147 cert to be of the same type.\n"));
148         }
149
150       type = key_type_to_gnutls_type (opt.private_key_type);
151
152       gnutls_certificate_set_x509_key_file (credentials, opt.cert_file,
153                                             opt.private_key,
154                                             type);
155     }
156
157   if (opt.ca_cert)
158     gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert,
159                                             GNUTLS_X509_FMT_PEM);
160
161   ssl_initialized = true;
162
163   return true;
164 }
165
166 struct wgnutls_transport_context
167 {
168   gnutls_session_t session;       /* GnuTLS session handle */
169   int last_error;               /* last error returned by read/write/... */
170
171   /* Since GnuTLS doesn't support the equivalent to recv(...,
172      MSG_PEEK) or SSL_peek(), we have to do it ourselves.  Peeked data
173      is stored to PEEKBUF, and wgnutls_read checks that buffer before
174      actually reading.  */
175   char peekbuf[512];
176   int peeklen;
177 };
178
179 #ifndef MIN
180 # define MIN(i, j) ((i) <= (j) ? (i) : (j))
181 #endif
182
183
184 static int
185 wgnutls_read_timeout (int fd, char *buf, int bufsize, void *arg, double timeout)
186 {
187 #ifdef F_GETFL
188   int flags = 0;
189 #endif
190   int ret = 0;
191   struct ptimer *timer = NULL;
192   struct wgnutls_transport_context *ctx = arg;
193   int timed_out = 0;
194
195   if (timeout)
196     {
197 #ifdef F_GETFL
198       flags = fcntl (fd, F_GETFL, 0);
199       if (flags < 0)
200         return flags;
201       if (fcntl (fd, F_SETFL, flags | O_NONBLOCK))
202         return -1;
203 #else
204       /* XXX: Assume it was blocking before.  */
205       const int one = 1;
206       if (ioctl (fd, FIONBIO, &one) < 0)
207         return -1;
208 #endif
209
210       timer = ptimer_new ();
211       if (timer == NULL)
212         return -1;
213     }
214
215   do
216     {
217       double next_timeout = 0;
218       if (timeout)
219         {
220           next_timeout = timeout - ptimer_measure (timer);
221           if (next_timeout < 0)
222             break;
223         }
224
225       ret = GNUTLS_E_AGAIN;
226       if (timeout == 0 || gnutls_record_check_pending (ctx->session)
227           || select_fd (fd, next_timeout, WAIT_FOR_READ))
228         {
229           ret = gnutls_record_recv (ctx->session, buf, bufsize);
230           timed_out = timeout && ptimer_measure (timer) >= timeout;
231         }
232     }
233   while (ret == GNUTLS_E_INTERRUPTED || (ret == GNUTLS_E_AGAIN && !timed_out));
234
235   if (timeout)
236     {
237       ptimer_destroy (timer);
238
239 #ifdef F_GETFL
240       if (fcntl (fd, F_SETFL, flags) < 0)
241         return -1;
242 #else
243       const int zero = 0;
244       if (ioctl (fd, FIONBIO, &zero) < 0)
245         return -1;
246 #endif
247
248       if (timed_out && ret == GNUTLS_E_AGAIN)
249         errno = ETIMEDOUT;
250     }
251
252   return ret;
253 }
254
255 static int
256 wgnutls_read (int fd, char *buf, int bufsize, void *arg)
257 {
258   int ret = 0;
259   struct wgnutls_transport_context *ctx = arg;
260
261   if (ctx->peeklen)
262     {
263       /* If we have any peek data, simply return that. */
264       int copysize = MIN (bufsize, ctx->peeklen);
265       memcpy (buf, ctx->peekbuf, copysize);
266       ctx->peeklen -= copysize;
267       if (ctx->peeklen != 0)
268         memmove (ctx->peekbuf, ctx->peekbuf + copysize, ctx->peeklen);
269
270       return copysize;
271     }
272
273   ret = wgnutls_read_timeout (fd, buf, bufsize, arg, opt.read_timeout);
274   if (ret < 0)
275     ctx->last_error = ret;
276
277   return ret;
278 }
279
280 static int
281 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
282 {
283   int ret;
284   struct wgnutls_transport_context *ctx = arg;
285   do
286     ret = gnutls_record_send (ctx->session, buf, bufsize);
287   while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
288   if (ret < 0)
289     ctx->last_error = ret;
290   return ret;
291 }
292
293 static int
294 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
295 {
296   struct wgnutls_transport_context *ctx = arg;
297
298   if (timeout)
299     return ctx->peeklen || gnutls_record_check_pending (ctx->session)
300       || select_fd (fd, timeout, wait_for);
301   else
302     return ctx->peeklen || gnutls_record_check_pending (ctx->session);
303 }
304
305 static int
306 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
307 {
308   int read = 0;
309   struct wgnutls_transport_context *ctx = arg;
310   int offset = MIN (bufsize, ctx->peeklen);
311
312   if (ctx->peeklen)
313     {
314       memcpy (buf, ctx->peekbuf, offset);
315       return offset;
316     }
317
318   if (bufsize > sizeof ctx->peekbuf)
319     bufsize = sizeof ctx->peekbuf;
320
321   if (bufsize > offset)
322     {
323       if (opt.read_timeout && gnutls_record_check_pending (ctx->session) == 0
324           && select_fd (fd, 0.0, WAIT_FOR_READ) <= 0)
325         read = 0;
326       else
327         read = wgnutls_read_timeout (fd, buf + offset, bufsize - offset,
328                                      ctx, opt.read_timeout);
329       if (read < 0)
330         {
331           if (offset)
332             read = 0;
333           else
334             return read;
335         }
336
337       if (read > 0)
338         {
339           memcpy (ctx->peekbuf + offset, buf + offset,
340                   read);
341           ctx->peeklen += read;
342         }
343     }
344
345   return offset + read;
346 }
347
348 static const char *
349 wgnutls_errstr (int fd, void *arg)
350 {
351   struct wgnutls_transport_context *ctx = arg;
352   return gnutls_strerror (ctx->last_error);
353 }
354
355 static void
356 wgnutls_close (int fd, void *arg)
357 {
358   struct wgnutls_transport_context *ctx = arg;
359   /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
360   gnutls_deinit (ctx->session);
361   xfree (ctx);
362   close (fd);
363 }
364
365 /* gnutls_transport is the singleton that describes the SSL transport
366    methods provided by this file.  */
367
368 static struct transport_implementation wgnutls_transport =
369 {
370   wgnutls_read, wgnutls_write, wgnutls_poll,
371   wgnutls_peek, wgnutls_errstr, wgnutls_close
372 };
373
374 bool
375 ssl_connect_wget (int fd, const char *hostname)
376 {
377 #ifdef F_GETFL
378   int flags = 0;
379 #endif
380   struct wgnutls_transport_context *ctx;
381   gnutls_session_t session;
382   int err,alert;
383   gnutls_init (&session, GNUTLS_CLIENT);
384   const char *str;
385
386   /* We set the server name but only if it's not an IP address. */
387   if (! is_valid_ip_address (hostname))
388     {
389       gnutls_server_name_set (session, GNUTLS_NAME_DNS, hostname,
390                               strlen (hostname));
391     }
392
393   gnutls_set_default_priority (session);
394   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
395 #ifndef FD_TO_SOCKET
396 # define FD_TO_SOCKET(X) (X)
397 #endif
398   gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) FD_TO_SOCKET (fd));
399
400   err = 0;
401 #if HAVE_GNUTLS_PRIORITY_SET_DIRECT
402   switch (opt.secure_protocol)
403     {
404     case secure_protocol_auto:
405       break;
406     case secure_protocol_sslv2:
407     case secure_protocol_sslv3:
408       err = gnutls_priority_set_direct (session, "NORMAL:-VERS-TLS-ALL:+VERS-SSL3.0", NULL);
409       break;
410     case secure_protocol_tlsv1:
411       err = gnutls_priority_set_direct (session, "NORMAL:-VERS-SSL3.0", NULL);
412       break;
413     default:
414       abort ();
415     }
416 #else
417   int allowed_protocols[4] = {0, 0, 0, 0};
418   switch (opt.secure_protocol)
419     {
420     case secure_protocol_auto:
421       break;
422     case secure_protocol_sslv2:
423     case secure_protocol_sslv3:
424       allowed_protocols[0] = GNUTLS_SSL3;
425       err = gnutls_protocol_set_priority (session, allowed_protocols);
426       break;
427
428     case secure_protocol_tlsv1:
429       allowed_protocols[0] = GNUTLS_TLS1_0;
430       allowed_protocols[1] = GNUTLS_TLS1_1;
431       allowed_protocols[2] = GNUTLS_TLS1_2;
432       err = gnutls_protocol_set_priority (session, allowed_protocols);
433       break;
434
435     default:
436       abort ();
437     }
438 #endif
439
440   if (err < 0)
441     {
442       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
443       gnutls_deinit (session);
444       return false;
445     }
446
447   if (opt.connect_timeout)
448     {
449 #ifdef F_GETFL
450       flags = fcntl (fd, F_GETFL, 0);
451       if (flags < 0)
452         return flags;
453       if (fcntl (fd, F_SETFL, flags | O_NONBLOCK))
454         return -1;
455 #else
456       /* XXX: Assume it was blocking before.  */
457       const int one = 1;
458       if (ioctl (fd, FIONBIO, &one) < 0)
459         return -1;
460 #endif
461     }
462
463   /* We don't stop the handshake process for non-fatal errors */
464   do
465     {
466       err = gnutls_handshake (session);
467
468       if (opt.connect_timeout && err == GNUTLS_E_AGAIN)
469         {
470           if (gnutls_record_get_direction (session))
471             {
472               /* wait for writeability */
473               err = select_fd (fd, opt.connect_timeout, WAIT_FOR_WRITE);
474             }
475           else
476             {
477               /* wait for readability */
478               err = select_fd (fd, opt.connect_timeout, WAIT_FOR_READ);
479             }
480
481           if (err <= 0)
482             {
483               if (err == 0)
484                 {
485                   errno = ETIMEDOUT;
486                   err = -1;
487                 }
488               break;
489             }
490
491           if (err <= 0)
492             break;
493         }
494       else if (err < 0)
495         {
496           logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
497           if (err == GNUTLS_E_WARNING_ALERT_RECEIVED ||
498               err == GNUTLS_E_FATAL_ALERT_RECEIVED)
499             {
500               alert = gnutls_alert_get (session);
501               str = gnutls_alert_get_name (alert);
502               if (str == NULL)
503                 str = "(unknown)";
504               logprintf (LOG_NOTQUIET, "GnuTLS: received alert [%d]: %s\n", alert, str);
505             }
506         }
507     }
508   while (err == GNUTLS_E_WARNING_ALERT_RECEIVED && gnutls_error_is_fatal (err) == 0);
509
510   if (opt.connect_timeout)
511     {
512 #ifdef F_GETFL
513       if (fcntl (fd, F_SETFL, flags) < 0)
514         return -1;
515 #else
516       const int zero = 0;
517       if (ioctl (fd, FIONBIO, &zero) < 0)
518         return -1;
519 #endif
520     }
521
522   if (err < 0)
523     {
524       gnutls_deinit (session);
525       return false;
526     }
527
528   ctx = xnew0 (struct wgnutls_transport_context);
529   ctx->session = session;
530   fd_register_transport (fd, &wgnutls_transport, ctx);
531   return true;
532 }
533
534 bool
535 ssl_check_certificate (int fd, const char *host)
536 {
537   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
538
539   unsigned int status;
540   int err;
541
542   /* If the user has specified --no-check-cert, we still want to warn
543      him about problems with the server's certificate.  */
544   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
545   bool success = true;
546
547   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
548   if (err < 0)
549     {
550       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
551                  severity, quotearg_style (escape_quoting_style, host));
552       success = false;
553       goto out;
554     }
555
556   if (status & GNUTLS_CERT_INVALID)
557     {
558       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
559                  severity, quote (host));
560       success = false;
561     }
562   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
563     {
564       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
565                  severity, quote (host));
566       success = false;
567     }
568   if (status & GNUTLS_CERT_REVOKED)
569     {
570       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
571                  severity, quote (host));
572       success = false;
573     }
574
575   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
576     {
577       time_t now = time (NULL);
578       gnutls_x509_crt_t cert;
579       const gnutls_datum_t *cert_list;
580       unsigned int cert_list_size;
581
582       if ((err = gnutls_x509_crt_init (&cert)) < 0)
583         {
584           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
585                      gnutls_strerror (err));
586           success = false;
587           goto out;
588         }
589
590       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
591       if (!cert_list)
592         {
593           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
594           success = false;
595           goto crt_deinit;
596         }
597       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
598       if (err < 0)
599         {
600           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
601                      gnutls_strerror (err));
602           success = false;
603           goto crt_deinit;
604         }
605       if (now < gnutls_x509_crt_get_activation_time (cert))
606         {
607           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
608           success = false;
609         }
610       if (now >= gnutls_x509_crt_get_expiration_time (cert))
611         {
612           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
613           success = false;
614         }
615       if (!gnutls_x509_crt_check_hostname (cert, host))
616         {
617           logprintf (LOG_NOTQUIET,
618                      _("The certificate's owner does not match hostname %s\n"),
619                      quote (host));
620           success = false;
621         }
622  crt_deinit:
623       gnutls_x509_crt_deinit (cert);
624    }
625
626  out:
627   return opt.check_cert ? success : true;
628 }