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