Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / remoting / host / setup / me2me_native_messaging_host.cc
index fabc8b0..a2a2707 100644 (file)
@@ -3,27 +3,43 @@
 // found in the LICENSE file.
 
 #include "remoting/host/setup/me2me_native_messaging_host.h"
-
 #include <string>
 
 #include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
+#include "base/command_line.h"
 #include "base/logging.h"
 #include "base/strings/stringize_macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread.h"
 #include "base/values.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
 #include "google_apis/google_api_keys.h"
+#include "ipc/ipc_channel.h"
 #include "net/base/net_util.h"
 #include "remoting/base/rsa_key_pair.h"
 #include "remoting/host/pin_hash.h"
 #include "remoting/host/setup/oauth_client.h"
 #include "remoting/protocol/pairing_registry.h"
 
+#if defined(OS_WIN)
+#include <shellapi.h>
+#include "base/win/win_util.h"
+#include "remoting/host/win/security_descriptor.h"
+#endif  // defined(OS_WIN)
+
 namespace {
 
+#if defined(OS_WIN)
+// Windows will use default buffer size when 0 is passed to CreateNamedPipeW().
+const DWORD kBufferSize = 0;
+const int kTimeOutMilliseconds = 2000;
+const char kChromePipeNamePrefix[] = "\\\\.\\pipe\\chrome_remote_desktop.";
+#endif  // defined(OS_WIN)
+
 // redirect_uri to use when authenticating service accounts (service account
 // codes are obtained "out-of-band", i.e., not through an OAuth redirect).
 const char* kServiceAccountRedirectUri = "oob";
@@ -37,10 +53,10 @@ const char* kSupportedFeatures[] = {
 // Helper to extract the "config" part of a message as a DictionaryValue.
 // Returns NULL on failure, and logs an error message.
 scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage(
-    const base::DictionaryValue& message) {
+    scoped_ptr<base::DictionaryValue> message) {
   scoped_ptr<base::DictionaryValue> result;
   const base::DictionaryValue* config_dict;
-  if (message.GetDictionary("config", &config_dict)) {
+  if (message->GetDictionary("config", &config_dict)) {
     result.reset(config_dict->DeepCopy());
   } else {
     LOG(ERROR) << "'config' dictionary not found";
@@ -53,11 +69,13 @@ scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage(
 namespace remoting {
 
 Me2MeNativeMessagingHost::Me2MeNativeMessagingHost(
+    bool needs_elevation,
     scoped_ptr<NativeMessagingChannel> channel,
     scoped_refptr<DaemonController> daemon_controller,
     scoped_refptr<protocol::PairingRegistry> pairing_registry,
     scoped_ptr<OAuthClient> oauth_client)
-    : channel_(channel.Pass()),
+    : needs_elevation_(needs_elevation),
+      channel_(channel.Pass()),
       daemon_controller_(daemon_controller),
       pairing_registry_(pairing_registry),
       oauth_client_(oauth_client.Pass()),
@@ -73,12 +91,14 @@ void Me2MeNativeMessagingHost::Start(
       const base::Closure& quit_closure) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
+  quit_closure_ = quit_closure;
+
   channel_->Start(
-      base::Bind(&Me2MeNativeMessagingHost::ProcessMessage, weak_ptr_),
-      quit_closure);
+      base::Bind(&Me2MeNativeMessagingHost::ProcessRequest, weak_ptr_),
+      base::Bind(&Me2MeNativeMessagingHost::Stop, weak_ptr_));
 }
 
-void Me2MeNativeMessagingHost::ProcessMessage(
+void Me2MeNativeMessagingHost::ProcessRequest(
     scoped_ptr<base::DictionaryValue> message) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -100,35 +120,35 @@ void Me2MeNativeMessagingHost::ProcessMessage(
   response->SetString("type", type + "Response");
 
   if (type == "hello") {
-    ProcessHello(*message, response.Pass());
+    ProcessHello(message.Pass(), response.Pass());
   } else if (type == "clearPairedClients") {
-    ProcessClearPairedClients(*message, response.Pass());
+    ProcessClearPairedClients(message.Pass(), response.Pass());
   } else if (type == "deletePairedClient") {
-    ProcessDeletePairedClient(*message, response.Pass());
+    ProcessDeletePairedClient(message.Pass(), response.Pass());
   } else if (type == "getHostName") {
-    ProcessGetHostName(*message, response.Pass());
+    ProcessGetHostName(message.Pass(), response.Pass());
   } else if (type == "getPinHash") {
-    ProcessGetPinHash(*message, response.Pass());
+    ProcessGetPinHash(message.Pass(), response.Pass());
   } else if (type == "generateKeyPair") {
-    ProcessGenerateKeyPair(*message, response.Pass());
+    ProcessGenerateKeyPair(message.Pass(), response.Pass());
   } else if (type == "updateDaemonConfig") {
-    ProcessUpdateDaemonConfig(*message, response.Pass());
+    ProcessUpdateDaemonConfig(message.Pass(), response.Pass());
   } else if (type == "getDaemonConfig") {
-    ProcessGetDaemonConfig(*message, response.Pass());
+    ProcessGetDaemonConfig(message.Pass(), response.Pass());
   } else if (type == "getPairedClients") {
-    ProcessGetPairedClients(*message, response.Pass());
+    ProcessGetPairedClients(message.Pass(), response.Pass());
   } else if (type == "getUsageStatsConsent") {
-    ProcessGetUsageStatsConsent(*message, response.Pass());
+    ProcessGetUsageStatsConsent(message.Pass(), response.Pass());
   } else if (type == "startDaemon") {
-    ProcessStartDaemon(*message, response.Pass());
+    ProcessStartDaemon(message.Pass(), response.Pass());
   } else if (type == "stopDaemon") {
-    ProcessStopDaemon(*message, response.Pass());
+    ProcessStopDaemon(message.Pass(), response.Pass());
   } else if (type == "getDaemonState") {
-    ProcessGetDaemonState(*message, response.Pass());
+    ProcessGetDaemonState(message.Pass(), response.Pass());
   } else if (type == "getHostClientId") {
-    ProcessGetHostClientId(*message, response.Pass());
+    ProcessGetHostClientId(message.Pass(), response.Pass());
   } else if (type == "getCredentialsFromAuthCode") {
-    ProcessGetCredentialsFromAuthCode(*message, response.Pass());
+    ProcessGetCredentialsFromAuthCode(message.Pass(), response.Pass());
   } else {
     LOG(ERROR) << "Unsupported request type: " << type;
     OnError();
@@ -136,7 +156,7 @@ void Me2MeNativeMessagingHost::ProcessMessage(
 }
 
 void Me2MeNativeMessagingHost::ProcessHello(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -149,10 +169,16 @@ void Me2MeNativeMessagingHost::ProcessHello(
 }
 
 void Me2MeNativeMessagingHost::ProcessClearPairedClients(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
+  if (needs_elevation_) {
+    if (!DelegateToElevatedHost(message.Pass()))
+      SendBooleanResult(response.Pass(), false);
+    return;
+  }
+
   if (pairing_registry_) {
     pairing_registry_->ClearAllPairings(
         base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
@@ -163,12 +189,19 @@ void Me2MeNativeMessagingHost::ProcessClearPairedClients(
 }
 
 void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
+  if (needs_elevation_) {
+    if (!DelegateToElevatedHost(message.Pass()))
+      SendBooleanResult(response.Pass(), false);
+    return;
+  }
+
   std::string client_id;
-  if (!message.GetString(protocol::PairingRegistry::kClientIdKey, &client_id)) {
+  if (!message->GetString(protocol::PairingRegistry::kClientIdKey,
+                          &client_id)) {
     LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey
                << "' string not found.";
     OnError();
@@ -185,7 +218,7 @@ void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
 }
 
 void Me2MeNativeMessagingHost::ProcessGetHostName(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -194,18 +227,18 @@ void Me2MeNativeMessagingHost::ProcessGetHostName(
 }
 
 void Me2MeNativeMessagingHost::ProcessGetPinHash(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   std::string host_id;
-  if (!message.GetString("hostId", &host_id)) {
+  if (!message->GetString("hostId", &host_id)) {
     LOG(ERROR) << "'hostId' not found: " << message;
     OnError();
     return;
   }
   std::string pin;
-  if (!message.GetString("pin", &pin)) {
+  if (!message->GetString("pin", &pin)) {
     LOG(ERROR) << "'pin' not found: " << message;
     OnError();
     return;
@@ -215,7 +248,7 @@ void Me2MeNativeMessagingHost::ProcessGetPinHash(
 }
 
 void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -226,12 +259,12 @@ void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
 }
 
 void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   scoped_ptr<base::DictionaryValue> config_dict =
-      ConfigDictionaryFromMessage(message);
+      ConfigDictionaryFromMessage(message.Pass());
   if (!config_dict) {
     OnError();
     return;
@@ -244,7 +277,7 @@ void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
 }
 
 void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -254,7 +287,7 @@ void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
 }
 
 void Me2MeNativeMessagingHost::ProcessGetPairedClients(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -269,7 +302,7 @@ void Me2MeNativeMessagingHost::ProcessGetPairedClients(
 }
 
 void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -279,19 +312,19 @@ void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
 }
 
 void Me2MeNativeMessagingHost::ProcessStartDaemon(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   bool consent;
-  if (!message.GetBoolean("consent", &consent)) {
+  if (!message->GetBoolean("consent", &consent)) {
     LOG(ERROR) << "'consent' not found.";
     OnError();
     return;
   }
 
   scoped_ptr<base::DictionaryValue> config_dict =
-      ConfigDictionaryFromMessage(message);
+      ConfigDictionaryFromMessage(message.Pass());
   if (!config_dict) {
     OnError();
     return;
@@ -304,7 +337,7 @@ void Me2MeNativeMessagingHost::ProcessStartDaemon(
 }
 
 void Me2MeNativeMessagingHost::ProcessStopDaemon(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -314,7 +347,7 @@ void Me2MeNativeMessagingHost::ProcessStopDaemon(
 }
 
 void Me2MeNativeMessagingHost::ProcessGetDaemonState(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -349,7 +382,7 @@ void Me2MeNativeMessagingHost::ProcessGetDaemonState(
 }
 
 void Me2MeNativeMessagingHost::ProcessGetHostClientId(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -359,12 +392,12 @@ void Me2MeNativeMessagingHost::ProcessGetHostClientId(
 }
 
 void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
-    const base::DictionaryValue& message,
+    scoped_ptr<base::DictionaryValue> message,
     scoped_ptr<base::DictionaryValue> response) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   std::string auth_code;
-  if (!message.GetString("authorizationCode", &auth_code)) {
+  if (!message->GetString("authorizationCode", &auth_code)) {
     LOG(ERROR) << "'authorizationCode' string not found.";
     OnError();
     return;
@@ -461,4 +494,192 @@ void Me2MeNativeMessagingHost::OnError() {
   channel_->SendMessage(scoped_ptr<base::DictionaryValue>());
 }
 
+void Me2MeNativeMessagingHost::Stop() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (!quit_closure_.is_null())
+    base::ResetAndReturn(&quit_closure_).Run();
+}
+
+#if defined(OS_WIN)
+
+bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
+    scoped_ptr<base::DictionaryValue> message) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  EnsureElevatedHostCreated();
+
+  // elevated_channel_ will be null if user rejects the UAC request.
+  if (elevated_channel_)
+    elevated_channel_->SendMessage(message.Pass());
+
+  return elevated_channel_ != NULL;
+}
+
+void Me2MeNativeMessagingHost::EnsureElevatedHostCreated() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(needs_elevation_);
+
+  if (elevated_channel_)
+    return;
+
+  // presubmit: allow wstring
+  std::wstring user_sid;
+  if (!base::win::GetUserSidString(&user_sid)) {
+    LOG(ERROR) << "Failed to query the current user SID.";
+    OnError();
+    return;
+  }
+
+  // Create a security descriptor that gives full access to the caller and
+  // denies access by anyone else.
+  std::string security_descriptor = base::StringPrintf(
+      "O:%1$sG:%1$sD:(A;;GA;;;%1$s)", WideToASCII(user_sid).c_str());
+
+  ScopedSd sd = ConvertSddlToSd(security_descriptor);
+  if (!sd) {
+    LOG_GETLASTERROR(ERROR) << "Failed to create a security descriptor for the"
+                            << "Chromoting Me2Me native messaging host.";
+    OnError();
+    return;
+  }
+
+  SECURITY_ATTRIBUTES security_attributes = {0};
+  security_attributes.nLength = sizeof(security_attributes);
+  security_attributes.lpSecurityDescriptor = sd.get();
+  security_attributes.bInheritHandle = FALSE;
+
+  // Generate a unique name for the input channel.
+  std::string input_pipe_name(kChromePipeNamePrefix);
+  input_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
+
+  base::win::ScopedHandle delegate_write_handle(::CreateNamedPipe(
+      base::ASCIIToUTF16(input_pipe_name).c_str(),
+      PIPE_ACCESS_OUTBOUND,
+      PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
+      1,
+      kBufferSize,
+      kBufferSize,
+      kTimeOutMilliseconds,
+      &security_attributes));
+
+  if (!delegate_write_handle.IsValid()) {
+    LOG_GETLASTERROR(ERROR) <<
+        "Failed to create named pipe '" << input_pipe_name << "'";
+    OnError();
+    return;
+  }
+
+  // Generate a unique name for the input channel.
+  std::string output_pipe_name(kChromePipeNamePrefix);
+  output_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
+
+  base::win::ScopedHandle delegate_read_handle(::CreateNamedPipe(
+      base::ASCIIToUTF16(output_pipe_name).c_str(),
+      PIPE_ACCESS_INBOUND,
+      PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
+      1,
+      kBufferSize,
+      kBufferSize,
+      kTimeOutMilliseconds,
+      &security_attributes));
+
+  if (!delegate_read_handle.IsValid()) {
+    LOG_GETLASTERROR(ERROR) <<
+        "Failed to create named pipe '" << output_pipe_name << "'";
+    OnError();
+    return;
+  }
+
+  const CommandLine* current_command_line = CommandLine::ForCurrentProcess();
+  const CommandLine::SwitchMap& switches = current_command_line->GetSwitches();
+  CommandLine::StringVector args = current_command_line->GetArgs();
+
+  // Create the child process command line by copying switches from the current
+  // command line.
+  CommandLine command_line(CommandLine::NO_PROGRAM);
+  command_line.AppendSwitch(kElevatingSwitchName);
+  command_line.AppendSwitchASCII(kInputSwitchName, input_pipe_name);
+  command_line.AppendSwitchASCII(kOutputSwitchName, output_pipe_name);
+
+  DCHECK(!current_command_line->HasSwitch(kElevatingSwitchName));
+  for (CommandLine::SwitchMap::const_iterator i = switches.begin();
+       i != switches.end(); ++i) {
+      command_line.AppendSwitchNative(i->first, i->second);
+  }
+  for (CommandLine::StringVector::const_iterator i = args.begin();
+       i != args.end(); ++i) {
+    command_line.AppendArgNative(*i);
+  }
+
+  // Get the name of the binary to launch.
+  base::FilePath binary = current_command_line->GetProgram();
+  CommandLine::StringType parameters = command_line.GetCommandLineString();
+
+  // Launch the child process requesting elevation.
+  SHELLEXECUTEINFO info;
+  memset(&info, 0, sizeof(info));
+  info.cbSize = sizeof(info);
+  info.lpVerb = L"runas";
+  info.lpFile = binary.value().c_str();
+  info.lpParameters = parameters.c_str();
+  info.nShow = SW_SHOWNORMAL;
+
+  if (!ShellExecuteEx(&info)) {
+    DWORD error = ::GetLastError();
+    LOG_GETLASTERROR(ERROR) << "Unable to launch '" << binary.value() << "'";
+    if (error != ERROR_CANCELLED) {
+      OnError();
+    }
+    return;
+  }
+
+  if (!::ConnectNamedPipe(delegate_write_handle.Get(), NULL)) {
+    DWORD error = ::GetLastError();
+    if (error != ERROR_PIPE_CONNECTED) {
+      LOG_GETLASTERROR(ERROR) << "Unable to connect '"
+                              << input_pipe_name << "'";
+      OnError();
+      return;
+    }
+  }
+
+  if (!::ConnectNamedPipe(delegate_read_handle.Get(), NULL)) {
+    DWORD error = ::GetLastError();
+    if (error != ERROR_PIPE_CONNECTED) {
+      LOG_GETLASTERROR(ERROR) << "Unable to connect '"
+                              << output_pipe_name << "'";
+      OnError();
+      return;
+    }
+  }
+
+  // Set up the native messaging channel to talk to the elevated host.
+  // Note that input for the elevate channel is output forthe elevated host.
+  elevated_channel_.reset(new NativeMessagingChannel(
+      delegate_read_handle.Take(), delegate_write_handle.Take()));
+
+  elevated_channel_->Start(
+      base::Bind(&Me2MeNativeMessagingHost::ProcessDelegateResponse, weak_ptr_),
+      base::Bind(&Me2MeNativeMessagingHost::Stop, weak_ptr_));
+}
+
+void Me2MeNativeMessagingHost::ProcessDelegateResponse(
+    scoped_ptr<base::DictionaryValue> message) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Simply pass along the response from the elevated host to the client.
+  channel_->SendMessage(message.Pass());
+}
+
+#else  // defined(OS_WIN)
+
+bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
+    scoped_ptr<base::DictionaryValue> message) {
+  NOTREACHED();
+  return false;
+}
+
+#endif  // !defined(OS_WIN)
+
 }  // namespace remoting