]> sjero.net Git - wget/blob - src/gnutls.c
e4b0fc2ea026bf731486169009f1ed38d883e790
[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 #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 = timeout - ptimer_measure (timer);
164       if (timeout && next_timeout < 0)
165         break;
166
167       ret = GNUTLS_E_AGAIN;
168       if (timeout == 0 || gnutls_record_check_pending (ctx->session)
169           || select_fd (fd, next_timeout, WAIT_FOR_READ))
170         {
171           if (timeout)
172             {
173 #ifdef F_GETFL
174               ret = fcntl (fd, F_SETFL, flags | O_NONBLOCK);
175               if (ret < 0)
176                 return ret;
177 #else
178               /* XXX: Assume it was blocking before.  */
179               const int one = 1;
180               ret = ioctl (fd, FIONBIO, &one);
181               if (ret < 0)
182                 return ret;
183 #endif
184             }
185
186           ret = gnutls_record_recv (ctx->session, buf, bufsize);
187
188           if (timeout)
189             {
190               int status;
191 #ifdef F_GETFL
192               status = fcntl (fd, F_SETFL, flags);
193               if (status < 0)
194                 return status;
195 #else
196               const int zero = 0;
197               status = ioctl (fd, FIONBIO, &zero);
198               if (status < 0)
199                 return status;
200 #endif
201             }
202         }
203
204       timed_out = timeout && ptimer_measure (timer) >= timeout;
205     }
206   while (ret == GNUTLS_E_INTERRUPTED || (ret == GNUTLS_E_AGAIN && !timed_out));
207
208   if (timeout)
209     ptimer_destroy (timer);
210
211   if (timeout && timed_out && ret == GNUTLS_E_AGAIN)
212     errno = ETIMEDOUT;
213
214   return ret;
215 }
216
217 static int
218 wgnutls_read (int fd, char *buf, int bufsize, void *arg)
219 {
220 #ifdef F_GETFL
221   int flags = 0;
222 #endif
223   int ret = 0;
224   struct ptimer *timer;
225   struct wgnutls_transport_context *ctx = arg;
226
227   if (ctx->peeklen)
228     {
229       /* If we have any peek data, simply return that. */
230       int copysize = MIN (bufsize, ctx->peeklen);
231       memcpy (buf, ctx->peekbuf, copysize);
232       ctx->peeklen -= copysize;
233       if (ctx->peeklen != 0)
234         memmove (ctx->peekbuf, ctx->peekbuf + copysize, ctx->peeklen);
235
236       return copysize;
237     }
238
239   ret = wgnutls_read_timeout (fd, buf, bufsize, arg, opt.read_timeout);
240   if (ret < 0)
241     ctx->last_error = ret;
242
243   return ret;
244 }
245
246 static int
247 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
248 {
249   int ret;
250   struct wgnutls_transport_context *ctx = arg;
251   do
252     ret = gnutls_record_send (ctx->session, buf, bufsize);
253   while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
254   if (ret < 0)
255     ctx->last_error = ret;
256   return ret;
257 }
258
259 static int
260 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
261 {
262   struct wgnutls_transport_context *ctx = arg;
263   return ctx->peeklen || gnutls_record_check_pending (ctx->session)
264     || select_fd (fd, timeout, wait_for);
265 }
266
267 static int
268 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
269 {
270   int read = 0;
271   struct wgnutls_transport_context *ctx = arg;
272   int offset = MIN (bufsize, ctx->peeklen);
273   if (bufsize > sizeof ctx->peekbuf)
274     bufsize = sizeof ctx->peekbuf;
275
276   if (ctx->peeklen)
277     memcpy (buf, ctx->peekbuf, offset);
278
279   if (bufsize > offset)
280     {
281       if (gnutls_record_check_pending (ctx->session) <= 0
282           && select_fd (fd, 0.0, WAIT_FOR_READ) <= 0)
283         read = 0;
284       else
285         read = wgnutls_read_timeout (fd, buf + offset, bufsize - offset,
286                                      ctx, opt.read_timeout);
287       if (read < 0)
288         {
289           if (offset)
290             read = 0;
291           else
292             return read;
293         }
294
295       if (read > 0)
296         {
297           memcpy (ctx->peekbuf + offset, buf + offset,
298                   read);
299           ctx->peeklen += read;
300         }
301     }
302
303   return offset + read;
304 }
305
306 static const char *
307 wgnutls_errstr (int fd, void *arg)
308 {
309   struct wgnutls_transport_context *ctx = arg;
310   return gnutls_strerror (ctx->last_error);
311 }
312
313 static void
314 wgnutls_close (int fd, void *arg)
315 {
316   struct wgnutls_transport_context *ctx = arg;
317   /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
318   gnutls_deinit (ctx->session);
319   xfree (ctx);
320   close (fd);
321 }
322
323 /* gnutls_transport is the singleton that describes the SSL transport
324    methods provided by this file.  */
325
326 static struct transport_implementation wgnutls_transport =
327 {
328   wgnutls_read, wgnutls_write, wgnutls_poll,
329   wgnutls_peek, wgnutls_errstr, wgnutls_close
330 };
331
332 bool
333 ssl_connect_wget (int fd)
334 {
335   struct wgnutls_transport_context *ctx;
336   gnutls_session session;
337   int err;
338   gnutls_init (&session, GNUTLS_CLIENT);
339   gnutls_set_default_priority (session);
340   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
341 #ifndef FD_TO_SOCKET
342 # define FD_TO_SOCKET(X) (X)
343 #endif
344   gnutls_transport_set_ptr (session, (gnutls_transport_ptr) FD_TO_SOCKET (fd));
345
346   err = 0;
347 #if HAVE_GNUTLS_PRIORITY_SET_DIRECT
348   switch (opt.secure_protocol)
349     {
350     case secure_protocol_auto:
351       break;
352     case secure_protocol_sslv2:
353     case secure_protocol_sslv3:
354       err = gnutls_priority_set_direct (session, "NORMAL:-VERS-TLS-ALL", NULL);
355       break;
356     case secure_protocol_tlsv1:
357       err = gnutls_priority_set_direct (session, "NORMAL:-VERS-SSL3.0", NULL);
358       break;
359     default:
360       abort ();
361     }
362 #else
363   int allowed_protocols[4] = {0, 0, 0, 0};
364   switch (opt.secure_protocol)
365     {
366     case secure_protocol_auto:
367       break;
368     case secure_protocol_sslv2:
369     case secure_protocol_sslv3:
370       allowed_protocols[0] = GNUTLS_SSL3;
371       err = gnutls_protocol_set_priority (session, allowed_protocols);
372       break;
373
374     case secure_protocol_tlsv1:
375       allowed_protocols[0] = GNUTLS_TLS1_0;
376       allowed_protocols[1] = GNUTLS_TLS1_1;
377       allowed_protocols[2] = GNUTLS_TLS1_2;
378       err = gnutls_protocol_set_priority (session, allowed_protocols);
379       break;
380
381     default:
382       abort ();
383     }
384 #endif
385
386   if (err < 0)
387     {
388       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
389       gnutls_deinit (session);
390       return false;
391     }
392
393   err = gnutls_handshake (session);
394   if (err < 0)
395     {
396       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
397       gnutls_deinit (session);
398       return false;
399     }
400
401   ctx = xnew0 (struct wgnutls_transport_context);
402   ctx->session = session;
403   fd_register_transport (fd, &wgnutls_transport, ctx);
404   return true;
405 }
406
407 bool
408 ssl_check_certificate (int fd, const char *host)
409 {
410   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
411
412   unsigned int status;
413   int err;
414
415   /* If the user has specified --no-check-cert, we still want to warn
416      him about problems with the server's certificate.  */
417   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
418   bool success = true;
419
420   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
421   if (err < 0)
422     {
423       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
424                  severity, quotearg_style (escape_quoting_style, host));
425       success = false;
426       goto out;
427     }
428
429   if (status & GNUTLS_CERT_INVALID)
430     {
431       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
432                  severity, quote (host));
433       success = false;
434     }
435   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
436     {
437       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
438                  severity, quote (host));
439       success = false;
440     }
441   if (status & GNUTLS_CERT_REVOKED)
442     {
443       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
444                  severity, quote (host));
445       success = false;
446     }
447
448   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
449     {
450       time_t now = time (NULL);
451       gnutls_x509_crt cert;
452       const gnutls_datum *cert_list;
453       unsigned int cert_list_size;
454
455       if ((err = gnutls_x509_crt_init (&cert)) < 0)
456         {
457           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
458                      gnutls_strerror (err));
459           success = false;
460           goto out;
461         }
462
463       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
464       if (!cert_list)
465         {
466           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
467           success = false;
468           goto out;
469         }
470       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
471       if (err < 0)
472         {
473           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
474                      gnutls_strerror (err));
475           success = false;
476           goto out;
477         }
478       if (now < gnutls_x509_crt_get_activation_time (cert))
479         {
480           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
481           success = false;
482         }
483       if (now >= gnutls_x509_crt_get_expiration_time (cert))
484         {
485           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
486           success = false;
487         }
488       if (!gnutls_x509_crt_check_hostname (cert, host))
489         {
490           logprintf (LOG_NOTQUIET,
491                      _("The certificate's owner does not match hostname %s\n"),
492                      quote (host));
493           success = false;
494         }
495       gnutls_x509_crt_deinit (cert);
496    }
497
498  out:
499   return opt.check_cert ? success : true;
500 }