]> sjero.net Git - wget/blobdiff - src/openssl.c
Avoid reusing same buffer for successive quoted args.
[wget] / src / openssl.c
index 253b903b65b7bc9da5574d1cc45f71edcdc9327e..b55ca8bfb5add32195151d9b35b6b1ce41414ad7 100644 (file)
@@ -1,12 +1,13 @@
 /* SSL support via OpenSSL library.
 /* SSL support via OpenSSL library.
-   Copyright (C) 2000-2005 Free Software Foundation, Inc.
+   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
+   2009 Free Software Foundation, Inc.
    Originally contributed by Christian Fraenkel.
 
 This file is part of GNU Wget.
 
 GNU Wget is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
    Originally contributed by Christian Fraenkel.
 
 This file is part of GNU Wget.
 
 GNU Wget is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
+the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version.
 
 GNU Wget is distributed in the hope that it will be useful,
 (at your option) any later version.
 
 GNU Wget is distributed in the hope that it will be useful,
@@ -15,20 +16,20 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with Wget; if not, write to the Free Software Foundation, Inc.,
-51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+along with Wget.  If not, see <http://www.gnu.org/licenses/>.
 
 
-In addition, as a special exception, the Free Software Foundation
-gives permission to link the code of its release of Wget with the
-OpenSSL project's "OpenSSL" library (or with modified versions of it
-that use the same license as the "OpenSSL" library), and distribute
-the linked executables.  You must obey the GNU General Public License
-in all respects for all of the code used other than "OpenSSL".  If you
-modify this file, you may extend this exception to your version of the
-file, but you are not obligated to do so.  If you do not wish to do
-so, delete this exception statement from your version.  */
+Additional permission under GNU GPL version 3 section 7
 
 
-#include <config.h>
+If you modify this program, or any covered work, by linking or
+combining it with the OpenSSL project's OpenSSL library (or a
+modified version of that library), containing parts covered by the
+terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
+grants you additional permission to convey the resulting work.
+Corresponding Source for a non-source form of such a combination
+shall include the source code for the parts of OpenSSL used as well
+as that of the covered work.  */
+
+#include "wget.h"
 
 #include <assert.h>
 #include <errno.h>
 
 #include <assert.h>
 #include <errno.h>
@@ -42,7 +43,6 @@ so, delete this exception statement from your version.  */
 #include <openssl/err.h>
 #include <openssl/rand.h>
 
 #include <openssl/err.h>
 #include <openssl/rand.h>
 
-#include "wget.h"
 #include "utils.h"
 #include "connect.h"
 #include "url.h"
 #include "utils.h"
 #include "connect.h"
 #include "url.h"
@@ -114,8 +114,8 @@ init_prng (void)
 
     while (RAND_status () == 0 && maxrand-- > 0)
       {
 
     while (RAND_status () == 0 && maxrand-- > 0)
       {
-       unsigned char rnd = random_number (256);
-       RAND_seed (&rnd, sizeof (rnd));
+        unsigned char rnd = random_number (256);
+        RAND_seed (&rnd, sizeof (rnd));
       }
   }
 #endif
       }
   }
 #endif
@@ -124,7 +124,7 @@ init_prng (void)
 /* Print errors in the OpenSSL error stack. */
 
 static void
 /* Print errors in the OpenSSL error stack. */
 
 static void
-print_errors (void) 
+print_errors (void)
 {
   unsigned long err;
   while ((err = ERR_get_error ()) != 0)
 {
   unsigned long err;
   while ((err = ERR_get_error ()) != 0)
@@ -170,7 +170,7 @@ ssl_init ()
   if (RAND_status () != 1)
     {
       logprintf (LOG_NOTQUIET,
   if (RAND_status () != 1)
     {
       logprintf (LOG_NOTQUIET,
-                _("Could not seed PRNG; consider using --random-file.\n"));
+                 _("Could not seed PRNG; consider using --random-file.\n"));
       goto error;
     }
 
       goto error;
     }
 
@@ -210,21 +210,32 @@ ssl_init ()
      than examining the error stack after a failed SSL_connect.  */
   SSL_CTX_set_verify (ssl_ctx, SSL_VERIFY_NONE, NULL);
 
      than examining the error stack after a failed SSL_connect.  */
   SSL_CTX_set_verify (ssl_ctx, SSL_VERIFY_NONE, NULL);
 
+  /* Use the private key from the cert file unless otherwise specified. */
+  if (opt.cert_file && !opt.private_key)
+    {
+      opt.private_key = opt.cert_file;
+      opt.private_key_type = opt.cert_type;
+    }
+
   if (opt.cert_file)
     if (SSL_CTX_use_certificate_file (ssl_ctx, opt.cert_file,
   if (opt.cert_file)
     if (SSL_CTX_use_certificate_file (ssl_ctx, opt.cert_file,
-                                     key_type_to_ssl_type (opt.cert_type))
-       != 1)
+                                      key_type_to_ssl_type (opt.cert_type))
+        != 1)
       goto error;
   if (opt.private_key)
     if (SSL_CTX_use_PrivateKey_file (ssl_ctx, opt.private_key,
       goto error;
   if (opt.private_key)
     if (SSL_CTX_use_PrivateKey_file (ssl_ctx, opt.private_key,
-                                    key_type_to_ssl_type (opt.private_key_type))
-       != 1)
+                                     key_type_to_ssl_type (opt.private_key_type))
+        != 1)
       goto error;
 
   /* Since fd_write unconditionally assumes partial writes (and
      handles them correctly), allow them in OpenSSL.  */
   SSL_CTX_set_mode (ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
 
       goto error;
 
   /* Since fd_write unconditionally assumes partial writes (and
      handles them correctly), allow them in OpenSSL.  */
   SSL_CTX_set_mode (ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
 
+  /* The OpenSSL library can handle renegotiations automatically, so
+     tell it to do so.  */
+  SSL_CTX_set_mode (ssl_ctx, SSL_MODE_AUTO_RETRY);
+
   return true;
 
  error:
   return true;
 
  error:
@@ -235,8 +246,8 @@ ssl_init ()
 }
 
 struct openssl_transport_context {
 }
 
 struct openssl_transport_context {
-  SSL *conn;                   /* SSL connection handle */
-  char *last_error;            /* last error printed with openssl_errstr */
+  SSL *conn;                    /* SSL connection handle */
+  char *last_error;             /* last error printed with openssl_errstr */
 };
 
 static int
 };
 
 static int
@@ -248,8 +259,8 @@ openssl_read (int fd, char *buf, int bufsize, void *arg)
   do
     ret = SSL_read (conn, buf, bufsize);
   while (ret == -1
   do
     ret = SSL_read (conn, buf, bufsize);
   while (ret == -1
-        && SSL_get_error (conn, ret) == SSL_ERROR_SYSCALL
-        && errno == EINTR);
+         && SSL_get_error (conn, ret) == SSL_ERROR_SYSCALL
+         && errno == EINTR);
   return ret;
 }
 
   return ret;
 }
 
@@ -262,8 +273,8 @@ openssl_write (int fd, char *buf, int bufsize, void *arg)
   do
     ret = SSL_write (conn, buf, bufsize);
   while (ret == -1
   do
     ret = SSL_write (conn, buf, bufsize);
   while (ret == -1
-        && SSL_get_error (conn, ret) == SSL_ERROR_SYSCALL
-        && errno == EINTR);
+         && SSL_get_error (conn, ret) == SSL_ERROR_SYSCALL
+         && errno == EINTR);
   return ret;
 }
 
   return ret;
 }
 
@@ -288,8 +299,8 @@ openssl_peek (int fd, char *buf, int bufsize, void *arg)
   do
     ret = SSL_peek (conn, buf, bufsize);
   while (ret == -1
   do
     ret = SSL_peek (conn, buf, bufsize);
   while (ret == -1
-        && SSL_get_error (conn, ret) == SSL_ERROR_SYSCALL
-        && errno == EINTR);
+         && SSL_get_error (conn, ret) == SSL_ERROR_SYSCALL
+         && errno == EINTR);
   return ret;
 }
 
   return ret;
 }
 
@@ -320,7 +331,7 @@ openssl_errstr (int fd, void *arg)
       int len = strlen (str);
 
       /* Allocate space for the existing message, plus two more chars
       int len = strlen (str);
 
       /* Allocate space for the existing message, plus two more chars
-        for the "; " separator and one for the terminating \0.  */
+         for the "; " separator and one for the terminating \0.  */
       errmsg = xrealloc (errmsg, msglen + len + 2 + 1);
       memcpy (errmsg + msglen, str, len);
       msglen += len;
       errmsg = xrealloc (errmsg, msglen + len + 2 + 1);
       memcpy (errmsg + msglen, str, len);
       msglen += len;
@@ -328,7 +339,7 @@ openssl_errstr (int fd, void *arg)
       /* Get next error and bail out if there are no more. */
       errcode = ERR_get_error ();
       if (errcode == 0)
       /* Get next error and bail out if there are no more. */
       errcode = ERR_get_error ();
       if (errcode == 0)
-       break;
+        break;
 
       errmsg[msglen++] = ';';
       errmsg[msglen++] = ' ';
 
       errmsg[msglen++] = ';';
       errmsg[msglen++] = ' ';
@@ -353,7 +364,7 @@ openssl_close (int fd, void *arg)
   xfree_null (ctx->last_error);
   xfree (ctx);
 
   xfree_null (ctx->last_error);
   xfree (ctx);
 
-#ifdef WINDOWS
+#if defined(WINDOWS) || defined(USE_WATT32)
   closesocket (fd);
 #else
   close (fd);
   closesocket (fd);
 #else
   close (fd);
@@ -379,7 +390,7 @@ static struct transport_implementation openssl_transport = {
    Returns true on success, false on failure.  */
 
 bool
    Returns true on success, false on failure.  */
 
 bool
-ssl_connect (int fd) 
+ssl_connect_wget (int fd)
 {
   SSL *conn;
   struct openssl_transport_context *ctx;
 {
   SSL *conn;
   struct openssl_transport_context *ctx;
@@ -403,7 +414,7 @@ ssl_connect (int fd)
      functions are used for reading, writing, and polling.  */
   fd_register_transport (fd, &openssl_transport, ctx);
   DEBUGP (("Handshake successful; connected socket %d to SSL handle 0x%0*lx\n",
      functions are used for reading, writing, and polling.  */
   fd_register_transport (fd, &openssl_transport, ctx);
   DEBUGP (("Handshake successful; connected socket %d to SSL handle 0x%0*lx\n",
-          fd, PTR_FORMAT (conn)));
+           fd, PTR_FORMAT (conn)));
   return true;
 
  error:
   return true;
 
  error:
@@ -414,7 +425,7 @@ ssl_connect (int fd)
   return false;
 }
 
   return false;
 }
 
-#define ASTERISK_EXCLUDES_DOT  /* mandated by rfc2818 */
+#define ASTERISK_EXCLUDES_DOT   /* mandated by rfc2818 */
 
 /* Return true is STRING (case-insensitively) matches PATTERN, false
    otherwise.  The recognized wildcard character is "*", which matches
 
 /* Return true is STRING (case-insensitively) matches PATTERN, false
    otherwise.  The recognized wildcard character is "*", which matches
@@ -435,24 +446,24 @@ pattern_match (const char *pattern, const char *string)
 {
   const char *p = pattern, *n = string;
   char c;
 {
   const char *p = pattern, *n = string;
   char c;
-  for (; (c = TOLOWER (*p++)) != '\0'; n++)
+  for (; (c = c_tolower (*p++)) != '\0'; n++)
     if (c == '*')
       {
     if (c == '*')
       {
-       for (c = TOLOWER (*p); c == '*'; c = TOLOWER (*++p))
-         ;
-       for (; *n != '\0'; n++)
-         if (TOLOWER (*n) == c && pattern_match (p, n))
-           return true;
+        for (c = c_tolower (*p); c == '*'; c = c_tolower (*++p))
+          ;
+        for (; *n != '\0'; n++)
+          if (c_tolower (*n) == c && pattern_match (p, n))
+            return true;
 #ifdef ASTERISK_EXCLUDES_DOT
 #ifdef ASTERISK_EXCLUDES_DOT
-         else if (*n == '.')
-           return false;
+          else if (*n == '.')
+            return false;
 #endif
 #endif
-       return c == '\0';
+        return c == '\0';
       }
     else
       {
       }
     else
       {
-       if (c != TOLOWER (*n))
-         return false;
+        if (c != c_tolower (*n))
+          return false;
       }
   return *n == '\0';
 }
       }
   return *n == '\0';
 }
@@ -462,7 +473,7 @@ pattern_match (const char *pattern, const char *string)
    its certificate, corresponds to HOST.  (HOST typically comes from
    the URL and is what the user thinks he's connecting to.)
 
    its certificate, corresponds to HOST.  (HOST typically comes from
    the URL and is what the user thinks he's connecting to.)
 
-   This assumes that ssl_connect has successfully finished, i.e. that
+   This assumes that ssl_connect_wget has successfully finished, i.e. that
    the SSL handshake has been performed and that FD is connected to an
    SSL handle.
 
    the SSL handshake has been performed and that FD is connected to an
    SSL handle.
 
@@ -491,9 +502,9 @@ ssl_check_certificate (int fd, const char *host)
   if (!cert)
     {
       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
   if (!cert)
     {
       logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
-                severity, escnonprint (host));
+                 severity, quotearg_style (escape_quoting_style, host));
       success = false;
       success = false;
-      goto no_cert;            /* must bail out since CERT is NULL */
+      goto no_cert;             /* must bail out since CERT is NULL */
     }
 
   IF_DEBUG
     }
 
   IF_DEBUG
@@ -501,7 +512,8 @@ ssl_check_certificate (int fd, const char *host)
       char *subject = X509_NAME_oneline (X509_get_subject_name (cert), 0, 0);
       char *issuer = X509_NAME_oneline (X509_get_issuer_name (cert), 0, 0);
       DEBUGP (("certificate:\n  subject: %s\n  issuer:  %s\n",
       char *subject = X509_NAME_oneline (X509_get_subject_name (cert), 0, 0);
       char *issuer = X509_NAME_oneline (X509_get_issuer_name (cert), 0, 0);
       DEBUGP (("certificate:\n  subject: %s\n  issuer:  %s\n",
-              escnonprint (subject), escnonprint (issuer)));
+               quotearg_n_style (0, escape_quoting_style, subject),
+               quotearg_n_style (1, escape_quoting_style, issuer)));
       OPENSSL_free (subject);
       OPENSSL_free (issuer);
     }
       OPENSSL_free (subject);
       OPENSSL_free (issuer);
     }
@@ -511,35 +523,36 @@ ssl_check_certificate (int fd, const char *host)
     {
       char *issuer = X509_NAME_oneline (X509_get_issuer_name (cert), 0, 0);
       logprintf (LOG_NOTQUIET,
     {
       char *issuer = X509_NAME_oneline (X509_get_issuer_name (cert), 0, 0);
       logprintf (LOG_NOTQUIET,
-                _("%s: cannot verify %s's certificate, issued by `%s':\n"),
-                severity, escnonprint (host), escnonprint (issuer));
+                 _("%s: cannot verify %s's certificate, issued by %s:\n"),
+                 severity, quotearg_n_style (0, escape_quoting_style, host),
+                 quote_n (1, issuer));
       /* Try to print more user-friendly (and translated) messages for
       /* Try to print more user-friendly (and translated) messages for
-        the frequent verification errors.  */
+         the frequent verification errors.  */
       switch (vresult)
       switch (vresult)
-       {
-       case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
-         logprintf (LOG_NOTQUIET,
-                    _("  Unable to locally verify the issuer's authority.\n"));
-         break;
-       case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
-       case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
-         logprintf (LOG_NOTQUIET, _("  Self-signed certificate encountered.\n"));
-         break;
-       case X509_V_ERR_CERT_NOT_YET_VALID:
-         logprintf (LOG_NOTQUIET, _("  Issued certificate not yet valid.\n"));
-         break;
-       case X509_V_ERR_CERT_HAS_EXPIRED:
-         logprintf (LOG_NOTQUIET, _("  Issued certificate has expired.\n"));
-         break;
-       default:
-         /* For the less frequent error strings, simply provide the
-            OpenSSL error message.  */
-         logprintf (LOG_NOTQUIET, "  %s\n",
-                    X509_verify_cert_error_string (vresult));
-       }
+        {
+        case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+          logprintf (LOG_NOTQUIET,
+                     _("  Unable to locally verify the issuer's authority.\n"));
+          break;
+        case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+        case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+          logprintf (LOG_NOTQUIET, _("  Self-signed certificate encountered.\n"));
+          break;
+        case X509_V_ERR_CERT_NOT_YET_VALID:
+          logprintf (LOG_NOTQUIET, _("  Issued certificate not yet valid.\n"));
+          break;
+        case X509_V_ERR_CERT_HAS_EXPIRED:
+          logprintf (LOG_NOTQUIET, _("  Issued certificate has expired.\n"));
+          break;
+        default:
+          /* For the less frequent error strings, simply provide the
+             OpenSSL error message.  */
+          logprintf (LOG_NOTQUIET, "  %s\n",
+                     X509_verify_cert_error_string (vresult));
+        }
       success = false;
       /* Fall through, so that the user is warned about *all* issues
       success = false;
       /* Fall through, so that the user is warned about *all* issues
-        with the cert (important with --no-check-certificate.)  */
+         with the cert (important with --no-check-certificate.)  */
     }
 
   /* Check that HOST matches the common name in the certificate.
     }
 
   /* Check that HOST matches the common name in the certificate.
@@ -556,27 +569,64 @@ ssl_check_certificate (int fd, const char *host)
      - Ensure that ASN1 strings from the certificate are encoded as
        UTF-8 which can be meaningfully compared to HOST.  */
 
      - Ensure that ASN1 strings from the certificate are encoded as
        UTF-8 which can be meaningfully compared to HOST.  */
 
+  X509_NAME *xname = X509_get_subject_name(cert);
   common_name[0] = '\0';
   common_name[0] = '\0';
-  X509_NAME_get_text_by_NID (X509_get_subject_name (cert),
-                            NID_commonName, common_name, sizeof (common_name));
+  X509_NAME_get_text_by_NID (xname, NID_commonName, common_name,
+                             sizeof (common_name));
+
   if (!pattern_match (common_name, host))
     {
       logprintf (LOG_NOTQUIET, _("\
   if (!pattern_match (common_name, host))
     {
       logprintf (LOG_NOTQUIET, _("\
-%s: certificate common name `%s' doesn't match requested host name `%s'.\n"),
-                severity, escnonprint (common_name), escnonprint (host));
+%s: certificate common name %s doesn't match requested host name %s.\n"),
+                 severity, quote_n (0, common_name), quote_n (1, host));
       success = false;
     }
       success = false;
     }
+  else
+    {
+      /* We now determine the length of the ASN1 string. If it differs from
+       * common_name's length, then there is a \0 before the string terminates.
+       * This can be an instance of a null-prefix attack.
+       *
+       * https://www.blackhat.com/html/bh-usa-09/bh-usa-09-archives.html#Marlinspike
+       * */
+
+      int i = -1, j;
+      X509_NAME_ENTRY *xentry;
+      ASN1_STRING *sdata;
+
+      if (xname) {
+        for (;;)
+          {
+            j = X509_NAME_get_index_by_NID (xname, NID_commonName, i);
+            if (j == -1) break;
+            i = j;
+          }
+      }
+
+      xentry = X509_NAME_get_entry(xname,i);
+      sdata = X509_NAME_ENTRY_get_data(xentry);
+      if (strlen (common_name) != ASN1_STRING_length (sdata))
+        {
+          logprintf (LOG_NOTQUIET, _("\
+%s: certificate common name is invalid (contains a NUL character).\n\
+This may be an indication that the host is not who it claims to be\n\
+(that is, it is not the real %s).\n"),
+                     severity, quote (host));
+          success = false;
+        }
+    }
+
 
   if (success)
     DEBUGP (("X509 certificate successfully verified and matches host %s\n",
 
   if (success)
     DEBUGP (("X509 certificate successfully verified and matches host %s\n",
-            escnonprint (host)));
+             quotearg_style (escape_quoting_style, host)));
   X509_free (cert);
 
  no_cert:
   if (opt.check_cert && !success)
     logprintf (LOG_NOTQUIET, _("\
 To connect to %s insecurely, use `--no-check-certificate'.\n"),
   X509_free (cert);
 
  no_cert:
   if (opt.check_cert && !success)
     logprintf (LOG_NOTQUIET, _("\
 To connect to %s insecurely, use `--no-check-certificate'.\n"),
-              escnonprint (host));
+               quotearg_style (escape_quoting_style, host));
 
   /* Allow --no-check-cert to disable certificate checking. */
   return opt.check_cert ? success : true;
 
   /* Allow --no-check-cert to disable certificate checking. */
   return opt.check_cert ? success : true;