You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by be...@apache.org on 2015/06/26 06:06:11 UTC

[1/5] mesos git commit: Fix ATOMIC_FLAG_INIT typo.

Repository: mesos
Updated Branches:
  refs/heads/master bdf2ea144 -> c8700f981


Fix ATOMIC_FLAG_INIT typo.

Review: https://reviews.apache.org/r/35908


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/fa13557b
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/fa13557b
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/fa13557b

Branch: refs/heads/master
Commit: fa13557ba3ad741e789c9dab74c5ba1019a26939
Parents: bdf2ea1
Author: Joris Van Remoortere <jo...@gmail.com>
Authored: Thu Jun 25 21:04:57 2015 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Thu Jun 25 21:04:57 2015 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/src/libevent_ssl_socket.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/fa13557b/3rdparty/libprocess/src/libevent_ssl_socket.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/libevent_ssl_socket.hpp b/3rdparty/libprocess/src/libevent_ssl_socket.hpp
index e94e6d5..4f2cd35 100644
--- a/3rdparty/libprocess/src/libevent_ssl_socket.hpp
+++ b/3rdparty/libprocess/src/libevent_ssl_socket.hpp
@@ -118,7 +118,7 @@ private:
   evconnlistener* listener;
 
   // Protects the following instance variables.
-  std::atomic_flag lock = ATOMIC_LOCK_INIT;
+  std::atomic_flag lock = ATOMIC_FLAG_INIT;
   Owned<RecvRequest> recv_request;
   Owned<SendRequest> send_request;
   Owned<ConnectRequest> connect_request;


[5/5] mesos git commit: Clear protocol filters for openssl before initializing.

Posted by be...@apache.org.
Clear protocol filters for openssl before initializing.

This does not impact applications, as they never have the chance to
reinitialize. This was necessary for the tests as the protocol filters
on the SSL context don't get over-written when calling
`SSL_CTX_set_options`.

Review: https://reviews.apache.org/r/35888


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/c8700f98
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/c8700f98
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/c8700f98

Branch: refs/heads/master
Commit: c8700f981dc0420d873b474f39101d8fedc9e7dd
Parents: 0b04fec
Author: Joris Van Remoortere <jo...@gmail.com>
Authored: Thu Jun 25 21:05:41 2015 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Thu Jun 25 21:05:41 2015 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/src/openssl.cpp | 11 +++++++++++
 1 file changed, 11 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/c8700f98/3rdparty/libprocess/src/openssl.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/openssl.cpp b/3rdparty/libprocess/src/openssl.cpp
index 40b60bf..3c0fc4b 100644
--- a/3rdparty/libprocess/src/openssl.cpp
+++ b/3rdparty/libprocess/src/openssl.cpp
@@ -434,6 +434,17 @@ void initialize()
     EXIT(EXIT_FAILURE) << "Could not set ciphers: " << ssl_flags->ciphers;
   }
 
+  // Clear all the protocol options. They will be reset if needed
+  // below. We do this because 'SSL_CTX_set_options' only augments, it
+  // does not do an overwrite.
+  SSL_CTX_clear_options(
+      ctx,
+      SSL_OP_NO_SSLv2 |
+      SSL_OP_NO_SSLv3 |
+      SSL_OP_NO_TLSv1 |
+      SSL_OP_NO_TLSv1_1 |
+      SSL_OP_NO_TLSv1_2);
+
   // Use server preference for cipher.
   long ssl_options = SSL_OP_CIPHER_SERVER_PREFERENCE;
   // Disable SSLv2.


[2/5] mesos git commit: Error if trying to create SSL socket when SSL is disabled.

Posted by be...@apache.org.
Error if trying to create SSL socket when SSL is disabled.

Review: https://reviews.apache.org/r/35852


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/ed84f36e
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/ed84f36e
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/ed84f36e

Branch: refs/heads/master
Commit: ed84f36e24b96a2bef831f33bd7361a0103b3380
Parents: fa13557
Author: Joris Van Remoortere <jo...@gmail.com>
Authored: Thu Jun 25 21:05:08 2015 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Thu Jun 25 21:05:09 2015 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/src/libevent_ssl_socket.cpp | 5 +++++
 1 file changed, 5 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/ed84f36e/3rdparty/libprocess/src/libevent_ssl_socket.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/libevent_ssl_socket.cpp b/3rdparty/libprocess/src/libevent_ssl_socket.cpp
index 5955796..2920e0e 100644
--- a/3rdparty/libprocess/src/libevent_ssl_socket.cpp
+++ b/3rdparty/libprocess/src/libevent_ssl_socket.cpp
@@ -75,6 +75,11 @@ namespace network {
 Try<std::shared_ptr<Socket::Impl>> LibeventSSLSocketImpl::create(int s)
 {
   openssl::initialize();
+
+  if (!openssl::flags().enabled) {
+    return Error("SSL is disabled");
+  }
+
   auto socket = std::make_shared<LibeventSSLSocketImpl>(s);
   // See comment at 'initialize' declaration for why we call this.
   socket->initialize();


[4/5] mesos git commit: Add openssl utility functions.

Posted by be...@apache.org.
Add openssl utility functions.

Review: https://reviews.apache.org/r/35854


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/0b04fec9
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/0b04fec9
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/0b04fec9

Branch: refs/heads/master
Commit: 0b04fec909afc0f744503ac59b842707d8f5d624
Parents: 69e0f5a
Author: Joris Van Remoortere <jo...@gmail.com>
Authored: Thu Jun 25 21:05:31 2015 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Thu Jun 25 21:05:31 2015 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/Makefile.am          |   4 +-
 3rdparty/libprocess/src/openssl_util.cpp | 248 ++++++++++++++++++++++++++
 3rdparty/libprocess/src/openssl_util.hpp |  97 ++++++++++
 3 files changed, 348 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/0b04fec9/3rdparty/libprocess/Makefile.am
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/Makefile.am b/3rdparty/libprocess/Makefile.am
index c781f6c..0a14eb9 100644
--- a/3rdparty/libprocess/Makefile.am
+++ b/3rdparty/libprocess/Makefile.am
@@ -66,7 +66,9 @@ if ENABLE_SSL
 libprocess_la_SOURCES +=	\
     src/libevent_ssl_socket.cpp	\
     src/openssl.cpp		\
-    src/openssl.hpp
+    src/openssl.hpp		\
+    src/openssl_util.cpp	\
+    src/openssl_util.hpp
 endif
 
 libprocess_la_CPPFLAGS =		\

http://git-wip-us.apache.org/repos/asf/mesos/blob/0b04fec9/3rdparty/libprocess/src/openssl_util.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/openssl_util.cpp b/3rdparty/libprocess/src/openssl_util.cpp
new file mode 100644
index 0000000..cd38f17
--- /dev/null
+++ b/3rdparty/libprocess/src/openssl_util.cpp
@@ -0,0 +1,248 @@
+#include "openssl_util.hpp"
+
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include <stout/net.hpp>
+#include <stout/stringify.hpp>
+
+// TODO(jmlvanre): Add higher level abstractions for key and
+// certificate generation.
+
+namespace process {
+namespace network {
+namespace openssl {
+
+Try<EVP_PKEY*> generate_private_rsa_key(int bits, unsigned long _exponent)
+{
+  // Allocate the in-memory structure for the private key.
+  EVP_PKEY* private_key = EVP_PKEY_new();
+  if (private_key == NULL) {
+    return Error("Failed to allocate key: EVP_PKEY_new");
+  }
+
+  // Allocate space for the exponent.
+  BIGNUM* exponent = BN_new();
+  if (exponent == NULL) {
+    EVP_PKEY_free(private_key);
+    return Error("Failed to allocate exponent: BN_new");
+  }
+
+  // Assign the exponent.
+  if (BN_set_word(exponent, _exponent) != 1) {
+    BN_free(exponent);
+    EVP_PKEY_free(private_key);
+    return Error("Failed to set exponent: BN_set_word");
+  }
+
+  // Allocate the in-memory structure for the key pair.
+  RSA* rsa = RSA_new();
+  if (rsa == NULL) {
+    BN_free(exponent);
+    EVP_PKEY_free(private_key);
+    return Error("Failed to allocate RSA: RSA_new");
+  }
+
+  // Generate the RSA key pair.
+  if (RSA_generate_key_ex(rsa, bits, exponent, NULL) != 1) {
+    RSA_free(rsa);
+    BN_free(exponent);
+    EVP_PKEY_free(private_key);
+    return Error(ERR_error_string(ERR_get_error(), NULL));
+  }
+
+  // We no longer need the exponent, so let's free it.
+  BN_free(exponent);
+
+  // Associate the RSA key with the private key. If this association
+  // is successful, then the RSA key will be freed when the private
+  // key is freed.
+  if (EVP_PKEY_assign_RSA(private_key, rsa) != 1) {
+    RSA_free(rsa);
+    EVP_PKEY_free(private_key);
+    return Error("Failed to assign RSA key: EVP_PKEY_assign_RSA");
+  }
+
+  return private_key;
+}
+
+
+Try<X509*> generate_x509(
+    EVP_PKEY* subject_key,
+    EVP_PKEY* sign_key,
+    const Option<X509*>& parent_certificate,
+    int serial,
+    int days,
+    Option<std::string> hostname)
+{
+  Option<X509_NAME*> issuer_name = None();
+  if (parent_certificate.isNone()) {
+    // If there is no parent certificate, then the subject and
+    // signing key must be the same.
+    if (subject_key != sign_key) {
+      return Error("Subject vs signing key mismatch");
+    }
+  } else {
+    // If there is a parent certificate, then set the issuer name to
+    // be that of the parent.
+    issuer_name = X509_get_subject_name(parent_certificate.get());
+
+    if (issuer_name.get() == NULL) {
+      return Error("Failed to get subject name of parent certificate: "
+        "X509_get_subject_name");
+    }
+  }
+
+  // Allocate the in-memory structure for the certificate.
+  X509* x509 = X509_new();
+  if (x509 == NULL) {
+    return Error("Failed to allocate certification: X509_new");
+  }
+
+  // Set the version to V3.
+  if (X509_set_version(x509, 2) != 1) {
+    X509_free(x509);
+    return Error("Failed to set version: X509_set_version");
+  }
+
+  // Set the serial number.
+  if (ASN1_INTEGER_set(X509_get_serialNumber(x509), serial) != 1) {
+    X509_free(x509);
+    return Error("Failed to set serial number: ASN1_INTEGER_set");
+  }
+
+  // Make this certificate valid for 'days' number of days from now.
+  if (X509_gmtime_adj(X509_get_notBefore(x509), 0) == NULL ||
+      X509_gmtime_adj(X509_get_notAfter(x509),
+                      60L * 60L * 24L * days) == NULL) {
+    X509_free(x509);
+    return Error("Failed to set valid days of certificate: X509_gmtime_adj");
+  }
+
+  // Set the public key for our certificate based on the subject key.
+  if (X509_set_pubkey(x509, subject_key) != 1) {
+    X509_free(x509);
+    return Error("Failed to set public key: X509_set_pubkey");
+  }
+
+  // Figure out our hostname if one was not provided.
+  if (hostname.isNone()) {
+    const Try<std::string> _hostname = net::hostname();
+    if (_hostname.isError()) {
+      X509_free(x509);
+      return Error("Failed to determine hostname");
+    }
+
+    hostname = _hostname.get();
+  }
+
+  // Grab the subject name of the new certificate.
+  X509_NAME* name = X509_get_subject_name(x509);
+  if (name == NULL) {
+    X509_free(x509);
+    return Error("Failed to get subject name: X509_get_subject_name");
+  }
+
+  // Set the country code, organization, and common name.
+  if (X509_NAME_add_entry_by_txt(
+          name,
+          "C",
+          MBSTRING_ASC,
+          reinterpret_cast<const unsigned char*>("US"),
+          -1,
+          -1,
+          0) != 1) {
+    X509_free(x509);
+    return Error("Failed to set country code: X509_NAME_add_entry_by_txt");
+  }
+
+  if (X509_NAME_add_entry_by_txt(
+          name,
+          "O",
+          MBSTRING_ASC,
+          reinterpret_cast<const unsigned char*>("Test"),
+          -1,
+          -1,
+          0) != 1) {
+    X509_free(x509);
+    return Error("Failed to set organization name: X509_NAME_add_entry_by_txt");
+  }
+
+  if (X509_NAME_add_entry_by_txt(
+          name,
+          "CN",
+          MBSTRING_ASC,
+          reinterpret_cast<const unsigned char*>(hostname.get().c_str()),
+          -1,
+          -1,
+          0) != 1) {
+    X509_free(x509);
+    return Error("Failed to set common name: X509_NAME_add_entry_by_txt");
+  }
+
+  // Set the issuer name to be the same as the subject if it is not
+  // already set (this is a self-signed certificate).
+  if (issuer_name.isNone()) {
+    issuer_name = name;
+  }
+
+  CHECK_SOME(issuer_name);
+  if (X509_set_issuer_name(x509, issuer_name.get()) != 1) {
+    X509_free(x509);
+    return Error("Failed to set issuer name: X509_set_issuer_name");
+  }
+
+  // Sign the certificate with the sign key.
+  if (X509_sign(x509, sign_key, EVP_sha1()) == 0) {
+    X509_free(x509);
+    return Error("Failed to sign certificate: X509_sign");
+  }
+
+  return x509;
+}
+
+
+Try<Nothing> write_key_file(EVP_PKEY* private_key, const Path& path)
+{
+  // We use 'FILE*' here because it is an API requirement by openssl.
+  FILE* file = fopen(path.value.c_str(), "wb");
+  if (file == NULL) {
+    return Error("Failed to open file '" + stringify(path) + "' for writing");
+  }
+
+  if (PEM_write_PrivateKey(file, private_key, NULL, NULL, 0, NULL, NULL) != 1) {
+    fclose(file);
+    return Error("Failed to write private key to file '" + stringify(path) +
+      "': PEM_write_PrivateKey");
+  }
+
+  fclose(file);
+
+  return Nothing();
+}
+
+
+Try<Nothing> write_certificate_file(X509* x509, const Path& path)
+{
+  // We use 'FILE*' here because it is an API requirement by openssl.
+  FILE* file = fopen(path.value.c_str(), "wb");
+  if (file == NULL) {
+    return Error("Failed to open file '" + stringify(path) + "' for writing");
+  }
+
+  if (PEM_write_X509(file, x509) != 1) {
+    fclose(file);
+    return Error("Failed to write certificate to file '" + stringify(path) +
+      "': PEM_write_X509");
+  }
+
+  fclose(file);
+
+  return Nothing();
+}
+
+} // namespace openssl {
+} // namespace network {
+} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/0b04fec9/3rdparty/libprocess/src/openssl_util.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/openssl_util.hpp b/3rdparty/libprocess/src/openssl_util.hpp
new file mode 100644
index 0000000..f855e27
--- /dev/null
+++ b/3rdparty/libprocess/src/openssl_util.hpp
@@ -0,0 +1,97 @@
+#ifndef __OPENSSL_UTIL_HPP__
+#define __OPENSSL_UTIL_HPP__
+
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+#include <stout/nothing.hpp>
+#include <stout/path.hpp>
+#include <stout/try.hpp>
+
+namespace process {
+namespace network {
+namespace openssl {
+
+/**
+ * Generates an RSA key.
+ *
+ * The caller is responsible for calling `EVP_PKEY_free` on the
+ * returned EVP_PKEY.
+ * @see <a href="https://www.openssl.org/docs/crypto/RSA_generate_key.html">RSA_generate_key_ex</a> // NOLINT
+ *
+ * @param bits The modulus size used to generate the key.
+ * @param exponent The public exponent, an odd number.
+ *
+ * @return A pointer to an EVP_PKEY if successful otherwise an Error.
+ */
+Try<EVP_PKEY*> generate_private_rsa_key(
+    int bits = 2048,
+    unsigned long exponent = RSA_F4);
+
+
+/**
+ * Generates an X509 certificate.
+ *
+ * The X509 certificate is generated for the @param subject_key and
+ * signed by the @param sign_key. The caller is responsible for
+ * calling `X509_free` on the returned X509 object.
+ * The common name of the certificate will be set to @param hostname
+ * or that of the localhost if @param hostname is not provided.
+ * If a @param parent_certificate is provided, then the issuer name of
+ * the certificate will be set to the subject name of the
+ * @param parent_certificate. Otherwise, it is assumed this is a
+ * self-signed certificate in which case the @param subject_key must
+ * be the same as the @param sign_key, and the issuer name will be the
+ * same as the subject name.
+ *
+ * @param subject_key The key that will be made public by the
+ *     certificate.
+ * @param sign_key The private key used to sign the certificate.
+ * @param parent_certificate An optional parent certificate that will
+ *     be used to set the issuer name.
+ * @param serial The serial number of the certificate.
+ * @param days The number of days from the current time that the
+ *     certificate will be valid.
+ * @param hostname An optional hostname used to set the common name of
+ *     the certificate.
+ *
+ * @return A pointer to an X509 certificate if successful otherwise an
+ *     Error.
+ */
+Try<X509*> generate_x509(
+    EVP_PKEY* subject_key,
+    EVP_PKEY* sign_key,
+    const Option<X509*>& parent_certificate = None(),
+    int serial = 1,
+    int days = 365,
+    Option<std::string> hostname = None());
+
+
+/**
+ * Writes a private key (EVP_PKEY) to a file on disk.
+ * @see <a href="https://www.openssl.org/docs/crypto/pem.html">PEM_write_PrivateKey</a> // NOLINT
+ *
+ * @param private_key The private key to write.
+ * @param path The file location to create the file.
+ *
+ * @return Nothing if successful otherwise an Error.
+ */
+Try<Nothing> write_key_file(EVP_PKEY* private_key, const Path& path);
+
+
+/**
+ * Writes an X509 certificate (X509) to a file on disk.
+ * @see <a href="https://www.openssl.org/docs/crypto/pem.html">PEM_write_X509</a> // NOLINT
+ *
+ * @param x509 The certificate to write.
+ * @param path The file location to create the file.
+ *
+ * @return Nothing if successful otherwise an Error.
+ */
+Try<Nothing> write_certificate_file(X509* x509, const Path& path);
+
+} // namespace openssl {
+} // namespace network {
+} // namespace process {
+
+#endif // __OPENSSL_UTIL_HPP__


[3/5] mesos git commit: Refactor Openssl certificate Subject Alternative Name logic.

Posted by be...@apache.org.
Refactor Openssl certificate Subject Alternative Name logic.

Review: https://reviews.apache.org/r/35853


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/69e0f5a2
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/69e0f5a2
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/69e0f5a2

Branch: refs/heads/master
Commit: 69e0f5a29449835e8001614a4a0d54a6c456d4be
Parents: ed84f36
Author: Joris Van Remoortere <jo...@gmail.com>
Authored: Thu Jun 25 21:05:20 2015 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Thu Jun 25 21:05:20 2015 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/src/openssl.cpp | 73 +++++++++++++++++---------------
 1 file changed, 38 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/69e0f5a2/3rdparty/libprocess/src/openssl.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/openssl.cpp b/3rdparty/libprocess/src/openssl.cpp
index 090e985..40b60bf 100644
--- a/3rdparty/libprocess/src/openssl.cpp
+++ b/3rdparty/libprocess/src/openssl.cpp
@@ -389,7 +389,8 @@ void initialize()
         EXIT(EXIT_FAILURE) << "Could not load default CA file and/or directory";
       }
 
-      VLOG(2) << "Using default CA file and/or directory";
+      VLOG(2) << "Using default CA file '" << X509_get_default_cert_file()
+              << "' and/or directory '" << X509_get_default_cert_dir() << "'";
     }
 
     // Set SSL peer verification callback.
@@ -488,44 +489,46 @@ Try<Nothing> verify(const SSL* const ssl, const Option<string>& hostname)
       : Try<Nothing>(Nothing());
   }
 
-  int extcount = X509_get_ext_count(cert);
-  if (extcount <= 0) {
-    X509_free(cert);
-    return Error("X509_get_ext_count failed: " + stringify(extcount));
-  }
-
-  for (int i = 0; i < extcount; i++) {
-    X509_EXTENSION* ext = X509_get_ext(cert, i);
-
-    const string extstr =
-      OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
-
-    if (extstr == "subjectAltName") {
-#if OPENSSL_VERSION_NUMBER <= 0x00909000L
-      X509V3_EXT_METHOD* method = X509V3_EXT_get(ext);
-#else
-      const X509V3_EXT_METHOD* method = X509V3_EXT_get(ext);
-#endif
-      if (method == NULL) {
-        break;
-      }
-
-      const unsigned char* data = ext->value->data;
-
-      STACK_OF(CONF_VALUE)* values = method->i2v(
-          method,
-          method->d2i(NULL, &data, ext->value->length),
-          NULL);
-
-      for (int j = 0; j < sk_CONF_VALUE_num(values); j++) {
-        CONF_VALUE* value = sk_CONF_VALUE_value(values, j);
-        if ((strcmp(value->name, "DNS") == 0) &&
-            (value->value == hostname.get())) {
+  // From https://wiki.openssl.org/index.php/Hostname_validation.
+  // Check the Subject Alternate Name extension (SAN). This is useful
+  // for certificates that serve multiple physical hosts.
+  STACK_OF(GENERAL_NAME)* san_names =
+    reinterpret_cast<STACK_OF(GENERAL_NAME)*>(X509_get_ext_d2i(
+        reinterpret_cast<X509*>(cert),
+        NID_subject_alt_name,
+        NULL,
+        NULL));
+
+  if (san_names != NULL) {
+    int san_names_num = sk_GENERAL_NAME_num(san_names);
+
+    // Check each name within the extension.
+    for (int i = 0; i < san_names_num; i++) {
+      const GENERAL_NAME* current_name = sk_GENERAL_NAME_value(san_names, i);
+
+      if (current_name->type == GEN_DNS) {
+        // Current name is a DNS name, let's check it.
+        const string dns_name =
+          reinterpret_cast<char*>(ASN1_STRING_data(current_name->d.dNSName));
+
+        // Make sure there isn't an embedded NUL character in the DNS name.
+        const size_t length = ASN1_STRING_length(current_name->d.dNSName);
+        if (length != dns_name.length()) {
+          sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
           X509_free(cert);
-          return Nothing();
+          return Error(
+            "X509 certificate malformed: embedded NUL character in DNS name");
+        } else { // Compare expected hostname with the DNS name.
+          if (hostname.get() == dns_name) {
+            sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
+            X509_free(cert);
+            return Nothing();
+          }
         }
       }
     }
+
+    sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
   }
 
   // If we still haven't verified the hostname, try doing it via