]> sjero.net Git - wget/blob - src/gnutls.c
Use correctly FIONBIO values.
[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 one = 1;
201       ret = ioctl (fd, FIONBIO, &one);
202       if (ret < 0)
203         return ret;
204 #endif
205       read = gnutls_record_recv (ctx->session, buf + offset,
206                                  bufsize - offset);
207       if (read < 0)
208         {
209           if (offset)
210             read = 0;
211           else
212             return read;
213         }
214
215       if (read > 0)
216         {
217           memcpy (ctx->peekbuf + offset, buf + offset,
218                   read);
219           ctx->peeklen += read;
220         }
221
222 #ifdef F_GETFL
223       ret = fcntl (fd, F_SETFL, flags);
224       if (ret < 0)
225         return ret;
226 #else
227       const int zero = 0;
228       ret = ioctl (fd, FIONBIO, &zero);
229       if (ret < 0)
230         return ret;
231 #endif
232     }
233
234   return offset + read;
235 }
236
237 static const char *
238 wgnutls_errstr (int fd, void *arg)
239 {
240   struct wgnutls_transport_context *ctx = arg;
241   return gnutls_strerror (ctx->last_error);
242 }
243
244 static void
245 wgnutls_close (int fd, void *arg)
246 {
247   struct wgnutls_transport_context *ctx = arg;
248   /*gnutls_bye (ctx->session, GNUTLS_SHUT_RDWR);*/
249   gnutls_deinit (ctx->session);
250   xfree (ctx);
251   close (fd);
252 }
253
254 /* gnutls_transport is the singleton that describes the SSL transport
255    methods provided by this file.  */
256
257 static struct transport_implementation wgnutls_transport =
258 {
259   wgnutls_read, wgnutls_write, wgnutls_poll,
260   wgnutls_peek, wgnutls_errstr, wgnutls_close
261 };
262
263 bool
264 ssl_connect_wget (int fd)
265 {
266   static const int cert_type_priority[] = {
267     GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0
268   };
269   struct wgnutls_transport_context *ctx;
270   gnutls_session session;
271   int err;
272   int allowed_protocols[4] = {0, 0, 0, 0};
273   gnutls_init (&session, GNUTLS_CLIENT);
274   gnutls_set_default_priority (session);
275   gnutls_certificate_type_set_priority (session, cert_type_priority);
276   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
277 #ifndef FD_TO_SOCKET
278 # define FD_TO_SOCKET(X) (X)
279 #endif
280   gnutls_transport_set_ptr (session, (gnutls_transport_ptr) FD_TO_SOCKET (fd));
281
282   err = 0;
283   switch (opt.secure_protocol)
284     {
285     case secure_protocol_auto:
286       break;
287     case secure_protocol_sslv2:
288     case secure_protocol_sslv3:
289       allowed_protocols[0] = GNUTLS_SSL3;
290       err = gnutls_protocol_set_priority (session, allowed_protocols);
291       break;
292     case secure_protocol_tlsv1:
293       allowed_protocols[0] = GNUTLS_TLS1_0;
294       allowed_protocols[1] = GNUTLS_TLS1_1;
295       allowed_protocols[2] = GNUTLS_TLS1_2;
296       err = gnutls_protocol_set_priority (session, allowed_protocols);
297       break;
298     default:
299       abort ();
300     }
301   if (err < 0)
302     {
303       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
304       gnutls_deinit (session);
305       return false;
306     }
307
308   err = gnutls_handshake (session);
309   if (err < 0)
310     {
311       logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
312       gnutls_deinit (session);
313       return false;
314     }
315
316   ctx = xnew0 (struct wgnutls_transport_context);
317   ctx->session = session;
318   fd_register_transport (fd, &wgnutls_transport, ctx);
319   return true;
320 }
321
322 bool
323 ssl_check_certificate (int fd, const char *host)
324 {
325   struct wgnutls_transport_context *ctx = fd_transport_context (fd);
326
327   unsigned int status;
328   int err;
329
330   /* If the user has specified --no-check-cert, we still want to warn
331      him about problems with the server's certificate.  */
332   const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");
333   bool success = true;
334
335   err = gnutls_certificate_verify_peers2 (ctx->session, &status);
336   if (err < 0)
337     {
338       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
339                  severity, quotearg_style (escape_quoting_style, host));
340       success = false;
341       goto out;
342     }
343
344   if (status & GNUTLS_CERT_INVALID)
345     {
346       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
347                  severity, quote (host));
348       success = false;
349     }
350   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
351     {
352       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
353                  severity, quote (host));
354       success = false;
355     }
356   if (status & GNUTLS_CERT_REVOKED)
357     {
358       logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
359                  severity, quote (host));
360       success = false;
361     }
362
363   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
364     {
365       time_t now = time (NULL);
366       gnutls_x509_crt cert;
367       const gnutls_datum *cert_list;
368       unsigned int cert_list_size;
369
370       if ((err = gnutls_x509_crt_init (&cert)) < 0)
371         {
372           logprintf (LOG_NOTQUIET, _("Error initializing X509 certificate: %s\n"),
373                      gnutls_strerror (err));
374           success = false;
375           goto out;
376         }
377
378       cert_list = gnutls_certificate_get_peers (ctx->session, &cert_list_size);
379       if (!cert_list)
380         {
381           logprintf (LOG_NOTQUIET, _("No certificate found\n"));
382           success = false;
383           goto out;
384         }
385       err = gnutls_x509_crt_import (cert, cert_list, GNUTLS_X509_FMT_DER);
386       if (err < 0)
387         {
388           logprintf (LOG_NOTQUIET, _("Error parsing certificate: %s\n"),
389                      gnutls_strerror (err));
390           success = false;
391           goto out;
392         }
393       if (now < gnutls_x509_crt_get_activation_time (cert))
394         {
395           logprintf (LOG_NOTQUIET, _("The certificate has not yet been activated\n"));
396           success = false;
397         }
398       if (now >= gnutls_x509_crt_get_expiration_time (cert))
399         {
400           logprintf (LOG_NOTQUIET, _("The certificate has expired\n"));
401           success = false;
402         }
403       if (!gnutls_x509_crt_check_hostname (cert, host))
404         {
405           logprintf (LOG_NOTQUIET,
406                      _("The certificate's owner does not match hostname %s\n"),
407                      quote (host));
408           success = false;
409         }
410       gnutls_x509_crt_deinit (cert);
411    }
412
413  out:
414   return opt.check_cert ? success : true;
415 }