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