Commit e59e30dc authored by jamiewalch@google.com's avatar jamiewalch@google.com

Make curtain-mode controllable by policy.

BUG=110111

Review URL: https://chromiumcodereview.appspot.com/10829306

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@151714 0039d316-1c4b-4281-b951-d872f2087c98
parent f0417774
......@@ -29,11 +29,12 @@ enum HostExitCodes {
kInvalidHostIdExitCode = 3,
kInvalidOauthCredentialsExitCode = 4,
kInvalidHostDomainExitCode = 5,
kLoginScreenNotSupportedExitCode = 6,
// The range of the exit codes that should be interpreted as a permanent error
// condition.
kMinPermanentErrorExitCode = kInvalidHostConfigurationExitCode,
kMaxPermanentErrorExitCode = kInvalidHostDomainExitCode
kMaxPermanentErrorExitCode = kLoginScreenNotSupportedExitCode
};
#if defined(OS_WIN)
......
......@@ -18,30 +18,37 @@ const char* kCGSessionPath =
namespace remoting {
CurtainMode::CurtainMode() : event_handler_(NULL) {
CurtainMode::CurtainMode(const base::Closure& on_session_activate,
const base::Closure& on_error)
: on_session_activate_(on_session_activate),
on_error_(on_error),
connection_active_(false),
event_handler_(NULL) {
}
CurtainMode::~CurtainMode() {
if (event_handler_) {
RemoveEventHandler(event_handler_);
}
SetEnabled(false);
}
bool CurtainMode::Init(const base::Closure& on_session_activate) {
DCHECK(on_session_activate_.is_null());
on_session_activate_ = on_session_activate;
EventTypeSpec event;
event.eventClass = kEventClassSystem;
event.eventKind = kEventSystemUserSessionActivated;
OSStatus result = InstallApplicationEventHandler(
NewEventHandlerUPP(SessionActivateHandler), 1, &event, this,
&event_handler_);
return result == noErr;
void CurtainMode::SetEnabled(bool enabled) {
if (enabled) {
if (connection_active_) {
if (!ActivateCurtain()) {
on_error_.Run();
}
}
} else {
RemoveEventHandler();
}
}
void CurtainMode::OnClientAuthenticated(const std::string& jid) {
// If the current session is attached to the console and is not showing
// the logon screen then switch it out to ensure privacy.
bool CurtainMode::ActivateCurtain() {
// Try to install the switch-in handler. Do this before switching out the
// current session so that the console session is not affected if it fails.
if (!InstallEventHandler()) {
return false;
}
base::mac::ScopedCFTypeRef<CFDictionaryRef> session(
CGSessionCopyCurrentDictionary());
const void* on_console = CFDictionaryGetValue(session,
......@@ -55,8 +62,29 @@ void CurtainMode::OnClientAuthenticated(const std::string& jid) {
} else if (child > 0) {
int status = 0;
waitpid(child, &status, 0);
if (status != 0) {
LOG(ERROR) << kCGSessionPath << " failed.";
return false;
}
} else {
LOG(ERROR) << "fork() failed.";
return false;
}
}
return true;
}
// TODO(jamiewalch): This code assumes at most one client connection at a time.
// Add OnFirstClientConnected and OnLastClientDisconnected optional callbacks
// to the HostStatusObserver interface to address this.
void CurtainMode::OnClientAuthenticated(const std::string& jid) {
connection_active_ = true;
SetEnabled(true);
}
void CurtainMode::OnClientDisconnected(const std::string& jid) {
SetEnabled(false);
connection_active_ = false;
}
OSStatus CurtainMode::SessionActivateHandler(EventHandlerCallRef handler,
......@@ -68,9 +96,28 @@ OSStatus CurtainMode::SessionActivateHandler(EventHandlerCallRef handler,
}
void CurtainMode::OnSessionActivate() {
if (!on_session_activate_.is_null()) {
on_session_activate_.Run();
on_session_activate_.Run();
}
bool CurtainMode::InstallEventHandler() {
OSStatus result = noErr;
if (!event_handler_) {
EventTypeSpec event;
event.eventClass = kEventClassSystem;
event.eventKind = kEventSystemUserSessionActivated;
result = ::InstallApplicationEventHandler(
NewEventHandlerUPP(SessionActivateHandler), 1, &event, this,
&event_handler_);
}
return result == noErr;
}
bool CurtainMode::RemoveEventHandler() {
OSStatus result = noErr;
if (event_handler_) {
result = ::RemoveEventHandler(event_handler_);
}
return result == noErr;
}
} // namespace remoting
......@@ -17,27 +17,46 @@ namespace remoting {
class CurtainMode : public HostStatusObserver {
public:
CurtainMode();
// Set callbacks to be invoked when the switched-out session is switched back
// to the console, or in case of errors activating curtain-mode. Typically,
// remote clients should be disconnected in both cases: for errors, because
// the privacy guarantee of curtain-mode cannot be honoured; for switch-in,
// to ensure that only one connection (console or remote) exists to a session.
// Note that only the session's owner (or someone who knows the password) can
// attach it to the console, so this is safe.
CurtainMode(const base::Closure& on_session_activate,
const base::Closure& on_error);
virtual ~CurtainMode();
// Set the callback to be invoked when the switched-out remote session is
// switched back to the console. Typically, remote connections should be
// closed in this event to make sure that only one connection (console or
// remote) exists to a session at any time to ensure privacy. Note that
// only the session's owner (or someone who knows the owner's password)
// can attach it to the console, so this is safe.
bool Init(const base::Closure& on_session_activate);
// Enable or disable curtain-mode. Note that disabling curtain-mode does not
// deactivate the curtain, but it does remove the switch-in handler, meaning
// that on_session_activate will not be invoked if curtain-mode is disabled.
// Conversely, enabling curtain-mode *does* activate the curtain if there is
// a connected client at the time.
void SetEnabled(bool enabled);
// Overridden from HostStatusObserver
virtual void OnClientAuthenticated(const std::string& jid) OVERRIDE;
virtual void OnClientDisconnected(const std::string& jid) OVERRIDE;
private:
// If the current session is attached to the console and is not showing
// the logon screen then switch it out to ensure privacy.
bool ActivateCurtain();
// Add or remove the switch-in event handler.
bool InstallEventHandler();
bool RemoveEventHandler();
// Handlers for the switch-in event.
static OSStatus SessionActivateHandler(EventHandlerCallRef handler,
EventRef event,
void* user_data);
void OnSessionActivate();
base::Closure on_session_activate_;
base::Closure on_error_;
bool connection_active_;
EventHandlerRef event_handler_;
};
......
......@@ -21,7 +21,7 @@ SIGTERM_EXIT_CODE=143
# has occurred and that the host should not be restarted. Please, keep these
# constants in sync with remoting/host/constants.h.
MIN_PERMANENT_ERROR_EXIT_CODE=2
MAX_PERMANENT_ERROR_EXIT_CODE=5
MAX_PERMANENT_ERROR_EXIT_CODE=6
HOST_PID=0
SIGNAL_WAS_TRAPPED=0
......
......@@ -123,7 +123,8 @@ const char PolicyWatcher::kHostRequireCurtainPolicyName[] =
const char* const PolicyWatcher::kBooleanPolicyNames[] =
{ PolicyWatcher::kNatPolicyName,
PolicyWatcher::kHostRequireTwoFactorPolicyName
PolicyWatcher::kHostRequireTwoFactorPolicyName,
PolicyWatcher::kHostRequireCurtainPolicyName
};
const int PolicyWatcher::kBooleanPolicyNamesNum =
......
......@@ -50,6 +50,7 @@
#include "remoting/protocol/me2me_host_authenticator_factory.h"
#if defined(OS_MACOSX)
#include "base/mac/scoped_cftyperef.h"
#include "base/mac/scoped_nsautorelease_pool.h"
#include "remoting/host/curtain_mode_mac.h"
#include "remoting/host/sighup_listener_mac.h"
......@@ -103,7 +104,14 @@ class HostProcess
allow_nat_traversal_(true),
restarting_(false),
shutting_down_(false),
exit_code_(kSuccessExitCode) {
exit_code_(kSuccessExitCode)
#if defined(OS_MACOSX)
, curtain_(base::Bind(&HostProcess::OnDisconnectRequested,
base::Unretained(this)),
base::Bind(&HostProcess::OnDisconnectRequested,
base::Unretained(this)))
#endif
{
context_.reset(
new ChromotingHostContext(message_loop_.message_loop_proxy()));
context_->Start();
......@@ -319,6 +327,10 @@ class HostProcess
return;
}
if (!host_) {
StartHost();
}
bool bool_value;
std::string string_value;
if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName,
......@@ -329,6 +341,11 @@ class HostProcess
&bool_value)) {
OnNatPolicyUpdate(bool_value);
}
if (policies->GetBoolean(
policy_hack::PolicyWatcher::kHostRequireCurtainPolicyName,
&bool_value)) {
OnCurtainPolicyUpdate(bool_value);
}
}
void OnHostDomainPolicyUpdate(const std::string& host_domain) {
......@@ -356,15 +373,44 @@ class HostProcess
bool policy_changed = allow_nat_traversal_ != nat_traversal_enabled;
allow_nat_traversal_ = nat_traversal_enabled;
if (host_) {
// Restart the host if the policy has changed while the host was
// online.
if (policy_changed)
RestartHost();
if (policy_changed) {
RestartHost();
}
}
void OnCurtainPolicyUpdate(bool curtain_required) {
#if defined(OS_MACOSX)
if (!context_->network_task_runner()->BelongsToCurrentThread()) {
context_->network_task_runner()->PostTask(FROM_HERE, base::Bind(
&HostProcess::OnCurtainPolicyUpdate, base::Unretained(this),
curtain_required));
return;
}
if (curtain_required) {
// If curtain mode is required, then we can't currently support remoting
// the login screen. This is because we don't curtain the login screen
// and the current daemon architecture means that the connction is closed
// immediately after login, leaving the host system uncurtained.
//
// TODO(jamiewalch): Fix this once we have implemented the multi-process
// daemon architecture (crbug.com/134894)
base::mac::ScopedCFTypeRef<CFDictionaryRef> session(
CGSessionCopyCurrentDictionary());
const void* logged_in = CFDictionaryGetValue(session,
kCGSessionLoginDoneKey);
if (logged_in != kCFBooleanTrue) {
Shutdown(kLoginScreenNotSupportedExitCode);
return;
}
host_->AddStatusObserver(&curtain_);
curtain_.SetEnabled(true);
} else {
// Just start the host otherwise.
StartHost();
curtain_.SetEnabled(false);
host_->RemoveStatusObserver(&curtain_);
}
#endif
}
void StartHost() {
......@@ -447,12 +493,6 @@ class HostProcess
base::Unretained(this)));
#endif
#if defined(OS_MACOSX)
curtain_.Init(base::Bind(&HostProcess::OnDisconnectRequested,
base::Unretained(this)));
host_->AddStatusObserver(&curtain_);
#endif
host_->Start();
CreateAuthenticatorFactory();
......
......@@ -916,6 +916,7 @@ def main():
logging.info("Host domain is blocked by policy - exiting.")
os.remove(host.config_file)
return 0
# Nothing to do for Mac-only status 6 (login screen unsupported)
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment