]> sjero.net Git - wget/blob - src/gnutls.c
Allow X509 v1 certificates.
[wget] / src / gnutls.c
1 /* SSL support via GnuTLS library.
2    Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
3    Foundation, 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 #include <unistd.h>
36 #include <string.h>
37 #include <stdio.h>
38 #include <dirent.h>
39 #include <stdlib.h>
40
41 #include <gnutls/gnutls.h>
42 #include <gnutls/x509.h>
43 #include <fcntl.h>
44 #include <sys/ioctl.h>
45
46 #include "utils.h"
47 #include "connect.h"
48 #include "url.h"
49 #include "ssl.h"
50
51 #ifdef WIN32
52 # include "w32sock.h"
53 #endif
54
55 /* Note: some of the functions private to this file have names that
56    begin with "wgnutls_" (e.g. wgnutls_read) so that they wouldn't be
57    confused with actual gnutls functions -- such as the gnutls_read
58    preprocessor macro.  */
59
60 static gnutls_certificate_credentials credentials;
61
62 bool
63 ssl_init ()
64 {
65   const char *ca_directory;
66   DIR *dir;
67
68   gnutls_global_init ();
69   gnutls_certificate_allocate_credentials (&credentials);
70   gnutls_certificate_set_verify_flags(credentials,
71                                       GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
72
73   ca_directory = opt.ca_directory ? opt.ca_directory : "/etc/ssl/certs";
74
75   dir = opendir (ca_directory);
76   if (dir == NULL)
77     {
78       if (opt.ca_directory)
79         logprintf (LOG_NOTQUIET, _("ERROR: Cannot open directory %s.\n"),
80                    opt.ca_directory);
81     }
82   else
83     {
84       struct dirent *dent;
85       while ((dent = readdir (dir)) != NULL)
86         {
87           struct stat st;
88           char *ca_file;
89           asprintf (&ca_file, "%s/%s", ca_directory, dent->d_name);
90
91           stat (ca_file, &st);
92
93           if (S_ISREG (st.st_mode))
94             gnutls_certificate_set_x509_trust_file (credentials, ca_file,
95                                                     GNUTLS_X509_FMT_PEM);
96
97           free (ca_file);
98         }
99
100       closedir (dir);
101     }
102
103   if (opt.ca_cert)
104     gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert,
105                                             GNUTLS_X509_FMT_PEM);
106   return true;
107 }
108
109 struct wgnutls_transport_context
110 {
111   gnutls_session session;       /* GnuTLS session handle */
112   int last_error;               /* last error returned by read/write/... */
113
114   /* Since GnuTLS doesn't support the equivalent to recv(...,
115      MSG_PEEK) or SSL_peek(), we have to do it ourselves.  Peeked data
116      is stored to PEEKBUF, and wgnutls_read checks that buffer before
117      actually reading.  */
118   char peekbuf[512];
119   int peeklen;
120 };
121
122 #ifndef MIN
123 # define MIN(i, j) ((i) <= (j) ? (i) : (j))
124 #endif
125
126 static int
127 wgnutls_read (int fd, char *buf, int bufsize, void *arg)
128 {
129   int ret = 0;
130   struct wgnutls_transport_context *ctx = arg;
131
132   if (ctx->peeklen)
133     {
134       /* If we have any peek data, simply return that. */
135       int copysize = MIN (bufsize, ctx->peeklen);
136       memcpy (buf, ctx->peekbuf, copysize);
137       ctx->peeklen -= copysize;
138       if (ctx->peeklen != 0)
139         memmove (ctx->peekbuf, ctx->peekbuf + copysize, ctx->peeklen);
140
141       return copysize;
142     }
143
144   do
145     ret = gnutls_record_recv (ctx->session, buf, bufsize);
146   while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
147
148   if (ret < 0)
149     ctx->last_error = ret;
150
151   return ret;
152 }
153
154 static int
155 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
156 {
157   int ret;
158   struct wgnutls_transport_context *ctx = arg;
159   do
160     ret = gnutls_record_send (ctx->session, buf, bufsize);
161   while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
162   if (ret < 0)
163     ctx->last_error = ret;
164   return ret;
165 }
166
167 static int
168 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
169 {
170   struct wgnutls_transport_context *ctx = arg;
171   return ctx->peeklen || gnutls_record_check_pending (ctx->session)
172     || select_fd (fd, timeout, wait_for);
173 }
174
175 static int
176 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
177 {
178   int ret = 0, read = 0;
179   struct wgnutls_transport_context *ctx = arg;
180   int offset = MIN (bufsize, ctx->peeklen);
181   if (bufsize > sizeof ctx->peekbuf)
182     bufsize = sizeof ctx->peekbuf;
183
184   if (ctx->peeklen)
185     memcpy (buf, ctx->peekbuf, offset);
186
187   if (bufsize > offset)
188     {
189 #ifdef F_GETFL
190       int flags;
191       flags = fcntl (fd, F_GETFL, 0);
192       if (flags < 0)
193         return ret;
194
195       ret = fcntl (fd, F_SETFL, flags | O_NONBLOCK);
196       if (ret < 0)
197         return ret;
198 #else
199       /* XXX: Assume it was blocking before.  */
200       const int zero = 0;
201       ret = ioctl (fd, FIONBIO, &zero);
202       if (ret < 0)
203         return ret;
204 #endif
205       do
206         {
207           ret = gnutls_record_recv (ctx->session, buf + offset,
208                                      bufsize - offset);
209         }
210       while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
211
212       read = ret;
213
214       if (read < 0)
215         {
216           if (offset)
217             read = 0;
218           else
219             return read;
220         }
221
222       if (read > 0)
223         {
224           memcpy (ctx->peekbuf + offset, buf + offset,
225                   read);
226           ctx->peeklen += read;
227         }
228
229 #ifdef F_GETFL
230       ret = fcntl (fd, F_SETFL, flags);
231       if (ret < 0)
232         return ret;
233 #else
234       const int one = 1;
235       ret = ioctl (fd, FIONBIO, &one);
236       if (ret < 0)
237         return ret;
238 #endif
239     }
240
241   return offset + read;
242 }
243
244 static const char *
245 wgnutls_errstr (int fd, void *arg)
246 {
247   struct wgnutls_transport_context *ctx = arg;
248   return gnutls_strerror (ctx->last_error);
249 }
250
251 static void
252 wgnutls_close (int fd, void *arg)
253 {
254   struct wgnutls_transport_context *ctx = arg;
255   /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
256   gnutls_deinit (ctx->session);
257   xfree (ctx);
258   close (fd);
259 }
260
261 /* gnutls_transport is the singleton that describes the SSL transport
262    methods provided by this file.  */
263
264 static struct transport_implementation wgnutls_transport =
265 {
266   wgnutls_read, wgnutls_write, wgnutls_poll,
267   wgnutls_peek, wgnutls_errstr, wgnutls_close
268 };
269
270 bool
271 ssl_connect_wget (int fd)
272 {
273   static const int cert_type_priority[] = {
274     GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0
275   };
276   struct wgnutls_transport_context *ctx;
277   gnutls_session session;
278   int err;
279   int allowed_protocols[4] = {0, 0, 0, 0};
280   gnutls_init (&session, GNUTLS_CLIENT);
281   gnutls_set_default_priority (session);
282   gnutls_certificate_type_set_priority (session, cert_type_priority);
283   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
284 #ifndef FD_TO_SOCKET
285 # define FD_TO_SOCKET(X) (X)
286 #endif
287   gnutls_transport_set_ptr (session, (gnutls_transport_ptr) FD_TO_SOCKET (fd));
288
289   err = 0;
290   switch (opt.secure_protocol)
291     {
292     case secure_protocol_auto:
293       break;
294     case secure_protocol_sslv2:
295     case secure_protocol_sslv3:
296       allowed_protocols[0] = GNUTLS_SSL3;
297       err = gnutls_protocol_set_priority (session, allowed_protocols);
298       break;
299     case secure_protocol_tlsv1:
300       allowed_protocols[0] = GNUTLS_TLS1_0;
301       allowed_protocols[1] = GNUTLS_TLS1_1;
302       allowed_protocols[2] = GNUTLS_TLS1_2;
303       err = gnutls_protocol_set_priority (session, allowed_protocols);
304       break;
305     default:
306       abort ();
307     }
308   if (err < 0)
309     {
310       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
311       gnutls_deinit (session);
312       return false;
313     }
314
315   err = gnutls_handshake (session);
316   if (err < 0)
317     {
318       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
319       gnutls_deinit (session);
320       return false;
321     }
322
323   ctx = xnew0 (struct wgnutls_transport_context);
324   ctx->session = session;
325   fd_register_transport (fd, &wgnutls_transport, ctx);
326   return true;
327 }
328
329 bool
330 ssl_check_certificate (int fd, const char *host)
331 {
332   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
333
334   unsigned int status;
335   int err;
336
337   /* If the user has specified --no-check-cert, we still want to warn
338      him about problems with the server's certificate.  */
339   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
340   bool success = true;
341
342   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
343   if (err < 0)
344     {
345       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
346                  severity, quotearg_style (escape_quoting_style, host));
347       success = false;
348       goto out;
349     }
350
351   if (status & GNUTLS_CERT_INVALID)
352     {
353       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
354                  severity, quote (host));
355       success = false;
356     }
357   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
358     {
359       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
360                  severity, quote (host));
361       success = false;
362     }
363   if (status & GNUTLS_CERT_REVOKED)
364     {
365       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
366                  severity, quote (host));
367       success = false;
368     }
369
370   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
371     {
372       time_t now = time (NULL);
373       gnutls_x509_crt cert;
374       const gnutls_datum *cert_list;
375       unsigned int cert_list_size;
376
377       if ((err = gnutls_x509_crt_init (&cert)) < 0)
378         {
379           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
380                      gnutls_strerror (err));
381           success = false;
382           goto out;
383         }
384
385       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
386       if (!cert_list)
387         {
388           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
389           success = false;
390           goto out;
391         }
392       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
393       if (err < 0)
394         {
395           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
396                      gnutls_strerror (err));
397           success = false;
398           goto out;
399         }
400       if (now < gnutls_x509_crt_get_activation_time (cert))
401         {
402           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
403           success = false;
404         }
405       if (now >= gnutls_x509_crt_get_expiration_time (cert))
406         {
407           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
408           success = false;
409         }
410       if (!gnutls_x509_crt_check_hostname (cert, host))
411         {
412           logprintf (LOG_NOTQUIET,
413                      _("The certificate's owner does not match hostname %s\n"),
414                      quote (host));
415           success = false;
416         }
417       gnutls_x509_crt_deinit (cert);
418    }
419
420  out:
421   return opt.check_cert ? success : true;
422 }