Home | History | Annotate | Download | only in host
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "remoting/host/pam_authorization_factory_posix.h"
      6 
      7 #include <security/pam_appl.h>
      8 
      9 #include "base/bind.h"
     10 #include "base/callback.h"
     11 #include "base/environment.h"
     12 #include "base/logging.h"
     13 #include "remoting/base/util.h"
     14 #include "remoting/protocol/channel_authenticator.h"
     15 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
     16 
     17 namespace remoting {
     18 
     19 namespace {
     20 class PamAuthorizer : public protocol::Authenticator {
     21  public:
     22   PamAuthorizer(scoped_ptr<protocol::Authenticator> underlying);
     23   virtual ~PamAuthorizer();
     24 
     25   // protocol::Authenticator interface.
     26   virtual State state() const OVERRIDE;
     27   virtual RejectionReason rejection_reason() const OVERRIDE;
     28   virtual void ProcessMessage(const buzz::XmlElement* message,
     29                               const base::Closure& resume_callback) OVERRIDE;
     30   virtual scoped_ptr<buzz::XmlElement> GetNextMessage() OVERRIDE;
     31   virtual scoped_ptr<protocol::ChannelAuthenticator>
     32       CreateChannelAuthenticator() const OVERRIDE;
     33 
     34  private:
     35   void MaybeCheckLocalLogin();
     36   bool IsLocalLoginAllowed();
     37   void OnMessageProcessed(const base::Closure& resume_callback);
     38 
     39   static int PamConversation(int num_messages,
     40                              const struct pam_message** messages,
     41                              struct pam_response** responses,
     42                              void* context);
     43 
     44   scoped_ptr<protocol::Authenticator> underlying_;
     45   enum { NOT_CHECKED, ALLOWED, DISALLOWED } local_login_status_;
     46 };
     47 }  // namespace
     48 
     49 PamAuthorizer::PamAuthorizer(scoped_ptr<protocol::Authenticator> underlying)
     50     : underlying_(underlying.Pass()),
     51       local_login_status_(NOT_CHECKED) {
     52 }
     53 
     54 PamAuthorizer::~PamAuthorizer() {
     55 }
     56 
     57 protocol::Authenticator::State PamAuthorizer::state() const {
     58   if (local_login_status_ == DISALLOWED) {
     59     return REJECTED;
     60   } else {
     61     return underlying_->state();
     62   }
     63 }
     64 
     65 protocol::Authenticator::RejectionReason
     66 PamAuthorizer::rejection_reason() const {
     67   if (local_login_status_ == DISALLOWED) {
     68     return INVALID_CREDENTIALS;
     69   } else {
     70     return underlying_->rejection_reason();
     71   }
     72 }
     73 
     74 void PamAuthorizer::ProcessMessage(const buzz::XmlElement* message,
     75                                    const base::Closure& resume_callback) {
     76   // |underlying_| is owned, so Unretained() is safe here.
     77   underlying_->ProcessMessage(message, base::Bind(
     78       &PamAuthorizer::OnMessageProcessed,
     79       base::Unretained(this), resume_callback));
     80 }
     81 
     82 void PamAuthorizer::OnMessageProcessed(const base::Closure& resume_callback) {
     83   MaybeCheckLocalLogin();
     84   resume_callback.Run();
     85 }
     86 
     87 scoped_ptr<buzz::XmlElement> PamAuthorizer::GetNextMessage() {
     88   scoped_ptr<buzz::XmlElement> result(underlying_->GetNextMessage());
     89   MaybeCheckLocalLogin();
     90   return result.Pass();
     91 }
     92 
     93 scoped_ptr<protocol::ChannelAuthenticator>
     94 PamAuthorizer::CreateChannelAuthenticator() const {
     95   return underlying_->CreateChannelAuthenticator();
     96 }
     97 
     98 void PamAuthorizer::MaybeCheckLocalLogin() {
     99   if (local_login_status_ == NOT_CHECKED && state() == ACCEPTED) {
    100     local_login_status_ = IsLocalLoginAllowed() ? ALLOWED : DISALLOWED;
    101   }
    102 }
    103 
    104 bool PamAuthorizer::IsLocalLoginAllowed() {
    105   std::string username = GetUsername();
    106   if (username.empty()) {
    107     return false;
    108   }
    109   struct pam_conv conv = { PamConversation, NULL };
    110   pam_handle_t* handle = NULL;
    111   int result = pam_start("chrome-remote-desktop", username.c_str(),
    112                          &conv, &handle);
    113   if (result == PAM_SUCCESS) {
    114     result = pam_acct_mgmt(handle, 0);
    115   }
    116   pam_end(handle, result);
    117 
    118   LOG(INFO) << "Local login check for " << username
    119             << (result == PAM_SUCCESS ? " succeeded." : " failed.");
    120 
    121   return result == PAM_SUCCESS;
    122 }
    123 
    124 int PamAuthorizer::PamConversation(int num_messages,
    125                                    const struct pam_message** messages,
    126                                    struct pam_response** responses,
    127                                    void* context) {
    128   // Assume we're only being asked to log messages, in which case our response
    129   // need to be free()-able zero-initialized memory.
    130   *responses = static_cast<struct pam_response*>(
    131       calloc(num_messages, sizeof(struct pam_response)));
    132 
    133   // We don't expect this function to be called. Since we have no easy way
    134   // of returning a response, we consider it to be an error if we're asked
    135   // for one and abort. Informational and error messages are logged.
    136   for (int i = 0; i < num_messages; ++i) {
    137     const struct pam_message* message = messages[i];
    138     switch (message->msg_style) {
    139       case PAM_ERROR_MSG:
    140         LOG(ERROR) << "PAM conversation error message: " << message->msg;
    141         break;
    142       case PAM_TEXT_INFO:
    143         LOG(INFO) << "PAM conversation message: " << message->msg;
    144         break;
    145       default:
    146         LOG(FATAL) << "Unexpected PAM conversation response required: "
    147                    << message->msg << "; msg_style = " << message->msg_style;
    148     }
    149   }
    150   return PAM_SUCCESS;
    151 }
    152 
    153 
    154 PamAuthorizationFactory::PamAuthorizationFactory(
    155     scoped_ptr<protocol::AuthenticatorFactory> underlying)
    156     : underlying_(underlying.Pass()) {
    157 }
    158 
    159 PamAuthorizationFactory::~PamAuthorizationFactory() {
    160 }
    161 
    162 scoped_ptr<protocol::Authenticator>
    163 PamAuthorizationFactory::CreateAuthenticator(
    164     const std::string& local_jid,
    165     const std::string& remote_jid,
    166     const buzz::XmlElement* first_message) {
    167   scoped_ptr<protocol::Authenticator> authenticator(
    168       underlying_->CreateAuthenticator(local_jid, remote_jid, first_message));
    169   return scoped_ptr<protocol::Authenticator>(
    170       new PamAuthorizer(authenticator.Pass()));
    171 }
    172 
    173 
    174 }  // namespace remoting
    175