1 /* SSL support via GnuTLS library.
2 Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
5 This file is part of GNU Wget.
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.
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.
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/>.
20 Additional permission under GNU GPL version 3 section 7
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. */
41 #include <gnutls/gnutls.h>
42 #include <gnutls/x509.h>
43 #include <sys/ioctl.h>
51 #include <sys/fcntl.h>
57 /* Note: some of the functions private to this file have names that
58 begin with "wgnutls_" (e.g. wgnutls_read) so that they wouldn't be
59 confused with actual gnutls functions -- such as the gnutls_read
60 preprocessor macro. */
62 static gnutls_certificate_credentials credentials;
66 /* Becomes true if GnuTLS is initialized. */
67 static bool ssl_initialized = false;
69 /* GnuTLS should be initialized only once. */
73 const char *ca_directory;
76 gnutls_global_init ();
77 gnutls_certificate_allocate_credentials (&credentials);
78 gnutls_certificate_set_verify_flags(credentials,
79 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
81 ca_directory = opt.ca_directory ? opt.ca_directory : "/etc/ssl/certs";
83 dir = opendir (ca_directory);
87 logprintf (LOG_NOTQUIET, _("ERROR: Cannot open directory %s.\n"),
93 while ((dent = readdir (dir)) != NULL)
97 asprintf (&ca_file, "%s/%s", ca_directory, dent->d_name);
101 if (S_ISREG (st.st_mode))
102 gnutls_certificate_set_x509_trust_file (credentials, ca_file,
103 GNUTLS_X509_FMT_PEM);
112 gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert,
113 GNUTLS_X509_FMT_PEM);
115 ssl_initialized = true;
120 struct wgnutls_transport_context
122 gnutls_session session; /* GnuTLS session handle */
123 int last_error; /* last error returned by read/write/... */
125 /* Since GnuTLS doesn't support the equivalent to recv(...,
126 MSG_PEEK) or SSL_peek(), we have to do it ourselves. Peeked data
127 is stored to PEEKBUF, and wgnutls_read checks that buffer before
134 # define MIN(i, j) ((i) <= (j) ? (i) : (j))
139 wgnutls_read_timeout (int fd, char *buf, int bufsize, void *arg, double timeout)
145 struct ptimer *timer;
146 struct wgnutls_transport_context *ctx = arg;
152 flags = fcntl (fd, F_GETFL, 0);
156 timer = ptimer_new ();
163 double next_timeout = timeout - ptimer_measure (timer);
164 if (timeout && next_timeout < 0)
167 ret = GNUTLS_E_AGAIN;
168 if (timeout == 0 || gnutls_record_check_pending (ctx->session)
169 || select_fd (fd, next_timeout, WAIT_FOR_READ))
174 ret = fcntl (fd, F_SETFL, flags | O_NONBLOCK);
178 /* XXX: Assume it was blocking before. */
180 ret = ioctl (fd, FIONBIO, &one);
186 ret = gnutls_record_recv (ctx->session, buf, bufsize);
192 status = fcntl (fd, F_SETFL, flags);
197 status = ioctl (fd, FIONBIO, &zero);
204 timed_out = timeout && ptimer_measure (timer) >= timeout;
206 while (ret == GNUTLS_E_INTERRUPTED || (ret == GNUTLS_E_AGAIN && !timed_out));
209 ptimer_destroy (timer);
211 if (timeout && timed_out && ret == GNUTLS_E_AGAIN)
218 wgnutls_read (int fd, char *buf, int bufsize, void *arg)
221 struct wgnutls_transport_context *ctx = arg;
225 /* If we have any peek data, simply return that. */
226 int copysize = MIN (bufsize, ctx->peeklen);
227 memcpy (buf, ctx->peekbuf, copysize);
228 ctx->peeklen -= copysize;
229 if (ctx->peeklen != 0)
230 memmove (ctx->peekbuf, ctx->peekbuf + copysize, ctx->peeklen);
235 ret = wgnutls_read_timeout (fd, buf, bufsize, arg, opt.read_timeout);
237 ctx->last_error = ret;
243 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
246 struct wgnutls_transport_context *ctx = arg;
248 ret = gnutls_record_send (ctx->session, buf, bufsize);
249 while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
251 ctx->last_error = ret;
256 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
258 struct wgnutls_transport_context *ctx = arg;
259 return ctx->peeklen || gnutls_record_check_pending (ctx->session)
260 || select_fd (fd, timeout, wait_for);
264 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
267 struct wgnutls_transport_context *ctx = arg;
268 int offset = MIN (bufsize, ctx->peeklen);
269 if (bufsize > sizeof ctx->peekbuf)
270 bufsize = sizeof ctx->peekbuf;
273 memcpy (buf, ctx->peekbuf, offset);
275 if (bufsize > offset)
277 if (gnutls_record_check_pending (ctx->session) <= 0
278 && select_fd (fd, 0.0, WAIT_FOR_READ) <= 0)
281 read = wgnutls_read_timeout (fd, buf + offset, bufsize - offset,
282 ctx, opt.read_timeout);
293 memcpy (ctx->peekbuf + offset, buf + offset,
295 ctx->peeklen += read;
299 return offset + read;
303 wgnutls_errstr (int fd, void *arg)
305 struct wgnutls_transport_context *ctx = arg;
306 return gnutls_strerror (ctx->last_error);
310 wgnutls_close (int fd, void *arg)
312 struct wgnutls_transport_context *ctx = arg;
313 /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
314 gnutls_deinit (ctx->session);
319 /* gnutls_transport is the singleton that describes the SSL transport
320 methods provided by this file. */
322 static struct transport_implementation wgnutls_transport =
324 wgnutls_read, wgnutls_write, wgnutls_poll,
325 wgnutls_peek, wgnutls_errstr, wgnutls_close
329 ssl_connect_wget (int fd)
331 struct wgnutls_transport_context *ctx;
332 gnutls_session session;
334 gnutls_init (&session, GNUTLS_CLIENT);
335 gnutls_set_default_priority (session);
336 gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
338 # define FD_TO_SOCKET(X) (X)
340 gnutls_transport_set_ptr (session, (gnutls_transport_ptr) FD_TO_SOCKET (fd));
343 #if HAVE_GNUTLS_PRIORITY_SET_DIRECT
344 switch (opt.secure_protocol)
346 case secure_protocol_auto:
348 case secure_protocol_sslv2:
349 case secure_protocol_sslv3:
350 err = gnutls_priority_set_direct (session, "NORMAL:-VERS-TLS-ALL", NULL);
352 case secure_protocol_tlsv1:
353 err = gnutls_priority_set_direct (session, "NORMAL:-VERS-SSL3.0", NULL);
359 int allowed_protocols[4] = {0, 0, 0, 0};
360 switch (opt.secure_protocol)
362 case secure_protocol_auto:
364 case secure_protocol_sslv2:
365 case secure_protocol_sslv3:
366 allowed_protocols[0] = GNUTLS_SSL3;
367 err = gnutls_protocol_set_priority (session, allowed_protocols);
370 case secure_protocol_tlsv1:
371 allowed_protocols[0] = GNUTLS_TLS1_0;
372 allowed_protocols[1] = GNUTLS_TLS1_1;
373 allowed_protocols[2] = GNUTLS_TLS1_2;
374 err = gnutls_protocol_set_priority (session, allowed_protocols);
384 logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
385 gnutls_deinit (session);
389 err = gnutls_handshake (session);
392 logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
393 gnutls_deinit (session);
397 ctx = xnew0 (struct wgnutls_transport_context);
398 ctx->session = session;
399 fd_register_transport (fd, &wgnutls_transport, ctx);
404 ssl_check_certificate (int fd, const char *host)
406 struct wgnutls_transport_context *ctx = fd_transport_context (fd);
411 /* If the user has specified --no-check-cert, we still want to warn
412 him about problems with the server's certificate. */
413 const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
416 err = gnutls_certificate_verify_peers2 (ctx->session, &status);
419 logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
420 severity, quotearg_style (escape_quoting_style, host));
425 if (status & GNUTLS_CERT_INVALID)
427 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
428 severity, quote (host));
431 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
433 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
434 severity, quote (host));
437 if (status & GNUTLS_CERT_REVOKED)
439 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
440 severity, quote (host));
444 if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
446 time_t now = time (NULL);
447 gnutls_x509_crt cert;
448 const gnutls_datum *cert_list;
449 unsigned int cert_list_size;
451 if ((err = gnutls_x509_crt_init (&cert)) < 0)
453 logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
454 gnutls_strerror (err));
459 cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
462 logprintf (LOG_NOTQUIET, _("No certificate found\n"));
466 err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
469 logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
470 gnutls_strerror (err));
474 if (now < gnutls_x509_crt_get_activation_time (cert))
476 logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
479 if (now >= gnutls_x509_crt_get_expiration_time (cert))
481 logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
484 if (!gnutls_x509_crt_check_hostname (cert, host))
486 logprintf (LOG_NOTQUIET,
487 _("The certificate's owner does not match hostname %s\n"),
491 gnutls_x509_crt_deinit (cert);
495 return opt.check_cert ? success : true;