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