]> sjero.net Git - wget/blob - src/gnutls.c
gnutls: Honor read timeout.
[wget] / src / gnutls.c
1 /* SSL support via GnuTLS library.
2    Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 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 #ifdef WIN32
52 # include "w32sock.h"
53 #endif
54
55 /* Note: some of the functions private to this file have names that
56    begin with "wgnutls_" (e.g. wgnutls_read) so that they wouldn't be
57    confused with actual gnutls functions -- such as the gnutls_read
58    preprocessor macro.  */
59
60 static gnutls_certificate_credentials credentials;
61
62 bool
63 ssl_init ()
64 {
65   const char *ca_directory;
66   DIR *dir;
67
68   gnutls_global_init ();
69   gnutls_certificate_allocate_credentials (&credentials);
70   gnutls_certificate_set_verify_flags(credentials,
71                                       GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
72
73   ca_directory = opt.ca_directory ? opt.ca_directory : "/etc/ssl/certs";
74
75   dir = opendir (ca_directory);
76   if (dir == NULL)
77     {
78       if (opt.ca_directory)
79         logprintf (LOG_NOTQUIET, _("ERROR: Cannot open directory %s.\n"),
80                    opt.ca_directory);
81     }
82   else
83     {
84       struct dirent *dent;
85       while ((dent = readdir (dir)) != NULL)
86         {
87           struct stat st;
88           char *ca_file;
89           asprintf (&ca_file, "%s/%s", ca_directory, dent->d_name);
90
91           stat (ca_file, &st);
92
93           if (S_ISREG (st.st_mode))
94             gnutls_certificate_set_x509_trust_file (credentials, ca_file,
95                                                     GNUTLS_X509_FMT_PEM);
96
97           free (ca_file);
98         }
99
100       closedir (dir);
101     }
102
103   if (opt.ca_cert)
104     gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert,
105                                             GNUTLS_X509_FMT_PEM);
106   return true;
107 }
108
109 struct wgnutls_transport_context
110 {
111   gnutls_session session;       /* GnuTLS session handle */
112   int last_error;               /* last error returned by read/write/... */
113
114   /* Since GnuTLS doesn't support the equivalent to recv(...,
115      MSG_PEEK) or SSL_peek(), we have to do it ourselves.  Peeked data
116      is stored to PEEKBUF, and wgnutls_read checks that buffer before
117      actually reading.  */
118   char peekbuf[512];
119   int peeklen;
120 };
121
122 #ifndef MIN
123 # define MIN(i, j) ((i) <= (j) ? (i) : (j))
124 #endif
125
126 static int
127 wgnutls_read (int fd, char *buf, int bufsize, void *arg)
128 {
129 #ifdef F_GETFL
130   int flags = 0;
131 #endif
132   int ret = 0;
133   struct ptimer *timer;
134   struct wgnutls_transport_context *ctx = arg;
135
136   if (ctx->peeklen)
137     {
138       /* If we have any peek data, simply return that. */
139       int copysize = MIN (bufsize, ctx->peeklen);
140       memcpy (buf, ctx->peekbuf, copysize);
141       ctx->peeklen -= copysize;
142       if (ctx->peeklen != 0)
143         memmove (ctx->peekbuf, ctx->peekbuf + copysize, ctx->peeklen);
144
145       return copysize;
146     }
147
148   if (opt.read_timeout)
149     {
150 #ifdef F_GETFL
151       flags = fcntl (fd, F_GETFL, 0);
152       if (flags < 0)
153         return ret;
154
155       ret = fcntl (fd, F_SETFL, flags | O_NONBLOCK);
156       if (ret < 0)
157         return ret;
158 #else
159       /* XXX: Assume it was blocking before.  */
160       const int one = 1;
161       ret = ioctl (fd, FIONBIO, &one);
162       if (ret < 0)
163         return ret;
164 #endif
165       timer = ptimer_new ();
166       if (timer == 0)
167         return -1;
168     }
169
170   do
171     {
172       do
173         ret = gnutls_record_recv (ctx->session, buf, bufsize);
174       while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
175     }
176   while (opt.read_timeout == 0 || ptimer_measure (timer) < opt.read_timeout);
177
178   if (opt.read_timeout)
179     {
180       ptimer_destroy (timer);
181 #ifdef F_GETFL
182       ret = fcntl (fd, F_SETFL, flags);
183       if (ret < 0)
184         return ret;
185 #else
186       const int zero = 0;
187       ret = ioctl (fd, FIONBIO, &zero);
188       if (ret < 0)
189         return ret;
190 #endif
191     }
192
193   if (ret < 0)
194     ctx->last_error = ret;
195
196   return ret;
197 }
198
199 static int
200 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
201 {
202   int ret;
203   struct wgnutls_transport_context *ctx = arg;
204   do
205     ret = gnutls_record_send (ctx->session, buf, bufsize);
206   while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
207   if (ret < 0)
208     ctx->last_error = ret;
209   return ret;
210 }
211
212 static int
213 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
214 {
215   struct wgnutls_transport_context *ctx = arg;
216   return ctx->peeklen || gnutls_record_check_pending (ctx->session)
217     || select_fd (fd, timeout, wait_for);
218 }
219
220 static int
221 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
222 {
223   int read = 0;
224   struct wgnutls_transport_context *ctx = arg;
225   int offset = MIN (bufsize, ctx->peeklen);
226   if (bufsize > sizeof ctx->peekbuf)
227     bufsize = sizeof ctx->peekbuf;
228
229   if (ctx->peeklen)
230     memcpy (buf, ctx->peekbuf, offset);
231
232   if (bufsize > offset)
233     {
234       if (gnutls_record_check_pending (ctx->session) <= 0
235           && select_fd (fd, 0.0, WAIT_FOR_READ) <= 0)
236         read = 0;
237       else
238         read = gnutls_record_recv (ctx->session, buf + offset,
239                                    bufsize - offset);
240
241       if (read < 0)
242         {
243           if (offset)
244             read = 0;
245           else
246             return read;
247         }
248
249       if (read > 0)
250         {
251           memcpy (ctx->peekbuf + offset, buf + offset,
252                   read);
253           ctx->peeklen += read;
254         }
255     }
256
257   return offset + read;
258 }
259
260 static const char *
261 wgnutls_errstr (int fd, void *arg)
262 {
263   struct wgnutls_transport_context *ctx = arg;
264   return gnutls_strerror (ctx->last_error);
265 }
266
267 static void
268 wgnutls_close (int fd, void *arg)
269 {
270   struct wgnutls_transport_context *ctx = arg;
271   /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
272   gnutls_deinit (ctx->session);
273   xfree (ctx);
274   close (fd);
275 }
276
277 /* gnutls_transport is the singleton that describes the SSL transport
278    methods provided by this file.  */
279
280 static struct transport_implementation wgnutls_transport =
281 {
282   wgnutls_read, wgnutls_write, wgnutls_poll,
283   wgnutls_peek, wgnutls_errstr, wgnutls_close
284 };
285
286 bool
287 ssl_connect_wget (int fd)
288 {
289   struct wgnutls_transport_context *ctx;
290   gnutls_session session;
291   int err;
292   gnutls_init (&session, GNUTLS_CLIENT);
293   gnutls_set_default_priority (session);
294   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
295 #ifndef FD_TO_SOCKET
296 # define FD_TO_SOCKET(X) (X)
297 #endif
298   gnutls_transport_set_ptr (session, (gnutls_transport_ptr) FD_TO_SOCKET (fd));
299
300   err = 0;
301 #if HAVE_GNUTLS_PRIORITY_SET_DIRECT
302   switch (opt.secure_protocol)
303     {
304     case secure_protocol_auto:
305       break;
306     case secure_protocol_sslv2:
307     case secure_protocol_sslv3:
308       err = gnutls_priority_set_direct (session, "NORMAL:-VERS-TLS-ALL", NULL);
309       break;
310     case secure_protocol_tlsv1:
311       err = gnutls_priority_set_direct (session, "NORMAL:-VERS-SSL3.0", NULL);
312       break;
313     default:
314       abort ();
315     }
316 #else
317   int allowed_protocols[4] = {0, 0, 0, 0};
318   switch (opt.secure_protocol)
319     {
320     case secure_protocol_auto:
321       break;
322     case secure_protocol_sslv2:
323     case secure_protocol_sslv3:
324       allowed_protocols[0] = GNUTLS_SSL3;
325       err = gnutls_protocol_set_priority (session, allowed_protocols);
326       break;
327
328     case secure_protocol_tlsv1:
329       allowed_protocols[0] = GNUTLS_TLS1_0;
330       allowed_protocols[1] = GNUTLS_TLS1_1;
331       allowed_protocols[2] = GNUTLS_TLS1_2;
332       err = gnutls_protocol_set_priority (session, allowed_protocols);
333       break;
334
335     default:
336       abort ();
337     }
338 #endif
339
340   if (err < 0)
341     {
342       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
343       gnutls_deinit (session);
344       return false;
345     }
346
347   err = gnutls_handshake (session);
348   if (err < 0)
349     {
350       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
351       gnutls_deinit (session);
352       return false;
353     }
354
355   ctx = xnew0 (struct wgnutls_transport_context);
356   ctx->session = session;
357   fd_register_transport (fd, &wgnutls_transport, ctx);
358   return true;
359 }
360
361 bool
362 ssl_check_certificate (int fd, const char *host)
363 {
364   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
365
366   unsigned int status;
367   int err;
368
369   /* If the user has specified --no-check-cert, we still want to warn
370      him about problems with the server's certificate.  */
371   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
372   bool success = true;
373
374   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
375   if (err < 0)
376     {
377       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
378                  severity, quotearg_style (escape_quoting_style, host));
379       success = false;
380       goto out;
381     }
382
383   if (status & GNUTLS_CERT_INVALID)
384     {
385       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
386                  severity, quote (host));
387       success = false;
388     }
389   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
390     {
391       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
392                  severity, quote (host));
393       success = false;
394     }
395   if (status & GNUTLS_CERT_REVOKED)
396     {
397       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
398                  severity, quote (host));
399       success = false;
400     }
401
402   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
403     {
404       time_t now = time (NULL);
405       gnutls_x509_crt cert;
406       const gnutls_datum *cert_list;
407       unsigned int cert_list_size;
408
409       if ((err = gnutls_x509_crt_init (&cert)) < 0)
410         {
411           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
412                      gnutls_strerror (err));
413           success = false;
414           goto out;
415         }
416
417       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
418       if (!cert_list)
419         {
420           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
421           success = false;
422           goto out;
423         }
424       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
425       if (err < 0)
426         {
427           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
428                      gnutls_strerror (err));
429           success = false;
430           goto out;
431         }
432       if (now < gnutls_x509_crt_get_activation_time (cert))
433         {
434           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
435           success = false;
436         }
437       if (now >= gnutls_x509_crt_get_expiration_time (cert))
438         {
439           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
440           success = false;
441         }
442       if (!gnutls_x509_crt_check_hostname (cert, host))
443         {
444           logprintf (LOG_NOTQUIET,
445                      _("The certificate's owner does not match hostname %s\n"),
446                      quote (host));
447           success = false;
448         }
449       gnutls_x509_crt_deinit (cert);
450    }
451
452  out:
453   return opt.check_cert ? success : true;
454 }