Repository: kudu
Updated Branches:
  refs/heads/master a9137c0d2 -> 000cf8286


client: support exporting/importing authentication data

This adds the C++ side of being able to export and import tokens. This
will be useful for Impala in particular -- the front-end can acquire a
token and pass it as part of the plan fragment descriptor to the
backends, so that the backends don't need to use Kerberos to
authenticate.

Change-Id: I846c47feab9a7be0dce625079193146bfc2dac7b
Reviewed-on: http://gerrit.cloudera.org:8080/6086
Tested-by: Kudu Jenkins
Reviewed-by: Todd Lipcon <[email protected]>


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

Branch: refs/heads/master
Commit: 000cf8286890fa023dc39bf96bd8a461c4e77f74
Parents: a9137c0
Author: Todd Lipcon <[email protected]>
Authored: Mon Feb 20 14:28:57 2017 -0800
Committer: Todd Lipcon <[email protected]>
Committed: Thu Feb 23 02:51:07 2017 +0000

----------------------------------------------------------------------
 src/kudu/client/client-test.cc            | 21 +++++++-
 src/kudu/client/client.cc                 | 68 ++++++++++++++++++++++++--
 src/kudu/client/client.h                  | 20 ++++++++
 src/kudu/client/client_builder-internal.h |  1 +
 src/kudu/security/tls_context.cc          | 25 +++++++++-
 src/kudu/security/tls_context.h           |  5 ++
 6 files changed, 135 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/000cf828/src/kudu/client/client-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/client/client-test.cc b/src/kudu/client/client-test.cc
index 2b0f339..bc57d6d 100644
--- a/src/kudu/client/client-test.cc
+++ b/src/kudu/client/client-test.cc
@@ -4652,11 +4652,30 @@ TEST_F(ClientTest, TestConnectToClusterCompatibility) {
 }
 
 // Test that, when the client connects to a cluster, that it gets the relevant
-// certificate authority and authentication token from the master.
+// certificate authority and authentication token from the master. Also checks 
that
+// the data can be exported and re-imported into a new client.
 TEST_F(ClientTest, TestGetSecurityInfoFromMaster) {
   // Client is already connected when the test starts.
   ASSERT_TRUE(client_->data_->messenger_->authn_token() != boost::none);
   ASSERT_EQ(1, 
client_->data_->messenger_->tls_context().trusted_cert_count_for_tests());
+
+  string authn_creds;
+  ASSERT_OK(client_->ExportAuthenticationCredentials(&authn_creds));
+
+  // Creating a new client by importing the authentication data should result 
in the
+  // having the same token.
+  shared_ptr<KuduClient> new_client;
+  ASSERT_OK(KuduClientBuilder()
+            
.add_master_server_addr(cluster_->mini_master()->bound_rpc_addr().ToString())
+            .import_authentication_credentials(authn_creds)
+            .Build(&new_client));
+  ASSERT_EQ(client_->data_->messenger_->authn_token()->ShortDebugString(),
+            new_client->data_->messenger_->authn_token()->ShortDebugString());
+
+  // The new client should yield the same authentication data as the original.
+  string new_authn_creds;
+  ASSERT_OK(new_client->ExportAuthenticationCredentials(&new_authn_creds));
+  ASSERT_EQ(authn_creds, new_authn_creds);
 }
 
 struct ServiceUnavailableRetryParams {

http://git-wip-us.apache.org/repos/asf/kudu/blob/000cf828/src/kudu/client/client.cc
----------------------------------------------------------------------
diff --git a/src/kudu/client/client.cc b/src/kudu/client/client.cc
index 720d6ef..b356227 100644
--- a/src/kudu/client/client.cc
+++ b/src/kudu/client/client.cc
@@ -60,7 +60,9 @@
 #include "kudu/rpc/messenger.h"
 #include "kudu/rpc/request_tracker.h"
 #include "kudu/rpc/sasl_common.h"
+#include "kudu/security/cert.h"
 #include "kudu/security/openssl_util.h"
+#include "kudu/security/tls_context.h"
 #include "kudu/util/init.h"
 #include "kudu/util/logging.h"
 #include "kudu/util/net/dns_resolver.h"
@@ -233,15 +235,53 @@ KuduClientBuilder& 
KuduClientBuilder::default_rpc_timeout(const MonoDelta& timeo
   return *this;
 }
 
+KuduClientBuilder& KuduClientBuilder::import_authentication_credentials(string 
authn_creds) {
+  data_->authn_creds_ = std::move(authn_creds);
+  return *this;
+}
+
+namespace {
+Status ImportAuthnCredsToMessenger(const string& authn_creds,
+                                   Messenger* messenger) {
+  AuthenticationCredentialsPB pb;
+  if (!pb.ParseFromString(authn_creds)) {
+    return Status::InvalidArgument("invalid authentication data");
+  }
+  if (pb.has_authn_token()) {
+    const auto& tok = pb.authn_token();
+    if (!tok.has_token_data() ||
+        !tok.has_signature() ||
+        !tok.has_signing_key_seq_num()) {
+      return Status::InvalidArgument("invalid authentication token");
+    }
+    messenger->set_authn_token(tok);
+  }
+  for (const string& cert_der : pb.ca_cert_ders()) {
+    security::Cert cert;
+    RETURN_NOT_OK_PREPEND(cert.FromString(cert_der, security::DataFormat::DER),
+                          "could not import CA cert");
+    
RETURN_NOT_OK_PREPEND(messenger->mutable_tls_context()->AddTrustedCertificate(cert),
+                          "could not trust CA cert");
+  }
+  return Status::OK();
+}
+} // anonymous namespace
+
 Status KuduClientBuilder::Build(shared_ptr<KuduClient>* client) {
   RETURN_NOT_OK(CheckCPUFlags());
 
-  shared_ptr<KuduClient> c(new KuduClient());
-
   // Init messenger.
   MessengerBuilder builder("client");
-  RETURN_NOT_OK(builder.Build(&c->data_->messenger_));
+  std::shared_ptr<Messenger> messenger;
+  RETURN_NOT_OK(builder.Build(&messenger));
 
+  // Parse and import the provided authn data, if any.
+  if (!data_->authn_creds_.empty()) {
+    RETURN_NOT_OK(ImportAuthnCredsToMessenger(data_->authn_creds_, 
messenger.get()));
+  }
+
+  shared_ptr<KuduClient> c(new KuduClient());
+  c->data_->messenger_ = std::move(messenger);
   c->data_->master_server_addrs_ = data_->master_server_addrs_;
   c->data_->default_admin_operation_timeout_ = 
data_->default_admin_operation_timeout_;
   c->data_->default_rpc_timeout_ = data_->default_rpc_timeout_;
@@ -488,6 +528,28 @@ void KuduClient::SetLatestObservedTimestamp(uint64_t 
ht_timestamp) {
   data_->UpdateLatestObservedTimestamp(ht_timestamp);
 }
 
+Status KuduClient::ExportAuthenticationCredentials(string* authn_creds) const {
+  AuthenticationCredentialsPB pb;
+
+  boost::optional<security::SignedTokenPB> tok = 
data_->messenger_->authn_token();
+  if (tok) {
+    pb.mutable_authn_token()->CopyFrom(*tok);
+  }
+
+  vector<string> cert_ders;
+  
RETURN_NOT_OK_PREPEND(data_->messenger_->tls_context().DumpTrustedCerts(&cert_ders),
+                        "could not export trusted certs");
+  for (auto& der : cert_ders) {
+    pb.add_ca_cert_ders()->assign(std::move(der));
+  }
+
+  if (!pb.SerializeToString(authn_creds)) {
+    return Status::RuntimeError("could not serialize authentication data");
+  }
+
+  return Status::OK();
+}
+
 ////////////////////////////////////////////////////////////
 // KuduTableCreator
 ////////////////////////////////////////////////////////////

http://git-wip-us.apache.org/repos/asf/kudu/blob/000cf828/src/kudu/client/client.h
----------------------------------------------------------------------
diff --git a/src/kudu/client/client.h b/src/kudu/client/client.h
index f8e0c73..d8f2610 100644
--- a/src/kudu/client/client.h
+++ b/src/kudu/client/client.h
@@ -224,6 +224,14 @@ class KUDU_EXPORT KuduClientBuilder {
   /// @return Reference to the updated object.
   KuduClientBuilder& default_rpc_timeout(const MonoDelta& timeout);
 
+  /// Import serialized authentication credentials from another client.
+  ///
+  /// @param [in] authn_creds
+  ///   The serialized authentication credentials, provided by a call to
+  ///   @c KuduClient.ExportAuthenticationCredentials in the C++ client or
+  ///   @c KuduClient#exportAuthenticationCredentials in the Java client.
+  KuduClientBuilder& import_authentication_credentials(std::string 
authn_creds);
+
   /// Create a client object.
   ///
   /// @note KuduClients objects are shared amongst multiple threads and,
@@ -477,6 +485,18 @@ class KUDU_EXPORT KuduClient : public 
sp::enable_shared_from_this<KuduClient> {
   ///   Timestamp encoded in HybridTime format.
   void SetLatestObservedTimestamp(uint64_t ht_timestamp);
 
+  /// Export the current authentication credentials from this client. This 
includes
+  /// the necessary credentials to authenticate to the cluster, as well as to
+  /// authenticate the cluster to the client.
+  ///
+  /// The resulting binary string may be passed into a new C++ client via the
+  /// @c KuduClientBuilder::import_authentication_credentials method, or into 
a new
+  /// Java client via @c KuduClient#importAuthenticationCredentials.
+  ///
+  /// @param [out] authn_creds
+  ///   The resulting binary authentication credsentials.
+  Status ExportAuthenticationCredentials(std::string* authn_creds) const;
+
  private:
   class KUDU_NO_EXPORT Data;
 

http://git-wip-us.apache.org/repos/asf/kudu/blob/000cf828/src/kudu/client/client_builder-internal.h
----------------------------------------------------------------------
diff --git a/src/kudu/client/client_builder-internal.h 
b/src/kudu/client/client_builder-internal.h
index 3136b79..070c244 100644
--- a/src/kudu/client/client_builder-internal.h
+++ b/src/kudu/client/client_builder-internal.h
@@ -34,6 +34,7 @@ class KuduClientBuilder::Data {
   std::vector<std::string> master_server_addrs_;
   MonoDelta default_admin_operation_timeout_;
   MonoDelta default_rpc_timeout_;
+  std::string authn_creds_;
 
   DISALLOW_COPY_AND_ASSIGN(Data);
 };

http://git-wip-us.apache.org/repos/asf/kudu/blob/000cf828/src/kudu/security/tls_context.cc
----------------------------------------------------------------------
diff --git a/src/kudu/security/tls_context.cc b/src/kudu/security/tls_context.cc
index 6106d9e..be298a4 100644
--- a/src/kudu/security/tls_context.cc
+++ b/src/kudu/security/tls_context.cc
@@ -18,6 +18,7 @@
 #include "kudu/security/tls_context.h"
 
 #include <string>
+#include <vector>
 
 #include <boost/algorithm/string/predicate.hpp>
 #include <gflags/gflags.h>
@@ -35,6 +36,7 @@
 #include "kudu/security/tls_handshake.h"
 #include "kudu/util/flag_tags.h"
 #include "kudu/util/net/net_util.h"
+#include "kudu/util/scoped_cleanup.h"
 #include "kudu/util/status.h"
 #include "kudu/util/user.h"
 
@@ -236,6 +238,28 @@ Status TlsContext::AddTrustedCertificate(const Cert& cert) 
{
   return Status::OK();
 }
 
+Status TlsContext::DumpTrustedCerts(vector<string>* cert_ders) const {
+  vector<string> ret;
+  auto* cert_store = SSL_CTX_get_cert_store(ctx_.get());
+
+  CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+  auto unlock = MakeScopedCleanup([&]() {
+      CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+    });
+  for (int i = 0; i < sk_X509_OBJECT_num(cert_store->objs); i++) {
+    X509_OBJECT* obj = sk_X509_OBJECT_value(cert_store->objs, i);
+    if (obj->type != X509_LU_X509) continue;
+    Cert c;
+    c.AdoptAndAddRefRawData(obj->data.x509);
+    string der;
+    RETURN_NOT_OK(c.ToString(&der, DataFormat::DER));
+    ret.emplace_back(std::move(der));
+  }
+
+  cert_ders->swap(ret);
+  return Status::OK();
+}
+
 namespace {
 Status SetCertAttributes(CertRequestGenerator::Config* config) {
   RETURN_NOT_OK_PREPEND(GetFQDN(&config->cn), "could not determine FQDN for 
CSR");
@@ -263,7 +287,6 @@ Status SetCertAttributes(CertRequestGenerator::Config* 
config) {
 Status TlsContext::GenerateSelfSignedCertAndKey() {
   CertRequestGenerator::Config config;
   RETURN_NOT_OK(SetCertAttributes(&config));
-
   // Step 1: generate the private key to be self signed.
   PrivateKey key;
   RETURN_NOT_OK_PREPEND(GeneratePrivateKey(FLAGS_ipki_server_key_size,

http://git-wip-us.apache.org/repos/asf/kudu/blob/000cf828/src/kudu/security/tls_context.h
----------------------------------------------------------------------
diff --git a/src/kudu/security/tls_context.h b/src/kudu/security/tls_context.h
index f92199d..27b9baa 100644
--- a/src/kudu/security/tls_context.h
+++ b/src/kudu/security/tls_context.h
@@ -19,6 +19,7 @@
 
 #include <functional>
 #include <string>
+#include <vector>
 
 #include <boost/optional.hpp>
 
@@ -101,6 +102,10 @@ class TlsContext {
   // If this cert has already been marked as trusted, this has no effect.
   Status AddTrustedCertificate(const Cert& cert);
 
+  // Dump all of the certs that are currently trusted by this context, in DER
+  // form, into 'cert_ders'.
+  Status DumpTrustedCerts(std::vector<std::string>* cert_ders) const;
+
   // Uses 'cert' and 'key' as the cert and key for use with TLS connections.
   //
   // Checks that the CA that issued the signature on 'cert' is already trusted

Reply via email to