]> sjero.net Git - wget/blob - src/gnutls.c
The GNU TLS backend loads default root certificates.
[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   gnutls_session session;       /* GnuTLS session handle */
109   int last_error;               /* last error returned by read/write/... */
110
111   /* Since GnuTLS doesn't support the equivalent to recv(...,
112      MSG_PEEK) or SSL_peek(), we have to do it ourselves.  Peeked data
113      is stored to PEEKBUF, and wgnutls_read checks that buffer before
114      actually reading.  */
115   char peekbuf[512];
116   int peeklen;
117 };
118
119 #ifndef MIN
120 # define MIN(i, j) ((i) <= (j) ? (i) : (j))
121 #endif
122
123 static int
124 wgnutls_read (int fd, char *buf, int bufsize, void *arg)
125 {
126   int ret = 0;
127   struct wgnutls_transport_context *ctx = arg;
128
129   if (ctx->peeklen)
130     {
131       /* If we have any peek data, simply return that. */
132       int copysize = MIN (bufsize, ctx->peeklen);
133       memcpy (buf, ctx->peekbuf, copysize);
134       ctx->peeklen -= copysize;
135       if (ctx->peeklen != 0)
136         memmove (ctx->peekbuf, ctx->peekbuf + copysize, ctx->peeklen);
137
138       return copysize;
139     }
140
141   do
142     ret = gnutls_record_recv (ctx->session, buf, bufsize);
143   while (ret == GNUTLS_E_INTERRUPTED);
144
145   if (ret < 0)
146     ctx->last_error = ret;
147   return ret;
148 }
149
150 static int
151 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
152 {
153   int ret;
154   struct wgnutls_transport_context *ctx = arg;
155   do
156     ret = gnutls_record_send (ctx->session, buf, bufsize);
157   while (ret == GNUTLS_E_INTERRUPTED);
158   if (ret < 0)
159     ctx->last_error = ret;
160   return ret;
161 }
162
163 static int
164 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
165 {
166   struct wgnutls_transport_context *ctx = arg;
167   return ctx->peeklen || gnutls_record_check_pending (ctx->session)
168     || select_fd (fd, timeout, wait_for);
169 }
170
171 static int
172 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
173 {
174   int ret = 0;
175   struct wgnutls_transport_context *ctx = arg;
176   int offset = ctx->peeklen;
177
178   if (bufsize > sizeof ctx->peekbuf)
179     bufsize = sizeof ctx->peekbuf;
180
181   if (offset)
182     memcpy (buf, ctx->peekbuf, offset);
183
184   do
185     {
186       if (gnutls_record_check_pending (ctx->session)
187           || select_fd (fd, 0, WAIT_FOR_READ))
188         ret = gnutls_record_recv (ctx->session, buf + offset, bufsize - offset);
189     }
190   while (ret == GNUTLS_E_INTERRUPTED);
191
192   if (ret > 0)
193     {
194       memcpy (ctx->peekbuf + offset, buf + offset, ret);
195       ctx->peeklen += ret;
196     }
197   return ctx->peeklen;
198 }
199
200 static const char *
201 wgnutls_errstr (int fd, void *arg)
202 {
203   struct wgnutls_transport_context *ctx = arg;
204   return gnutls_strerror (ctx->last_error);
205 }
206
207 static void
208 wgnutls_close (int fd, void *arg)
209 {
210   struct wgnutls_transport_context *ctx = arg;
211   /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
212   gnutls_deinit (ctx->session);
213   xfree (ctx);
214   close (fd);
215 }
216
217 /* gnutls_transport is the singleton that describes the SSL transport
218    methods provided by this file.  */
219
220 static struct transport_implementation wgnutls_transport = {
221   wgnutls_read, wgnutls_write, wgnutls_poll,
222   wgnutls_peek, wgnutls_errstr, wgnutls_close
223 };
224
225 bool
226 ssl_connect_wget (int fd)
227 {
228   static const int cert_type_priority[] = {
229     GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0
230   };
231   struct wgnutls_transport_context *ctx;
232   gnutls_session session;
233   int err;
234   int allowed_protocols[4] = {0, 0, 0, 0};
235   gnutls_init (&session, GNUTLS_CLIENT);
236   gnutls_set_default_priority (session);
237   gnutls_certificate_type_set_priority (session, cert_type_priority);
238   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
239 #ifndef FD_TO_SOCKET
240 # define FD_TO_SOCKET(X) (X)
241 #endif
242   gnutls_transport_set_ptr (session, (gnutls_transport_ptr) FD_TO_SOCKET (fd));
243
244   err = 0;
245   switch (opt.secure_protocol)
246     {
247     case secure_protocol_auto:
248       break;
249     case secure_protocol_sslv2:
250     case secure_protocol_sslv3:
251       allowed_protocols[0] = GNUTLS_SSL3;
252       err = gnutls_protocol_set_priority (session, allowed_protocols);
253       break;
254     case secure_protocol_tlsv1:
255       allowed_protocols[0] = GNUTLS_TLS1_0;
256       allowed_protocols[1] = GNUTLS_TLS1_1;
257       allowed_protocols[2] = GNUTLS_TLS1_2;
258       err = gnutls_protocol_set_priority (session, allowed_protocols);
259       break;
260     default:
261       abort ();
262     }
263   if (err < 0)
264     {
265       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
266       gnutls_deinit (session);
267       return false;
268     }
269
270   err = gnutls_handshake (session);
271   if (err < 0)
272     {
273       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
274       gnutls_deinit (session);
275       return false;
276     }
277
278   ctx = xnew0 (struct wgnutls_transport_context);
279   ctx->session = session;
280   fd_register_transport (fd, &wgnutls_transport, ctx);
281   return true;
282 }
283
284 bool
285 ssl_check_certificate (int fd, const char *host)
286 {
287   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
288
289   unsigned int status;
290   int err;
291
292   /* If the user has specified --no-check-cert, we still want to warn
293      him about problems with the server's certificate.  */
294   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
295   bool success = true;
296
297   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
298   if (err < 0)
299     {
300       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
301                  severity, quotearg_style (escape_quoting_style, host));
302       success = false;
303       goto out;
304     }
305
306   if (status & GNUTLS_CERT_INVALID)
307     {
308       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
309                  severity, quote (host));
310       success = false;
311     }
312   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
313     {
314       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
315                  severity, quote (host));
316       success = false;
317     }
318   if (status & GNUTLS_CERT_REVOKED)
319     {
320       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
321                  severity, quote (host));
322       success = false;
323     }
324
325   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
326     {
327       time_t now = time (NULL);
328       gnutls_x509_crt cert;
329       const gnutls_datum *cert_list;
330       unsigned int cert_list_size;
331
332       if ((err = gnutls_x509_crt_init (&cert)) < 0)
333         {
334           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
335                      gnutls_strerror (err));
336           success = false;
337           goto out;
338         }
339
340       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
341       if (!cert_list)
342         {
343           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
344           success = false;
345           goto out;
346         }
347       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
348       if (err < 0)
349         {
350           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
351                      gnutls_strerror (err));
352           success = false;
353           goto out;
354         }
355       if (now < gnutls_x509_crt_get_activation_time (cert))
356         {
357           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
358           success = false;
359         }
360       if (now >= gnutls_x509_crt_get_expiration_time (cert))
361         {
362           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
363           success = false;
364         }
365       if (!gnutls_x509_crt_check_hostname (cert, host))
366         {
367           logprintf (LOG_NOTQUIET,
368                      _("The certificate's owner does not match hostname %s\n"),
369                      quote (host));
370           success = false;
371         }
372       gnutls_x509_crt_deinit (cert);
373    }
374
375  out:
376   return opt.check_cert ? success : true;
377 }