1 /* SSL support via GnuTLS library.
2 Copyright (C) 2005, 2006, 2007, 2008 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 Additional permission under GNU GPL version 3 section 7
21 If you modify this program, or any covered work, by linking or
22 combining it with the OpenSSL project's OpenSSL library (or a
23 modified version of that library), containing parts covered by the
24 terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
25 grants you additional permission to convey the resulting work.
26 Corresponding Source for a non-source form of such a combination
27 shall include the source code for the parts of OpenSSL used as well
28 as that of the covered work. */
40 #include <gnutls/gnutls.h>
41 #include <gnutls/x509.h>
48 /* Note: some of the functions private to this file have names that
49 begin with "wgnutls_" (e.g. wgnutls_read) so that they wouldn't be
50 confused with actual gnutls functions -- such as the gnutls_read
51 preprocessor macro. */
53 static gnutls_certificate_credentials credentials;
58 gnutls_global_init ();
59 gnutls_certificate_allocate_credentials (&credentials);
61 gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert,
66 struct wgnutls_transport_context {
67 gnutls_session session; /* GnuTLS session handle */
68 int last_error; /* last error returned by read/write/... */
70 /* Since GnuTLS doesn't support the equivalent to recv(...,
71 MSG_PEEK) or SSL_peek(), we have to do it ourselves. Peeked data
72 is stored to PEEKBUF, and wgnutls_read checks that buffer before
75 int peekstart, peeklen;
79 # define MIN(i, j) ((i) <= (j) ? (i) : (j))
83 wgnutls_read (int fd, char *buf, int bufsize, void *arg)
86 struct wgnutls_transport_context *ctx = arg;
90 /* If we have any peek data, simply return that. */
91 int copysize = MIN (bufsize, ctx->peeklen);
92 memcpy (buf, ctx->peekbuf + ctx->peekstart, copysize);
93 ctx->peeklen -= copysize;
94 if (ctx->peeklen != 0)
95 ctx->peekstart += copysize;
102 ret = gnutls_record_recv (ctx->session, buf, bufsize);
103 while (ret == GNUTLS_E_INTERRUPTED);
106 ctx->last_error = ret;
111 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
114 struct wgnutls_transport_context *ctx = arg;
116 ret = gnutls_record_send (ctx->session, buf, bufsize);
117 while (ret == GNUTLS_E_INTERRUPTED);
119 ctx->last_error = ret;
124 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
130 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
133 struct wgnutls_transport_context *ctx = arg;
135 /* We don't support peeks following peeks: the reader must drain all
136 peeked data before the next peek. */
137 assert (ctx->peeklen == 0);
138 if (bufsize > sizeof ctx->peekbuf)
139 bufsize = sizeof ctx->peekbuf;
142 ret = gnutls_record_recv (ctx->session, buf, bufsize);
143 while (ret == GNUTLS_E_INTERRUPTED);
147 memcpy (ctx->peekbuf, buf, ret);
154 wgnutls_errstr (int fd, void *arg)
156 struct wgnutls_transport_context *ctx = arg;
157 return gnutls_strerror (ctx->last_error);
161 wgnutls_close (int fd, void *arg)
163 struct wgnutls_transport_context *ctx = arg;
164 /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
165 gnutls_deinit (ctx->session);
174 /* gnutls_transport is the singleton that describes the SSL transport
175 methods provided by this file. */
177 static struct transport_implementation wgnutls_transport = {
178 wgnutls_read, wgnutls_write, wgnutls_poll,
179 wgnutls_peek, wgnutls_errstr, wgnutls_close
185 static const int cert_type_priority[] = {
186 GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0
188 struct wgnutls_transport_context *ctx;
189 gnutls_session session;
191 gnutls_init (&session, GNUTLS_CLIENT);
192 gnutls_set_default_priority (session);
193 gnutls_certificate_type_set_priority (session, cert_type_priority);
194 gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
195 gnutls_transport_set_ptr (session, (gnutls_transport_ptr) fd);
196 err = gnutls_handshake (session);
199 logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
200 gnutls_deinit (session);
203 ctx = xnew0 (struct wgnutls_transport_context);
204 ctx->session = session;
205 fd_register_transport (fd, &wgnutls_transport, ctx);
210 ssl_check_certificate (int fd, const char *host)
212 struct wgnutls_transport_context *ctx = fd_transport_context (fd);
217 /* If the user has specified --no-check-cert, we still want to warn
218 him about problems with the server's certificate. */
219 const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
222 err = gnutls_certificate_verify_peers2 (ctx->session, &status);
225 logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
226 severity, quotearg_style (escape_quoting_style, host));
231 if (status & GNUTLS_CERT_INVALID)
233 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
234 severity, quote (host));
237 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
239 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
240 severity, quote (host));
243 if (status & GNUTLS_CERT_REVOKED)
245 logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
246 severity, quote (host));
250 if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
252 time_t now = time (NULL);
253 gnutls_x509_crt cert;
254 const gnutls_datum *cert_list;
255 unsigned int cert_list_size;
257 if ((err = gnutls_x509_crt_init (&cert)) < 0)
259 logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
260 gnutls_strerror (err));
265 cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
268 logprintf (LOG_NOTQUIET, _("No certificate found\n"));
272 err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
275 logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
276 gnutls_strerror (err));
280 if (now < gnutls_x509_crt_get_activation_time (cert))
282 logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
285 if (now >= gnutls_x509_crt_get_expiration_time (cert))
287 logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
290 if (!gnutls_x509_crt_check_hostname (cert, host))
292 logprintf (LOG_NOTQUIET,
293 _("The certificate's owner does not match hostname %s\n"),
297 gnutls_x509_crt_deinit (cert);
301 return opt.check_cert ? success : true;