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