1 /* SSL support via GnuTLS library.
2 Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 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>
53 /* Note: some of the functions private to this file have names that
54 begin with "wgnutls_" (e.g. wgnutls_read) so that they wouldn't be
55 confused with actual gnutls functions -- such as the gnutls_read
56 preprocessor macro. */
58 static gnutls_certificate_credentials credentials;
63 const char *ca_directory;
66 gnutls_global_init ();
67 gnutls_certificate_allocate_credentials (&credentials);
69 ca_directory = opt.ca_directory ? opt.ca_directory : "/etc/ssl/certs";
71 dir = opendir (ca_directory);
75 logprintf (LOG_NOTQUIET, _("ERROR: Cannot open directory %s.\n"),
81 while ((dent = readdir (dir)) != NULL)
85 asprintf (&ca_file, "%s/%s", ca_directory, dent->d_name);
89 if (S_ISREG (st.st_mode))
90 gnutls_certificate_set_x509_trust_file (credentials, ca_file,
100 gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert,
101 GNUTLS_X509_FMT_PEM);
105 struct wgnutls_transport_context
107 gnutls_session session; /* GnuTLS session handle */
108 int last_error; /* last error returned by read/write/... */
110 /* Since GnuTLS doesn't support the equivalent to recv(...,
111 MSG_PEEK) or SSL_peek(), we have to do it ourselves. Peeked data
112 is stored to PEEKBUF, and wgnutls_read checks that buffer before
119 # define MIN(i, j) ((i) <= (j) ? (i) : (j))
123 wgnutls_read (int fd, char *buf, int bufsize, void *arg)
126 struct wgnutls_transport_context *ctx = arg;
130 /* If we have any peek data, simply return that. */
131 int copysize = MIN (bufsize, ctx->peeklen);
132 memcpy (buf, ctx->peekbuf, copysize);
133 ctx->peeklen -= copysize;
134 if (ctx->peeklen != 0)
135 memmove (ctx->peekbuf, ctx->peekbuf + copysize, ctx->peeklen);
141 ret = gnutls_record_recv (ctx->session, buf, bufsize);
142 while (ret == GNUTLS_E_INTERRUPTED);
145 ctx->last_error = ret;
151 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
154 struct wgnutls_transport_context *ctx = arg;
156 ret = gnutls_record_send (ctx->session, buf, bufsize);
157 while (ret == GNUTLS_E_INTERRUPTED);
159 ctx->last_error = ret;
164 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
166 struct wgnutls_transport_context *ctx = arg;
167 return ctx->peeklen || gnutls_record_check_pending (ctx->session)
168 || select_fd (fd, timeout, wait_for);
172 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
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;
181 memcpy (buf, ctx->peekbuf, offset);
183 if (bufsize > offset)
187 ret = gnutls_record_recv (ctx->session, buf + offset,
190 while (ret == GNUTLS_E_INTERRUPTED);
202 memcpy (ctx->peekbuf + offset, buf + offset,
212 wgnutls_errstr (int fd, void *arg)
214 struct wgnutls_transport_context *ctx = arg;
215 return gnutls_strerror (ctx->last_error);
219 wgnutls_close (int fd, void *arg)
221 struct wgnutls_transport_context *ctx = arg;
222 /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
223 gnutls_deinit (ctx->session);
228 /* gnutls_transport is the singleton that describes the SSL transport
229 methods provided by this file. */
231 static struct transport_implementation wgnutls_transport =
233 wgnutls_read, wgnutls_write, wgnutls_poll,
234 wgnutls_peek, wgnutls_errstr, wgnutls_close
238 ssl_connect_wget (int fd)
240 static const int cert_type_priority[] = {
241 GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0
243 struct wgnutls_transport_context *ctx;
244 gnutls_session session;
246 int allowed_protocols[4] = {0, 0, 0, 0};
247 gnutls_init (&session, GNUTLS_CLIENT);
248 gnutls_set_default_priority (session);
249 gnutls_certificate_type_set_priority (session, cert_type_priority);
250 gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
252 # define FD_TO_SOCKET(X) (X)
254 gnutls_transport_set_ptr (session, (gnutls_transport_ptr) FD_TO_SOCKET (fd));
257 switch (opt.secure_protocol)
259 case secure_protocol_auto:
261 case secure_protocol_sslv2:
262 case secure_protocol_sslv3:
263 allowed_protocols[0] = GNUTLS_SSL3;
264 err = gnutls_protocol_set_priority (session, allowed_protocols);
266 case secure_protocol_tlsv1:
267 allowed_protocols[0] = GNUTLS_TLS1_0;
268 allowed_protocols[1] = GNUTLS_TLS1_1;
269 allowed_protocols[2] = GNUTLS_TLS1_2;
270 err = gnutls_protocol_set_priority (session, allowed_protocols);
277 logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
278 gnutls_deinit (session);
282 err = gnutls_handshake (session);
285 logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
286 gnutls_deinit (session);
290 ctx = xnew0 (struct wgnutls_transport_context);
291 ctx->session = session;
292 fd_register_transport (fd, &wgnutls_transport, ctx);
297 ssl_check_certificate (int fd, const char *host)
299 struct wgnutls_transport_context *ctx = fd_transport_context (fd);
304 /* If the user has specified --no-check-cert, we still want to warn
305 him about problems with the server's certificate. */
306 const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
309 err = gnutls_certificate_verify_peers2 (ctx->session, &status);
312 logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
313 severity, quotearg_style (escape_quoting_style, host));
318 if (status & GNUTLS_CERT_INVALID)
320 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
321 severity, quote (host));
324 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
326 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
327 severity, quote (host));
330 if (status & GNUTLS_CERT_REVOKED)
332 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
333 severity, quote (host));
337 if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
339 time_t now = time (NULL);
340 gnutls_x509_crt cert;
341 const gnutls_datum *cert_list;
342 unsigned int cert_list_size;
344 if ((err = gnutls_x509_crt_init (&cert)) < 0)
346 logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
347 gnutls_strerror (err));
352 cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
355 logprintf (LOG_NOTQUIET, _("No certificate found\n"));
359 err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
362 logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
363 gnutls_strerror (err));
367 if (now < gnutls_x509_crt_get_activation_time (cert))
369 logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
372 if (now >= gnutls_x509_crt_get_expiration_time (cert))
374 logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
377 if (!gnutls_x509_crt_check_hostname (cert, host))
379 logprintf (LOG_NOTQUIET,
380 _("The certificate's owner does not match hostname %s\n"),
384 gnutls_x509_crt_deinit (cert);
388 return opt.check_cert ? success : true;