]> sjero.net Git - wget/blob - src/gnutls.c
0bb49ca533cd794b56ccd4420d74e51f31b15b0c
[wget] / src / gnutls.c
1 /* SSL support via GnuTLS library.
2    Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
3    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
41 #include <gnutls/gnutls.h>
42 #include <gnutls/x509.h>
43
44 #include "utils.h"
45 #include "connect.h"
46 #include "url.h"
47 #include "ssl.h"
48
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.  */
53
54 static gnutls_certificate_credentials credentials;
55
56 bool
57 ssl_init ()
58 {
59   gnutls_global_init ();
60   gnutls_certificate_allocate_credentials (&credentials);
61   if (opt.ca_cert)
62     gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert,
63                                             GNUTLS_X509_FMT_PEM);
64   return true;
65 }
66
67 struct wgnutls_transport_context {
68   gnutls_session session;       /* GnuTLS session handle */
69   int last_error;               /* last error returned by read/write/... */
70
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
74      actually reading.  */
75   char peekbuf[512];
76   int peekstart, peeklen;
77 };
78
79 #ifndef MIN
80 # define MIN(i, j) ((i) <= (j) ? (i) : (j))
81 #endif
82
83 static int
84 wgnutls_read (int fd, char *buf, int bufsize, void *arg)
85 {
86   int ret;
87   struct wgnutls_transport_context *ctx = arg;
88
89   if (ctx->peeklen)
90     {
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;
97       else
98         ctx->peekstart = 0;
99       return copysize;
100     }
101
102   do
103     ret = gnutls_record_recv (ctx->session, buf, bufsize);
104   while (ret == GNUTLS_E_INTERRUPTED);
105
106   if (ret < 0)
107     ctx->last_error = ret;
108   return ret;
109 }
110
111 static int
112 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
113 {
114   int ret;
115   struct wgnutls_transport_context *ctx = arg;
116   do
117     ret = gnutls_record_send (ctx->session, buf, bufsize);
118   while (ret == GNUTLS_E_INTERRUPTED);
119   if (ret < 0)
120     ctx->last_error = ret;
121   return ret;
122 }
123
124 static int
125 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
126 {
127   return 1;
128 }
129
130 static int
131 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
132 {
133   int ret;
134   struct wgnutls_transport_context *ctx = arg;
135
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;
141
142   do
143     ret = gnutls_record_recv (ctx->session, buf, bufsize);
144   while (ret == GNUTLS_E_INTERRUPTED);
145
146   if (ret >= 0)
147     {
148       memcpy (ctx->peekbuf, buf, ret);
149       ctx->peeklen = ret;
150     }
151   return ret;
152 }
153
154 static const char *
155 wgnutls_errstr (int fd, void *arg)
156 {
157   struct wgnutls_transport_context *ctx = arg;
158   return gnutls_strerror (ctx->last_error);
159 }
160
161 static void
162 wgnutls_close (int fd, void *arg)
163 {
164   struct wgnutls_transport_context *ctx = arg;
165   /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
166   gnutls_deinit (ctx->session);
167   xfree (ctx);
168   close (fd);
169 }
170
171 /* gnutls_transport is the singleton that describes the SSL transport
172    methods provided by this file.  */
173
174 static struct transport_implementation wgnutls_transport = {
175   wgnutls_read, wgnutls_write, wgnutls_poll,
176   wgnutls_peek, wgnutls_errstr, wgnutls_close
177 };
178
179 bool
180 ssl_connect (int fd)
181 {
182   static const int cert_type_priority[] = {
183     GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0
184   };
185   struct wgnutls_transport_context *ctx;
186   gnutls_session session;
187   int err;
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);
194   if (err < 0)
195     {
196       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
197       gnutls_deinit (session);
198       return false;
199     }
200   ctx = xnew0 (struct wgnutls_transport_context);
201   ctx->session = session;
202   fd_register_transport (fd, &wgnutls_transport, ctx);
203   return true;
204 }
205
206 bool
207 ssl_check_certificate (int fd, const char *host)
208 {
209   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
210
211   unsigned int status;
212   int err;
213
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");
217   bool success = true;
218
219   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
220   if (err < 0)
221     {
222       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
223                  severity, quotearg_style (escape_quoting_style, host));
224       success = false;
225       goto out;
226     }
227
228   if (status & GNUTLS_CERT_INVALID)
229     {
230       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
231                  severity, quote (host));
232       success = false;
233     }
234   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
235     {
236       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
237                  severity, quote (host));
238       success = false;
239     }
240   if (status & GNUTLS_CERT_REVOKED)
241     {
242       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
243                  severity, quote (host));
244       success = false;
245     }
246
247   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
248     {
249       time_t now = time (NULL);
250       gnutls_x509_crt cert;
251       const gnutls_datum *cert_list;
252       unsigned int cert_list_size;
253
254       if ((err = gnutls_x509_crt_init (&cert)) < 0)
255         {
256           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
257                      gnutls_strerror (err));
258           success = false;
259           goto out;
260         }
261
262       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
263       if (!cert_list)
264         {
265           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
266           success = false;
267           goto out;
268         }
269       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
270       if (err < 0)
271         {
272           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
273                      gnutls_strerror (err));
274           success = false;
275           goto out;
276         }
277       if (now < gnutls_x509_crt_get_activation_time (cert))
278         {
279           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
280           success = false;
281         }
282       if (now >= gnutls_x509_crt_get_expiration_time (cert))
283         {
284           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
285           success = false;
286         }
287       if (!gnutls_x509_crt_check_hostname (cert, host))
288         {
289           logprintf (LOG_NOTQUIET,
290                      _("The certificate's owner does not match hostname %s\n"),
291                      quote (host));
292           success = false;
293         }
294       gnutls_x509_crt_deinit (cert);
295    }
296
297  out:
298   return opt.check_cert ? success : true;
299 }