You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by me...@apache.org on 2015/03/27 23:51:03 UTC

[3/3] mesos git commit: Split cram_md5 authenticator into declaration and implementation without functional changes.

Split cram_md5 authenticator into declaration and implementation without functional changes.

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


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

Branch: refs/heads/master
Commit: 4809d161ee6ba51983027f358691af1c779fb915
Parents: 17a0658
Author: Till Toenshoff <to...@me.com>
Authored: Fri Mar 27 15:25:04 2015 -0700
Committer: Adam B <ad...@mesosphere.io>
Committed: Fri Mar 27 15:25:04 2015 -0700

----------------------------------------------------------------------
 src/Makefile.am                               |   1 +
 src/authentication/cram_md5/authenticator.cpp | 483 +++++++++++++++++++++
 src/authentication/cram_md5/authenticator.hpp | 444 +------------------
 3 files changed, 488 insertions(+), 440 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/4809d161/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 84a62d4..9ce85a6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -297,6 +297,7 @@ nodist_libmesos_no_3rdparty_la_SOURCES =				\
 # TODO(tillt): Remove authentication/cram_md5/* which will enable us to
 # lose the immediate cyrus-sasl2 dependency.
 libmesos_no_3rdparty_la_SOURCES =					\
+	authentication/cram_md5/authenticator.cpp			\
 	authentication/cram_md5/auxprop.cpp				\
 	authorizer/authorizer.cpp					\
 	common/attributes.cpp						\

http://git-wip-us.apache.org/repos/asf/mesos/blob/4809d161/src/authentication/cram_md5/authenticator.cpp
----------------------------------------------------------------------
diff --git a/src/authentication/cram_md5/authenticator.cpp b/src/authentication/cram_md5/authenticator.cpp
new file mode 100644
index 0000000..2125b45
--- /dev/null
+++ b/src/authentication/cram_md5/authenticator.cpp
@@ -0,0 +1,483 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>   // For size_t needed by sasl.h.
+
+#include <sasl/sasl.h>
+#include <sasl/saslplug.h>
+
+#include <map>
+#include <vector>
+
+#include <mesos/mesos.hpp>
+
+#include <process/defer.hpp>
+#include <process/once.hpp>
+#include <process/protobuf.hpp>
+
+#include <stout/check.hpp>
+
+#include "authentication/cram_md5/auxprop.hpp"
+
+#include "authenticator.hpp"
+
+#include "messages/messages.hpp"
+
+namespace mesos {
+namespace internal {
+namespace cram_md5 {
+
+using namespace process;
+using std::string;
+
+class CRAMMD5AuthenticatorProcess
+  : public ProtobufProcess<CRAMMD5AuthenticatorProcess>
+{
+public:
+  explicit CRAMMD5AuthenticatorProcess(const UPID& _pid)
+    : ProcessBase(ID::generate("crammd5_authenticator")),
+      status(READY),
+      pid(_pid),
+      connection(NULL) {}
+
+  virtual ~CRAMMD5AuthenticatorProcess()
+  {
+    if (connection != NULL) {
+      sasl_dispose(&connection);
+    }
+  }
+
+  virtual void finalize()
+  {
+    discarded(); // Fail the promise.
+  }
+
+  Future<Option<string>> authenticate()
+  {
+    static Once* initialize = new Once();
+    static bool initialized = false;
+
+    if (!initialize->once()) {
+      LOG(INFO) << "Initializing server SASL";
+
+      int result = sasl_server_init(NULL, "mesos");
+
+      if (result != SASL_OK) {
+        string error = "Failed to initialize SASL: ";
+        error += sasl_errstring(result, NULL, NULL);
+        LOG(ERROR) << error;
+        AuthenticationErrorMessage message;
+        message.set_error(error);
+        send(pid, message);
+        status = ERROR;
+        promise.fail(error);
+        initialize->done();
+        return promise.future();
+      }
+
+      result = sasl_auxprop_add_plugin(
+          InMemoryAuxiliaryPropertyPlugin::name(),
+          &InMemoryAuxiliaryPropertyPlugin::initialize);
+
+      if (result != SASL_OK) {
+        string error =
+          "Failed to add \"in-memory\" auxiliary property plugin: ";
+        error += sasl_errstring(result, NULL, NULL);
+        LOG(ERROR) << error;
+        AuthenticationErrorMessage message;
+        message.set_error(error);
+        send(pid, message);
+        status = ERROR;
+        promise.fail(error);
+        initialize->done();
+        return promise.future();
+      }
+
+      initialized = true;
+
+      initialize->done();
+    }
+
+    if (!initialized) {
+      promise.fail("Failed to initialize SASL");
+      return promise.future();
+    }
+
+    if (status != READY) {
+      return promise.future();
+    }
+
+    callbacks[0].id = SASL_CB_GETOPT;
+    callbacks[0].proc = (int(*)()) &getopt;
+    callbacks[0].context = NULL;
+
+    callbacks[1].id = SASL_CB_CANON_USER;
+    callbacks[1].proc = (int(*)()) &canonicalize;
+    // Pass in the principal so we can set it in canon_user().
+    callbacks[1].context = &principal;
+
+    callbacks[2].id = SASL_CB_LIST_END;
+    callbacks[2].proc = NULL;
+    callbacks[2].context = NULL;
+
+    LOG(INFO) << "Creating new server SASL connection";
+
+    int result = sasl_server_new(
+        "mesos",    // Registered name of service.
+        NULL,       // Server's FQDN; NULL uses gethostname().
+        NULL,       // The user realm used for password lookups;
+                    // NULL means default to FQDN.
+                    // NOTE: This does not affect Kerberos.
+        NULL, NULL, // IP address information strings.
+        callbacks,  // Callbacks supported only for this connection.
+        0,          // Security flags (security layers are enabled
+                    // using security properties, separately).
+        &connection);
+
+    if (result != SASL_OK) {
+      string error = "Failed to create server SASL connection: ";
+      error += sasl_errstring(result, NULL, NULL);
+      LOG(ERROR) << error;
+      AuthenticationErrorMessage message;
+      message.set_error(error);
+      send(pid, message);
+      status = ERROR;
+      promise.fail(error);
+      return promise.future();
+    }
+
+    // Get the list of mechanisms.
+    const char* output = NULL;
+    unsigned length = 0;
+    int count = 0;
+
+    result = sasl_listmech(
+        connection,  // The context for this connection.
+        NULL,        // Not supported.
+        "",          // What to prepend to the output string.
+        ",",         // What to separate mechanisms with.
+        "",          // What to append to the output string.
+        &output,     // The output string.
+        &length,     // The length of the output string.
+        &count);     // The count of the mechanisms in output.
+
+    if (result != SASL_OK) {
+      string error = "Failed to get list of mechanisms: ";
+      LOG(WARNING) << error << sasl_errstring(result, NULL, NULL);
+      AuthenticationErrorMessage message;
+      error += sasl_errdetail(connection);
+      message.set_error(error);
+      send(pid, message);
+      status = ERROR;
+      promise.fail(error);
+      return promise.future();
+    }
+
+    std::vector<string> mechanisms = strings::tokenize(output, ",");
+
+    // Send authentication mechanisms.
+    AuthenticationMechanismsMessage message;
+    foreach (const string& mechanism, mechanisms) {
+      message.add_mechanisms(mechanism);
+    }
+
+    send(pid, message);
+
+    status = STARTING;
+
+    // Stop authenticating if nobody cares.
+    promise.future().onDiscard(defer(self(), &Self::discarded));
+
+    return promise.future();
+  }
+
+protected:
+  virtual void initialize()
+  {
+    link(pid); // Don't bother waiting for a lost authenticatee.
+
+    // Anticipate start and steps messages from the client.
+    install<AuthenticationStartMessage>(
+        &CRAMMD5AuthenticatorProcess::start,
+        &AuthenticationStartMessage::mechanism,
+        &AuthenticationStartMessage::data);
+
+    install<AuthenticationStepMessage>(
+        &CRAMMD5AuthenticatorProcess::step,
+        &AuthenticationStepMessage::data);
+  }
+
+  virtual void exited(const UPID& _pid)
+  {
+    if (pid == _pid) {
+      status = ERROR;
+      promise.fail("Failed to communicate with authenticatee");
+    }
+  }
+
+  void start(const string& mechanism, const string& data)
+  {
+    if (status != STARTING) {
+      AuthenticationErrorMessage message;
+      message.set_error("Unexpected authentication 'start' received");
+      send(pid, message);
+      status = ERROR;
+      promise.fail(message.error());
+      return;
+    }
+
+    LOG(INFO) << "Received SASL authentication start";
+
+    // Start the server.
+    const char* output = NULL;
+    unsigned length = 0;
+
+    int result = sasl_server_start(
+        connection,
+        mechanism.c_str(),
+        data.length() == 0 ? NULL : data.data(),
+        data.length(),
+        &output,
+        &length);
+
+    handle(result, output, length);
+  }
+
+  void step(const string& data)
+  {
+    if (status != STEPPING) {
+      AuthenticationErrorMessage message;
+      message.set_error("Unexpected authentication 'step' received");
+      send(pid, message);
+      status = ERROR;
+      promise.fail(message.error());
+      return;
+    }
+
+    LOG(INFO) << "Received SASL authentication step";
+
+    const char* output = NULL;
+    unsigned length = 0;
+
+    int result = sasl_server_step(
+        connection,
+        data.length() == 0 ? NULL : data.data(),
+        data.length(),
+        &output,
+        &length);
+
+    handle(result, output, length);
+  }
+
+  void discarded()
+  {
+    status = DISCARDED;
+    promise.fail("Authentication discarded");
+  }
+
+private:
+  static int getopt(
+      void* context,
+      const char* plugin,
+      const char* option,
+      const char** result,
+      unsigned* length)
+  {
+    bool found = false;
+    if (string(option) == "auxprop_plugin") {
+      *result = "in-memory-auxprop";
+      found = true;
+    } else if (string(option) == "mech_list") {
+      *result = "CRAM-MD5";
+      found = true;
+    } else if (string(option) == "pwcheck_method") {
+      *result = "auxprop";
+      found = true;
+    }
+
+    if (found && length != NULL) {
+      *length = strlen(*result);
+    }
+
+    return SASL_OK;
+  }
+
+  // Callback for canonicalizing the username (principal). We use it
+  // to record the principal in CRAMMD5Authenticator.
+  static int canonicalize(
+      sasl_conn_t* connection,
+      void* context,
+      const char* input,
+      unsigned inputLength,
+      unsigned flags,
+      const char* userRealm,
+      char* output,
+      unsigned outputMaxLength,
+      unsigned* outputLength)
+  {
+    CHECK_NOTNULL(input);
+    CHECK_NOTNULL(context);
+    CHECK_NOTNULL(output);
+
+    // Save the input.
+    Option<string>* principal =
+      static_cast<Option<string>*>(context);
+    CHECK(principal->isNone());
+    *principal = string(input, inputLength);
+
+    // Tell SASL that the canonical username is the same as the
+    // client-supplied username.
+    memcpy(output, input, inputLength);
+    *outputLength = inputLength;
+
+    return SASL_OK;
+  }
+
+  // Helper for handling result of server start and step.
+  void handle(int result, const char* output, unsigned length)
+  {
+    if (result == SASL_OK) {
+      // Principal must have been set if authentication succeeded.
+      CHECK_SOME(principal);
+
+      LOG(INFO) << "Authentication success";
+      // Note that we're not using SASL_SUCCESS_DATA which means that
+      // we should not have any data to send when we get a SASL_OK.
+      CHECK(output == NULL);
+      send(pid, AuthenticationCompletedMessage());
+      status = COMPLETED;
+      promise.set(principal);
+    } else if (result == SASL_CONTINUE) {
+      LOG(INFO) << "Authentication requires more steps";
+      AuthenticationStepMessage message;
+      message.set_data(CHECK_NOTNULL(output), length);
+      send(pid, message);
+      status = STEPPING;
+    } else if (result == SASL_NOUSER || result == SASL_BADAUTH) {
+      LOG(WARNING) << "Authentication failure: "
+                   << sasl_errstring(result, NULL, NULL);
+      send(pid, AuthenticationFailedMessage());
+      status = FAILED;
+      promise.set(Option<string>::none());
+    } else {
+      LOG(ERROR) << "Authentication error: "
+                 << sasl_errstring(result, NULL, NULL);
+      AuthenticationErrorMessage message;
+      string error(sasl_errdetail(connection));
+      message.set_error(error);
+      send(pid, message);
+      status = ERROR;
+      promise.fail(message.error());
+    }
+  }
+
+  enum {
+    READY,
+    STARTING,
+    STEPPING,
+    COMPLETED,
+    FAILED,
+    ERROR,
+    DISCARDED
+  } status;
+
+  sasl_callback_t callbacks[3];
+
+  const UPID pid;
+
+  sasl_conn_t* connection;
+
+  Promise<Option<string>> promise;
+
+  Option<string> principal;
+};
+
+
+namespace secrets {
+
+// Loads secrets (principal -> secret) into the in-memory auxiliary
+// property plugin that is used by the authenticators.
+void load(const std::map<string, string>& secrets)
+{
+  Multimap<string, Property> properties;
+
+  foreachpair (const string& principal,
+               const string& secret, secrets) {
+    Property property;
+    property.name = SASL_AUX_PASSWORD_PROP;
+    property.values.push_back(secret);
+    properties.put(principal, property);
+  }
+
+  InMemoryAuxiliaryPropertyPlugin::load(properties);
+}
+
+void load(const Credentials& credentials)
+{
+  std::map<string, string> secrets;
+  foreach(const Credential& credential, credentials.credentials()) {
+    secrets[credential.principal()] = credential.secret();
+  }
+  load(secrets);
+}
+
+} // namespace secrets {
+
+
+Try<Authenticator*> CRAMMD5Authenticator::create()
+{
+  return new CRAMMD5Authenticator();
+}
+
+
+CRAMMD5Authenticator::CRAMMD5Authenticator() : process(NULL) {}
+
+
+CRAMMD5Authenticator::~CRAMMD5Authenticator()
+{
+  if (process != NULL) {
+    // TODO(vinod): As a short term fix for the race condition #1 in
+    // MESOS-1866, we inject the 'terminate' event at the end of the
+    // CRAMMD5AuthenticatorProcess queue instead of at the front.
+    // The long term fix for this is https://reviews.apache.org/r/25945/.
+    terminate(process, false);
+
+    wait(process);
+    delete process;
+  }
+}
+
+
+void CRAMMD5Authenticator::initialize(const UPID& pid)
+{
+  CHECK(process == NULL) << "Authenticator has already been initialized";
+  process = new CRAMMD5AuthenticatorProcess(pid);
+  spawn(process);
+}
+
+
+Future<Option<string>> CRAMMD5Authenticator::authenticate(void)
+{
+  CHECK(process != NULL) << "Authenticator has not been initialized";
+  return dispatch(
+      process, &CRAMMD5AuthenticatorProcess::authenticate);
+}
+
+} // namespace cram_md5 {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/4809d161/src/authentication/cram_md5/authenticator.hpp
----------------------------------------------------------------------
diff --git a/src/authentication/cram_md5/authenticator.hpp b/src/authentication/cram_md5/authenticator.hpp
index c6f465f..3463d7c 100644
--- a/src/authentication/cram_md5/authenticator.hpp
+++ b/src/authentication/cram_md5/authenticator.hpp
@@ -19,28 +19,15 @@
 #ifndef __AUTHENTICATION_CRAM_MD5_AUTHENTICATOR_HPP__
 #define __AUTHENTICATION_CRAM_MD5_AUTHENTICATOR_HPP__
 
-#include <sasl/sasl.h>
-#include <sasl/saslplug.h>
-
 #include <string>
-#include <vector>
-
-#include <mesos/mesos.hpp>
 
 #include <mesos/module/authenticator.hpp>
 
-#include <process/defer.hpp>
 #include <process/future.hpp>
 #include <process/id.hpp>
-#include <process/once.hpp>
-#include <process/process.hpp>
-#include <process/protobuf.hpp>
-
-#include <stout/check.hpp>
 
-#include "authentication/cram_md5/auxprop.hpp"
-
-#include "messages/messages.hpp"
+#include <stout/option.hpp>
+#include <stout/try.hpp>
 
 namespace mesos {
 namespace internal {
@@ -77,439 +64,16 @@ private:
 };
 
 
-class CRAMMD5AuthenticatorProcess
-  : public ProtobufProcess<CRAMMD5AuthenticatorProcess>
-{
-public:
-  explicit CRAMMD5AuthenticatorProcess(const process::UPID& _pid)
-    : ProcessBase(process::ID::generate("crammd5_authenticator")),
-      status(READY),
-      pid(_pid),
-      connection(NULL) {}
-
-  virtual ~CRAMMD5AuthenticatorProcess()
-  {
-    if (connection != NULL) {
-      sasl_dispose(&connection);
-    }
-  }
-
-  virtual void finalize()
-  {
-    discarded(); // Fail the promise.
-  }
-
-  process::Future<Option<std::string>> authenticate()
-  {
-    static process::Once* initialize = new process::Once();
-    static bool initialized = false;
-
-    if (!initialize->once()) {
-      LOG(INFO) << "Initializing server SASL";
-
-      int result = sasl_server_init(NULL, "mesos");
-
-      if (result != SASL_OK) {
-        std::string error = "Failed to initialize SASL: ";
-        error += sasl_errstring(result, NULL, NULL);
-        LOG(ERROR) << error;
-        AuthenticationErrorMessage message;
-        message.set_error(error);
-        send(pid, message);
-        status = ERROR;
-        promise.fail(error);
-        initialize->done();
-        return promise.future();
-      }
-
-      result = sasl_auxprop_add_plugin(
-          InMemoryAuxiliaryPropertyPlugin::name(),
-          &InMemoryAuxiliaryPropertyPlugin::initialize);
-
-      if (result != SASL_OK) {
-        std::string error =
-          "Failed to add \"in-memory\" auxiliary property plugin: ";
-        error += sasl_errstring(result, NULL, NULL);
-        LOG(ERROR) << error;
-        AuthenticationErrorMessage message;
-        message.set_error(error);
-        send(pid, message);
-        status = ERROR;
-        promise.fail(error);
-        initialize->done();
-        return promise.future();
-      }
-
-      initialized = true;
-
-      initialize->done();
-    }
-
-    if (!initialized) {
-      promise.fail("Failed to initialize SASL");
-      return promise.future();
-    }
-
-    if (status != READY) {
-      return promise.future();
-    }
-
-    callbacks[0].id = SASL_CB_GETOPT;
-    callbacks[0].proc = (int(*)()) &getopt;
-    callbacks[0].context = NULL;
-
-    callbacks[1].id = SASL_CB_CANON_USER;
-    callbacks[1].proc = (int(*)()) &canonicalize;
-    // Pass in the principal so we can set it in canon_user().
-    callbacks[1].context = &principal;
-
-    callbacks[2].id = SASL_CB_LIST_END;
-    callbacks[2].proc = NULL;
-    callbacks[2].context = NULL;
-
-    LOG(INFO) << "Creating new server SASL connection";
-
-    int result = sasl_server_new(
-        "mesos",    // Registered name of service.
-        NULL,       // Server's FQDN; NULL uses gethostname().
-        NULL,       // The user realm used for password lookups;
-                    // NULL means default to FQDN.
-                    // NOTE: This does not affect Kerberos.
-        NULL, NULL, // IP address information strings.
-        callbacks,  // Callbacks supported only for this connection.
-        0,          // Security flags (security layers are enabled
-                    // using security properties, separately).
-        &connection);
-
-    if (result != SASL_OK) {
-      std::string error = "Failed to create server SASL connection: ";
-      error += sasl_errstring(result, NULL, NULL);
-      LOG(ERROR) << error;
-      AuthenticationErrorMessage message;
-      message.set_error(error);
-      send(pid, message);
-      status = ERROR;
-      promise.fail(error);
-      return promise.future();
-    }
-
-    // Get the list of mechanisms.
-    const char* output = NULL;
-    unsigned length = 0;
-    int count = 0;
-
-    result = sasl_listmech(
-        connection,  // The context for this connection.
-        NULL,        // Not supported.
-        "",          // What to prepend to the output string.
-        ",",         // What to separate mechanisms with.
-        "",          // What to append to the output string.
-        &output,     // The output string.
-        &length,     // The length of the output string.
-        &count);     // The count of the mechanisms in output.
-
-    if (result != SASL_OK) {
-      std::string error = "Failed to get list of mechanisms: ";
-      LOG(WARNING) << error << sasl_errstring(result, NULL, NULL);
-      AuthenticationErrorMessage message;
-      error += sasl_errdetail(connection);
-      message.set_error(error);
-      send(pid, message);
-      status = ERROR;
-      promise.fail(error);
-      return promise.future();
-    }
-
-    std::vector<std::string> mechanisms = strings::tokenize(output, ",");
-
-    // Send authentication mechanisms.
-    AuthenticationMechanismsMessage message;
-    foreach (const std::string& mechanism, mechanisms) {
-      message.add_mechanisms(mechanism);
-    }
-
-    send(pid, message);
-
-    status = STARTING;
-
-    // Stop authenticating if nobody cares.
-    promise.future().onDiscard(defer(self(), &Self::discarded));
-
-    return promise.future();
-  }
-
-protected:
-  virtual void initialize()
-  {
-    link(pid); // Don't bother waiting for a lost authenticatee.
-
-    // Anticipate start and steps messages from the client.
-    install<AuthenticationStartMessage>(
-        &CRAMMD5AuthenticatorProcess::start,
-        &AuthenticationStartMessage::mechanism,
-        &AuthenticationStartMessage::data);
-
-    install<AuthenticationStepMessage>(
-        &CRAMMD5AuthenticatorProcess::step,
-        &AuthenticationStepMessage::data);
-  }
-
-  virtual void exited(const process::UPID& _pid)
-  {
-    if (pid == _pid) {
-      status = ERROR;
-      promise.fail("Failed to communicate with authenticatee");
-    }
-  }
-
-  void start(const std::string& mechanism, const std::string& data)
-  {
-    if (status != STARTING) {
-      AuthenticationErrorMessage message;
-      message.set_error("Unexpected authentication 'start' received");
-      send(pid, message);
-      status = ERROR;
-      promise.fail(message.error());
-      return;
-    }
-
-    LOG(INFO) << "Received SASL authentication start";
-
-    // Start the server.
-    const char* output = NULL;
-    unsigned length = 0;
-
-    int result = sasl_server_start(
-        connection,
-        mechanism.c_str(),
-        data.length() == 0 ? NULL : data.data(),
-        data.length(),
-        &output,
-        &length);
-
-    handle(result, output, length);
-  }
-
-  void step(const std::string& data)
-  {
-    if (status != STEPPING) {
-      AuthenticationErrorMessage message;
-      message.set_error("Unexpected authentication 'step' received");
-      send(pid, message);
-      status = ERROR;
-      promise.fail(message.error());
-      return;
-    }
-
-    LOG(INFO) << "Received SASL authentication step";
-
-    const char* output = NULL;
-    unsigned length = 0;
-
-    int result = sasl_server_step(
-        connection,
-        data.length() == 0 ? NULL : data.data(),
-        data.length(),
-        &output,
-        &length);
-
-    handle(result, output, length);
-  }
-
-  void discarded()
-  {
-    status = DISCARDED;
-    promise.fail("Authentication discarded");
-  }
-
-private:
-  static int getopt(
-      void* context,
-      const char* plugin,
-      const char* option,
-      const char** result,
-      unsigned* length)
-  {
-    bool found = false;
-    if (std::string(option) == "auxprop_plugin") {
-      *result = "in-memory-auxprop";
-      found = true;
-    } else if (std::string(option) == "mech_list") {
-      *result = "CRAM-MD5";
-      found = true;
-    } else if (std::string(option) == "pwcheck_method") {
-      *result = "auxprop";
-      found = true;
-    }
-
-    if (found && length != NULL) {
-      *length = strlen(*result);
-    }
-
-    return SASL_OK;
-  }
-
-  // Callback for canonicalizing the username (principal). We use it
-  // to record the principal in CRAMMD5Authenticator.
-  static int canonicalize(
-      sasl_conn_t* connection,
-      void* context,
-      const char* input,
-      unsigned inputLength,
-      unsigned flags,
-      const char* userRealm,
-      char* output,
-      unsigned outputMaxLength,
-      unsigned* outputLength)
-  {
-    CHECK_NOTNULL(input);
-    CHECK_NOTNULL(context);
-    CHECK_NOTNULL(output);
-
-    // Save the input.
-    Option<std::string>* principal =
-      static_cast<Option<std::string>*>(context);
-    CHECK(principal->isNone());
-    *principal = std::string(input, inputLength);
-
-    // Tell SASL that the canonical username is the same as the
-    // client-supplied username.
-    memcpy(output, input, inputLength);
-    *outputLength = inputLength;
-
-    return SASL_OK;
-  }
-
-  // Helper for handling result of server start and step.
-  void handle(int result, const char* output, unsigned length)
-  {
-    if (result == SASL_OK) {
-      // Principal must have been set if authentication succeeded.
-      CHECK_SOME(principal);
-
-      LOG(INFO) << "Authentication success";
-      // Note that we're not using SASL_SUCCESS_DATA which means that
-      // we should not have any data to send when we get a SASL_OK.
-      CHECK(output == NULL);
-      send(pid, AuthenticationCompletedMessage());
-      status = COMPLETED;
-      promise.set(principal);
-    } else if (result == SASL_CONTINUE) {
-      LOG(INFO) << "Authentication requires more steps";
-      AuthenticationStepMessage message;
-      message.set_data(CHECK_NOTNULL(output), length);
-      send(pid, message);
-      status = STEPPING;
-    } else if (result == SASL_NOUSER || result == SASL_BADAUTH) {
-      LOG(WARNING) << "Authentication failure: "
-                   << sasl_errstring(result, NULL, NULL);
-      send(pid, AuthenticationFailedMessage());
-      status = FAILED;
-      promise.set(Option<std::string>::none());
-    } else {
-      LOG(ERROR) << "Authentication error: "
-                 << sasl_errstring(result, NULL, NULL);
-      AuthenticationErrorMessage message;
-      std::string error(sasl_errdetail(connection));
-      message.set_error(error);
-      send(pid, message);
-      status = ERROR;
-      promise.fail(message.error());
-    }
-  }
-
-  enum {
-    READY,
-    STARTING,
-    STEPPING,
-    COMPLETED,
-    FAILED,
-    ERROR,
-    DISCARDED
-  } status;
-
-  sasl_callback_t callbacks[3];
-
-  const process::UPID pid;
-
-  sasl_conn_t* connection;
-
-  process::Promise<Option<std::string>> promise;
-
-  Option<std::string> principal;
-};
-
-
 namespace secrets {
 
 // Loads secrets (principal -> secret) into the in-memory auxiliary
 // property plugin that is used by the authenticators.
-void load(const std::map<std::string, std::string>& secrets)
-{
-  Multimap<std::string, Property> properties;
+void load(const std::map<std::string, std::string>& secrets);
 
-  foreachpair (const std::string& principal,
-               const std::string& secret, secrets) {
-    Property property;
-    property.name = SASL_AUX_PASSWORD_PROP;
-    property.values.push_back(secret);
-    properties.put(principal, property);
-  }
-
-  InMemoryAuxiliaryPropertyPlugin::load(properties);
-}
-
-void load(const Credentials& credentials)
-{
-  std::map<std::string, std::string> secrets;
-  foreach(const Credential& credential, credentials.credentials()) {
-    secrets[credential.principal()] = credential.secret();
-  }
-  load(secrets);
-}
+void load(const Credentials& credentials);
 
 } // namespace secrets {
 
-
-Try<Authenticator*> CRAMMD5Authenticator::create()
-{
-  return new CRAMMD5Authenticator();
-}
-
-
-CRAMMD5Authenticator::CRAMMD5Authenticator() : process(NULL) {}
-
-
-CRAMMD5Authenticator::~CRAMMD5Authenticator()
-{
-  if (process != NULL) {
-    // TODO(vinod): As a short term fix for the race condition #1 in
-    // MESOS-1866, we inject the 'terminate' event at the end of the
-    // CRAMMD5AuthenticatorProcess queue instead of at the front.
-    // The long term fix for this is https://reviews.apache.org/r/25945/.
-    process::terminate(process, false);
-
-    process::wait(process);
-    delete process;
-  }
-}
-
-
-void CRAMMD5Authenticator::initialize(const process::UPID& pid)
-{
-  CHECK(process == NULL) << "Authenticator has already been initialized";
-  process = new CRAMMD5AuthenticatorProcess(pid);
-  process::spawn(process);
-}
-
-
-process::Future<Option<std::string>> CRAMMD5Authenticator::authenticate(void)
-{
-  CHECK(process != NULL) << "Authenticator has not been initialized";
-  return process::dispatch(
-      process, &CRAMMD5AuthenticatorProcess::authenticate);
-}
-
 } // namespace cram_md5 {
 } // namespace internal {
 } // namespace mesos {