// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "remoting/host/pam_authorization_factory_posix.h" #include #include "base/bind.h" #include "base/callback.h" #include "base/environment.h" #include "remoting/base/logging.h" #include "remoting/host/username.h" #include "remoting/protocol/channel_authenticator.h" #include "third_party/libjingle/source/talk/xmllite/xmlelement.h" namespace remoting { namespace { class PamAuthorizer : public protocol::Authenticator { public: PamAuthorizer(scoped_ptr underlying); virtual ~PamAuthorizer(); // protocol::Authenticator interface. virtual State state() const OVERRIDE; virtual bool started() const OVERRIDE; virtual RejectionReason rejection_reason() const OVERRIDE; virtual void ProcessMessage(const buzz::XmlElement* message, const base::Closure& resume_callback) OVERRIDE; virtual scoped_ptr GetNextMessage() OVERRIDE; virtual scoped_ptr CreateChannelAuthenticator() const OVERRIDE; private: void MaybeCheckLocalLogin(); bool IsLocalLoginAllowed(); void OnMessageProcessed(const base::Closure& resume_callback); static int PamConversation(int num_messages, const struct pam_message** messages, struct pam_response** responses, void* context); scoped_ptr underlying_; enum { NOT_CHECKED, ALLOWED, DISALLOWED } local_login_status_; }; } // namespace PamAuthorizer::PamAuthorizer(scoped_ptr underlying) : underlying_(underlying.Pass()), local_login_status_(NOT_CHECKED) { } PamAuthorizer::~PamAuthorizer() { } protocol::Authenticator::State PamAuthorizer::state() const { if (local_login_status_ == DISALLOWED) { return REJECTED; } else { return underlying_->state(); } } bool PamAuthorizer::started() const { return underlying_->started(); } protocol::Authenticator::RejectionReason PamAuthorizer::rejection_reason() const { if (local_login_status_ == DISALLOWED) { return INVALID_CREDENTIALS; } else { return underlying_->rejection_reason(); } } void PamAuthorizer::ProcessMessage(const buzz::XmlElement* message, const base::Closure& resume_callback) { // |underlying_| is owned, so Unretained() is safe here. underlying_->ProcessMessage(message, base::Bind( &PamAuthorizer::OnMessageProcessed, base::Unretained(this), resume_callback)); } void PamAuthorizer::OnMessageProcessed(const base::Closure& resume_callback) { MaybeCheckLocalLogin(); resume_callback.Run(); } scoped_ptr PamAuthorizer::GetNextMessage() { scoped_ptr result(underlying_->GetNextMessage()); MaybeCheckLocalLogin(); return result.Pass(); } scoped_ptr PamAuthorizer::CreateChannelAuthenticator() const { return underlying_->CreateChannelAuthenticator(); } void PamAuthorizer::MaybeCheckLocalLogin() { if (local_login_status_ == NOT_CHECKED && state() == ACCEPTED) { local_login_status_ = IsLocalLoginAllowed() ? ALLOWED : DISALLOWED; } } bool PamAuthorizer::IsLocalLoginAllowed() { std::string username = GetUsername(); if (username.empty()) { return false; } struct pam_conv conv = { PamConversation, NULL }; pam_handle_t* handle = NULL; int result = pam_start("chrome-remote-desktop", username.c_str(), &conv, &handle); if (result == PAM_SUCCESS) { result = pam_acct_mgmt(handle, 0); } pam_end(handle, result); HOST_LOG << "Local login check for " << username << (result == PAM_SUCCESS ? " succeeded." : " failed."); return result == PAM_SUCCESS; } int PamAuthorizer::PamConversation(int num_messages, const struct pam_message** messages, struct pam_response** responses, void* context) { // Assume we're only being asked to log messages, in which case our response // need to be free()-able zero-initialized memory. *responses = static_cast( calloc(num_messages, sizeof(struct pam_response))); // We don't expect this function to be called. Since we have no easy way // of returning a response, we consider it to be an error if we're asked // for one and abort. Informational and error messages are logged. for (int i = 0; i < num_messages; ++i) { const struct pam_message* message = messages[i]; switch (message->msg_style) { case PAM_ERROR_MSG: LOG(ERROR) << "PAM conversation error message: " << message->msg; break; case PAM_TEXT_INFO: HOST_LOG << "PAM conversation message: " << message->msg; break; default: LOG(FATAL) << "Unexpected PAM conversation response required: " << message->msg << "; msg_style = " << message->msg_style; } } return PAM_SUCCESS; } PamAuthorizationFactory::PamAuthorizationFactory( scoped_ptr underlying) : underlying_(underlying.Pass()) { } PamAuthorizationFactory::~PamAuthorizationFactory() { } scoped_ptr PamAuthorizationFactory::CreateAuthenticator( const std::string& local_jid, const std::string& remote_jid, const buzz::XmlElement* first_message) { scoped_ptr authenticator( underlying_->CreateAuthenticator(local_jid, remote_jid, first_message)); return scoped_ptr( new PamAuthorizer(authenticator.Pass())); } } // namespace remoting