]> sjero.net Git - wget/blob - src/gnutls.c
3c4c5b4cf6f8383e9be5a13fa30b988343c4217c
[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   int allowed_protocols[4] = {0, 0, 0, 0};
200   gnutls_init (&session, GNUTLS_CLIENT);
201   gnutls_set_default_priority (session);
202   gnutls_certificate_type_set_priority (session, cert_type_priority);
203   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
204 #ifndef FD_TO_SOCKET
205 # define FD_TO_SOCKET(X) (X)
206 #endif
207   gnutls_transport_set_ptr (session, (gnutls_transport_ptr) FD_TO_SOCKET (fd));
208
209   err = 0;
210   switch (opt.secure_protocol)
211     {
212     case secure_protocol_auto:
213       break;
214     case secure_protocol_sslv2:
215     case secure_protocol_sslv3:
216       allowed_protocols[0] = GNUTLS_SSL3;
217       err = gnutls_protocol_set_priority (session, allowed_protocols);
218       break;
219     case secure_protocol_tlsv1:
220       allowed_protocols[0] = GNUTLS_TLS1_0;
221       allowed_protocols[1] = GNUTLS_TLS1_1;
222       allowed_protocols[2] = GNUTLS_TLS1_2;
223       err = gnutls_protocol_set_priority (session, allowed_protocols);
224       break;
225     default:
226       abort ();
227     }
228   if (err < 0)
229     {
230       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
231       gnutls_deinit (session);
232       return false;
233     }
234
235   err = gnutls_handshake (session);
236   if (err < 0)
237     {
238       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
239       gnutls_deinit (session);
240       return false;
241     }
242
243   ctx = xnew0 (struct wgnutls_transport_context);
244   ctx->session = session;
245   fd_register_transport (fd, &wgnutls_transport, ctx);
246   return true;
247 }
248
249 bool
250 ssl_check_certificate (int fd, const char *host)
251 {
252   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
253
254   unsigned int status;
255   int err;
256
257   /* If the user has specified --no-check-cert, we still want to warn
258      him about problems with the server's certificate.  */
259   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
260   bool success = true;
261
262   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
263   if (err < 0)
264     {
265       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
266                  severity, quotearg_style (escape_quoting_style, host));
267       success = false;
268       goto out;
269     }
270
271   if (status & GNUTLS_CERT_INVALID)
272     {
273       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
274                  severity, quote (host));
275       success = false;
276     }
277   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
278     {
279       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
280                  severity, quote (host));
281       success = false;
282     }
283   if (status & GNUTLS_CERT_REVOKED)
284     {
285       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
286                  severity, quote (host));
287       success = false;
288     }
289
290   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
291     {
292       time_t now = time (NULL);
293       gnutls_x509_crt cert;
294       const gnutls_datum *cert_list;
295       unsigned int cert_list_size;
296
297       if ((err = gnutls_x509_crt_init (&cert)) < 0)
298         {
299           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
300                      gnutls_strerror (err));
301           success = false;
302           goto out;
303         }
304
305       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
306       if (!cert_list)
307         {
308           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
309           success = false;
310           goto out;
311         }
312       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
313       if (err < 0)
314         {
315           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
316                      gnutls_strerror (err));
317           success = false;
318           goto out;
319         }
320       if (now < gnutls_x509_crt_get_activation_time (cert))
321         {
322           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
323           success = false;
324         }
325       if (now >= gnutls_x509_crt_get_expiration_time (cert))
326         {
327           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
328           success = false;
329         }
330       if (!gnutls_x509_crt_check_hostname (cert, host))
331         {
332           logprintf (LOG_NOTQUIET,
333                      _("The certificate's owner does not match hostname %s\n"),
334                      quote (host));
335           success = false;
336         }
337       gnutls_x509_crt_deinit (cert);
338    }
339
340  out:
341   return opt.check_cert ? success : true;
342 }