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>
49 /* Note: some of the functions private to this file have names that
50 begin with "wgnutls_" (e.g. wgnutls_read) so that they wouldn't be
51 confused with actual gnutls functions -- such as the gnutls_read
52 preprocessor macro. */
54 static gnutls_certificate_credentials credentials;
59 gnutls_global_init ();
60 gnutls_certificate_allocate_credentials (&credentials);
62 gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert,
67 struct wgnutls_transport_context {
68 gnutls_session session; /* GnuTLS session handle */
69 int last_error; /* last error returned by read/write/... */
71 /* Since GnuTLS doesn't support the equivalent to recv(...,
72 MSG_PEEK) or SSL_peek(), we have to do it ourselves. Peeked data
73 is stored to PEEKBUF, and wgnutls_read checks that buffer before
76 int peekstart, peeklen;
80 # define MIN(i, j) ((i) <= (j) ? (i) : (j))
84 wgnutls_read (int fd, char *buf, int bufsize, void *arg)
87 struct wgnutls_transport_context *ctx = arg;
91 /* If we have any peek data, simply return that. */
92 int copysize = MIN (bufsize, ctx->peeklen);
93 memcpy (buf, ctx->peekbuf + ctx->peekstart, copysize);
94 ctx->peeklen -= copysize;
95 if (ctx->peeklen != 0)
96 ctx->peekstart += copysize;
103 ret = gnutls_record_recv (ctx->session, buf, bufsize);
104 while (ret == GNUTLS_E_INTERRUPTED);
107 ctx->last_error = ret;
112 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
115 struct wgnutls_transport_context *ctx = arg;
117 ret = gnutls_record_send (ctx->session, buf, bufsize);
118 while (ret == GNUTLS_E_INTERRUPTED);
120 ctx->last_error = ret;
125 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
131 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
134 struct wgnutls_transport_context *ctx = arg;
136 /* We don't support peeks following peeks: the reader must drain all
137 peeked data before the next peek. */
138 assert (ctx->peeklen == 0);
139 if (bufsize > sizeof ctx->peekbuf)
140 bufsize = sizeof ctx->peekbuf;
143 ret = gnutls_record_recv (ctx->session, buf, bufsize);
144 while (ret == GNUTLS_E_INTERRUPTED);
148 memcpy (ctx->peekbuf, buf, ret);
155 wgnutls_errstr (int fd, void *arg)
157 struct wgnutls_transport_context *ctx = arg;
158 return gnutls_strerror (ctx->last_error);
162 wgnutls_close (int fd, void *arg)
164 struct wgnutls_transport_context *ctx = arg;
165 /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
166 gnutls_deinit (ctx->session);
171 /* gnutls_transport is the singleton that describes the SSL transport
172 methods provided by this file. */
174 static struct transport_implementation wgnutls_transport = {
175 wgnutls_read, wgnutls_write, wgnutls_poll,
176 wgnutls_peek, wgnutls_errstr, wgnutls_close
182 static const int cert_type_priority[] = {
183 GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0
185 struct wgnutls_transport_context *ctx;
186 gnutls_session session;
188 gnutls_init (&session, GNUTLS_CLIENT);
189 gnutls_set_default_priority (session);
190 gnutls_certificate_type_set_priority (session, cert_type_priority);
191 gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
192 gnutls_transport_set_ptr (session, (gnutls_transport_ptr) fd);
193 err = gnutls_handshake (session);
196 logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
197 gnutls_deinit (session);
200 ctx = xnew0 (struct wgnutls_transport_context);
201 ctx->session = session;
202 fd_register_transport (fd, &wgnutls_transport, ctx);
207 ssl_check_certificate (int fd, const char *host)
209 struct wgnutls_transport_context *ctx = fd_transport_context (fd);
214 /* If the user has specified --no-check-cert, we still want to warn
215 him about problems with the server's certificate. */
216 const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
219 err = gnutls_certificate_verify_peers2 (ctx->session, &status);
222 logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
223 severity, quotearg_style (escape_quoting_style, host));
228 if (status & GNUTLS_CERT_INVALID)
230 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
231 severity, quote (host));
234 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
236 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
237 severity, quote (host));
240 if (status & GNUTLS_CERT_REVOKED)
242 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
243 severity, quote (host));
247 if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
249 time_t now = time (NULL);
250 gnutls_x509_crt cert;
251 const gnutls_datum *cert_list;
252 unsigned int cert_list_size;
254 if ((err = gnutls_x509_crt_init (&cert)) < 0)
256 logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
257 gnutls_strerror (err));
262 cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
265 logprintf (LOG_NOTQUIET, _("No certificate found\n"));
269 err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
272 logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
273 gnutls_strerror (err));
277 if (now < gnutls_x509_crt_get_activation_time (cert))
279 logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
282 if (now >= gnutls_x509_crt_get_expiration_time (cert))
284 logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
287 if (!gnutls_x509_crt_check_hostname (cert, host))
289 logprintf (LOG_NOTQUIET,
290 _("The certificate's owner does not match hostname %s\n"),
294 gnutls_x509_crt_deinit (cert);
298 return opt.check_cert ? success : true;