]> sjero.net Git - wget/blob - src/gnutls.c
[svn] Check for the validity of the presented X509 cert.
[wget] / src / gnutls.c
1 /* SSL support via GnuTLS library.
2    Copyright (C) 2005 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 2 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, write to the Free Software Foundation, Inc.,
18 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 In addition, as a special exception, the Free Software Foundation
21 gives permission to link the code of its release of Wget with the
22 OpenSSL project's "OpenSSL" library (or with modified versions of it
23 that use the same license as the "OpenSSL" library), and distribute
24 the linked executables.  You must obey the GNU General Public License
25 in all respects for all of the code used other than "OpenSSL".  If you
26 modify this file, you may extend this exception to your version of the
27 file, but you are not obligated to do so.  If you do not wish to do
28 so, delete this exception statement from your version.  */
29
30 #include <config.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 "wget.h"
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 #ifndef WINDOWS
169   close (fd);
170 #else
171   closesocket (fd);
172 #endif
173 }
174
175 /* gnutls_transport is the singleton that describes the SSL transport
176    methods provided by this file.  */
177
178 static struct transport_implementation wgnutls_transport = {
179   wgnutls_read, wgnutls_write, wgnutls_poll,
180   wgnutls_peek, wgnutls_errstr, wgnutls_close
181 };
182
183 bool
184 ssl_connect (int fd) 
185 {
186   static const int cert_type_priority[] = {
187     GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0
188   };
189   struct wgnutls_transport_context *ctx;
190   gnutls_session session;
191   int err;
192   gnutls_init (&session, GNUTLS_CLIENT);
193   gnutls_set_default_priority (session);
194   gnutls_certificate_type_set_priority (session, cert_type_priority);
195   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
196   gnutls_transport_set_ptr (session, (gnutls_transport_ptr) fd);
197   err = gnutls_handshake (session);
198   if (err < 0)
199     {
200       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
201       gnutls_deinit (session);
202       return false;
203     }
204   ctx = xnew0 (struct wgnutls_transport_context);
205   ctx->session = session;
206   fd_register_transport (fd, &wgnutls_transport, ctx);
207   return true;
208 }
209
210 bool
211 ssl_check_certificate (int fd, const char *host)
212 {
213   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
214
215   unsigned int status;
216   int err;
217
218   /* If the user has specified --no-check-cert, we still want to warn
219      him about problems with the server's certificate.  */
220   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
221   bool success = true;
222
223   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
224   if (err < 0)
225     {
226       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
227                  severity, escnonprint (host));
228       success = false;
229       goto out;
230     }
231
232   if (status & GNUTLS_CERT_INVALID)
233     {
234       logprintf (LOG_NOTQUIET, _("%s: The certificate of `%s' is not trusted.\n"),
235                  severity, escnonprint (host));
236       success = false;
237     }
238   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
239     {
240       logprintf (LOG_NOTQUIET, _("%s: The certificate of `%s' hasn't got a known issuer.\n"),
241                  severity, escnonprint (host));
242       success = false;
243     }
244   if (status & GNUTLS_CERT_REVOKED)
245     {
246       logprintf (LOG_NOTQUIET, _("%s: The certificate of `%s' has been revoked.\n"),
247                  severity, escnonprint (host));
248       success = false;
249     }
250
251   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
252     {
253       time_t now = time (NULL);
254       gnutls_x509_crt cert;
255       const gnutls_datum *cert_list;
256       unsigned int cert_list_size;
257
258       if ((err = gnutls_x509_crt_init (&cert)) < 0)
259         {
260           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
261                      gnutls_strerror (err));
262           success = false;
263           goto out;
264         }
265
266       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
267       if (!cert_list)
268         {
269           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
270           success = false;
271           goto out;
272         }
273       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
274       if (err < 0)
275         {
276           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
277                      gnutls_strerror (err));
278           success = false;
279           goto out;
280         }
281       if (now < gnutls_x509_crt_get_activation_time (cert))
282         {
283           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
284           success = false;
285         }
286       if (now >= gnutls_x509_crt_get_expiration_time (cert))
287         {
288           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
289           success = false;
290         }
291       if (!gnutls_x509_crt_check_hostname (cert, host))
292         {
293           logprintf (LOG_NOTQUIET,
294                      _("The certificate's owner does not match hostname '%s'\n"),
295                      host);
296           success = false;
297         }
298       gnutls_x509_crt_deinit (cert);
299    }
300
301  out:
302   return opt.check_cert ? success : true;
303 }