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/callback_helpers.h" 13#include "base/logging.h" 14#include "base/strings/stringize_macros.h" 15#include "base/threading/thread.h" 16#include "base/values.h" 17#include "google_apis/gaia/gaia_oauth_client.h" 18#include "google_apis/google_api_keys.h" 19#include "net/base/net_util.h" 20#include "remoting/base/rsa_key_pair.h" 21#include "remoting/host/pin_hash.h" 22#include "remoting/host/setup/oauth_client.h" 23#include "remoting/protocol/pairing_registry.h" 24 25namespace { 26 27// redirect_uri to use when authenticating service accounts (service account 28// codes are obtained "out-of-band", i.e., not through an OAuth redirect). 29const char* kServiceAccountRedirectUri = "oob"; 30 31// Features supported in addition to the base protocol. 32const char* kSupportedFeatures[] = { 33 "pairingRegistry", 34 "oauthClient" 35}; 36 37// Helper to extract the "config" part of a message as a DictionaryValue. 38// Returns NULL on failure, and logs an error message. 39scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage( 40 const base::DictionaryValue& message) { 41 scoped_ptr<base::DictionaryValue> result; 42 const base::DictionaryValue* config_dict; 43 if (message.GetDictionary("config", &config_dict)) { 44 result.reset(config_dict->DeepCopy()); 45 } else { 46 LOG(ERROR) << "'config' dictionary not found"; 47 } 48 return result.Pass(); 49} 50 51} // namespace 52 53namespace remoting { 54 55Me2MeNativeMessagingHost::Me2MeNativeMessagingHost( 56 scoped_ptr<NativeMessagingChannel> channel, 57 scoped_refptr<DaemonController> daemon_controller, 58 scoped_refptr<protocol::PairingRegistry> pairing_registry, 59 scoped_ptr<OAuthClient> oauth_client) 60 : channel_(channel.Pass()), 61 daemon_controller_(daemon_controller), 62 pairing_registry_(pairing_registry), 63 oauth_client_(oauth_client.Pass()), 64 weak_factory_(this) { 65 weak_ptr_ = weak_factory_.GetWeakPtr(); 66} 67 68Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() { 69 DCHECK(thread_checker_.CalledOnValidThread()); 70} 71 72void Me2MeNativeMessagingHost::Start( 73 const base::Closure& quit_closure) { 74 DCHECK(thread_checker_.CalledOnValidThread()); 75 76 channel_->Start( 77 base::Bind(&Me2MeNativeMessagingHost::ProcessMessage, weak_ptr_), 78 quit_closure); 79} 80 81void Me2MeNativeMessagingHost::ProcessMessage( 82 scoped_ptr<base::DictionaryValue> message) { 83 DCHECK(thread_checker_.CalledOnValidThread()); 84 85 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue()); 86 87 // If the client supplies an ID, it will expect it in the response. This 88 // might be a string or a number, so cope with both. 89 const base::Value* id; 90 if (message->Get("id", &id)) 91 response->Set("id", id->DeepCopy()); 92 93 std::string type; 94 if (!message->GetString("type", &type)) { 95 LOG(ERROR) << "'type' not found"; 96 channel_->SendMessage(scoped_ptr<base::DictionaryValue>()); 97 return; 98 } 99 100 response->SetString("type", type + "Response"); 101 102 bool success = false; 103 if (type == "hello") { 104 success = ProcessHello(*message, response.Pass()); 105 } else if (type == "clearPairedClients") { 106 success = ProcessClearPairedClients(*message, response.Pass()); 107 } else if (type == "deletePairedClient") { 108 success = ProcessDeletePairedClient(*message, response.Pass()); 109 } else if (type == "getHostName") { 110 success = ProcessGetHostName(*message, response.Pass()); 111 } else if (type == "getPinHash") { 112 success = ProcessGetPinHash(*message, response.Pass()); 113 } else if (type == "generateKeyPair") { 114 success = ProcessGenerateKeyPair(*message, response.Pass()); 115 } else if (type == "updateDaemonConfig") { 116 success = ProcessUpdateDaemonConfig(*message, response.Pass()); 117 } else if (type == "getDaemonConfig") { 118 success = ProcessGetDaemonConfig(*message, response.Pass()); 119 } else if (type == "getPairedClients") { 120 success = ProcessGetPairedClients(*message, response.Pass()); 121 } else if (type == "getUsageStatsConsent") { 122 success = ProcessGetUsageStatsConsent(*message, response.Pass()); 123 } else if (type == "startDaemon") { 124 success = ProcessStartDaemon(*message, response.Pass()); 125 } else if (type == "stopDaemon") { 126 success = ProcessStopDaemon(*message, response.Pass()); 127 } else if (type == "getDaemonState") { 128 success = ProcessGetDaemonState(*message, response.Pass()); 129 } else if (type == "getHostClientId") { 130 success = ProcessGetHostClientId(*message, response.Pass()); 131 } else if (type == "getCredentialsFromAuthCode") { 132 success = ProcessGetCredentialsFromAuthCode(*message, response.Pass()); 133 } else { 134 LOG(ERROR) << "Unsupported request type: " << type; 135 } 136 137 if (!success) 138 channel_->SendMessage(scoped_ptr<base::DictionaryValue>()); 139} 140 141bool Me2MeNativeMessagingHost::ProcessHello( 142 const base::DictionaryValue& message, 143 scoped_ptr<base::DictionaryValue> response) { 144 DCHECK(thread_checker_.CalledOnValidThread()); 145 146 response->SetString("version", STRINGIZE(VERSION)); 147 scoped_ptr<base::ListValue> supported_features_list(new base::ListValue()); 148 supported_features_list->AppendStrings(std::vector<std::string>( 149 kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures))); 150 response->Set("supportedFeatures", supported_features_list.release()); 151 channel_->SendMessage(response.Pass()); 152 return true; 153} 154 155bool Me2MeNativeMessagingHost::ProcessClearPairedClients( 156 const base::DictionaryValue& message, 157 scoped_ptr<base::DictionaryValue> response) { 158 DCHECK(thread_checker_.CalledOnValidThread()); 159 160 if (pairing_registry_) { 161 pairing_registry_->ClearAllPairings( 162 base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_, 163 base::Passed(&response))); 164 } else { 165 SendBooleanResult(response.Pass(), false); 166 } 167 return true; 168} 169 170bool Me2MeNativeMessagingHost::ProcessDeletePairedClient( 171 const base::DictionaryValue& message, 172 scoped_ptr<base::DictionaryValue> response) { 173 DCHECK(thread_checker_.CalledOnValidThread()); 174 175 std::string client_id; 176 if (!message.GetString(protocol::PairingRegistry::kClientIdKey, &client_id)) { 177 LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey 178 << "' string not found."; 179 return false; 180 } 181 182 if (pairing_registry_) { 183 pairing_registry_->DeletePairing( 184 client_id, base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, 185 weak_ptr_, base::Passed(&response))); 186 } else { 187 SendBooleanResult(response.Pass(), false); 188 } 189 return true; 190} 191 192bool Me2MeNativeMessagingHost::ProcessGetHostName( 193 const base::DictionaryValue& message, 194 scoped_ptr<base::DictionaryValue> response) { 195 DCHECK(thread_checker_.CalledOnValidThread()); 196 197 response->SetString("hostname", net::GetHostName()); 198 channel_->SendMessage(response.Pass()); 199 return true; 200} 201 202bool Me2MeNativeMessagingHost::ProcessGetPinHash( 203 const base::DictionaryValue& message, 204 scoped_ptr<base::DictionaryValue> response) { 205 DCHECK(thread_checker_.CalledOnValidThread()); 206 207 std::string host_id; 208 if (!message.GetString("hostId", &host_id)) { 209 LOG(ERROR) << "'hostId' not found: " << message; 210 return false; 211 } 212 std::string pin; 213 if (!message.GetString("pin", &pin)) { 214 LOG(ERROR) << "'pin' not found: " << message; 215 return false; 216 } 217 response->SetString("hash", MakeHostPinHash(host_id, pin)); 218 channel_->SendMessage(response.Pass()); 219 return true; 220} 221 222bool Me2MeNativeMessagingHost::ProcessGenerateKeyPair( 223 const base::DictionaryValue& message, 224 scoped_ptr<base::DictionaryValue> response) { 225 DCHECK(thread_checker_.CalledOnValidThread()); 226 227 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate(); 228 response->SetString("privateKey", key_pair->ToString()); 229 response->SetString("publicKey", key_pair->GetPublicKey()); 230 channel_->SendMessage(response.Pass()); 231 return true; 232} 233 234bool Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig( 235 const base::DictionaryValue& message, 236 scoped_ptr<base::DictionaryValue> response) { 237 DCHECK(thread_checker_.CalledOnValidThread()); 238 239 scoped_ptr<base::DictionaryValue> config_dict = 240 ConfigDictionaryFromMessage(message); 241 if (!config_dict) 242 return false; 243 244 daemon_controller_->UpdateConfig( 245 config_dict.Pass(), 246 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_, 247 base::Passed(&response))); 248 return true; 249} 250 251bool Me2MeNativeMessagingHost::ProcessGetDaemonConfig( 252 const base::DictionaryValue& message, 253 scoped_ptr<base::DictionaryValue> response) { 254 DCHECK(thread_checker_.CalledOnValidThread()); 255 256 daemon_controller_->GetConfig( 257 base::Bind(&Me2MeNativeMessagingHost::SendConfigResponse, weak_ptr_, 258 base::Passed(&response))); 259 return true; 260} 261 262bool Me2MeNativeMessagingHost::ProcessGetPairedClients( 263 const base::DictionaryValue& message, 264 scoped_ptr<base::DictionaryValue> response) { 265 DCHECK(thread_checker_.CalledOnValidThread()); 266 267 if (pairing_registry_) { 268 pairing_registry_->GetAllPairings( 269 base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse, 270 weak_ptr_, base::Passed(&response))); 271 } else { 272 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue); 273 SendPairedClientsResponse(response.Pass(), no_paired_clients.Pass()); 274 } 275 return true; 276} 277 278bool Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent( 279 const base::DictionaryValue& message, 280 scoped_ptr<base::DictionaryValue> response) { 281 DCHECK(thread_checker_.CalledOnValidThread()); 282 283 daemon_controller_->GetUsageStatsConsent( 284 base::Bind(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse, 285 weak_ptr_, base::Passed(&response))); 286 return true; 287} 288 289bool Me2MeNativeMessagingHost::ProcessStartDaemon( 290 const base::DictionaryValue& message, 291 scoped_ptr<base::DictionaryValue> response) { 292 DCHECK(thread_checker_.CalledOnValidThread()); 293 294 bool consent; 295 if (!message.GetBoolean("consent", &consent)) { 296 LOG(ERROR) << "'consent' not found."; 297 return false; 298 } 299 300 scoped_ptr<base::DictionaryValue> config_dict = 301 ConfigDictionaryFromMessage(message); 302 if (!config_dict) 303 return false; 304 305 daemon_controller_->SetConfigAndStart( 306 config_dict.Pass(), consent, 307 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_, 308 base::Passed(&response))); 309 return true; 310} 311 312bool Me2MeNativeMessagingHost::ProcessStopDaemon( 313 const base::DictionaryValue& message, 314 scoped_ptr<base::DictionaryValue> response) { 315 DCHECK(thread_checker_.CalledOnValidThread()); 316 317 daemon_controller_->Stop( 318 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_, 319 base::Passed(&response))); 320 return true; 321} 322 323bool Me2MeNativeMessagingHost::ProcessGetDaemonState( 324 const base::DictionaryValue& message, 325 scoped_ptr<base::DictionaryValue> response) { 326 DCHECK(thread_checker_.CalledOnValidThread()); 327 328 DaemonController::State state = daemon_controller_->GetState(); 329 switch (state) { 330 case DaemonController::STATE_NOT_IMPLEMENTED: 331 response->SetString("state", "NOT_IMPLEMENTED"); 332 break; 333 case DaemonController::STATE_NOT_INSTALLED: 334 response->SetString("state", "NOT_INSTALLED"); 335 break; 336 case DaemonController::STATE_INSTALLING: 337 response->SetString("state", "INSTALLING"); 338 break; 339 case DaemonController::STATE_STOPPED: 340 response->SetString("state", "STOPPED"); 341 break; 342 case DaemonController::STATE_STARTING: 343 response->SetString("state", "STARTING"); 344 break; 345 case DaemonController::STATE_STARTED: 346 response->SetString("state", "STARTED"); 347 break; 348 case DaemonController::STATE_STOPPING: 349 response->SetString("state", "STOPPING"); 350 break; 351 case DaemonController::STATE_UNKNOWN: 352 response->SetString("state", "UNKNOWN"); 353 break; 354 } 355 channel_->SendMessage(response.Pass()); 356 return true; 357} 358 359bool Me2MeNativeMessagingHost::ProcessGetHostClientId( 360 const base::DictionaryValue& message, 361 scoped_ptr<base::DictionaryValue> response) { 362 DCHECK(thread_checker_.CalledOnValidThread()); 363 364 response->SetString("clientId", google_apis::GetOAuth2ClientID( 365 google_apis::CLIENT_REMOTING_HOST)); 366 channel_->SendMessage(response.Pass()); 367 return true; 368} 369 370bool Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode( 371 const base::DictionaryValue& message, 372 scoped_ptr<base::DictionaryValue> response) { 373 DCHECK(thread_checker_.CalledOnValidThread()); 374 375 std::string auth_code; 376 if (!message.GetString("authorizationCode", &auth_code)) { 377 LOG(ERROR) << "'authorizationCode' string not found."; 378 return false; 379 } 380 381 gaia::OAuthClientInfo oauth_client_info = { 382 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST), 383 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST), 384 kServiceAccountRedirectUri 385 }; 386 387 oauth_client_->GetCredentialsFromAuthCode( 388 oauth_client_info, auth_code, base::Bind( 389 &Me2MeNativeMessagingHost::SendCredentialsResponse, weak_ptr_, 390 base::Passed(&response))); 391 392 return true; 393} 394 395void Me2MeNativeMessagingHost::SendConfigResponse( 396 scoped_ptr<base::DictionaryValue> response, 397 scoped_ptr<base::DictionaryValue> config) { 398 DCHECK(thread_checker_.CalledOnValidThread()); 399 400 if (config) { 401 response->Set("config", config.release()); 402 } else { 403 response->Set("config", Value::CreateNullValue()); 404 } 405 channel_->SendMessage(response.Pass()); 406} 407 408void Me2MeNativeMessagingHost::SendPairedClientsResponse( 409 scoped_ptr<base::DictionaryValue> response, 410 scoped_ptr<base::ListValue> pairings) { 411 DCHECK(thread_checker_.CalledOnValidThread()); 412 413 response->Set("pairedClients", pairings.release()); 414 channel_->SendMessage(response.Pass()); 415} 416 417void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse( 418 scoped_ptr<base::DictionaryValue> response, 419 const DaemonController::UsageStatsConsent& consent) { 420 DCHECK(thread_checker_.CalledOnValidThread()); 421 422 response->SetBoolean("supported", consent.supported); 423 response->SetBoolean("allowed", consent.allowed); 424 response->SetBoolean("setByPolicy", consent.set_by_policy); 425 channel_->SendMessage(response.Pass()); 426} 427 428void Me2MeNativeMessagingHost::SendAsyncResult( 429 scoped_ptr<base::DictionaryValue> response, 430 DaemonController::AsyncResult result) { 431 DCHECK(thread_checker_.CalledOnValidThread()); 432 433 switch (result) { 434 case DaemonController::RESULT_OK: 435 response->SetString("result", "OK"); 436 break; 437 case DaemonController::RESULT_FAILED: 438 response->SetString("result", "FAILED"); 439 break; 440 case DaemonController::RESULT_CANCELLED: 441 response->SetString("result", "CANCELLED"); 442 break; 443 case DaemonController::RESULT_FAILED_DIRECTORY: 444 response->SetString("result", "FAILED_DIRECTORY"); 445 break; 446 } 447 channel_->SendMessage(response.Pass()); 448} 449 450void Me2MeNativeMessagingHost::SendBooleanResult( 451 scoped_ptr<base::DictionaryValue> response, bool result) { 452 DCHECK(thread_checker_.CalledOnValidThread()); 453 454 response->SetBoolean("result", result); 455 channel_->SendMessage(response.Pass()); 456} 457 458void Me2MeNativeMessagingHost::SendCredentialsResponse( 459 scoped_ptr<base::DictionaryValue> response, 460 const std::string& user_email, 461 const std::string& refresh_token) { 462 DCHECK(thread_checker_.CalledOnValidThread()); 463 464 response->SetString("userEmail", user_email); 465 response->SetString("refreshToken", refresh_token); 466 channel_->SendMessage(response.Pass()); 467} 468 469} // namespace remoting 470