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