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