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.
5 #include "remoting/host/pam_authorization_factory_posix.h"
7 #include <security/pam_appl.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/webrtc/libjingle/xmllite/xmlelement.h"
20 class PamAuthorizer
: public protocol::Authenticator
{
22 PamAuthorizer(scoped_ptr
<protocol::Authenticator
> underlying
);
23 ~PamAuthorizer() override
;
25 // protocol::Authenticator interface.
26 State
state() const override
;
27 bool started() const override
;
28 RejectionReason
rejection_reason() const override
;
29 void ProcessMessage(const buzz::XmlElement
* message
,
30 const base::Closure
& resume_callback
) override
;
31 scoped_ptr
<buzz::XmlElement
> GetNextMessage() override
;
32 const std::string
& GetAuthKey() const override
;
33 scoped_ptr
<protocol::ChannelAuthenticator
> CreateChannelAuthenticator()
37 void MaybeCheckLocalLogin();
38 bool IsLocalLoginAllowed();
39 void OnMessageProcessed(const base::Closure
& resume_callback
);
41 static int PamConversation(int num_messages
,
42 const struct pam_message
** messages
,
43 struct pam_response
** responses
,
46 scoped_ptr
<protocol::Authenticator
> underlying_
;
47 enum { NOT_CHECKED
, ALLOWED
, DISALLOWED
} local_login_status_
;
51 PamAuthorizer::PamAuthorizer(scoped_ptr
<protocol::Authenticator
> underlying
)
52 : underlying_(underlying
.Pass()),
53 local_login_status_(NOT_CHECKED
) {
56 PamAuthorizer::~PamAuthorizer() {
59 protocol::Authenticator::State
PamAuthorizer::state() const {
60 if (local_login_status_
== DISALLOWED
) {
63 return underlying_
->state();
67 bool PamAuthorizer::started() const {
68 return underlying_
->started();
71 protocol::Authenticator::RejectionReason
72 PamAuthorizer::rejection_reason() const {
73 if (local_login_status_
== DISALLOWED
) {
74 return INVALID_CREDENTIALS
;
76 return underlying_
->rejection_reason();
80 void PamAuthorizer::ProcessMessage(const buzz::XmlElement
* message
,
81 const base::Closure
& resume_callback
) {
82 // |underlying_| is owned, so Unretained() is safe here.
83 underlying_
->ProcessMessage(message
, base::Bind(
84 &PamAuthorizer::OnMessageProcessed
,
85 base::Unretained(this), resume_callback
));
88 void PamAuthorizer::OnMessageProcessed(const base::Closure
& resume_callback
) {
89 MaybeCheckLocalLogin();
90 resume_callback
.Run();
93 scoped_ptr
<buzz::XmlElement
> PamAuthorizer::GetNextMessage() {
94 scoped_ptr
<buzz::XmlElement
> result(underlying_
->GetNextMessage());
95 MaybeCheckLocalLogin();
99 const std::string
& PamAuthorizer::GetAuthKey() const {
100 return underlying_
->GetAuthKey();
103 scoped_ptr
<protocol::ChannelAuthenticator
>
104 PamAuthorizer::CreateChannelAuthenticator() const {
105 return underlying_
->CreateChannelAuthenticator();
108 void PamAuthorizer::MaybeCheckLocalLogin() {
109 if (local_login_status_
== NOT_CHECKED
&& state() == ACCEPTED
) {
110 local_login_status_
= IsLocalLoginAllowed() ? ALLOWED
: DISALLOWED
;
114 bool PamAuthorizer::IsLocalLoginAllowed() {
115 std::string username
= GetUsername();
116 if (username
.empty()) {
119 struct pam_conv conv
= { PamConversation
, nullptr };
120 pam_handle_t
* handle
= nullptr;
121 int result
= pam_start("chrome-remote-desktop", username
.c_str(),
123 if (result
== PAM_SUCCESS
) {
124 result
= pam_acct_mgmt(handle
, 0);
126 pam_end(handle
, result
);
128 HOST_LOG
<< "Local login check for " << username
129 << (result
== PAM_SUCCESS
? " succeeded." : " failed.");
131 return result
== PAM_SUCCESS
;
134 int PamAuthorizer::PamConversation(int num_messages
,
135 const struct pam_message
** messages
,
136 struct pam_response
** responses
,
138 // Assume we're only being asked to log messages, in which case our response
139 // need to be free()-able zero-initialized memory.
140 *responses
= static_cast<struct pam_response
*>(
141 calloc(num_messages
, sizeof(struct pam_response
)));
143 // We don't expect this function to be called. Since we have no easy way
144 // of returning a response, we consider it to be an error if we're asked
145 // for one and abort. Informational and error messages are logged.
146 for (int i
= 0; i
< num_messages
; ++i
) {
147 const struct pam_message
* message
= messages
[i
];
148 switch (message
->msg_style
) {
150 LOG(ERROR
) << "PAM conversation error message: " << message
->msg
;
153 HOST_LOG
<< "PAM conversation message: " << message
->msg
;
156 LOG(FATAL
) << "Unexpected PAM conversation response required: "
157 << message
->msg
<< "; msg_style = " << message
->msg_style
;
164 PamAuthorizationFactory::PamAuthorizationFactory(
165 scoped_ptr
<protocol::AuthenticatorFactory
> underlying
)
166 : underlying_(underlying
.Pass()) {
169 PamAuthorizationFactory::~PamAuthorizationFactory() {
172 scoped_ptr
<protocol::Authenticator
>
173 PamAuthorizationFactory::CreateAuthenticator(
174 const std::string
& local_jid
,
175 const std::string
& remote_jid
,
176 const buzz::XmlElement
* first_message
) {
177 scoped_ptr
<protocol::Authenticator
> authenticator(
178 underlying_
->CreateAuthenticator(local_jid
, remote_jid
, first_message
));
179 return make_scoped_ptr(new PamAuthorizer(authenticator
.Pass()));
183 } // namespace remoting