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