>From 0fd97ed11d9f740de78d540a13db40548c8d7391 Mon Sep 17 00:00:00 2001
From: Tim Ruehsen
Date: Fri, 9 Aug 2013 21:56:01 +0200
Subject: [PATCH] gnutls improvements
---
src/ChangeLog | 7 ++++
src/gnutls.c | 133 +++++++++++++++++++++++++++++++++++++---------------------
2 files changed, 92 insertions(+), 48 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index ab38d45..edfb80f 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,10 @@
+2013-08-09 Tim Ruehsen
+
+ * gnutls.c (ssl_init): Prevent CA files from being loaded twice
+ if possible.
+ * gnutls.c (ssl_check_certificate): Added some error messages
+ * gnutls.c: Fixed some compiler warnings
+
2013-08-08 Will Dietz (tiny change):
* main.c (format_and_print_line): Wrap correctly long tokens.
diff --git a/src/gnutls.c b/src/gnutls.c
index 06f9020..06e263e 100644
--- a/src/gnutls.c
+++ b/src/gnutls.c
@@ -46,6 +46,7 @@ as that of the covered work. */
#include "connect.h"
#include "url.h"
#include "ptimer.h"
+#include "hash.h"
#include "ssl.h"
#include
@@ -81,49 +82,79 @@ ssl_init (void)
{
/* Becomes true if GnuTLS is initialized. */
static bool ssl_initialized = false;
+ const char *ca_directory;
+ DIR *dir;
+ int ncerts = -1;
/* GnuTLS should be initialized only once. */
if (ssl_initialized)
return true;
- const char *ca_directory;
- DIR *dir;
-
gnutls_global_init ();
gnutls_certificate_allocate_credentials (&credentials);
- gnutls_certificate_set_verify_flags(credentials,
- GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
+ gnutls_certificate_set_verify_flags (credentials,
+ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
- ca_directory = opt.ca_directory ? opt.ca_directory : "/etc/ssl/certs";
+#if GNUTLS_VERSION_MAJOR >= 3
+ if (!opt.ca_directory)
+ ncerts = gnutls_certificate_set_x509_system_trust (credentials);
+#endif
- dir = opendir (ca_directory);
- if (dir == NULL)
+ /* If GnuTLS version is too old or CA loading failed, fallback to old behaviour.
+ * Also use old behaviour if the CA directory is user-provided */
+ if (ncerts <= 0)
{
- if (opt.ca_directory && *opt.ca_directory)
- logprintf (LOG_NOTQUIET, _("ERROR: Cannot open directory %s.\n"),
- opt.ca_directory);
- }
- else
- {
- struct dirent *dent;
- while ((dent = readdir (dir)) != NULL)
+ ca_directory = opt.ca_directory ? opt.ca_directory : "/etc/ssl/certs";
+
+ if ((dir = opendir (ca_directory)) == NULL)
{
- struct stat st;
- char *ca_file;
- asprintf (&ca_file, "%s/%s", ca_directory, dent->d_name);
+ if (opt.ca_directory && *opt.ca_directory)
+ logprintf (LOG_NOTQUIET, _("ERROR: Cannot open directory %s.\n"),
+ opt.ca_directory);
+ }
+ else
+ {
+ struct hash_table *inode_map = hash_table_new (196, NULL, NULL);
+ struct dirent *dent;
+ size_t dirlen = strlen(ca_directory);
+ int rc;
- stat (ca_file, &st);
+ ncerts = 0;
- if (S_ISREG (st.st_mode))
- gnutls_certificate_set_x509_trust_file (credentials, ca_file,
- GNUTLS_X509_FMT_PEM);
+ while ((dent = readdir (dir)) != NULL)
+ {
+ struct stat st;
+ char ca_file[dirlen + strlen(dent->d_name) + 2];
- free (ca_file);
- }
+ snprintf (ca_file, sizeof(ca_file), "%s/%s", ca_directory, dent->d_name);
+
+ if (stat (ca_file, &st) != 0)
+ continue;
+
+ if (! S_ISREG (st.st_mode))
+ continue;
+
+ /* avoid loading the same file twice by checking the inode */
+ if (hash_table_contains (inode_map, (void *)(intptr_t) st.st_ino))
+ continue;
+
+ hash_table_put (inode_map, (void *)(intptr_t) st.st_ino, NULL);
+
+ if ((rc = gnutls_certificate_set_x509_trust_file (credentials, ca_file,
+ GNUTLS_X509_FMT_PEM)) <= 0)
+ logprintf (LOG_NOTQUIET, _("ERROR: Failed to open cert %s: (%d).\n"),
+ ca_file, rc);
+ else
+ ncerts += rc;
+ }
- closedir (dir);
+ hash_table_destroy (inode_map);
+ closedir (dir);
+ }
}
+ DEBUGP (("Certificates loaded: %d\n", ncerts));
+
/* Use the private key from the cert file unless otherwise specified. */
if (opt.cert_file && !opt.private_key)
{
@@ -278,7 +309,7 @@ wgnutls_read (int fd, char *buf, int bufsize, void *arg)
}
static int
-wgnutls_write (int fd, char *buf, int bufsize, void *arg)
+wgnutls_write (int fd _GL_UNUSED, char *buf, int bufsize, void *arg)
{
int ret;
struct wgnutls_transport_context *ctx = arg;
@@ -315,7 +346,7 @@ wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
return offset;
}
- if (bufsize > sizeof ctx->peekbuf)
+ if (bufsize > (int) sizeof ctx->peekbuf)
bufsize = sizeof ctx->peekbuf;
if (bufsize > offset)
@@ -346,7 +377,7 @@ wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
}
static const char *
-wgnutls_errstr (int fd, void *arg)
+wgnutls_errstr (int fd _GL_UNUSED, void *arg)
{
struct wgnutls_transport_context *ctx = arg;
return gnutls_strerror (ctx->last_error);
@@ -395,7 +426,11 @@ ssl_connect_wget (int fd, const char *hostname)
#ifndef FD_TO_SOCKET
# define FD_TO_SOCKET(X) (X)
#endif
+#ifdef HAVE_INTPTR_T
+ gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) (intptr_t) FD_TO_SOCKET (fd));
+#else
gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) FD_TO_SOCKET (fd));
+#endif
err = 0;
#if HAVE_GNUTLS_PRIORITY_SET_DIRECT
@@ -531,6 +566,14 @@ ssl_connect_wget (int fd, const char *hostname)
return true;
}
+#define _CHECK_CERT(flag,msg) \
+ if (status & (flag))\
+ {\
+ logprintf (LOG_NOTQUIET, (msg),\
+ severity, quote (host));\
+ success = false;\
+ }
+
bool
ssl_check_certificate (int fd, const char *host)
{
@@ -553,24 +596,13 @@ ssl_check_certificate (int fd, const char *host)
goto out;
}
- if (status & GNUTLS_CERT_INVALID)
- {
- logprintf (LOG_NOTQUIET, _("%s: The certificate of %s is not trusted.\n"),
- severity, quote (host));
- success = false;
- }
- if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
- {
- logprintf (LOG_NOTQUIET, _("%s: The certificate of %s hasn't got a known issuer.\n"),
- severity, quote (host));
- success = false;
- }
- if (status & GNUTLS_CERT_REVOKED)
- {
- logprintf (LOG_NOTQUIET, _("%s: The certificate of %s has been revoked.\n"),
- severity, quote (host));
- success = false;
- }
+ _CHECK_CERT (GNUTLS_CERT_INVALID, _("%s: The certificate of %s is not trusted.\n"));
+ _CHECK_CERT (GNUTLS_CERT_SIGNER_NOT_FOUND, _("%s: The certificate of %s hasn't got a known issuer.\n"));
+ _CHECK_CERT (GNUTLS_CERT_REVOKED, _("%s: The certificate of %s has been revoked.\n"));
+ _CHECK_CERT (GNUTLS_CERT_SIGNER_NOT_CA, _("%s: The certificate signer of %s was not a CA.\n"));
+ _CHECK_CERT (GNUTLS_CERT_INSECURE_ALGORITHM, _("%s: The certificate of %s was signed using an insecure algorithm.\n"));
+ _CHECK_CERT (GNUTLS_CERT_NOT_ACTIVATED, _("%s: The certificate of %s is not yet activated.\n"));
+ _CHECK_CERT (GNUTLS_CERT_EXPIRED, _("%s: The certificate of %s has expired.\n"));
if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
{
@@ -621,7 +653,12 @@ ssl_check_certificate (int fd, const char *host)
}
crt_deinit:
gnutls_x509_crt_deinit (cert);
- }
+ }
+ else
+ {
+ logprintf (LOG_NOTQUIET, _("Certificate must be X.509\n"));
+ success = false;
+ }
out:
return opt.check_cert ? success : true;
--
1.8.4.rc1