]> sjero.net Git - wget/commitdiff
gnutls: Prevent CA files from being loaded twice if possible
authorTim Ruehsen <tim.ruehsen@gmx.de>
Fri, 9 Aug 2013 19:56:01 +0000 (21:56 +0200)
committerGiuseppe Scrivano <gscrivan@redhat.com>
Fri, 9 Aug 2013 21:06:17 +0000 (23:06 +0200)
src/ChangeLog
src/gnutls.c

index ab38d45ebda71d4e03a80a349f331359a2bd7c5e..edfb80f6c01996b0460f314a5911fbd9bfadc0a2 100644 (file)
@@ -1,3 +1,10 @@
+2013-08-09  Tim Ruehsen  <tim.ruehsen@gmx.de>
+
+       * 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  <w@wdtz.org> (tiny change):
 
        * main.c (format_and_print_line): Wrap correctly long tokens.
 2013-08-08  Will Dietz  <w@wdtz.org> (tiny change):
 
        * main.c (format_and_print_line): Wrap correctly long tokens.
index 06f90200c514ca303c8dd3739c7c59c7cbcafe52..0499a2502c742109ab78d513df44e596996b2ec5 100644 (file)
@@ -46,6 +46,7 @@ as that of the covered work.  */
 #include "connect.h"
 #include "url.h"
 #include "ptimer.h"
 #include "connect.h"
 #include "url.h"
 #include "ptimer.h"
+#include "hash.h"
 #include "ssl.h"
 
 #include <sys/fcntl.h>
 #include "ssl.h"
 
 #include <sys/fcntl.h>
@@ -81,49 +82,76 @@ ssl_init (void)
 {
   /* Becomes true if GnuTLS is initialized. */
   static bool ssl_initialized = false;
 {
   /* 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;
 
 
   /* 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_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 (opt.ca_directory && *opt.ca_directory)
-        logprintf (LOG_NOTQUIET, _("ERROR: Cannot open directory %s.\n"),
-                   opt.ca_directory);
-    }
-  else
+  /* 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)
     {
     {
-      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];
+
+              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;
+            }
 
 
-          free (ca_file);
+          hash_table_destroy (inode_map);
+          closedir (dir);
         }
         }
-
-      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)
     {
   /* Use the private key from the cert file unless otherwise specified. */
   if (opt.cert_file && !opt.private_key)
     {
@@ -141,17 +169,17 @@ ssl_init (void)
     {
       int type;
       if (opt.private_key_type != opt.cert_type)
     {
       int type;
       if (opt.private_key_type != opt.cert_type)
-       {
-         /* GnuTLS can't handle this */
-         logprintf (LOG_NOTQUIET, _("ERROR: GnuTLS requires the key and the \
+        {
+          /* GnuTLS can't handle this */
+          logprintf (LOG_NOTQUIET, _("ERROR: GnuTLS requires the key and the \
 cert to be of the same type.\n"));
 cert to be of the same type.\n"));
-       }
+        }
 
       type = key_type_to_gnutls_type (opt.private_key_type);
 
       gnutls_certificate_set_x509_key_file (credentials, opt.cert_file,
 
       type = key_type_to_gnutls_type (opt.private_key_type);
 
       gnutls_certificate_set_x509_key_file (credentials, opt.cert_file,
-                                           opt.private_key,
-                                           type);
+                                            opt.private_key,
+                                            type);
     }
 
   if (opt.ca_cert)
     }
 
   if (opt.ca_cert)
@@ -278,7 +306,7 @@ wgnutls_read (int fd, char *buf, int bufsize, void *arg)
 }
 
 static int
 }
 
 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;
 {
   int ret;
   struct wgnutls_transport_context *ctx = arg;
@@ -315,7 +343,7 @@ wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
       return offset;
     }
 
       return offset;
     }
 
-  if (bufsize > sizeof ctx->peekbuf)
+  if (bufsize > (int) sizeof ctx->peekbuf)
     bufsize = sizeof ctx->peekbuf;
 
   if (bufsize > offset)
     bufsize = sizeof ctx->peekbuf;
 
   if (bufsize > offset)
@@ -346,7 +374,7 @@ wgnutls_peek (int fd, char *buf, int bufsize, void *arg)
 }
 
 static const char *
 }
 
 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);
 {
   struct wgnutls_transport_context *ctx = arg;
   return gnutls_strerror (ctx->last_error);
@@ -387,7 +415,7 @@ ssl_connect_wget (int fd, const char *hostname)
   if (! is_valid_ip_address (hostname))
     {
       gnutls_server_name_set (session, GNUTLS_NAME_DNS, hostname,
   if (! is_valid_ip_address (hostname))
     {
       gnutls_server_name_set (session, GNUTLS_NAME_DNS, hostname,
-                             strlen (hostname));
+                              strlen (hostname));
     }
 
   gnutls_set_default_priority (session);
     }
 
   gnutls_set_default_priority (session);
@@ -395,7 +423,11 @@ ssl_connect_wget (int fd, const char *hostname)
 #ifndef FD_TO_SOCKET
 # define FD_TO_SOCKET(X) (X)
 #endif
 #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));
   gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) FD_TO_SOCKET (fd));
+#endif
 
   err = 0;
 #if HAVE_GNUTLS_PRIORITY_SET_DIRECT
 
   err = 0;
 #if HAVE_GNUTLS_PRIORITY_SET_DIRECT
@@ -531,6 +563,14 @@ ssl_connect_wget (int fd, const char *hostname)
   return true;
 }
 
   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)
 {
 bool
 ssl_check_certificate (int fd, const char *host)
 {
@@ -553,24 +593,13 @@ ssl_check_certificate (int fd, const char *host)
       goto out;
     }
 
       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)
     {
 
   if (gnutls_certificate_type_get (ctx->session) == GNUTLS_CRT_X509)
     {
@@ -621,7 +650,12 @@ ssl_check_certificate (int fd, const char *host)
         }
  crt_deinit:
       gnutls_x509_crt_deinit (cert);
         }
  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;
 
  out:
   return opt.check_cert ? success : true;