>From 1194317f35a014c878526dc3d2ada55ebd5fd6de Mon Sep 17 00:00:00 2001 From: Tim Ruehsen Date: Sat, 3 Aug 2013 19:56:39 +0200 Subject: [PATCH] gnutls improvements --- src/ChangeLog | 7 ++++ src/gnutls.c | 131 +++++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 91 insertions(+), 47 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index fe7ce5f..fcb931f 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,10 @@ +2013-07-13 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-07-16 Darshit Shah * wget.h (err_t): Added new errors, ATTRMISSING and UNKNOWNATTR to diff --git a/src/gnutls.c b/src/gnutls.c index d90f46a..1661fb3 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); - 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 @@ -526,11 +561,19 @@ ssl_connect_wget (int fd, const char *hostname) } ctx = xnew0 (struct wgnutls_transport_context); - ctx->session = session; + ctx->session = session; fd_register_transport (fd, &wgnutls_transport, ctx); return true; } +#define _CHECK_CERT(flag,msg) \ + if (status & (flag))\ + {\ + logprintf (LOG_NOTQUIET, _("%s: " 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, "The certificate of %s is not trusted.\n"); + _CHECK_CERT(GNUTLS_CERT_SIGNER_NOT_FOUND, "The certificate of %s hasn't got a known issuer.\n"); + _CHECK_CERT(GNUTLS_CERT_REVOKED, "The certificate of %s has been revoked.\n"); + _CHECK_CERT(GNUTLS_CERT_SIGNER_NOT_CA, "The certificate signer of %s was not a CA.\n"); + _CHECK_CERT(GNUTLS_CERT_INSECURE_ALGORITHM, "The certificate of %s was signed using an insecure algorithm.\n"); + _CHECK_CERT(GNUTLS_CERT_NOT_ACTIVATED, "The certificate of %s is not yet activated.\n"); + _CHECK_CERT(GNUTLS_CERT_EXPIRED, "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