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__