me2me_native_messaging_host.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
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. 4 5#include "remoting/host/setup/me2me_native_messaging_host.h" 6 7#include <string> 8 9#include "base/basictypes.h" 10#include "base/bind.h" 11#include "base/callback.h" 12#include "base/command_line.h" 13#include "base/logging.h" 14#include "base/message_loop/message_loop.h" 15#include "base/run_loop.h" 16#include "base/strings/string_number_conversions.h" 17#include "base/strings/stringize_macros.h" 18#include "base/threading/thread.h" 19#include "base/values.h" 20#include "google_apis/gaia/gaia_oauth_client.h" 21#include "google_apis/google_api_keys.h" 22#include "net/base/net_util.h" 23#include "net/url_request/url_fetcher.h" 24#include "remoting/base/rsa_key_pair.h" 25#include "remoting/base/url_request_context.h" 26#include "remoting/host/host_exit_codes.h" 27#include "remoting/host/pairing_registry_delegate.h" 28#include "remoting/host/pin_hash.h" 29#include "remoting/host/setup/oauth_client.h" 30#include "remoting/protocol/pairing_registry.h" 31 32namespace { 33 34const char kParentWindowSwitchName[] = "parent-window"; 35 36// redirect_uri to use when authenticating service accounts (service account 37// codes are obtained "out-of-band", i.e., not through an OAuth redirect). 38const char* kServiceAccountRedirectUri = "oob"; 39 40// Features supported in addition to the base protocol. 41const char* kSupportedFeatures[] = { 42 "pairingRegistry", 43 "oauthClient" 44}; 45 46// Helper to extract the "config" part of a message as a DictionaryValue. 47// Returns NULL on failure, and logs an error message. 48scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage( 49 const base::DictionaryValue& message) { 50 scoped_ptr<base::DictionaryValue> result; 51 const base::DictionaryValue* config_dict; 52 if (message.GetDictionary("config", &config_dict)) { 53 result.reset(config_dict->DeepCopy()); 54 } else { 55 LOG(ERROR) << "'config' dictionary not found"; 56 } 57 return result.Pass(); 58} 59 60} // namespace 61 62namespace remoting { 63 64NativeMessagingHost::NativeMessagingHost( 65 scoped_refptr<DaemonController> daemon_controller, 66 scoped_refptr<protocol::PairingRegistry> pairing_registry, 67 scoped_ptr<OAuthClient> oauth_client) 68 : daemon_controller_(daemon_controller), 69 pairing_registry_(pairing_registry), 70 oauth_client_(oauth_client.Pass()), 71 weak_factory_(this) { 72 weak_ptr_ = weak_factory_.GetWeakPtr(); 73} 74 75NativeMessagingHost::~NativeMessagingHost() { 76} 77 78void NativeMessagingHost::ProcessMessage( 79 scoped_ptr<base::DictionaryValue> message, 80 const SendResponseCallback& done) { 81 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue()); 82 83 // If the client supplies an ID, it will expect it in the response. This 84 // might be a string or a number, so cope with both. 85 const base::Value* id; 86 if (message->Get("id", &id)) 87 response->Set("id", id->DeepCopy()); 88 89 std::string type; 90 if (!message->GetString("type", &type)) { 91 LOG(ERROR) << "'type' not found"; 92 done.Run(scoped_ptr<base::DictionaryValue>()); 93 return; 94 } 95 96 response->SetString("type", type + "Response"); 97 98 bool success = false; 99 if (type == "hello") { 100 success = ProcessHello(*message, response.Pass(), done); 101 } else if (type == "clearPairedClients") { 102 success = ProcessClearPairedClients(*message, response.Pass(), done); 103 } else if (type == "deletePairedClient") { 104 success = ProcessDeletePairedClient(*message, response.Pass(), done); 105 } else if (type == "getHostName") { 106 success = ProcessGetHostName(*message, response.Pass(), done); 107 } else if (type == "getPinHash") { 108 success = ProcessGetPinHash(*message, response.Pass(), done); 109 } else if (type == "generateKeyPair") { 110 success = ProcessGenerateKeyPair(*message, response.Pass(), done); 111 } else if (type == "updateDaemonConfig") { 112 success = ProcessUpdateDaemonConfig(*message, response.Pass(), done); 113 } else if (type == "getDaemonConfig") { 114 success = ProcessGetDaemonConfig(*message, response.Pass(), done); 115 } else if (type == "getPairedClients") { 116 success = ProcessGetPairedClients(*message, response.Pass(), done); 117 } else if (type == "getUsageStatsConsent") { 118 success = ProcessGetUsageStatsConsent(*message, response.Pass(), done); 119 } else if (type == "startDaemon") { 120 success = ProcessStartDaemon(*message, response.Pass(), done); 121 } else if (type == "stopDaemon") { 122 success = ProcessStopDaemon(*message, response.Pass(), done); 123 } else if (type == "getDaemonState") { 124 success = ProcessGetDaemonState(*message, response.Pass(), done); 125 } else if (type == "getHostClientId") { 126 success = ProcessGetHostClientId(*message, response.Pass(), done); 127 } else if (type == "getCredentialsFromAuthCode") { 128 success = ProcessGetCredentialsFromAuthCode(*message, response.Pass(), 129 done); 130 } else { 131 LOG(ERROR) << "Unsupported request type: " << type; 132 } 133 134 if (!success) 135 done.Run(scoped_ptr<base::DictionaryValue>()); 136} 137 138bool NativeMessagingHost::ProcessHello( 139 const base::DictionaryValue& message, 140 scoped_ptr<base::DictionaryValue> response, 141 const SendResponseCallback& done) { 142 response->SetString("version", STRINGIZE(VERSION)); 143 scoped_ptr<base::ListValue> supported_features_list(new base::ListValue()); 144 supported_features_list->AppendStrings(std::vector<std::string>( 145 kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures))); 146 response->Set("supportedFeatures", supported_features_list.release()); 147 done.Run(response.Pass()); 148 return true; 149} 150 151bool NativeMessagingHost::ProcessClearPairedClients( 152 const base::DictionaryValue& message, 153 scoped_ptr<base::DictionaryValue> response, 154 const SendResponseCallback& done) { 155 if (pairing_registry_) { 156 pairing_registry_->ClearAllPairings( 157 base::Bind(&NativeMessagingHost::SendBooleanResult, weak_ptr_, 158 done, base::Passed(&response))); 159 } else { 160 SendBooleanResult(done, response.Pass(), false); 161 } 162 return true; 163} 164 165bool NativeMessagingHost::ProcessDeletePairedClient( 166 const base::DictionaryValue& message, 167 scoped_ptr<base::DictionaryValue> response, 168 const SendResponseCallback& done) { 169 std::string client_id; 170 if (!message.GetString(protocol::PairingRegistry::kClientIdKey, &client_id)) { 171 LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey 172 << "' string not found."; 173 return false; 174 } 175 176 if (pairing_registry_) { 177 pairing_registry_->DeletePairing( 178 client_id, base::Bind(&NativeMessagingHost::SendBooleanResult, 179 weak_ptr_, done, base::Passed(&response))); 180 } else { 181 SendBooleanResult(done, response.Pass(), false); 182 } 183 return true; 184} 185 186bool NativeMessagingHost::ProcessGetHostName( 187 const base::DictionaryValue& message, 188 scoped_ptr<base::DictionaryValue> response, 189 const SendResponseCallback& done) { 190 response->SetString("hostname", net::GetHostName()); 191 done.Run(response.Pass()); 192 return true; 193} 194 195bool NativeMessagingHost::ProcessGetPinHash( 196 const base::DictionaryValue& message, 197 scoped_ptr<base::DictionaryValue> response, 198 const SendResponseCallback& done) { 199 std::string host_id; 200 if (!message.GetString("hostId", &host_id)) { 201 LOG(ERROR) << "'hostId' not found: " << message; 202 return false; 203 } 204 std::string pin; 205 if (!message.GetString("pin", &pin)) { 206 LOG(ERROR) << "'pin' not found: " << message; 207 return false; 208 } 209 response->SetString("hash", MakeHostPinHash(host_id, pin)); 210 done.Run(response.Pass()); 211 return true; 212} 213 214bool NativeMessagingHost::ProcessGenerateKeyPair( 215 const base::DictionaryValue& message, 216 scoped_ptr<base::DictionaryValue> response, 217 const SendResponseCallback& done) { 218 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate(); 219 response->SetString("privateKey", key_pair->ToString()); 220 response->SetString("publicKey", key_pair->GetPublicKey()); 221 done.Run(response.Pass()); 222 return true; 223} 224 225bool NativeMessagingHost::ProcessUpdateDaemonConfig( 226 const base::DictionaryValue& message, 227 scoped_ptr<base::DictionaryValue> response, 228 const SendResponseCallback& done) { 229 scoped_ptr<base::DictionaryValue> config_dict = 230 ConfigDictionaryFromMessage(message); 231 if (!config_dict) 232 return false; 233 234 daemon_controller_->UpdateConfig( 235 config_dict.Pass(), 236 base::Bind(&NativeMessagingHost::SendAsyncResult, weak_ptr_, 237 done, base::Passed(&response))); 238 return true; 239} 240 241bool NativeMessagingHost::ProcessGetDaemonConfig( 242 const base::DictionaryValue& message, 243 scoped_ptr<base::DictionaryValue> response, 244 const SendResponseCallback& done) { 245 daemon_controller_->GetConfig( 246 base::Bind(&NativeMessagingHost::SendConfigResponse, weak_ptr_, 247 done, base::Passed(&response))); 248 return true; 249} 250 251bool NativeMessagingHost::ProcessGetPairedClients( 252 const base::DictionaryValue& message, 253 scoped_ptr<base::DictionaryValue> response, 254 const SendResponseCallback& done) { 255 if (pairing_registry_) { 256 pairing_registry_->GetAllPairings( 257 base::Bind(&NativeMessagingHost::SendPairedClientsResponse, weak_ptr_, 258 done, base::Passed(&response))); 259 } else { 260 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue); 261 SendPairedClientsResponse(done, response.Pass(), no_paired_clients.Pass()); 262 } 263 return true; 264} 265 266bool NativeMessagingHost::ProcessGetUsageStatsConsent( 267 const base::DictionaryValue& message, 268 scoped_ptr<base::DictionaryValue> response, 269 const SendResponseCallback& done) { 270 daemon_controller_->GetUsageStatsConsent( 271 base::Bind(&NativeMessagingHost::SendUsageStatsConsentResponse, 272 weak_ptr_, done, base::Passed(&response))); 273 return true; 274} 275 276bool NativeMessagingHost::ProcessStartDaemon( 277 const base::DictionaryValue& message, 278 scoped_ptr<base::DictionaryValue> response, 279 const SendResponseCallback& done) { 280 bool consent; 281 if (!message.GetBoolean("consent", &consent)) { 282 LOG(ERROR) << "'consent' not found."; 283 return false; 284 } 285 286 scoped_ptr<base::DictionaryValue> config_dict = 287 ConfigDictionaryFromMessage(message); 288 if (!config_dict) 289 return false; 290 291 daemon_controller_->SetConfigAndStart( 292 config_dict.Pass(), consent, 293 base::Bind(&NativeMessagingHost::SendAsyncResult, weak_ptr_, 294 done, base::Passed(&response))); 295 return true; 296} 297 298bool NativeMessagingHost::ProcessStopDaemon( 299 const base::DictionaryValue& message, 300 scoped_ptr<base::DictionaryValue> response, 301 const SendResponseCallback& done) { 302 daemon_controller_->Stop( 303 base::Bind(&NativeMessagingHost::SendAsyncResult, weak_ptr_, 304 done, base::Passed(&response))); 305 return true; 306} 307 308bool NativeMessagingHost::ProcessGetDaemonState( 309 const base::DictionaryValue& message, 310 scoped_ptr<base::DictionaryValue> response, 311 const SendResponseCallback& done) { 312 DaemonController::State state = daemon_controller_->GetState(); 313 switch (state) { 314 case DaemonController::STATE_NOT_IMPLEMENTED: 315 response->SetString("state", "NOT_IMPLEMENTED"); 316 break; 317 case DaemonController::STATE_NOT_INSTALLED: 318 response->SetString("state", "NOT_INSTALLED"); 319 break; 320 case DaemonController::STATE_INSTALLING: 321 response->SetString("state", "INSTALLING"); 322 break; 323 case DaemonController::STATE_STOPPED: 324 response->SetString("state", "STOPPED"); 325 break; 326 case DaemonController::STATE_STARTING: 327 response->SetString("state", "STARTING"); 328 break; 329 case DaemonController::STATE_STARTED: 330 response->SetString("state", "STARTED"); 331 break; 332 case DaemonController::STATE_STOPPING: 333 response->SetString("state", "STOPPING"); 334 break; 335 case DaemonController::STATE_UNKNOWN: 336 response->SetString("state", "UNKNOWN"); 337 break; 338 } 339 done.Run(response.Pass()); 340 return true; 341} 342 343bool NativeMessagingHost::ProcessGetHostClientId( 344 const base::DictionaryValue& message, 345 scoped_ptr<base::DictionaryValue> response, 346 const SendResponseCallback& done) { 347 response->SetString("clientId", google_apis::GetOAuth2ClientID( 348 google_apis::CLIENT_REMOTING_HOST)); 349 done.Run(response.Pass()); 350 return true; 351} 352 353bool NativeMessagingHost::ProcessGetCredentialsFromAuthCode( 354 const base::DictionaryValue& message, 355 scoped_ptr<base::DictionaryValue> response, 356 const SendResponseCallback& done) { 357 std::string auth_code; 358 if (!message.GetString("authorizationCode", &auth_code)) { 359 LOG(ERROR) << "'authorizationCode' string not found."; 360 return false; 361 } 362 363 gaia::OAuthClientInfo oauth_client_info = { 364 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST), 365 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST), 366 kServiceAccountRedirectUri 367 }; 368 369 oauth_client_->GetCredentialsFromAuthCode( 370 oauth_client_info, auth_code, base::Bind( 371 &NativeMessagingHost::SendCredentialsResponse, weak_ptr_, 372 done, base::Passed(&response))); 373 374 return true; 375} 376 377void NativeMessagingHost::SendConfigResponse( 378 const SendResponseCallback& done, 379 scoped_ptr<base::DictionaryValue> response, 380 scoped_ptr<base::DictionaryValue> config) { 381 if (config) { 382 response->Set("config", config.release()); 383 } else { 384 response->Set("config", Value::CreateNullValue()); 385 } 386 done.Run(response.Pass()); 387} 388 389void NativeMessagingHost::SendPairedClientsResponse( 390 const SendResponseCallback& done, 391 scoped_ptr<base::DictionaryValue> response, 392 scoped_ptr<base::ListValue> pairings) { 393 response->Set("pairedClients", pairings.release()); 394 done.Run(response.Pass()); 395} 396 397void NativeMessagingHost::SendUsageStatsConsentResponse( 398 const SendResponseCallback& done, 399 scoped_ptr<base::DictionaryValue> response, 400 const DaemonController::UsageStatsConsent& consent) { 401 response->SetBoolean("supported", consent.supported); 402 response->SetBoolean("allowed", consent.allowed); 403 response->SetBoolean("setByPolicy", consent.set_by_policy); 404 done.Run(response.Pass()); 405} 406 407void NativeMessagingHost::SendAsyncResult( 408 const SendResponseCallback& done, 409 scoped_ptr<base::DictionaryValue> response, 410 DaemonController::AsyncResult result) { 411 switch (result) { 412 case DaemonController::RESULT_OK: 413 response->SetString("result", "OK"); 414 break; 415 case DaemonController::RESULT_FAILED: 416 response->SetString("result", "FAILED"); 417 break; 418 case DaemonController::RESULT_CANCELLED: 419 response->SetString("result", "CANCELLED"); 420 break; 421 case DaemonController::RESULT_FAILED_DIRECTORY: 422 response->SetString("result", "FAILED_DIRECTORY"); 423 break; 424 } 425 done.Run(response.Pass()); 426} 427 428void NativeMessagingHost::SendBooleanResult( 429 const SendResponseCallback& done, 430 scoped_ptr<base::DictionaryValue> response, bool result) { 431 response->SetBoolean("result", result); 432 done.Run(response.Pass()); 433} 434 435void NativeMessagingHost::SendCredentialsResponse( 436 const SendResponseCallback& done, 437 scoped_ptr<base::DictionaryValue> response, 438 const std::string& user_email, 439 const std::string& refresh_token) { 440 response->SetString("userEmail", user_email); 441 response->SetString("refreshToken", refresh_token); 442 done.Run(response.Pass()); 443} 444 445int NativeMessagingHostMain() { 446#if defined(OS_WIN) 447 // GetStdHandle() returns pseudo-handles for stdin and stdout even if 448 // the hosting executable specifies "Windows" subsystem. However the returned 449 // handles are invalid in that case unless standard input and output are 450 // redirected to a pipe or file. 451 base::PlatformFile read_file = GetStdHandle(STD_INPUT_HANDLE); 452 base::PlatformFile write_file = GetStdHandle(STD_OUTPUT_HANDLE); 453#elif defined(OS_POSIX) 454 base::PlatformFile read_file = STDIN_FILENO; 455 base::PlatformFile write_file = STDOUT_FILENO; 456#else 457#error Not implemented. 458#endif 459 460 // Mac OS X requires that the main thread be a UI message loop in order to 461 // receive distributed notifications from the System Preferences pane. An 462 // IO thread is needed for the pairing registry and URL context getter. 463 base::Thread io_thread("io_thread"); 464 io_thread.StartWithOptions( 465 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); 466 467 base::MessageLoopForUI message_loop; 468 base::RunLoop run_loop; 469 470 scoped_refptr<DaemonController> daemon_controller = 471 DaemonController::Create(); 472 473 // Pass handle of the native view to the controller so that the UAC prompts 474 // are focused properly. 475 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 476 if (command_line->HasSwitch(kParentWindowSwitchName)) { 477 std::string native_view = 478 command_line->GetSwitchValueASCII(kParentWindowSwitchName); 479 int64 native_view_handle = 0; 480 if (base::StringToInt64(native_view, &native_view_handle)) { 481 daemon_controller->SetWindow(reinterpret_cast<void*>(native_view_handle)); 482 } else { 483 LOG(WARNING) << "Invalid parameter value --" << kParentWindowSwitchName 484 << "=" << native_view; 485 } 486 } 487 488 // OAuth client (for credential requests). 489 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter( 490 new URLRequestContextGetter(io_thread.message_loop_proxy())); 491 scoped_ptr<OAuthClient> oauth_client( 492 new OAuthClient(url_request_context_getter)); 493 494 net::URLFetcher::SetIgnoreCertificateRequests(true); 495 496 // Create the pairing registry and native messaging host. 497 scoped_refptr<protocol::PairingRegistry> pairing_registry = 498 CreatePairingRegistry(io_thread.message_loop_proxy()); 499 scoped_ptr<NativeMessagingChannel::Delegate> host( 500 new NativeMessagingHost(daemon_controller, 501 pairing_registry, 502 oauth_client.Pass())); 503 504 // Set up the native messaging channel. 505 scoped_ptr<NativeMessagingChannel> channel( 506 new NativeMessagingChannel(host.Pass(), read_file, write_file)); 507 channel->Start(run_loop.QuitClosure()); 508 509 // Run the loop until channel is alive. 510 run_loop.Run(); 511 return kSuccessExitCode; 512} 513 514} // namespace remoting 515