1 /* SSL support via GnuTLS library.
2 Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc.
4 This file is part of GNU Wget.
6 GNU Wget is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 GNU Wget is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Wget. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the Free Software Foundation
20 gives permission to link the code of its release of Wget with the
21 OpenSSL project's "OpenSSL" library (or with modified versions of it
22 that use the same license as the "OpenSSL" library), and distribute
23 the linked executables. You must obey the GNU General Public License
24 in all respects for all of the code used other than "OpenSSL". If you
25 modify this file, you may extend this exception to your version of the
26 file, but you are not obligated to do so. If you do not wish to do
27 so, delete this exception statement from your version. */
39 #include <gnutls/gnutls.h>
40 #include <gnutls/x509.h>
47 /* Note: some of the functions private to this file have names that
48 begin with "wgnutls_" (e.g. wgnutls_read) so that they wouldn't be
49 confused with actual gnutls functions -- such as the gnutls_read
50 preprocessor macro. */
52 static gnutls_certificate_credentials credentials;
57 gnutls_global_init ();
58 gnutls_certificate_allocate_credentials (&credentials);
60 gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert,
65 struct wgnutls_transport_context {
66 gnutls_session session; /* GnuTLS session handle */
67 int last_error; /* last error returned by read/write/... */
69 /* Since GnuTLS doesn't support the equivalent to recv(...,
70 MSG_PEEK) or SSL_peek(), we have to do it ourselves. Peeked data
71 is stored to PEEKBUF, and wgnutls_read checks that buffer before
74 int peekstart, peeklen;
78 # define MIN(i, j) ((i) <= (j) ? (i) : (j))
82 wgnutls_read (int fd, char *buf, int bufsize, void *arg)
85 struct wgnutls_transport_context *ctx = arg;
89 /* If we have any peek data, simply return that. */
90 int copysize = MIN (bufsize, ctx->peeklen);
91 memcpy (buf, ctx->peekbuf + ctx->peekstart, copysize);
92 ctx->peeklen -= copysize;
93 if (ctx->peeklen != 0)
94 ctx->peekstart += copysize;
101 ret = gnutls_record_recv (ctx->session, buf, bufsize);
102 while (ret == GNUTLS_E_INTERRUPTED);
105 ctx->last_error = ret;
110 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
113 struct wgnutls_transport_context *ctx = arg;
115 ret = gnutls_record_send (ctx->session, buf, bufsize);
116 while (ret == GNUTLS_E_INTERRUPTED);
118 ctx->last_error = ret;
123 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
129 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
132 struct wgnutls_transport_context *ctx = arg;
134 /* We don't support peeks following peeks: the reader must drain all
135 peeked data before the next peek. */
136 assert (ctx->peeklen == 0);
137 if (bufsize > sizeof ctx->peekbuf)
138 bufsize = sizeof ctx->peekbuf;
141 ret = gnutls_record_recv (ctx->session, buf, bufsize);
142 while (ret == GNUTLS_E_INTERRUPTED);
146 memcpy (ctx->peekbuf, buf, ret);
153 wgnutls_errstr (int fd, void *arg)
155 struct wgnutls_transport_context *ctx = arg;
156 return gnutls_strerror (ctx->last_error);
160 wgnutls_close (int fd, void *arg)
162 struct wgnutls_transport_context *ctx = arg;
163 /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
164 gnutls_deinit (ctx->session);
173 /* gnutls_transport is the singleton that describes the SSL transport
174 methods provided by this file. */
176 static struct transport_implementation wgnutls_transport = {
177 wgnutls_read, wgnutls_write, wgnutls_poll,
178 wgnutls_peek, wgnutls_errstr, wgnutls_close
184 static const int cert_type_priority[] = {
185 GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0
187 struct wgnutls_transport_context *ctx;
188 gnutls_session session;
190 gnutls_init (&session, GNUTLS_CLIENT);
191 gnutls_set_default_priority (session);
192 gnutls_certificate_type_set_priority (session, cert_type_priority);
193 gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
194 gnutls_transport_set_ptr (session, (gnutls_transport_ptr) fd);
195 err = gnutls_handshake (session);
198 logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
199 gnutls_deinit (session);
202 ctx = xnew0 (struct wgnutls_transport_context);
203 ctx->session = session;
204 fd_register_transport (fd, &wgnutls_transport, ctx);
209 ssl_check_certificate (int fd, const char *host)
211 struct wgnutls_transport_context *ctx = fd_transport_context (fd);
216 /* If the user has specified --no-check-cert, we still want to warn
217 him about problems with the server's certificate. */
218 const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
221 err = gnutls_certificate_verify_peers2 (ctx->session, &status);
224 logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
225 severity, escnonprint (host));
230 if (status & GNUTLS_CERT_INVALID)
232 logprintf (LOG_NOTQUIET, _("%s: The certificate of `%s' is not trusted.\n"),
233 severity, escnonprint (host));
236 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
238 logprintf (LOG_NOTQUIET, _("%s: The certificate of `%s' hasn't got a known issuer.\n"),
239 severity, escnonprint (host));
242 if (status & GNUTLS_CERT_REVOKED)
244 logprintf (LOG_NOTQUIET, _("%s: The certificate of `%s' has been revoked.\n"),
245 severity, escnonprint (host));
249 if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
251 time_t now = time (NULL);
252 gnutls_x509_crt cert;
253 const gnutls_datum *cert_list;
254 unsigned int cert_list_size;
256 if ((err = gnutls_x509_crt_init (&cert)) < 0)
258 logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
259 gnutls_strerror (err));
264 cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
267 logprintf (LOG_NOTQUIET, _("No certificate found\n"));
271 err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
274 logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
275 gnutls_strerror (err));
279 if (now < gnutls_x509_crt_get_activation_time (cert))
281 logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
284 if (now >= gnutls_x509_crt_get_expiration_time (cert))
286 logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
289 if (!gnutls_x509_crt_check_hostname (cert, host))
291 logprintf (LOG_NOTQUIET,
292 _("The certificate's owner does not match hostname '%s'\n"),
296 gnutls_x509_crt_deinit (cert);
300 return opt.check_cert ? success : true;