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:14 UTC

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

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__