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