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