]> sjero.net Git - wget/blob - src/gnutls.c
Make wgnutls_peek non blocking.
[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 <fcntl.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
70   ca_directory = opt.ca_directory ? opt.ca_directory : "/etc/ssl/certs";
71
72   dir = opendir (ca_directory);
73   if (dir == NULL)
74     {
75       if (opt.ca_directory)
76         logprintf (LOG_NOTQUIET, _("ERROR: Cannot open directory %s.\n"),
77                    opt.ca_directory);
78     }
79   else
80     {
81       struct dirent *dent;
82       while ((dent = readdir (dir)) != NULL)
83         {
84           struct stat st;
85           char *ca_file;
86           asprintf (&ca_file, "%s/%s", ca_directory, dent->d_name);
87
88           stat (ca_file, &st);
89
90           if (S_ISREG (st.st_mode))
91             gnutls_certificate_set_x509_trust_file (credentials, ca_file,
92                                                     GNUTLS_X509_FMT_PEM);
93
94           free (ca_file);
95         }
96
97       closedir (dir);
98     }
99
100   if (opt.ca_cert)
101     gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert,
102                                             GNUTLS_X509_FMT_PEM);
103   return true;
104 }
105
106 struct wgnutls_transport_context
107 {
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
148   return ret;
149 }
150
151 static int
152 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
153 {
154   int ret;
155   struct wgnutls_transport_context *ctx = arg;
156   do
157     ret = gnutls_record_send (ctx->session, buf, bufsize);
158   while (ret == GNUTLS_E_INTERRUPTED);
159   if (ret < 0)
160     ctx->last_error = ret;
161   return ret;
162 }
163
164 static int
165 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
166 {
167   struct wgnutls_transport_context *ctx = arg;
168   return ctx->peeklen || gnutls_record_check_pending (ctx->session)
169     || select_fd (fd, timeout, wait_for);
170 }
171
172 static int
173 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
174 {
175   int ret = 0;
176   struct wgnutls_transport_context *ctx = arg;
177   int offset = MIN (bufsize, ctx->peeklen);
178   if (bufsize > sizeof ctx->peekbuf)
179     bufsize = sizeof ctx->peekbuf;
180
181   if (ctx->peeklen)
182     memcpy (buf, ctx->peekbuf, offset);
183
184   if (bufsize > offset)
185     {
186       int flags;
187       flags = fcntl (fd, F_GETFL, 0);
188       if (flags < 0)
189         return ret;
190
191       ret = fcntl (fd, F_SETFL, flags | O_NONBLOCK);
192       if (ret < 0)
193         return ret;
194
195       do
196         {
197           ret = gnutls_record_recv (ctx->session, buf + offset,
198                                     bufsize - offset);
199         }
200       while (ret == GNUTLS_E_INTERRUPTED);
201
202       if (ret < 0)
203         {
204           if (offset)
205             ret = 0;
206           else
207             return ret;
208         }
209
210       if (ret > 0)
211         {
212           memcpy (ctx->peekbuf + offset, buf + offset,
213                   ret);
214           ctx->peeklen += ret;
215         }
216
217       fcntl (fd, F_SETFL, flags);
218       if (ret < 0)
219         return ret;
220     }
221
222   return offset + ret;
223 }
224
225 static const char *
226 wgnutls_errstr (int fd, void *arg)
227 {
228   struct wgnutls_transport_context *ctx = arg;
229   return gnutls_strerror (ctx->last_error);
230 }
231
232 static void
233 wgnutls_close (int fd, void *arg)
234 {
235   struct wgnutls_transport_context *ctx = arg;
236   /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
237   gnutls_deinit (ctx->session);
238   xfree (ctx);
239   close (fd);
240 }
241
242 /* gnutls_transport is the singleton that describes the SSL transport
243    methods provided by this file.  */
244
245 static struct transport_implementation wgnutls_transport =
246 {
247   wgnutls_read, wgnutls_write, wgnutls_poll,
248   wgnutls_peek, wgnutls_errstr, wgnutls_close
249 };
250
251 bool
252 ssl_connect_wget (int fd)
253 {
254   static const int cert_type_priority[] = {
255     GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0
256   };
257   struct wgnutls_transport_context *ctx;
258   gnutls_session session;
259   int err;
260   int allowed_protocols[4] = {0, 0, 0, 0};
261   gnutls_init (&session, GNUTLS_CLIENT);
262   gnutls_set_default_priority (session);
263   gnutls_certificate_type_set_priority (session, cert_type_priority);
264   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
265 #ifndef FD_TO_SOCKET
266 # define FD_TO_SOCKET(X) (X)
267 #endif
268   gnutls_transport_set_ptr (session, (gnutls_transport_ptr) FD_TO_SOCKET (fd));
269
270   err = 0;
271   switch (opt.secure_protocol)
272     {
273     case secure_protocol_auto:
274       break;
275     case secure_protocol_sslv2:
276     case secure_protocol_sslv3:
277       allowed_protocols[0] = GNUTLS_SSL3;
278       err = gnutls_protocol_set_priority (session, allowed_protocols);
279       break;
280     case secure_protocol_tlsv1:
281       allowed_protocols[0] = GNUTLS_TLS1_0;
282       allowed_protocols[1] = GNUTLS_TLS1_1;
283       allowed_protocols[2] = GNUTLS_TLS1_2;
284       err = gnutls_protocol_set_priority (session, allowed_protocols);
285       break;
286     default:
287       abort ();
288     }
289   if (err < 0)
290     {
291       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
292       gnutls_deinit (session);
293       return false;
294     }
295
296   err = gnutls_handshake (session);
297   if (err < 0)
298     {
299       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
300       gnutls_deinit (session);
301       return false;
302     }
303
304   ctx = xnew0 (struct wgnutls_transport_context);
305   ctx->session = session;
306   fd_register_transport (fd, &wgnutls_transport, ctx);
307   return true;
308 }
309
310 bool
311 ssl_check_certificate (int fd, const char *host)
312 {
313   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
314
315   unsigned int status;
316   int err;
317
318   /* If the user has specified --no-check-cert, we still want to warn
319      him about problems with the server's certificate.  */
320   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
321   bool success = true;
322
323   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
324   if (err < 0)
325     {
326       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
327                  severity, quotearg_style (escape_quoting_style, host));
328       success = false;
329       goto out;
330     }
331
332   if (status & GNUTLS_CERT_INVALID)
333     {
334       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
335                  severity, quote (host));
336       success = false;
337     }
338   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
339     {
340       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
341                  severity, quote (host));
342       success = false;
343     }
344   if (status & GNUTLS_CERT_REVOKED)
345     {
346       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
347                  severity, quote (host));
348       success = false;
349     }
350
351   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
352     {
353       time_t now = time (NULL);
354       gnutls_x509_crt cert;
355       const gnutls_datum *cert_list;
356       unsigned int cert_list_size;
357
358       if ((err = gnutls_x509_crt_init (&cert)) < 0)
359         {
360           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
361                      gnutls_strerror (err));
362           success = false;
363           goto out;
364         }
365
366       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
367       if (!cert_list)
368         {
369           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
370           success = false;
371           goto out;
372         }
373       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
374       if (err < 0)
375         {
376           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
377                      gnutls_strerror (err));
378           success = false;
379           goto out;
380         }
381       if (now < gnutls_x509_crt_get_activation_time (cert))
382         {
383           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
384           success = false;
385         }
386       if (now >= gnutls_x509_crt_get_expiration_time (cert))
387         {
388           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
389           success = false;
390         }
391       if (!gnutls_x509_crt_check_hostname (cert, host))
392         {
393           logprintf (LOG_NOTQUIET,
394                      _("The certificate's owner does not match hostname %s\n"),
395                      quote (host));
396           success = false;
397         }
398       gnutls_x509_crt_deinit (cert);
399    }
400
401  out:
402   return opt.check_cert ? success : true;
403 }