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