/* SSL support via GnuTLS library.
- Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
Foundation, Inc.
This file is part of GNU Wget.
#include <assert.h>
#include <errno.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
+#include <unistd.h>
#include <string.h>
#include <stdio.h>
+#include <dirent.h>
#include <stdlib.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
#include "utils.h"
#include "connect.h"
#include "url.h"
#include "ssl.h"
+#ifdef WIN32
+# include "w32sock.h"
+#endif
+
/* Note: some of the functions private to this file have names that
begin with "wgnutls_" (e.g. wgnutls_read) so that they wouldn't be
confused with actual gnutls functions -- such as the gnutls_read
bool
ssl_init ()
{
+ const char *ca_directory;
+ DIR *dir;
+
gnutls_global_init ();
gnutls_certificate_allocate_credentials (&credentials);
+
+ ca_directory = opt.ca_directory ? opt.ca_directory : "/etc/ssl/certs";
+
+ dir = opendir (ca_directory);
+ if (dir == NULL)
+ {
+ if (opt.ca_directory)
+ logprintf (LOG_NOTQUIET, _("ERROR: Cannot open directory %s.\n"),
+ opt.ca_directory);
+ }
+ else
+ {
+ struct dirent *dent;
+ while ((dent = readdir (dir)) != NULL)
+ {
+ struct stat st;
+ char *ca_file;
+ asprintf (&ca_file, "%s/%s", ca_directory, dent->d_name);
+
+ stat (ca_file, &st);
+
+ if (S_ISREG (st.st_mode))
+ gnutls_certificate_set_x509_trust_file (credentials, ca_file,
+ GNUTLS_X509_FMT_PEM);
+
+ free (ca_file);
+ }
+
+ closedir (dir);
+ }
+
if (opt.ca_cert)
gnutls_certificate_set_x509_trust_file (credentials, opt.ca_cert,
GNUTLS_X509_FMT_PEM);
return true;
}
-struct wgnutls_transport_context {
+struct wgnutls_transport_context
+{
gnutls_session session; /* GnuTLS session handle */
int last_error; /* last error returned by read/write/... */
if (ret < 0)
ctx->last_error = ret;
+
return ret;
}
{
int ret = 0;
struct wgnutls_transport_context *ctx = arg;
- int offset = ctx->peeklen;
-
+ int offset = MIN (bufsize, ctx->peeklen);
if (bufsize > sizeof ctx->peekbuf)
bufsize = sizeof ctx->peekbuf;
- if (offset)
+ if (ctx->peeklen)
memcpy (buf, ctx->peekbuf, offset);
- do
+ if (bufsize > offset)
{
- if (gnutls_record_check_pending (ctx->session)
- || select_fd (fd, 0, WAIT_FOR_READ))
- ret = gnutls_record_recv (ctx->session, buf + offset, bufsize - offset);
- }
- while (ret == GNUTLS_E_INTERRUPTED);
+#ifdef F_GETFL
+ int flags;
+ flags = fcntl (fd, F_GETFL, 0);
+ if (flags < 0)
+ return ret;
+
+ ret = fcntl (fd, F_SETFL, flags | O_NONBLOCK);
+ if (ret < 0)
+ return ret;
+#else
+ /* XXX: Assume it was blocking before. */
+ const int zero = 0;
+ ret = ioctl (fd, FIONBIO, &zero);
+ if (ret < 0)
+ return ret;
+#endif
+ do
+ {
+ ret = gnutls_record_recv (ctx->session, buf + offset,
+ bufsize - offset);
+ }
+ while (ret == GNUTLS_E_INTERRUPTED);
- if (ret > 0)
- {
- memcpy (ctx->peekbuf + offset, buf + offset, ret);
- ctx->peeklen += ret;
+ if (ret < 0)
+ {
+ if (offset)
+ ret = 0;
+ else
+ return ret;
+ }
+
+ if (ret > 0)
+ {
+ memcpy (ctx->peekbuf + offset, buf + offset,
+ ret);
+ ctx->peeklen += ret;
+ }
+
+#ifdef F_GETFL
+ fcntl (fd, F_SETFL, flags);
+ if (ret < 0)
+ return ret;
+#else
+ const int one = 1;
+ ret = ioctl (fd, FIONBIO, &one);
+ if (ret < 0)
+ return ret;
+#endif
}
- return ctx->peeklen;
+
+ return offset + ret;
}
static const char *
/* gnutls_transport is the singleton that describes the SSL transport
methods provided by this file. */
-static struct transport_implementation wgnutls_transport = {
+static struct transport_implementation wgnutls_transport =
+{
wgnutls_read, wgnutls_write, wgnutls_poll,
wgnutls_peek, wgnutls_errstr, wgnutls_close
};
struct wgnutls_transport_context *ctx;
gnutls_session session;
int err;
+ int allowed_protocols[4] = {0, 0, 0, 0};
gnutls_init (&session, GNUTLS_CLIENT);
gnutls_set_default_priority (session);
gnutls_certificate_type_set_priority (session, cert_type_priority);
gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, credentials);
- gnutls_transport_set_ptr (session, (gnutls_transport_ptr) fd);
+#ifndef FD_TO_SOCKET
+# define FD_TO_SOCKET(X) (X)
+#endif
+ gnutls_transport_set_ptr (session, (gnutls_transport_ptr) FD_TO_SOCKET (fd));
+
+ err = 0;
+ switch (opt.secure_protocol)
+ {
+ case secure_protocol_auto:
+ break;
+ case secure_protocol_sslv2:
+ case secure_protocol_sslv3:
+ allowed_protocols[0] = GNUTLS_SSL3;
+ err = gnutls_protocol_set_priority (session, allowed_protocols);
+ break;
+ case secure_protocol_tlsv1:
+ allowed_protocols[0] = GNUTLS_TLS1_0;
+ allowed_protocols[1] = GNUTLS_TLS1_1;
+ allowed_protocols[2] = GNUTLS_TLS1_2;
+ err = gnutls_protocol_set_priority (session, allowed_protocols);
+ break;
+ default:
+ abort ();
+ }
+ if (err < 0)
+ {
+ logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
+ gnutls_deinit (session);
+ return false;
+ }
+
err = gnutls_handshake (session);
if (err < 0)
{
gnutls_deinit (session);
return false;
}
+
ctx = xnew0 (struct wgnutls_transport_context);
ctx->session = session;
fd_register_transport (fd, &wgnutls_transport, ctx);