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