]> sjero.net Git - wget/blob - src/gnutls.c
Fix GNU TLS backend under Windows.
[wget] / src / gnutls.c
1 /* SSL support via GnuTLS library.
2    Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
3    Foundation, Inc.
4
5 This file is part of GNU Wget.
6
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.
11
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.
16
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/>.
19
20 Additional permission under GNU GPL version 3 section 7
21
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.  */
30
31 #include "wget.h"
32
33 #include <assert.h>
34 #include <errno.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #include <string.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41
42 #include <gnutls/gnutls.h>
43 #include <gnutls/x509.h>
44
45 #include "utils.h"
46 #include "connect.h"
47 #include "url.h"
48 #include "ssl.h"
49
50 #ifdef WIN32
51 # include "w32sock.h"
52 #endif
53
54 /* Note: some of the functions private to this file have names that
55    begin with "wgnutls_" (e.g. wgnutls_read) so that they wouldn't be
56    confused with actual gnutls functions -- such as the gnutls_read
57    preprocessor macro.  */
58
59 static gnutls_certificate_credentials credentials;
60
61 bool
62 ssl_init ()
63 {
64   gnutls_global_init ();
65   gnutls_certificate_allocate_credentials (&credentials);
66   if (opt.ca_cert)
67     gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert,
68                                             GNUTLS_X509_FMT_PEM);
69   return true;
70 }
71
72 struct wgnutls_transport_context {
73   gnutls_session session;       /* GnuTLS session handle */
74   int last_error;               /* last error returned by read/write/... */
75
76   /* Since GnuTLS doesn't support the equivalent to recv(...,
77      MSG_PEEK) or SSL_peek(), we have to do it ourselves.  Peeked data
78      is stored to PEEKBUF, and wgnutls_read checks that buffer before
79      actually reading.  */
80   char peekbuf[512];
81   int peeklen;
82 };
83
84 #ifndef MIN
85 # define MIN(i, j) ((i) <= (j) ? (i) : (j))
86 #endif
87
88 static int
89 wgnutls_read (int fd, char *buf, int bufsize, void *arg)
90 {
91   int ret = 0;
92   struct wgnutls_transport_context *ctx = arg;
93
94   if (ctx->peeklen)
95     {
96       /* If we have any peek data, simply return that. */
97       int copysize = MIN (bufsize, ctx->peeklen);
98       memcpy (buf, ctx->peekbuf, copysize);
99       ctx->peeklen -= copysize;
100       if (ctx->peeklen != 0)
101         memmove (ctx->peekbuf, ctx->peekbuf + copysize, ctx->peeklen);
102
103       return copysize;
104     }
105
106   do
107     ret = gnutls_record_recv (ctx->session, buf, bufsize);
108   while (ret == GNUTLS_E_INTERRUPTED);
109
110   if (ret < 0)
111     ctx->last_error = ret;
112   return ret;
113 }
114
115 static int
116 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
117 {
118   int ret;
119   struct wgnutls_transport_context *ctx = arg;
120   do
121     ret = gnutls_record_send (ctx->session, buf, bufsize);
122   while (ret == GNUTLS_E_INTERRUPTED);
123   if (ret < 0)
124     ctx->last_error = ret;
125   return ret;
126 }
127
128 static int
129 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
130 {
131   struct wgnutls_transport_context *ctx = arg;
132   return ctx->peeklen || gnutls_record_check_pending (ctx->session)
133     || select_fd (fd, timeout, wait_for);
134 }
135
136 static int
137 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
138 {
139   int ret = 0;
140   struct wgnutls_transport_context *ctx = arg;
141   int offset = ctx->peeklen;
142
143   if (bufsize > sizeof ctx->peekbuf)
144     bufsize = sizeof ctx->peekbuf;
145
146   if (offset)
147     memcpy (buf, ctx->peekbuf, offset);
148
149   do
150     {
151       if (gnutls_record_check_pending (ctx->session)
152           || select_fd (fd, 0, WAIT_FOR_READ))
153         ret = gnutls_record_recv (ctx->session, buf + offset, bufsize - offset);
154     }
155   while (ret == GNUTLS_E_INTERRUPTED);
156
157   if (ret > 0)
158     {
159       memcpy (ctx->peekbuf + offset, buf + offset, ret);
160       ctx->peeklen += ret;
161     }
162   return ctx->peeklen;
163 }
164
165 static const char *
166 wgnutls_errstr (int fd, void *arg)
167 {
168   struct wgnutls_transport_context *ctx = arg;
169   return gnutls_strerror (ctx->last_error);
170 }
171
172 static void
173 wgnutls_close (int fd, void *arg)
174 {
175   struct wgnutls_transport_context *ctx = arg;
176   /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
177   gnutls_deinit (ctx->session);
178   xfree (ctx);
179   close (fd);
180 }
181
182 /* gnutls_transport is the singleton that describes the SSL transport
183    methods provided by this file.  */
184
185 static struct transport_implementation wgnutls_transport = {
186   wgnutls_read, wgnutls_write, wgnutls_poll,
187   wgnutls_peek, wgnutls_errstr, wgnutls_close
188 };
189
190 bool
191 ssl_connect_wget (int fd)
192 {
193   static const int cert_type_priority[] = {
194     GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0
195   };
196   struct wgnutls_transport_context *ctx;
197   gnutls_session session;
198   int err;
199   gnutls_init (&session, GNUTLS_CLIENT);
200   gnutls_set_default_priority (session);
201   gnutls_certificate_type_set_priority (session, cert_type_priority);
202   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
203 #ifndef FD_TO_SOCKET
204 # define FD_TO_SOCKET(X) (X)
205 #endif
206   gnutls_transport_set_ptr (session, (gnutls_transport_ptr) FD_TO_SOCKET (fd));
207   err = gnutls_handshake (session);
208   if (err < 0)
209     {
210       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
211       gnutls_deinit (session);
212       return false;
213     }
214   ctx = xnew0 (struct wgnutls_transport_context);
215   ctx->session = session;
216   fd_register_transport (fd, &wgnutls_transport, ctx);
217   return true;
218 }
219
220 bool
221 ssl_check_certificate (int fd, const char *host)
222 {
223   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
224
225   unsigned int status;
226   int err;
227
228   /* If the user has specified --no-check-cert, we still want to warn
229      him about problems with the server's certificate.  */
230   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
231   bool success = true;
232
233   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
234   if (err < 0)
235     {
236       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
237                  severity, quotearg_style (escape_quoting_style, host));
238       success = false;
239       goto out;
240     }
241
242   if (status & GNUTLS_CERT_INVALID)
243     {
244       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
245                  severity, quote (host));
246       success = false;
247     }
248   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
249     {
250       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
251                  severity, quote (host));
252       success = false;
253     }
254   if (status & GNUTLS_CERT_REVOKED)
255     {
256       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
257                  severity, quote (host));
258       success = false;
259     }
260
261   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
262     {
263       time_t now = time (NULL);
264       gnutls_x509_crt cert;
265       const gnutls_datum *cert_list;
266       unsigned int cert_list_size;
267
268       if ((err = gnutls_x509_crt_init (&cert)) < 0)
269         {
270           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
271                      gnutls_strerror (err));
272           success = false;
273           goto out;
274         }
275
276       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
277       if (!cert_list)
278         {
279           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
280           success = false;
281           goto out;
282         }
283       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
284       if (err < 0)
285         {
286           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
287                      gnutls_strerror (err));
288           success = false;
289           goto out;
290         }
291       if (now < gnutls_x509_crt_get_activation_time (cert))
292         {
293           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
294           success = false;
295         }
296       if (now >= gnutls_x509_crt_get_expiration_time (cert))
297         {
298           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
299           success = false;
300         }
301       if (!gnutls_x509_crt_check_hostname (cert, host))
302         {
303           logprintf (LOG_NOTQUIET,
304                      _("The certificate's owner does not match hostname %s\n"),
305                      quote (host));
306           success = false;
307         }
308       gnutls_x509_crt_deinit (cert);
309    }
310
311  out:
312   return opt.check_cert ? success : true;
313 }