]> sjero.net Git - wget/blob - src/gnutls.c
gnutls: do not call fcntl in a loop.
[wget] / src / gnutls.c
1 /* SSL support via GnuTLS library.
2    Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 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 "ptimer.h"
49 #include "ssl.h"
50
51 #include <sys/fcntl.h>
52
53 #ifdef WIN32
54 # include "w32sock.h"
55 #endif
56
57 #include "host.h"
58
59 static int
60 key_type_to_gnutls_type (enum keyfile_type type)
61 {
62   switch (type)
63     {
64     case keyfile_pem:
65       return GNUTLS_X509_FMT_PEM;
66     case keyfile_asn1:
67       return GNUTLS_X509_FMT_DER;
68     default:
69       abort ();
70     }
71 }
72
73 /* Note: some of the functions private to this file have names that
74    begin with "wgnutls_" (e.g. wgnutls_read) so that they wouldn't be
75    confused with actual gnutls functions -- such as the gnutls_read
76    preprocessor macro.  */
77
78 static gnutls_certificate_credentials_t credentials;
79 bool
80 ssl_init (void)
81 {
82   /* Becomes true if GnuTLS is initialized. */
83   static bool ssl_initialized = false;
84
85   /* GnuTLS should be initialized only once. */
86   if (ssl_initialized)
87     return true;
88
89   const char *ca_directory;
90   DIR *dir;
91
92   gnutls_global_init ();
93   gnutls_certificate_allocate_credentials (&credentials);
94   gnutls_certificate_set_verify_flags(credentials,
95                                       GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
96
97   ca_directory = opt.ca_directory ? opt.ca_directory : "/etc/ssl/certs";
98
99   dir = opendir (ca_directory);
100   if (dir == NULL)
101     {
102       if (opt.ca_directory)
103         logprintf (LOG_NOTQUIET, _("ERROR: Cannot open directory %s.\n"),
104                    opt.ca_directory);
105     }
106   else
107     {
108       struct dirent *dent;
109       while ((dent = readdir (dir)) != NULL)
110         {
111           struct stat st;
112           char *ca_file;
113           asprintf (&ca_file, "%s/%s", ca_directory, dent->d_name);
114
115           stat (ca_file, &st);
116
117           if (S_ISREG (st.st_mode))
118             gnutls_certificate_set_x509_trust_file (credentials, ca_file,
119                                                     GNUTLS_X509_FMT_PEM);
120
121           free (ca_file);
122         }
123
124       closedir (dir);
125     }
126
127   /* Use the private key from the cert file unless otherwise specified. */
128   if (opt.cert_file && !opt.private_key)
129     {
130       opt.private_key = opt.cert_file;
131       opt.private_key_type = opt.cert_type;
132     }
133   /* Use the cert from the private key file unless otherwise specified. */
134   if (!opt.cert_file && opt.private_key)
135     {
136       opt.cert_file = opt.private_key;
137       opt.cert_type = opt.private_key_type;
138     }
139
140   if (opt.cert_file && opt.private_key)
141     {
142       int type;
143       if (opt.private_key_type != opt.cert_type)
144         {
145           /* GnuTLS can't handle this */
146           logprintf (LOG_NOTQUIET, _("ERROR: GnuTLS requires the key and the \
147 cert to be of the same type.\n"));
148         }
149
150       type = key_type_to_gnutls_type (opt.private_key_type);
151
152       gnutls_certificate_set_x509_key_file (credentials, opt.cert_file,
153                                             opt.private_key,
154                                             type);
155     }
156
157   if (opt.ca_cert)
158     gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert,
159                                             GNUTLS_X509_FMT_PEM);
160
161   ssl_initialized = true;
162
163   return true;
164 }
165
166 struct wgnutls_transport_context
167 {
168   gnutls_session_t session;       /* GnuTLS session handle */
169   int last_error;               /* last error returned by read/write/... */
170
171   /* Since GnuTLS doesn't support the equivalent to recv(...,
172      MSG_PEEK) or SSL_peek(), we have to do it ourselves.  Peeked data
173      is stored to PEEKBUF, and wgnutls_read checks that buffer before
174      actually reading.  */
175   char peekbuf[512];
176   int peeklen;
177 };
178
179 #ifndef MIN
180 # define MIN(i, j) ((i) <= (j) ? (i) : (j))
181 #endif
182
183
184 static int
185 wgnutls_read_timeout (int fd, char *buf, int bufsize, void *arg, double timeout)
186 {
187 #ifdef F_GETFL
188   int flags = 0;
189 #endif
190   int ret = 0;
191   struct ptimer *timer = NULL;
192   struct wgnutls_transport_context *ctx = arg;
193   int timed_out = 0;
194
195   if (timeout)
196     {
197 #ifdef F_GETFL
198       flags = fcntl (fd, F_GETFL, 0);
199       if (flags < 0)
200         return flags;
201       if (fcntl (fd, F_SETFL, flags | O_NONBLOCK))
202         return -1;
203 #else
204       /* XXX: Assume it was blocking before.  */
205       const int one = 1;
206       if (ioctl (fd, FIONBIO, &one) < 0)
207         return -1;
208 #endif
209
210       timer = ptimer_new ();
211       if (timer == NULL)
212         return -1;
213     }
214
215   do
216     {
217       double next_timeout = 0;
218       if (timeout)
219         {
220           next_timeout = timeout - ptimer_measure (timer);
221           if (next_timeout < 0)
222             break;
223         }
224
225       ret = GNUTLS_E_AGAIN;
226       if (timeout == 0 || gnutls_record_check_pending (ctx->session)
227           || select_fd (fd, next_timeout, WAIT_FOR_READ))
228         {
229           ret = gnutls_record_recv (ctx->session, buf, bufsize);
230           timed_out = timeout && ptimer_measure (timer) >= timeout;
231         }
232     }
233   while (ret == GNUTLS_E_INTERRUPTED || (ret == GNUTLS_E_AGAIN && !timed_out));
234
235   if (timeout)
236     {
237       ptimer_destroy (timer);
238
239 #ifdef F_GETFL
240       if (fcntl (fd, F_SETFL, flags) < 0)
241         return -1;
242 #else
243       const int zero = 0;
244       if (ioctl (fd, FIONBIO, &zero) < 0)
245         return -1;
246 #endif
247
248       if (timed_out && ret == GNUTLS_E_AGAIN)
249         errno = ETIMEDOUT;
250     }
251
252   return ret;
253 }
254
255 static int
256 wgnutls_read (int fd, char *buf, int bufsize, void *arg)
257 {
258   int ret = 0;
259   struct wgnutls_transport_context *ctx = arg;
260
261   if (ctx->peeklen)
262     {
263       /* If we have any peek data, simply return that. */
264       int copysize = MIN (bufsize, ctx->peeklen);
265       memcpy (buf, ctx->peekbuf, copysize);
266       ctx->peeklen -= copysize;
267       if (ctx->peeklen != 0)
268         memmove (ctx->peekbuf, ctx->peekbuf + copysize, ctx->peeklen);
269
270       return copysize;
271     }
272
273   ret = wgnutls_read_timeout (fd, buf, bufsize, arg, opt.read_timeout);
274   if (ret < 0)
275     ctx->last_error = ret;
276
277   return ret;
278 }
279
280 static int
281 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
282 {
283   int ret;
284   struct wgnutls_transport_context *ctx = arg;
285   do
286     ret = gnutls_record_send (ctx->session, buf, bufsize);
287   while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
288   if (ret < 0)
289     ctx->last_error = ret;
290   return ret;
291 }
292
293 static int
294 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
295 {
296   struct wgnutls_transport_context *ctx = arg;
297   return ctx->peeklen || gnutls_record_check_pending (ctx->session)
298     || select_fd (fd, timeout, wait_for);
299 }
300
301 static int
302 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
303 {
304   int read = 0;
305   struct wgnutls_transport_context *ctx = arg;
306   int offset = MIN (bufsize, ctx->peeklen);
307   if (bufsize > sizeof ctx->peekbuf)
308     bufsize = sizeof ctx->peekbuf;
309
310   if (ctx->peeklen)
311     memcpy (buf, ctx->peekbuf, offset);
312
313   if (bufsize > offset)
314     {
315       if (gnutls_record_check_pending (ctx->session) <= 0
316           && select_fd (fd, 0.0, WAIT_FOR_READ) <= 0)
317         read = 0;
318       else
319         read = wgnutls_read_timeout (fd, buf + offset, bufsize - offset,
320                                      ctx, opt.read_timeout);
321       if (read < 0)
322         {
323           if (offset)
324             read = 0;
325           else
326             return read;
327         }
328
329       if (read > 0)
330         {
331           memcpy (ctx->peekbuf + offset, buf + offset,
332                   read);
333           ctx->peeklen += read;
334         }
335     }
336
337   return offset + read;
338 }
339
340 static const char *
341 wgnutls_errstr (int fd, void *arg)
342 {
343   struct wgnutls_transport_context *ctx = arg;
344   return gnutls_strerror (ctx->last_error);
345 }
346
347 static void
348 wgnutls_close (int fd, void *arg)
349 {
350   struct wgnutls_transport_context *ctx = arg;
351   /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
352   gnutls_deinit (ctx->session);
353   xfree (ctx);
354   close (fd);
355 }
356
357 /* gnutls_transport is the singleton that describes the SSL transport
358    methods provided by this file.  */
359
360 static struct transport_implementation wgnutls_transport =
361 {
362   wgnutls_read, wgnutls_write, wgnutls_poll,
363   wgnutls_peek, wgnutls_errstr, wgnutls_close
364 };
365
366 bool
367 ssl_connect_wget (int fd, const char *hostname)
368 {
369   struct wgnutls_transport_context *ctx;
370   gnutls_session_t session;
371   int err;
372   gnutls_init (&session, GNUTLS_CLIENT);
373
374   /* We set the server name but only if it's not an IP address. */
375   if (! is_valid_ip_address (hostname))
376     {
377       gnutls_server_name_set (session, GNUTLS_NAME_DNS, hostname,
378                               strlen (hostname));
379     }
380
381   gnutls_set_default_priority (session);
382   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
383 #ifndef FD_TO_SOCKET
384 # define FD_TO_SOCKET(X) (X)
385 #endif
386   gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) FD_TO_SOCKET (fd));
387
388   err = 0;
389 #if HAVE_GNUTLS_PRIORITY_SET_DIRECT
390   switch (opt.secure_protocol)
391     {
392     case secure_protocol_auto:
393       break;
394     case secure_protocol_sslv2:
395     case secure_protocol_sslv3:
396       err = gnutls_priority_set_direct (session, "NORMAL:-VERS-TLS-ALL", NULL);
397       break;
398     case secure_protocol_tlsv1:
399       err = gnutls_priority_set_direct (session, "NORMAL:-VERS-SSL3.0", NULL);
400       break;
401     default:
402       abort ();
403     }
404 #else
405   int allowed_protocols[4] = {0, 0, 0, 0};
406   switch (opt.secure_protocol)
407     {
408     case secure_protocol_auto:
409       break;
410     case secure_protocol_sslv2:
411     case secure_protocol_sslv3:
412       allowed_protocols[0] = GNUTLS_SSL3;
413       err = gnutls_protocol_set_priority (session, allowed_protocols);
414       break;
415
416     case secure_protocol_tlsv1:
417       allowed_protocols[0] = GNUTLS_TLS1_0;
418       allowed_protocols[1] = GNUTLS_TLS1_1;
419       allowed_protocols[2] = GNUTLS_TLS1_2;
420       err = gnutls_protocol_set_priority (session, allowed_protocols);
421       break;
422
423     default:
424       abort ();
425     }
426 #endif
427
428   if (err < 0)
429     {
430       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
431       gnutls_deinit (session);
432       return false;
433     }
434
435   err = gnutls_handshake (session);
436   if (err < 0)
437     {
438       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
439       gnutls_deinit (session);
440       return false;
441     }
442
443   ctx = xnew0 (struct wgnutls_transport_context);
444   ctx->session = session;
445   fd_register_transport (fd, &wgnutls_transport, ctx);
446   return true;
447 }
448
449 bool
450 ssl_check_certificate (int fd, const char *host)
451 {
452   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
453
454   unsigned int status;
455   int err;
456
457   /* If the user has specified --no-check-cert, we still want to warn
458      him about problems with the server's certificate.  */
459   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
460   bool success = true;
461
462   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
463   if (err < 0)
464     {
465       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
466                  severity, quotearg_style (escape_quoting_style, host));
467       success = false;
468       goto out;
469     }
470
471   if (status & GNUTLS_CERT_INVALID)
472     {
473       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
474                  severity, quote (host));
475       success = false;
476     }
477   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
478     {
479       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
480                  severity, quote (host));
481       success = false;
482     }
483   if (status & GNUTLS_CERT_REVOKED)
484     {
485       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
486                  severity, quote (host));
487       success = false;
488     }
489
490   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
491     {
492       time_t now = time (NULL);
493       gnutls_x509_crt_t cert;
494       const gnutls_datum_t *cert_list;
495       unsigned int cert_list_size;
496
497       if ((err = gnutls_x509_crt_init (&cert)) < 0)
498         {
499           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
500                      gnutls_strerror (err));
501           success = false;
502           goto out;
503         }
504
505       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
506       if (!cert_list)
507         {
508           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
509           success = false;
510           goto out;
511         }
512       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
513       if (err < 0)
514         {
515           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
516                      gnutls_strerror (err));
517           success = false;
518           goto out;
519         }
520       if (now < gnutls_x509_crt_get_activation_time (cert))
521         {
522           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
523           success = false;
524         }
525       if (now >= gnutls_x509_crt_get_expiration_time (cert))
526         {
527           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
528           success = false;
529         }
530       if (!gnutls_x509_crt_check_hostname (cert, host))
531         {
532           logprintf (LOG_NOTQUIET,
533                      _("The certificate's owner does not match hostname %s\n"),
534                      quote (host));
535           success = false;
536         }
537       gnutls_x509_crt_deinit (cert);
538    }
539
540  out:
541   return opt.check_cert ? success : true;
542 }