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