| Index: remoting/host/user_authenticator_linux.cc
|
| diff --git a/remoting/host/user_authenticator_linux.cc b/remoting/host/user_authenticator_linux.cc
|
| index d3b837c86ca2804c89474eb0e7a078de3b5e90fa..b1a70cd6e1dbbeac8eff1060812539e406f35c3c 100644
|
| --- a/remoting/host/user_authenticator_linux.cc
|
| +++ b/remoting/host/user_authenticator_linux.cc
|
| @@ -2,10 +2,132 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -#include "remoting/host/user_authenticator_pam.h"
|
| +#include "remoting/host/user_authenticator.h"
|
| +
|
| +#include <security/pam_appl.h>
|
| +#include <stdlib.h>
|
| +
|
| +#include <string>
|
| +
|
| +#include "base/basictypes.h"
|
|
|
| namespace remoting {
|
|
|
| +namespace {
|
| +
|
| +// Class to perform a single PAM user authentication.
|
| +//
|
| +// TODO(lambroslambrou): As pam_authenticate() can be blocking, this needs to
|
| +// expose an asynchronous API, with pam_authenticate() called in a background
|
| +// thread.
|
| +class UserAuthenticatorPam : public UserAuthenticator {
|
| + public:
|
| + UserAuthenticatorPam() {}
|
| + virtual ~UserAuthenticatorPam() {}
|
| + virtual bool Authenticate(const std::string& username,
|
| + const std::string& password);
|
| +
|
| + private:
|
| + // Conversation function passed to PAM as a callback.
|
| + static int ConvFunction(int num_msg,
|
| + const pam_message** msg,
|
| + pam_response** resp,
|
| + void* appdata_ptr);
|
| +
|
| + // Store these for the PAM conversation function.
|
| + std::string username_;
|
| + std::string password_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(UserAuthenticatorPam);
|
| +};
|
| +
|
| +const char kPamServiceName[] = "chromoting";
|
| +
|
| +bool UserAuthenticatorPam::Authenticate(const std::string& username,
|
| + const std::string& password) {
|
| + username_ = username;
|
| + password_ = password;
|
| + pam_conv conversation;
|
| + conversation.conv = ConvFunction;
|
| + conversation.appdata_ptr = static_cast<void*>(this);
|
| + // TODO(lambroslambrou): Allow PAM service name to be configurable.
|
| + pam_handle_t* pam_handle;
|
| + if (pam_start(kPamServiceName, username_.c_str(),
|
| + &conversation, &pam_handle) != PAM_SUCCESS) {
|
| + return false;
|
| + }
|
| +
|
| + // TODO(lambroslambrou): Move to separate thread.
|
| + int pam_status = pam_authenticate(pam_handle, 0);
|
| + pam_end(pam_handle, pam_status);
|
| + return pam_status == PAM_SUCCESS;
|
| +}
|
| +
|
| +// static
|
| +int UserAuthenticatorPam::ConvFunction(int num_msg,
|
| + const pam_message** msg,
|
| + pam_response** resp,
|
| + void* appdata_ptr) {
|
| + if (num_msg <= 0)
|
| + return PAM_CONV_ERR;
|
| + UserAuthenticatorPam* user_auth =
|
| + static_cast<UserAuthenticatorPam*>(appdata_ptr);
|
| + // Must allocate with malloc(), as the calling PAM module will
|
| + // release the memory with free().
|
| + pam_response* resp_tmp = static_cast<pam_response*>(
|
| + malloc(num_msg * sizeof(pam_response)));
|
| + if (resp_tmp == NULL)
|
| + return PAM_CONV_ERR;
|
| +
|
| + bool raise_error = false;
|
| + // On exit from the loop, 'count' will hold the number of initialised items
|
| + // that the cleanup code needs to look at, in case of error.
|
| + int count;
|
| + for (count = 0; count < num_msg; count++) {
|
| + // Alias for readability.
|
| + pam_response* resp_item = &resp_tmp[count];
|
| + resp_item->resp_retcode = 0;
|
| + resp_item->resp = NULL;
|
| + switch (msg[count]->msg_style) {
|
| + case PAM_PROMPT_ECHO_ON:
|
| + resp_item->resp = strdup(user_auth->username_.c_str());
|
| + if (resp_item->resp == NULL)
|
| + raise_error = true;
|
| + break;
|
| + case PAM_PROMPT_ECHO_OFF:
|
| + resp_item->resp = strdup(user_auth->password_.c_str());
|
| + if (resp_item->resp == NULL)
|
| + raise_error = true;
|
| + break;
|
| + case PAM_TEXT_INFO:
|
| + // No response needed, as this instructs the PAM client to display
|
| + // text to the user. Leave as NULL and continue with next prompt.
|
| + break;
|
| + default:
|
| + // Unexpected style code, so abort.
|
| + raise_error = true;
|
| + }
|
| + if (raise_error)
|
| + break;
|
| + }
|
| +
|
| + if (raise_error) {
|
| + // Not passing the response back, so free up any memory used.
|
| + for (int n = 0; n < count; n++) {
|
| + if (resp_tmp[n].resp) {
|
| + free(resp_tmp[n].resp);
|
| + }
|
| + }
|
| + free(resp_tmp);
|
| + return PAM_CONV_ERR;
|
| + } else {
|
| + *resp = resp_tmp;
|
| + return PAM_SUCCESS;
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| // static
|
| UserAuthenticator* UserAuthenticator::Create() {
|
| return new UserAuthenticatorPam();
|
|
|