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