]> sjero.net Git - wget/blob - src/gnutls.c
1bf4aefdbfdb17251eb8955f74a28065d99aef90
[wget] / src / gnutls.c
1 /* SSL support via GnuTLS library.
2    Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 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 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #include <string.h>
39 #include <stdio.h>
40 #include <dirent.h>
41 #include <stdlib.h>
42
43 #include <gnutls/gnutls.h>
44 #include <gnutls/x509.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       do
188         {
189           ret = gnutls_record_recv (ctx->session, buf + offset,
190                                     bufsize - offset);
191         }
192       while (ret == GNUTLS_E_INTERRUPTED);
193
194       if (ret < 0)
195         {
196           if (offset)
197             ret = 0;
198           else
199             return ret;
200         }
201
202       if (ret > 0)
203         {
204           memcpy (ctx->peekbuf + offset, buf + offset,
205                   ret);
206           ctx->peeklen += ret;
207         }
208     }
209
210   return offset + ret;
211 }
212
213 static const char *
214 wgnutls_errstr (int fd, void *arg)
215 {
216   struct wgnutls_transport_context *ctx = arg;
217   return gnutls_strerror (ctx->last_error);
218 }
219
220 static void
221 wgnutls_close (int fd, void *arg)
222 {
223   struct wgnutls_transport_context *ctx = arg;
224   /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
225   gnutls_deinit (ctx->session);
226   xfree (ctx);
227   close (fd);
228 }
229
230 /* gnutls_transport is the singleton that describes the SSL transport
231    methods provided by this file.  */
232
233 static struct transport_implementation wgnutls_transport =
234 {
235   wgnutls_read, wgnutls_write, wgnutls_poll,
236   wgnutls_peek, wgnutls_errstr, wgnutls_close
237 };
238
239 bool
240 ssl_connect_wget (int fd)
241 {
242   static const int cert_type_priority[] = {
243     GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0
244   };
245   struct wgnutls_transport_context *ctx;
246   gnutls_session session;
247   int err;
248   int allowed_protocols[4] = {0, 0, 0, 0};
249   gnutls_init (&session, GNUTLS_CLIENT);
250   gnutls_set_default_priority (session);
251   gnutls_certificate_type_set_priority (session, cert_type_priority);
252   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
253 #ifndef FD_TO_SOCKET
254 # define FD_TO_SOCKET(X) (X)
255 #endif
256   gnutls_transport_set_ptr (session, (gnutls_transport_ptr) FD_TO_SOCKET (fd));
257
258   err = 0;
259   switch (opt.secure_protocol)
260     {
261     case secure_protocol_auto:
262       break;
263     case secure_protocol_sslv2:
264     case secure_protocol_sslv3:
265       allowed_protocols[0] = GNUTLS_SSL3;
266       err = gnutls_protocol_set_priority (session, allowed_protocols);
267       break;
268     case secure_protocol_tlsv1:
269       allowed_protocols[0] = GNUTLS_TLS1_0;
270       allowed_protocols[1] = GNUTLS_TLS1_1;
271       allowed_protocols[2] = GNUTLS_TLS1_2;
272       err = gnutls_protocol_set_priority (session, allowed_protocols);
273       break;
274     default:
275       abort ();
276     }
277   if (err < 0)
278     {
279       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
280       gnutls_deinit (session);
281       return false;
282     }
283
284   err = gnutls_handshake (session);
285   if (err < 0)
286     {
287       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
288       gnutls_deinit (session);
289       return false;
290     }
291
292   ctx = xnew0 (struct wgnutls_transport_context);
293   ctx->session = session;
294   fd_register_transport (fd, &wgnutls_transport, ctx);
295   return true;
296 }
297
298 bool
299 ssl_check_certificate (int fd, const char *host)
300 {
301   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
302
303   unsigned int status;
304   int err;
305
306   /* If the user has specified --no-check-cert, we still want to warn
307      him about problems with the server's certificate.  */
308   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
309   bool success = true;
310
311   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
312   if (err < 0)
313     {
314       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
315                  severity, quotearg_style (escape_quoting_style, host));
316       success = false;
317       goto out;
318     }
319
320   if (status & GNUTLS_CERT_INVALID)
321     {
322       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
323                  severity, quote (host));
324       success = false;
325     }
326   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
327     {
328       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
329                  severity, quote (host));
330       success = false;
331     }
332   if (status & GNUTLS_CERT_REVOKED)
333     {
334       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
335                  severity, quote (host));
336       success = false;
337     }
338
339   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
340     {
341       time_t now = time (NULL);
342       gnutls_x509_crt cert;
343       const gnutls_datum *cert_list;
344       unsigned int cert_list_size;
345
346       if ((err = gnutls_x509_crt_init (&cert)) < 0)
347         {
348           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
349                      gnutls_strerror (err));
350           success = false;
351           goto out;
352         }
353
354       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
355       if (!cert_list)
356         {
357           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
358           success = false;
359           goto out;
360         }
361       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
362       if (err < 0)
363         {
364           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
365                      gnutls_strerror (err));
366           success = false;
367           goto out;
368         }
369       if (now < gnutls_x509_crt_get_activation_time (cert))
370         {
371           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
372           success = false;
373         }
374       if (now >= gnutls_x509_crt_get_expiration_time (cert))
375         {
376           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
377           success = false;
378         }
379       if (!gnutls_x509_crt_check_hostname (cert, host))
380         {
381           logprintf (LOG_NOTQUIET,
382                      _("The certificate's owner does not match hostname %s\n"),
383                      quote (host));
384           success = false;
385         }
386       gnutls_x509_crt_deinit (cert);
387    }
388
389  out:
390   return opt.check_cert ? success : true;
391 }