]> sjero.net Git - wget/blob - src/gnutls.c
gnutls: use the blocking socket.
[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 ret = 0, 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   static const int cert_type_priority[] = {
244     GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0
245   };
246   struct wgnutls_transport_context *ctx;
247   gnutls_session session;
248   int err;
249   int allowed_protocols[4] = {0, 0, 0, 0};
250   gnutls_init (&session, GNUTLS_CLIENT);
251   gnutls_set_default_priority (session);
252   gnutls_certificate_type_set_priority (session, cert_type_priority);
253   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
254 #ifndef FD_TO_SOCKET
255 # define FD_TO_SOCKET(X) (X)
256 #endif
257   gnutls_transport_set_ptr (session, (gnutls_transport_ptr) FD_TO_SOCKET (fd));
258
259   err = 0;
260   switch (opt.secure_protocol)
261     {
262     case secure_protocol_auto:
263       break;
264     case secure_protocol_sslv2:
265     case secure_protocol_sslv3:
266       allowed_protocols[0] = GNUTLS_SSL3;
267       err = gnutls_protocol_set_priority (session, allowed_protocols);
268       break;
269     case secure_protocol_tlsv1:
270       allowed_protocols[0] = GNUTLS_TLS1_0;
271       allowed_protocols[1] = GNUTLS_TLS1_1;
272       allowed_protocols[2] = GNUTLS_TLS1_2;
273       err = gnutls_protocol_set_priority (session, allowed_protocols);
274       break;
275     default:
276       abort ();
277     }
278   if (err < 0)
279     {
280       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
281       gnutls_deinit (session);
282       return false;
283     }
284
285   err = gnutls_handshake (session);
286   if (err < 0)
287     {
288       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
289       gnutls_deinit (session);
290       return false;
291     }
292
293   ctx = xnew0 (struct wgnutls_transport_context);
294   ctx->session = session;
295   fd_register_transport (fd, &wgnutls_transport, ctx);
296   return true;
297 }
298
299 bool
300 ssl_check_certificate (int fd, const char *host)
301 {
302   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
303
304   unsigned int status;
305   int err;
306
307   /* If the user has specified --no-check-cert, we still want to warn
308      him about problems with the server's certificate.  */
309   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
310   bool success = true;
311
312   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
313   if (err < 0)
314     {
315       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
316                  severity, quotearg_style (escape_quoting_style, host));
317       success = false;
318       goto out;
319     }
320
321   if (status & GNUTLS_CERT_INVALID)
322     {
323       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
324                  severity, quote (host));
325       success = false;
326     }
327   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
328     {
329       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
330                  severity, quote (host));
331       success = false;
332     }
333   if (status & GNUTLS_CERT_REVOKED)
334     {
335       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
336                  severity, quote (host));
337       success = false;
338     }
339
340   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
341     {
342       time_t now = time (NULL);
343       gnutls_x509_crt cert;
344       const gnutls_datum *cert_list;
345       unsigned int cert_list_size;
346
347       if ((err = gnutls_x509_crt_init (&cert)) < 0)
348         {
349           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
350                      gnutls_strerror (err));
351           success = false;
352           goto out;
353         }
354
355       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
356       if (!cert_list)
357         {
358           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
359           success = false;
360           goto out;
361         }
362       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
363       if (err < 0)
364         {
365           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
366                      gnutls_strerror (err));
367           success = false;
368           goto out;
369         }
370       if (now < gnutls_x509_crt_get_activation_time (cert))
371         {
372           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
373           success = false;
374         }
375       if (now >= gnutls_x509_crt_get_expiration_time (cert))
376         {
377           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
378           success = false;
379         }
380       if (!gnutls_x509_crt_check_hostname (cert, host))
381         {
382           logprintf (LOG_NOTQUIET,
383                      _("The certificate's owner does not match hostname %s\n"),
384                      quote (host));
385           success = false;
386         }
387       gnutls_x509_crt_deinit (cert);
388    }
389
390  out:
391   return opt.check_cert ? success : true;
392 }