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