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