1 // Copyright 2013 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/setup/me2me_native_messaging_host.h"
8 #include "base/basictypes.h"
10 #include "base/callback.h"
11 #include "base/callback_helpers.h"
12 #include "base/command_line.h"
13 #include "base/logging.h"
14 #include "base/strings/stringize_macros.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/threading/thread.h"
18 #include "base/values.h"
19 #include "google_apis/gaia/gaia_oauth_client.h"
20 #include "google_apis/google_api_keys.h"
21 #include "ipc/ipc_channel.h"
22 #include "net/base/net_util.h"
23 #include "remoting/base/rsa_key_pair.h"
24 #include "remoting/host/pin_hash.h"
25 #include "remoting/host/setup/oauth_client.h"
26 #include "remoting/protocol/pairing_registry.h"
30 #include "base/win/win_util.h"
31 #include "remoting/host/win/security_descriptor.h"
32 #endif // defined(OS_WIN)
37 // Windows will use default buffer size when 0 is passed to CreateNamedPipeW().
38 const DWORD kBufferSize = 0;
39 const int kTimeOutMilliseconds = 2000;
40 const char kChromePipeNamePrefix[] = "\\\\.\\pipe\\chrome_remote_desktop.";
41 #endif // defined(OS_WIN)
43 // redirect_uri to use when authenticating service accounts (service account
44 // codes are obtained "out-of-band", i.e., not through an OAuth redirect).
45 const char* kServiceAccountRedirectUri = "oob";
47 // Features supported in addition to the base protocol.
48 const char* kSupportedFeatures[] = {
53 // Helper to extract the "config" part of a message as a DictionaryValue.
54 // Returns NULL on failure, and logs an error message.
55 scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage(
56 scoped_ptr<base::DictionaryValue> message) {
57 scoped_ptr<base::DictionaryValue> result;
58 const base::DictionaryValue* config_dict;
59 if (message->GetDictionary("config", &config_dict)) {
60 result.reset(config_dict->DeepCopy());
62 LOG(ERROR) << "'config' dictionary not found";
71 Me2MeNativeMessagingHost::Me2MeNativeMessagingHost(
73 scoped_ptr<NativeMessagingChannel> channel,
74 scoped_refptr<DaemonController> daemon_controller,
75 scoped_refptr<protocol::PairingRegistry> pairing_registry,
76 scoped_ptr<OAuthClient> oauth_client)
77 : needs_elevation_(needs_elevation),
78 channel_(channel.Pass()),
79 daemon_controller_(daemon_controller),
80 pairing_registry_(pairing_registry),
81 oauth_client_(oauth_client.Pass()),
83 weak_ptr_ = weak_factory_.GetWeakPtr();
86 Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() {
87 DCHECK(thread_checker_.CalledOnValidThread());
90 void Me2MeNativeMessagingHost::Start(
91 const base::Closure& quit_closure) {
92 DCHECK(thread_checker_.CalledOnValidThread());
94 quit_closure_ = quit_closure;
97 base::Bind(&Me2MeNativeMessagingHost::ProcessRequest, weak_ptr_),
98 base::Bind(&Me2MeNativeMessagingHost::Stop, weak_ptr_));
101 void Me2MeNativeMessagingHost::ProcessRequest(
102 scoped_ptr<base::DictionaryValue> message) {
103 DCHECK(thread_checker_.CalledOnValidThread());
105 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
107 // If the client supplies an ID, it will expect it in the response. This
108 // might be a string or a number, so cope with both.
109 const base::Value* id;
110 if (message->Get("id", &id))
111 response->Set("id", id->DeepCopy());
114 if (!message->GetString("type", &type)) {
115 LOG(ERROR) << "'type' not found";
116 channel_->SendMessage(scoped_ptr<base::DictionaryValue>());
120 response->SetString("type", type + "Response");
122 if (type == "hello") {
123 ProcessHello(message.Pass(), response.Pass());
124 } else if (type == "clearPairedClients") {
125 ProcessClearPairedClients(message.Pass(), response.Pass());
126 } else if (type == "deletePairedClient") {
127 ProcessDeletePairedClient(message.Pass(), response.Pass());
128 } else if (type == "getHostName") {
129 ProcessGetHostName(message.Pass(), response.Pass());
130 } else if (type == "getPinHash") {
131 ProcessGetPinHash(message.Pass(), response.Pass());
132 } else if (type == "generateKeyPair") {
133 ProcessGenerateKeyPair(message.Pass(), response.Pass());
134 } else if (type == "updateDaemonConfig") {
135 ProcessUpdateDaemonConfig(message.Pass(), response.Pass());
136 } else if (type == "getDaemonConfig") {
137 ProcessGetDaemonConfig(message.Pass(), response.Pass());
138 } else if (type == "getPairedClients") {
139 ProcessGetPairedClients(message.Pass(), response.Pass());
140 } else if (type == "getUsageStatsConsent") {
141 ProcessGetUsageStatsConsent(message.Pass(), response.Pass());
142 } else if (type == "startDaemon") {
143 ProcessStartDaemon(message.Pass(), response.Pass());
144 } else if (type == "stopDaemon") {
145 ProcessStopDaemon(message.Pass(), response.Pass());
146 } else if (type == "getDaemonState") {
147 ProcessGetDaemonState(message.Pass(), response.Pass());
148 } else if (type == "getHostClientId") {
149 ProcessGetHostClientId(message.Pass(), response.Pass());
150 } else if (type == "getCredentialsFromAuthCode") {
151 ProcessGetCredentialsFromAuthCode(message.Pass(), response.Pass());
153 LOG(ERROR) << "Unsupported request type: " << type;
158 void Me2MeNativeMessagingHost::ProcessHello(
159 scoped_ptr<base::DictionaryValue> message,
160 scoped_ptr<base::DictionaryValue> response) {
161 DCHECK(thread_checker_.CalledOnValidThread());
163 response->SetString("version", STRINGIZE(VERSION));
164 scoped_ptr<base::ListValue> supported_features_list(new base::ListValue());
165 supported_features_list->AppendStrings(std::vector<std::string>(
166 kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures)));
167 response->Set("supportedFeatures", supported_features_list.release());
168 channel_->SendMessage(response.Pass());
171 void Me2MeNativeMessagingHost::ProcessClearPairedClients(
172 scoped_ptr<base::DictionaryValue> message,
173 scoped_ptr<base::DictionaryValue> response) {
174 DCHECK(thread_checker_.CalledOnValidThread());
176 if (needs_elevation_) {
177 if (!DelegateToElevatedHost(message.Pass()))
178 SendBooleanResult(response.Pass(), false);
182 if (pairing_registry_) {
183 pairing_registry_->ClearAllPairings(
184 base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
185 base::Passed(&response)));
187 SendBooleanResult(response.Pass(), false);
191 void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
192 scoped_ptr<base::DictionaryValue> message,
193 scoped_ptr<base::DictionaryValue> response) {
194 DCHECK(thread_checker_.CalledOnValidThread());
196 if (needs_elevation_) {
197 if (!DelegateToElevatedHost(message.Pass()))
198 SendBooleanResult(response.Pass(), false);
202 std::string client_id;
203 if (!message->GetString(protocol::PairingRegistry::kClientIdKey,
205 LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey
206 << "' string not found.";
211 if (pairing_registry_) {
212 pairing_registry_->DeletePairing(
213 client_id, base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult,
214 weak_ptr_, base::Passed(&response)));
216 SendBooleanResult(response.Pass(), false);
220 void Me2MeNativeMessagingHost::ProcessGetHostName(
221 scoped_ptr<base::DictionaryValue> message,
222 scoped_ptr<base::DictionaryValue> response) {
223 DCHECK(thread_checker_.CalledOnValidThread());
225 response->SetString("hostname", net::GetHostName());
226 channel_->SendMessage(response.Pass());
229 void Me2MeNativeMessagingHost::ProcessGetPinHash(
230 scoped_ptr<base::DictionaryValue> message,
231 scoped_ptr<base::DictionaryValue> response) {
232 DCHECK(thread_checker_.CalledOnValidThread());
235 if (!message->GetString("hostId", &host_id)) {
236 LOG(ERROR) << "'hostId' not found: " << message;
241 if (!message->GetString("pin", &pin)) {
242 LOG(ERROR) << "'pin' not found: " << message;
246 response->SetString("hash", MakeHostPinHash(host_id, pin));
247 channel_->SendMessage(response.Pass());
250 void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
251 scoped_ptr<base::DictionaryValue> message,
252 scoped_ptr<base::DictionaryValue> response) {
253 DCHECK(thread_checker_.CalledOnValidThread());
255 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
256 response->SetString("privateKey", key_pair->ToString());
257 response->SetString("publicKey", key_pair->GetPublicKey());
258 channel_->SendMessage(response.Pass());
261 void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
262 scoped_ptr<base::DictionaryValue> message,
263 scoped_ptr<base::DictionaryValue> response) {
264 DCHECK(thread_checker_.CalledOnValidThread());
266 scoped_ptr<base::DictionaryValue> config_dict =
267 ConfigDictionaryFromMessage(message.Pass());
273 daemon_controller_->UpdateConfig(
275 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
276 base::Passed(&response)));
279 void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
280 scoped_ptr<base::DictionaryValue> message,
281 scoped_ptr<base::DictionaryValue> response) {
282 DCHECK(thread_checker_.CalledOnValidThread());
284 daemon_controller_->GetConfig(
285 base::Bind(&Me2MeNativeMessagingHost::SendConfigResponse, weak_ptr_,
286 base::Passed(&response)));
289 void Me2MeNativeMessagingHost::ProcessGetPairedClients(
290 scoped_ptr<base::DictionaryValue> message,
291 scoped_ptr<base::DictionaryValue> response) {
292 DCHECK(thread_checker_.CalledOnValidThread());
294 if (pairing_registry_) {
295 pairing_registry_->GetAllPairings(
296 base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse,
297 weak_ptr_, base::Passed(&response)));
299 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
300 SendPairedClientsResponse(response.Pass(), no_paired_clients.Pass());
304 void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
305 scoped_ptr<base::DictionaryValue> message,
306 scoped_ptr<base::DictionaryValue> response) {
307 DCHECK(thread_checker_.CalledOnValidThread());
309 daemon_controller_->GetUsageStatsConsent(
310 base::Bind(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse,
311 weak_ptr_, base::Passed(&response)));
314 void Me2MeNativeMessagingHost::ProcessStartDaemon(
315 scoped_ptr<base::DictionaryValue> message,
316 scoped_ptr<base::DictionaryValue> response) {
317 DCHECK(thread_checker_.CalledOnValidThread());
320 if (!message->GetBoolean("consent", &consent)) {
321 LOG(ERROR) << "'consent' not found.";
326 scoped_ptr<base::DictionaryValue> config_dict =
327 ConfigDictionaryFromMessage(message.Pass());
333 daemon_controller_->SetConfigAndStart(
334 config_dict.Pass(), consent,
335 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
336 base::Passed(&response)));
339 void Me2MeNativeMessagingHost::ProcessStopDaemon(
340 scoped_ptr<base::DictionaryValue> message,
341 scoped_ptr<base::DictionaryValue> response) {
342 DCHECK(thread_checker_.CalledOnValidThread());
344 daemon_controller_->Stop(
345 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
346 base::Passed(&response)));
349 void Me2MeNativeMessagingHost::ProcessGetDaemonState(
350 scoped_ptr<base::DictionaryValue> message,
351 scoped_ptr<base::DictionaryValue> response) {
352 DCHECK(thread_checker_.CalledOnValidThread());
354 DaemonController::State state = daemon_controller_->GetState();
356 case DaemonController::STATE_NOT_IMPLEMENTED:
357 response->SetString("state", "NOT_IMPLEMENTED");
359 case DaemonController::STATE_NOT_INSTALLED:
360 response->SetString("state", "NOT_INSTALLED");
362 case DaemonController::STATE_INSTALLING:
363 response->SetString("state", "INSTALLING");
365 case DaemonController::STATE_STOPPED:
366 response->SetString("state", "STOPPED");
368 case DaemonController::STATE_STARTING:
369 response->SetString("state", "STARTING");
371 case DaemonController::STATE_STARTED:
372 response->SetString("state", "STARTED");
374 case DaemonController::STATE_STOPPING:
375 response->SetString("state", "STOPPING");
377 case DaemonController::STATE_UNKNOWN:
378 response->SetString("state", "UNKNOWN");
381 channel_->SendMessage(response.Pass());
384 void Me2MeNativeMessagingHost::ProcessGetHostClientId(
385 scoped_ptr<base::DictionaryValue> message,
386 scoped_ptr<base::DictionaryValue> response) {
387 DCHECK(thread_checker_.CalledOnValidThread());
389 response->SetString("clientId", google_apis::GetOAuth2ClientID(
390 google_apis::CLIENT_REMOTING_HOST));
391 channel_->SendMessage(response.Pass());
394 void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
395 scoped_ptr<base::DictionaryValue> message,
396 scoped_ptr<base::DictionaryValue> response) {
397 DCHECK(thread_checker_.CalledOnValidThread());
399 std::string auth_code;
400 if (!message->GetString("authorizationCode", &auth_code)) {
401 LOG(ERROR) << "'authorizationCode' string not found.";
406 gaia::OAuthClientInfo oauth_client_info = {
407 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST),
408 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST),
409 kServiceAccountRedirectUri
412 oauth_client_->GetCredentialsFromAuthCode(
413 oauth_client_info, auth_code, base::Bind(
414 &Me2MeNativeMessagingHost::SendCredentialsResponse, weak_ptr_,
415 base::Passed(&response)));
418 void Me2MeNativeMessagingHost::SendConfigResponse(
419 scoped_ptr<base::DictionaryValue> response,
420 scoped_ptr<base::DictionaryValue> config) {
421 DCHECK(thread_checker_.CalledOnValidThread());
424 response->Set("config", config.release());
426 response->Set("config", base::Value::CreateNullValue());
428 channel_->SendMessage(response.Pass());
431 void Me2MeNativeMessagingHost::SendPairedClientsResponse(
432 scoped_ptr<base::DictionaryValue> response,
433 scoped_ptr<base::ListValue> pairings) {
434 DCHECK(thread_checker_.CalledOnValidThread());
436 response->Set("pairedClients", pairings.release());
437 channel_->SendMessage(response.Pass());
440 void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse(
441 scoped_ptr<base::DictionaryValue> response,
442 const DaemonController::UsageStatsConsent& consent) {
443 DCHECK(thread_checker_.CalledOnValidThread());
445 response->SetBoolean("supported", consent.supported);
446 response->SetBoolean("allowed", consent.allowed);
447 response->SetBoolean("setByPolicy", consent.set_by_policy);
448 channel_->SendMessage(response.Pass());
451 void Me2MeNativeMessagingHost::SendAsyncResult(
452 scoped_ptr<base::DictionaryValue> response,
453 DaemonController::AsyncResult result) {
454 DCHECK(thread_checker_.CalledOnValidThread());
457 case DaemonController::RESULT_OK:
458 response->SetString("result", "OK");
460 case DaemonController::RESULT_FAILED:
461 response->SetString("result", "FAILED");
463 case DaemonController::RESULT_CANCELLED:
464 response->SetString("result", "CANCELLED");
466 case DaemonController::RESULT_FAILED_DIRECTORY:
467 response->SetString("result", "FAILED_DIRECTORY");
470 channel_->SendMessage(response.Pass());
473 void Me2MeNativeMessagingHost::SendBooleanResult(
474 scoped_ptr<base::DictionaryValue> response, bool result) {
475 DCHECK(thread_checker_.CalledOnValidThread());
477 response->SetBoolean("result", result);
478 channel_->SendMessage(response.Pass());
481 void Me2MeNativeMessagingHost::SendCredentialsResponse(
482 scoped_ptr<base::DictionaryValue> response,
483 const std::string& user_email,
484 const std::string& refresh_token) {
485 DCHECK(thread_checker_.CalledOnValidThread());
487 response->SetString("userEmail", user_email);
488 response->SetString("refreshToken", refresh_token);
489 channel_->SendMessage(response.Pass());
492 void Me2MeNativeMessagingHost::OnError() {
493 // Trigger a host shutdown by sending a NULL message.
494 channel_->SendMessage(scoped_ptr<base::DictionaryValue>());
497 void Me2MeNativeMessagingHost::Stop() {
498 DCHECK(thread_checker_.CalledOnValidThread());
500 if (!quit_closure_.is_null())
501 base::ResetAndReturn(&quit_closure_).Run();
506 bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
507 scoped_ptr<base::DictionaryValue> message) {
508 DCHECK(thread_checker_.CalledOnValidThread());
510 EnsureElevatedHostCreated();
512 // elevated_channel_ will be null if user rejects the UAC request.
513 if (elevated_channel_)
514 elevated_channel_->SendMessage(message.Pass());
516 return elevated_channel_ != NULL;
519 void Me2MeNativeMessagingHost::EnsureElevatedHostCreated() {
520 DCHECK(thread_checker_.CalledOnValidThread());
521 DCHECK(needs_elevation_);
523 if (elevated_channel_)
526 // presubmit: allow wstring
527 std::wstring user_sid;
528 if (!base::win::GetUserSidString(&user_sid)) {
529 LOG(ERROR) << "Failed to query the current user SID.";
534 // Create a security descriptor that gives full access to the caller and
535 // denies access by anyone else.
536 std::string security_descriptor = base::StringPrintf(
537 "O:%1$sG:%1$sD:(A;;GA;;;%1$s)", WideToASCII(user_sid).c_str());
539 ScopedSd sd = ConvertSddlToSd(security_descriptor);
541 LOG_GETLASTERROR(ERROR) << "Failed to create a security descriptor for the"
542 << "Chromoting Me2Me native messaging host.";
547 SECURITY_ATTRIBUTES security_attributes = {0};
548 security_attributes.nLength = sizeof(security_attributes);
549 security_attributes.lpSecurityDescriptor = sd.get();
550 security_attributes.bInheritHandle = FALSE;
552 // Generate a unique name for the input channel.
553 std::string input_pipe_name(kChromePipeNamePrefix);
554 input_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
556 base::win::ScopedHandle delegate_write_handle(::CreateNamedPipe(
557 base::ASCIIToUTF16(input_pipe_name).c_str(),
558 PIPE_ACCESS_OUTBOUND,
559 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
563 kTimeOutMilliseconds,
564 &security_attributes));
566 if (!delegate_write_handle.IsValid()) {
567 LOG_GETLASTERROR(ERROR) <<
568 "Failed to create named pipe '" << input_pipe_name << "'";
573 // Generate a unique name for the input channel.
574 std::string output_pipe_name(kChromePipeNamePrefix);
575 output_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
577 base::win::ScopedHandle delegate_read_handle(::CreateNamedPipe(
578 base::ASCIIToUTF16(output_pipe_name).c_str(),
580 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
584 kTimeOutMilliseconds,
585 &security_attributes));
587 if (!delegate_read_handle.IsValid()) {
588 LOG_GETLASTERROR(ERROR) <<
589 "Failed to create named pipe '" << output_pipe_name << "'";
594 const CommandLine* current_command_line = CommandLine::ForCurrentProcess();
595 const CommandLine::SwitchMap& switches = current_command_line->GetSwitches();
596 CommandLine::StringVector args = current_command_line->GetArgs();
598 // Create the child process command line by copying switches from the current
600 CommandLine command_line(CommandLine::NO_PROGRAM);
601 command_line.AppendSwitch(kElevatingSwitchName);
602 command_line.AppendSwitchASCII(kInputSwitchName, input_pipe_name);
603 command_line.AppendSwitchASCII(kOutputSwitchName, output_pipe_name);
605 DCHECK(!current_command_line->HasSwitch(kElevatingSwitchName));
606 for (CommandLine::SwitchMap::const_iterator i = switches.begin();
607 i != switches.end(); ++i) {
608 command_line.AppendSwitchNative(i->first, i->second);
610 for (CommandLine::StringVector::const_iterator i = args.begin();
611 i != args.end(); ++i) {
612 command_line.AppendArgNative(*i);
615 // Get the name of the binary to launch.
616 base::FilePath binary = current_command_line->GetProgram();
617 CommandLine::StringType parameters = command_line.GetCommandLineString();
619 // Launch the child process requesting elevation.
620 SHELLEXECUTEINFO info;
621 memset(&info, 0, sizeof(info));
622 info.cbSize = sizeof(info);
623 info.lpVerb = L"runas";
624 info.lpFile = binary.value().c_str();
625 info.lpParameters = parameters.c_str();
626 info.nShow = SW_SHOWNORMAL;
628 if (!ShellExecuteEx(&info)) {
629 DWORD error = ::GetLastError();
630 LOG_GETLASTERROR(ERROR) << "Unable to launch '" << binary.value() << "'";
631 if (error != ERROR_CANCELLED) {
637 if (!::ConnectNamedPipe(delegate_write_handle.Get(), NULL)) {
638 DWORD error = ::GetLastError();
639 if (error != ERROR_PIPE_CONNECTED) {
640 LOG_GETLASTERROR(ERROR) << "Unable to connect '"
641 << input_pipe_name << "'";
647 if (!::ConnectNamedPipe(delegate_read_handle.Get(), NULL)) {
648 DWORD error = ::GetLastError();
649 if (error != ERROR_PIPE_CONNECTED) {
650 LOG_GETLASTERROR(ERROR) << "Unable to connect '"
651 << output_pipe_name << "'";
657 // Set up the native messaging channel to talk to the elevated host.
658 // Note that input for the elevate channel is output forthe elevated host.
659 elevated_channel_.reset(new NativeMessagingChannel(
660 delegate_read_handle.Take(), delegate_write_handle.Take()));
662 elevated_channel_->Start(
663 base::Bind(&Me2MeNativeMessagingHost::ProcessDelegateResponse, weak_ptr_),
664 base::Bind(&Me2MeNativeMessagingHost::Stop, weak_ptr_));
667 void Me2MeNativeMessagingHost::ProcessDelegateResponse(
668 scoped_ptr<base::DictionaryValue> message) {
669 DCHECK(thread_checker_.CalledOnValidThread());
671 // Simply pass along the response from the elevated host to the client.
672 channel_->SendMessage(message.Pass());
675 #else // defined(OS_WIN)
677 bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
678 scoped_ptr<base::DictionaryValue> message) {
683 #endif // !defined(OS_WIN)
685 } // namespace remoting