]> sjero.net Git - wget/blob - src/gnutls.c
Enable client certificates when GNU TLS is used.
[wget] / src / gnutls.c
1 /* SSL support via GnuTLS library.
2    Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 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 <sys/ioctl.h>
44
45 #include "utils.h"
46 #include "connect.h"
47 #include "url.h"
48 #include "ptimer.h"
49 #include "ssl.h"
50
51 #include <sys/fcntl.h>
52
53 #ifdef WIN32
54 # include "w32sock.h"
55 #endif
56
57 static int
58 key_type_to_gnutls_type (enum keyfile_type type)
59 {
60   switch (type)
61     {
62     case keyfile_pem:
63       return GNUTLS_X509_FMT_PEM;
64     case keyfile_asn1:
65       return GNUTLS_X509_FMT_DER;
66     default:
67       abort ();
68     }
69 }
70
71 /* Note: some of the functions private to this file have names that
72    begin with "wgnutls_" (e.g. wgnutls_read) so that they wouldn't be
73    confused with actual gnutls functions -- such as the gnutls_read
74    preprocessor macro.  */
75
76 static gnutls_certificate_credentials credentials;
77 bool
78 ssl_init ()
79 {
80   /* Becomes true if GnuTLS is initialized. */
81   static bool ssl_initialized = false;
82
83   /* GnuTLS should be initialized only once. */
84   if (ssl_initialized)
85     return true;
86
87   const char *ca_directory;
88   DIR *dir;
89
90   gnutls_global_init ();
91   gnutls_certificate_allocate_credentials (&credentials);
92   gnutls_certificate_set_verify_flags(credentials,
93                                       GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
94
95   ca_directory = opt.ca_directory ? opt.ca_directory : "/etc/ssl/certs";
96
97   dir = opendir (ca_directory);
98   if (dir == NULL)
99     {
100       if (opt.ca_directory)
101         logprintf (LOG_NOTQUIET, _("ERROR: Cannot open directory %s.\n"),
102                    opt.ca_directory);
103     }
104   else
105     {
106       struct dirent *dent;
107       while ((dent = readdir (dir)) != NULL)
108         {
109           struct stat st;
110           char *ca_file;
111           asprintf (&ca_file, "%s/%s", ca_directory, dent->d_name);
112
113           stat (ca_file, &st);
114
115           if (S_ISREG (st.st_mode))
116             gnutls_certificate_set_x509_trust_file (credentials, ca_file,
117                                                     GNUTLS_X509_FMT_PEM);
118
119           free (ca_file);
120         }
121
122       closedir (dir);
123     }
124
125   /* Use the private key from the cert file unless otherwise specified. */
126   if (opt.cert_file && !opt.private_key)
127     {
128       opt.private_key = opt.cert_file;
129       opt.private_key_type = opt.cert_type;
130     }
131   /* Use the cert from the private key file unless otherwise specified. */
132   if (!opt.cert_file && opt.private_key)
133     {
134       opt.cert_file = opt.private_key;
135       opt.cert_type = opt.private_key_type;
136     }
137
138   if (opt.cert_file && opt.private_key)
139     {
140       int type;
141       if (opt.private_key_type != opt.cert_type)
142         {
143           /* GnuTLS can't handle this */
144           logprintf (LOG_NOTQUIET, _("ERROR: GnuTLS requires the key and the \
145 cert to be of the same type.\n"));
146         }
147
148       type = key_type_to_gnutls_type (opt.private_key_type);
149
150       gnutls_certificate_set_x509_key_file (credentials, opt.cert_file,
151                                             opt.private_key,
152                                             type);
153     }
154
155   if (opt.ca_cert)
156     gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert,
157                                             GNUTLS_X509_FMT_PEM);
158
159   ssl_initialized = true;
160
161   return true;
162 }
163
164 struct wgnutls_transport_context
165 {
166   gnutls_session session;       /* GnuTLS session handle */
167   int last_error;               /* last error returned by read/write/... */
168
169   /* Since GnuTLS doesn't support the equivalent to recv(...,
170      MSG_PEEK) or SSL_peek(), we have to do it ourselves.  Peeked data
171      is stored to PEEKBUF, and wgnutls_read checks that buffer before
172      actually reading.  */
173   char peekbuf[512];
174   int peeklen;
175 };
176
177 #ifndef MIN
178 # define MIN(i, j) ((i) <= (j) ? (i) : (j))
179 #endif
180
181
182 static int
183 wgnutls_read_timeout (int fd, char *buf, int bufsize, void *arg, double timeout)
184 {
185 #ifdef F_GETFL
186   int flags = 0;
187 #endif
188   int ret = 0;
189   struct ptimer *timer;
190   struct wgnutls_transport_context *ctx = arg;
191   int timed_out = 0;
192
193   if (timeout)
194     {
195 #ifdef F_GETFL
196       flags = fcntl (fd, F_GETFL, 0);
197       if (flags < 0)
198         return flags;
199 #endif
200       timer = ptimer_new ();
201       if (timer == 0)
202         return -1;
203     }
204
205   do
206     {
207       double next_timeout;
208       if (timeout > 0.0)
209         {
210           next_timeout = timeout - ptimer_measure (timer);
211           if (next_timeout < 0.0)
212             break;
213         }
214
215       ret = GNUTLS_E_AGAIN;
216       if (timeout == 0 || gnutls_record_check_pending (ctx->session)
217           || select_fd (fd, next_timeout, WAIT_FOR_READ))
218         {
219           if (timeout)
220             {
221 #ifdef F_GETFL
222               if (fcntl (fd, F_SETFL, flags | O_NONBLOCK))
223                 break;
224 #else
225               /* XXX: Assume it was blocking before.  */
226               const int one = 1;
227               if (ioctl (fd, FIONBIO, &one) < 0)
228                 break;
229 #endif
230             }
231
232           ret = gnutls_record_recv (ctx->session, buf, bufsize);
233
234           if (timeout)
235             {
236 #ifdef F_GETFL
237               if (fcntl (fd, F_SETFL, flags) < 0)
238                 break;
239 #else
240               const int zero = 0;
241               if (ioctl (fd, FIONBIO, &zero) < 0)
242                 break;
243 #endif
244             }
245         }
246
247       timed_out = timeout && ptimer_measure (timer) >= timeout;
248     }
249   while (ret == GNUTLS_E_INTERRUPTED || (ret == GNUTLS_E_AGAIN && !timed_out));
250
251   if (timeout)
252     ptimer_destroy (timer);
253
254   if (timeout && timed_out && ret == GNUTLS_E_AGAIN)
255     errno = ETIMEDOUT;
256
257   return ret;
258 }
259
260 static int
261 wgnutls_read (int fd, char *buf, int bufsize, void *arg)
262 {
263   int ret = 0;
264   struct wgnutls_transport_context *ctx = arg;
265
266   if (ctx->peeklen)
267     {
268       /* If we have any peek data, simply return that. */
269       int copysize = MIN (bufsize, ctx->peeklen);
270       memcpy (buf, ctx->peekbuf, copysize);
271       ctx->peeklen -= copysize;
272       if (ctx->peeklen != 0)
273         memmove (ctx->peekbuf, ctx->peekbuf + copysize, ctx->peeklen);
274
275       return copysize;
276     }
277
278   ret = wgnutls_read_timeout (fd, buf, bufsize, arg, opt.read_timeout);
279   if (ret < 0)
280     ctx->last_error = ret;
281
282   return ret;
283 }
284
285 static int
286 wgnutls_write (int fd, char *buf, int bufsize, void *arg)
287 {
288   int ret;
289   struct wgnutls_transport_context *ctx = arg;
290   do
291     ret = gnutls_record_send (ctx->session, buf, bufsize);
292   while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
293   if (ret < 0)
294     ctx->last_error = ret;
295   return ret;
296 }
297
298 static int
299 wgnutls_poll (int fd, double timeout, int wait_for, void *arg)
300 {
301   struct wgnutls_transport_context *ctx = arg;
302   return ctx->peeklen || gnutls_record_check_pending (ctx->session)
303     || select_fd (fd, timeout, wait_for);
304 }
305
306 static int
307 wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
308 {
309   int read = 0;
310   struct wgnutls_transport_context *ctx = arg;
311   int offset = MIN (bufsize, ctx->peeklen);
312   if (bufsize > sizeof ctx->peekbuf)
313     bufsize = sizeof ctx->peekbuf;
314
315   if (ctx->peeklen)
316     memcpy (buf, ctx->peekbuf, offset);
317
318   if (bufsize > offset)
319     {
320       if (gnutls_record_check_pending (ctx->session) <= 0
321           && select_fd (fd, 0.0, WAIT_FOR_READ) <= 0)
322         read = 0;
323       else
324         read = wgnutls_read_timeout (fd, buf + offset, bufsize - offset,
325                                      ctx, opt.read_timeout);
326       if (read < 0)
327         {
328           if (offset)
329             read = 0;
330           else
331             return read;
332         }
333
334       if (read > 0)
335         {
336           memcpy (ctx->peekbuf + offset, buf + offset,
337                   read);
338           ctx->peeklen += read;
339         }
340     }
341
342   return offset + read;
343 }
344
345 static const char *
346 wgnutls_errstr (int fd, void *arg)
347 {
348   struct wgnutls_transport_context *ctx = arg;
349   return gnutls_strerror (ctx->last_error);
350 }
351
352 static void
353 wgnutls_close (int fd, void *arg)
354 {
355   struct wgnutls_transport_context *ctx = arg;
356   /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
357   gnutls_deinit (ctx->session);
358   xfree (ctx);
359   close (fd);
360 }
361
362 /* gnutls_transport is the singleton that describes the SSL transport
363    methods provided by this file.  */
364
365 static struct transport_implementation wgnutls_transport =
366 {
367   wgnutls_read, wgnutls_write, wgnutls_poll,
368   wgnutls_peek, wgnutls_errstr, wgnutls_close
369 };
370
371 bool
372 ssl_connect_wget (int fd)
373 {
374   struct wgnutls_transport_context *ctx;
375   gnutls_session session;
376   int err;
377   gnutls_init (&session, GNUTLS_CLIENT);
378   gnutls_set_default_priority (session);
379   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
380 #ifndef FD_TO_SOCKET
381 # define FD_TO_SOCKET(X) (X)
382 #endif
383   gnutls_transport_set_ptr (session, (gnutls_transport_ptr) FD_TO_SOCKET (fd));
384
385   err = 0;
386 #if HAVE_GNUTLS_PRIORITY_SET_DIRECT
387   switch (opt.secure_protocol)
388     {
389     case secure_protocol_auto:
390       break;
391     case secure_protocol_sslv2:
392     case secure_protocol_sslv3:
393       err = gnutls_priority_set_direct (session, "NORMAL:-VERS-TLS-ALL", NULL);
394       break;
395     case secure_protocol_tlsv1:
396       err = gnutls_priority_set_direct (session, "NORMAL:-VERS-SSL3.0", NULL);
397       break;
398     default:
399       abort ();
400     }
401 #else
402   int allowed_protocols[4] = {0, 0, 0, 0};
403   switch (opt.secure_protocol)
404     {
405     case secure_protocol_auto:
406       break;
407     case secure_protocol_sslv2:
408     case secure_protocol_sslv3:
409       allowed_protocols[0] = GNUTLS_SSL3;
410       err = gnutls_protocol_set_priority (session, allowed_protocols);
411       break;
412
413     case secure_protocol_tlsv1:
414       allowed_protocols[0] = GNUTLS_TLS1_0;
415       allowed_protocols[1] = GNUTLS_TLS1_1;
416       allowed_protocols[2] = GNUTLS_TLS1_2;
417       err = gnutls_protocol_set_priority (session, allowed_protocols);
418       break;
419
420     default:
421       abort ();
422     }
423 #endif
424
425   if (err < 0)
426     {
427       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
428       gnutls_deinit (session);
429       return false;
430     }
431
432   err = gnutls_handshake (session);
433   if (err < 0)
434     {
435       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
436       gnutls_deinit (session);
437       return false;
438     }
439
440   ctx = xnew0 (struct wgnutls_transport_context);
441   ctx->session = session;
442   fd_register_transport (fd, &wgnutls_transport, ctx);
443   return true;
444 }
445
446 bool
447 ssl_check_certificate (int fd, const char *host)
448 {
449   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
450
451   unsigned int status;
452   int err;
453
454   /* If the user has specified --no-check-cert, we still want to warn
455      him about problems with the server's certificate.  */
456   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
457   bool success = true;
458
459   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
460   if (err < 0)
461     {
462       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
463                  severity, quotearg_style (escape_quoting_style, host));
464       success = false;
465       goto out;
466     }
467
468   if (status & GNUTLS_CERT_INVALID)
469     {
470       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
471                  severity, quote (host));
472       success = false;
473     }
474   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
475     {
476       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
477                  severity, quote (host));
478       success = false;
479     }
480   if (status & GNUTLS_CERT_REVOKED)
481     {
482       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
483                  severity, quote (host));
484       success = false;
485     }
486
487   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
488     {
489       time_t now = time (NULL);
490       gnutls_x509_crt cert;
491       const gnutls_datum *cert_list;
492       unsigned int cert_list_size;
493
494       if ((err = gnutls_x509_crt_init (&cert)) < 0)
495         {
496           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
497                      gnutls_strerror (err));
498           success = false;
499           goto out;
500         }
501
502       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
503       if (!cert_list)
504         {
505           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
506           success = false;
507           goto out;
508         }
509       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
510       if (err < 0)
511         {
512           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
513                      gnutls_strerror (err));
514           success = false;
515           goto out;
516         }
517       if (now < gnutls_x509_crt_get_activation_time (cert))
518         {
519           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
520           success = false;
521         }
522       if (now >= gnutls_x509_crt_get_expiration_time (cert))
523         {
524           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
525           success = false;
526         }
527       if (!gnutls_x509_crt_check_hostname (cert, host))
528         {
529           logprintf (LOG_NOTQUIET,
530                      _("The certificate's owner does not match hostname %s\n"),
531                      quote (host));
532           success = false;
533         }
534       gnutls_x509_crt_deinit (cert);
535    }
536
537  out:
538   return opt.check_cert ? success : true;
539 }