summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authoralexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-03 02:24:41 +0000
committeralexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-03 02:24:41 +0000
commitc09a61db857990675454493a56141d1cdcac427c (patch)
treee7e97ca8cefe9f95e519304fe4ba68d0513441f2 /remoting
parent14689246fd04d424204bc1ced1cf20a3bd569427 (diff)
downloadchromium_src-c09a61db857990675454493a56141d1cdcac427c.zip
chromium_src-c09a61db857990675454493a56141d1cdcac427c.tar.gz
chromium_src-c09a61db857990675454493a56141d1cdcac427c.tar.bz2
Implemented a pairing registry delegate that stores the pairings in the Windows registry.
The delegate supports two different keys for storing privileged (the shared secret) and unprivileged data (the rest of the fields). BUG=156182 Review URL: https://chromiumcodereview.appspot.com/21641002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@215461 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r--remoting/base/scoped_reg_key_win.h42
-rw-r--r--remoting/host/pairing_registry_delegate_linux_unittest.cc2
-rw-r--r--remoting/host/pairing_registry_delegate_win.cc237
-rw-r--r--remoting/host/pairing_registry_delegate_win.h60
-rw-r--r--remoting/host/pairing_registry_delegate_win_unittest.cc128
-rw-r--r--remoting/remoting.gyp4
6 files changed, 470 insertions, 3 deletions
diff --git a/remoting/base/scoped_reg_key_win.h b/remoting/base/scoped_reg_key_win.h
new file mode 100644
index 0000000..c68b99f
--- /dev/null
+++ b/remoting/base/scoped_reg_key_win.h
@@ -0,0 +1,42 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_BASE_SCOPED_REG_KEY_WIN_H_
+#define REMOTING_BASE_SCOPED_REG_KEY_WIN_H_
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/win/scoped_handle.h"
+
+namespace remoting {
+
+// The traits class for Win32 registry handles.
+class RegKeyTraits {
+ public:
+ typedef HKEY Handle;
+
+ static bool CloseHandle(HKEY handle) {
+ return RegCloseKey(handle) == ERROR_SUCCESS;
+ }
+
+ static bool IsHandleValid(HKEY handle) {
+ return handle != NULL;
+ }
+
+ static HKEY NullHandle() {
+ return NULL;
+ }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(RegKeyTraits);
+};
+
+typedef base::win::GenericScopedHandle<
+ RegKeyTraits, base::win::DummyVerifierTraits> ScopedRegKey;
+
+} // namespace remoting
+
+#endif // REMOTING_BASE_SCOPED_REG_KEY_WIN_H_
diff --git a/remoting/host/pairing_registry_delegate_linux_unittest.cc b/remoting/host/pairing_registry_delegate_linux_unittest.cc
index d044a04..5596e3b 100644
--- a/remoting/host/pairing_registry_delegate_linux_unittest.cc
+++ b/remoting/host/pairing_registry_delegate_linux_unittest.cc
@@ -47,7 +47,7 @@ TEST_F(PairingRegistryDelegateLinuxTest, SaveAndLoad) {
EXPECT_TRUE(delegate->Save(pairing2));
// Verify that there are two pairings in the store now.
-EXPECT_EQ(delegate->LoadAll()->GetSize(), 2u);
+ EXPECT_EQ(delegate->LoadAll()->GetSize(), 2u);
// Verify that they can be retrieved.
EXPECT_EQ(delegate->Load(pairing1.client_id()), pairing1);
diff --git a/remoting/host/pairing_registry_delegate_win.cc b/remoting/host/pairing_registry_delegate_win.cc
index dba3ec3..d0527ab 100644
--- a/remoting/host/pairing_registry_delegate_win.cc
+++ b/remoting/host/pairing_registry_delegate_win.cc
@@ -2,14 +2,247 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "remoting/host/pairing_registry_delegate.h"
+#include "remoting/host/pairing_registry_delegate_win.h"
-#include "base/task_runner.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "base/win/registry.h"
namespace remoting {
+namespace {
+
+const wchar_t kClientNameKey[] = L"clientName";
+const wchar_t kCreatedTimeKey[] = L"createdTime";
+const wchar_t kSharedSecretKey[] = L"sharedSecret";
+
+// Duplicates a registry key handle (returned by RegCreateXxx/RegOpenXxx).
+// The returned handle cannot be inherited and has the same permissions as
+// the source one.
+bool DuplicateKeyHandle(HKEY source, ScopedRegKey* dest) {
+ HANDLE handle;
+ if (!DuplicateHandle(GetCurrentProcess(),
+ source,
+ GetCurrentProcess(),
+ &handle,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS)) {
+ PLOG(ERROR) << "Failed to duplicate a registry key handle";
+ return false;
+ }
+
+ dest->Set(reinterpret_cast<HKEY>(handle));
+ return true;
+}
+
+} // namespace
+
using protocol::PairingRegistry;
+PairingRegistryDelegateWin::PairingRegistryDelegateWin() {
+}
+
+PairingRegistryDelegateWin::~PairingRegistryDelegateWin() {
+}
+
+bool PairingRegistryDelegateWin::SetRootKeys(HKEY privileged,
+ HKEY unprivileged) {
+ DCHECK(!privileged_);
+ DCHECK(!unprivileged_);
+ DCHECK(unprivileged);
+
+ if (!DuplicateKeyHandle(unprivileged, &unprivileged_))
+ return false;
+
+ if (privileged) {
+ if (!DuplicateKeyHandle(privileged, &privileged_))
+ return false;
+ }
+
+ return true;
+}
+
+scoped_ptr<base::ListValue> PairingRegistryDelegateWin::LoadAll() {
+ scoped_ptr<base::ListValue> pairings(new base::ListValue());
+
+ DWORD index = 0;
+ for (LONG result = ERROR_SUCCESS; result == ERROR_SUCCESS; ++index) {
+ wchar_t name[MAX_PATH];
+ result = RegEnumKey(unprivileged_, index, name, arraysize(name));
+ if (result == ERROR_SUCCESS) {
+ PairingRegistry::Pairing pairing = Load(WideToUTF8(name));
+ if (pairing.is_valid())
+ pairings->Append(pairing.ToValue().release());
+ }
+ }
+
+ return pairings.Pass();
+}
+
+bool PairingRegistryDelegateWin::DeleteAll() {
+ LONG result = ERROR_SUCCESS;
+ bool success = true;
+ while (result == ERROR_SUCCESS) {
+ wchar_t name[MAX_PATH];
+ result = RegEnumKey(unprivileged_, 0, name, arraysize(name));
+ if (result == ERROR_SUCCESS)
+ success = success && Delete(WideToUTF8(name));
+ }
+
+ success = success && result == ERROR_NO_MORE_ITEMS;
+ return success;
+}
+
+PairingRegistry::Pairing PairingRegistryDelegateWin::Load(
+ const std::string& client_id) {
+ string16 key_name = UTF8ToUTF16(client_id);
+
+ FILETIME created_time;
+ string16 client_name;
+ string16 shared_secret;
+
+ // Read unprivileged fields first.
+ base::win::RegKey pairing_key;
+ LONG result = pairing_key.Open(unprivileged_, key_name.c_str(), KEY_READ);
+ if (result != ERROR_SUCCESS) {
+ SetLastError(result);
+ PLOG(ERROR) << "Cannot open pairing entry '" << client_id << "'";
+ return PairingRegistry::Pairing();
+ }
+ result = pairing_key.ReadValue(kClientNameKey, &client_name);
+ if (result != ERROR_SUCCESS) {
+ SetLastError(result);
+ PLOG(ERROR) << "Cannot read '" << client_id << "/" << kClientNameKey << "'";
+ return PairingRegistry::Pairing();
+ }
+ DWORD size = sizeof(created_time);
+ DWORD type;
+ result = pairing_key.ReadValue(kCreatedTimeKey,
+ &created_time,
+ &size,
+ &type);
+ if (result != ERROR_SUCCESS ||
+ size != sizeof(created_time) ||
+ type != REG_QWORD) {
+ SetLastError(result);
+ PLOG(ERROR) << "Cannot read '" << client_id << "/" << kCreatedTimeKey
+ << "'";
+ return PairingRegistry::Pairing();
+ }
+
+ // Read the shared secret.
+ if (privileged_.IsValid()) {
+ result = pairing_key.Open(privileged_, key_name.c_str(), KEY_READ);
+ if (result != ERROR_SUCCESS) {
+ SetLastError(result);
+ PLOG(ERROR) << "Cannot open pairing entry '" << client_id << "'";
+ return PairingRegistry::Pairing();
+ }
+ result = pairing_key.ReadValue(kSharedSecretKey, &shared_secret);
+ if (result != ERROR_SUCCESS) {
+ SetLastError(result);
+ PLOG(ERROR) << "Cannot read '" << client_id << "/" << kSharedSecretKey
+ << "'";
+ return PairingRegistry::Pairing();
+ }
+ }
+
+ return PairingRegistry::Pairing(base::Time::FromFileTime(created_time),
+ UTF16ToUTF8(client_name),
+ client_id,
+ UTF16ToUTF8(shared_secret));
+}
+
+bool PairingRegistryDelegateWin::Save(const PairingRegistry::Pairing& pairing) {
+ string16 key_name = UTF8ToUTF16(pairing.client_id());
+
+ if (!privileged_.IsValid()) {
+ LOG(ERROR) << "Cannot save pairing entry '" << pairing.client_id()
+ << "': the delegate is read-only.";
+ return false;
+ }
+
+ // Store the shared secret first.
+ base::win::RegKey pairing_key;
+ LONG result = pairing_key.Create(privileged_, key_name.c_str(), KEY_WRITE);
+ if (result != ERROR_SUCCESS) {
+ SetLastError(result);
+ PLOG(ERROR) << "Cannot save pairing entry '" << pairing.client_id()
+ << "'";
+ return false;
+ }
+ result = pairing_key.WriteValue(kSharedSecretKey,
+ UTF8ToUTF16(pairing.shared_secret()).c_str());
+ if (result != ERROR_SUCCESS) {
+ SetLastError(result);
+ PLOG(ERROR) << "Cannot write '" << pairing.client_id() << "/"
+ << kSharedSecretKey << "'";
+ return false;
+ }
+
+ // Store the rest of the fields.
+ result = pairing_key.Create(unprivileged_, key_name.c_str(), KEY_WRITE);
+ if (result != ERROR_SUCCESS) {
+ SetLastError(result);
+ PLOG(ERROR) << "Cannot save pairing entry '" << pairing.client_id()
+ << "'";
+ return false;
+ }
+ result = pairing_key.WriteValue(kClientNameKey,
+ UTF8ToUTF16(pairing.client_name()).c_str());
+ if (result != ERROR_SUCCESS) {
+ SetLastError(result);
+ PLOG(ERROR) << "Cannot write '" << pairing.client_id() << "/"
+ << kClientNameKey << "'";
+ return false;
+ }
+ FILETIME created_time = pairing.created_time().ToFileTime();
+ result = pairing_key.WriteValue(kCreatedTimeKey,
+ &created_time,
+ sizeof(created_time),
+ REG_QWORD);
+ if (result != ERROR_SUCCESS) {
+ SetLastError(result);
+ PLOG(ERROR) << "Cannot write '" << pairing.client_id() << "/"
+ << kCreatedTimeKey << "'";
+ return false;
+ }
+
+ return true;
+}
+
+bool PairingRegistryDelegateWin::Delete(const std::string& client_id) {
+ string16 key_name = UTF8ToUTF16(client_id);
+
+ if (!privileged_.IsValid()) {
+ LOG(ERROR) << "Cannot delete pairing entry '" << client_id
+ << "': the delegate is read-only.";
+ return false;
+ }
+
+ LONG result = RegDeleteKey(privileged_, key_name.c_str());
+ if (result != ERROR_SUCCESS &&
+ result != ERROR_FILE_NOT_FOUND &&
+ result != ERROR_PATH_NOT_FOUND) {
+ SetLastError(result);
+ PLOG(ERROR) << "Cannot delete pairing entry '" << client_id << "'";
+ return false;
+ }
+
+ result = RegDeleteKey(unprivileged_, key_name.c_str());
+ if (result != ERROR_SUCCESS &&
+ result != ERROR_FILE_NOT_FOUND &&
+ result != ERROR_PATH_NOT_FOUND) {
+ SetLastError(result);
+ PLOG(ERROR) << "Cannot delete pairing entry '" << client_id << "'";
+ return false;
+ }
+
+ return true;
+}
+
scoped_ptr<PairingRegistry::Delegate> CreatePairingRegistryDelegate() {
return scoped_ptr<PairingRegistry::Delegate>();
}
diff --git a/remoting/host/pairing_registry_delegate_win.h b/remoting/host/pairing_registry_delegate_win.h
new file mode 100644
index 0000000..8dac568
--- /dev/null
+++ b/remoting/host/pairing_registry_delegate_win.h
@@ -0,0 +1,60 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_PROTOCOL_PAIRING_REGISTRY_DELEGATE_WIN_H_
+#define REMOTING_PROTOCOL_PAIRING_REGISTRY_DELEGATE_WIN_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "remoting/base/scoped_reg_key_win.h"
+#include "remoting/protocol/pairing_registry.h"
+
+namespace base {
+class ListValue;
+} // namespace base
+
+namespace remoting {
+
+// Stores client pairing information in Windows registry. Two separate registry
+// keys are used:
+// - |privileged| - contains the shared secrets of all pairings. This key must
+// be protected by a strong ACL denying access to unprivileged
+// code.
+// - |unprivileged| - contains the rest of pairing state.
+//
+// Creator of this object is responsible for passing the registry key handles
+// with appropriate access. |privileged| may be NULL if read-only access is
+// sufficient. Shared secrets will not be returned in such a case.
+class PairingRegistryDelegateWin
+ : public protocol::PairingRegistry::Delegate {
+ public:
+ PairingRegistryDelegateWin();
+ virtual ~PairingRegistryDelegateWin();
+
+ // Passes the root keys to be used to access the pairing registry store.
+ // |privileged| is optional and may be NULL. The caller retains ownership of
+ // the passed handles.
+ bool SetRootKeys(HKEY privileged, HKEY unprivileged);
+
+ // PairingRegistry::Delegate interface
+ virtual scoped_ptr<base::ListValue> LoadAll() OVERRIDE;
+ virtual bool DeleteAll() OVERRIDE;
+ virtual protocol::PairingRegistry::Pairing Load(
+ const std::string& client_id) OVERRIDE;
+ virtual bool Save(const protocol::PairingRegistry::Pairing& pairing) OVERRIDE;
+ virtual bool Delete(const std::string& client_id) OVERRIDE;
+
+ private:
+ ScopedRegKey privileged_;
+ ScopedRegKey unprivileged_;
+
+ DISALLOW_COPY_AND_ASSIGN(PairingRegistryDelegateWin);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_PROTOCOL_PAIRING_REGISTRY_DELEGATE_WIN_H_
diff --git a/remoting/host/pairing_registry_delegate_win_unittest.cc b/remoting/host/pairing_registry_delegate_win_unittest.cc
new file mode 100644
index 0000000..8ba6bb1
--- /dev/null
+++ b/remoting/host/pairing_registry_delegate_win_unittest.cc
@@ -0,0 +1,128 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/host/pairing_registry_delegate_win.h"
+
+#include <shlwapi.h>
+
+#include "base/guid.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "base/win/registry.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace remoting {
+
+using protocol::PairingRegistry;
+
+class PairingRegistryDelegateWinTest : public testing::Test {
+ public:
+ virtual void SetUp() OVERRIDE {
+ key_name_ = base::GenerateGUID();
+
+ base::win::RegKey root;
+ EXPECT_TRUE(root.Create(HKEY_CURRENT_USER, UTF8ToWide(key_name_).c_str(),
+ KEY_READ | KEY_WRITE) == ERROR_SUCCESS);
+
+ EXPECT_TRUE(privileged_.Create(root.Handle(), L"privileged",
+ KEY_READ | KEY_WRITE) == ERROR_SUCCESS);
+ EXPECT_TRUE(unprivileged_.Create(root.Handle(), L"unprivileged",
+ KEY_READ | KEY_WRITE) == ERROR_SUCCESS);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ privileged_.Close();
+ unprivileged_.Close();
+ EXPECT_TRUE(SHDeleteKey(HKEY_CURRENT_USER,
+ UTF8ToWide(key_name_).c_str()) == ERROR_SUCCESS);
+ }
+
+ protected:
+ std::string key_name_;
+ base::win::RegKey privileged_;
+ base::win::RegKey unprivileged_;
+};
+
+TEST_F(PairingRegistryDelegateWinTest, SaveAndLoad) {
+ scoped_ptr<PairingRegistryDelegateWin> delegate(
+ new PairingRegistryDelegateWin());
+ delegate->SetRootKeys(privileged_.Handle(), unprivileged_.Handle());
+
+ // Check that registry is initially empty.
+ EXPECT_TRUE(delegate->LoadAll()->empty());
+
+ // Add a couple of pairings.
+ PairingRegistry::Pairing pairing1(base::Time::Now(), "xxx", "xxx", "xxx");
+ PairingRegistry::Pairing pairing2(base::Time::Now(), "yyy", "yyy", "yyy");
+ EXPECT_TRUE(delegate->Save(pairing1));
+ EXPECT_TRUE(delegate->Save(pairing2));
+
+ // Verify that there are two pairings in the store now.
+ EXPECT_EQ(delegate->LoadAll()->GetSize(), 2u);
+
+ // Verify that they can be retrieved.
+ EXPECT_EQ(delegate->Load(pairing1.client_id()), pairing1);
+ EXPECT_EQ(delegate->Load(pairing2.client_id()), pairing2);
+
+ // Delete the first pairing.
+ EXPECT_TRUE(delegate->Delete(pairing1.client_id()));
+
+ // Verify that there is only one pairing left.
+ EXPECT_EQ(delegate->Load(pairing1.client_id()), PairingRegistry::Pairing());
+ EXPECT_EQ(delegate->Load(pairing2.client_id()), pairing2);
+
+ // Verify that the only remaining value is |pairing2|.
+ EXPECT_EQ(delegate->LoadAll()->GetSize(), 1u);
+ scoped_ptr<base::ListValue> pairings = delegate->LoadAll();
+ base::Value* json;
+ EXPECT_TRUE(pairings->Get(0, &json));
+ EXPECT_EQ(PairingRegistry::Pairing::CreateFromValue(*json), pairing2);
+
+ // Delete the rest and verify.
+ EXPECT_TRUE(delegate->DeleteAll());
+ EXPECT_TRUE(delegate->LoadAll()->empty());
+}
+
+// Verifies that the delegate is stateless by using two different instances.
+TEST_F(PairingRegistryDelegateWinTest, Stateless) {
+ scoped_ptr<PairingRegistryDelegateWin> load_delegate(
+ new PairingRegistryDelegateWin());
+ load_delegate->SetRootKeys(privileged_.Handle(), unprivileged_.Handle());
+ scoped_ptr<PairingRegistryDelegateWin> save_delegate(
+ new PairingRegistryDelegateWin());
+ save_delegate->SetRootKeys(privileged_.Handle(), unprivileged_.Handle());
+
+ PairingRegistry::Pairing pairing(base::Time::Now(), "xxx", "xxx", "xxx");
+ EXPECT_TRUE(save_delegate->Save(pairing));
+ EXPECT_EQ(load_delegate->Load(pairing.client_id()), pairing);
+}
+
+TEST_F(PairingRegistryDelegateWinTest, Unprivileged) {
+ scoped_ptr<PairingRegistryDelegateWin> delegate(
+ new PairingRegistryDelegateWin());
+ delegate->SetRootKeys(privileged_.Handle(), unprivileged_.Handle());
+
+ PairingRegistry::Pairing pairing(base::Time::Now(), "xxx", "xxx", "xxx");
+ EXPECT_TRUE(delegate->Save(pairing));
+ EXPECT_EQ(delegate->Load(pairing.client_id()), pairing);
+
+ // Strip the delegate from write access and validate that it still can be used
+ // to read the pairings.
+ delegate.reset(new PairingRegistryDelegateWin());
+ delegate->SetRootKeys(NULL, unprivileged_.Handle());
+
+ PairingRegistry::Pairing unprivileged_pairing =
+ delegate->Load(pairing.client_id());
+ EXPECT_EQ(pairing.client_id(), unprivileged_pairing.client_id());
+ EXPECT_EQ(pairing.client_name(), unprivileged_pairing.client_name());
+ EXPECT_EQ(pairing.created_time(), unprivileged_pairing.created_time());
+
+ // Verify that the shared secret if not available.
+ EXPECT_TRUE(unprivileged_pairing.shared_secret().empty());
+
+ // Verify that a pairing cannot be saved.
+ EXPECT_FALSE(delegate->Save(pairing));
+}
+
+} // namespace remoting
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index d008fb7..5675799 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -402,6 +402,7 @@
'host/pairing_registry_delegate_linux.h',
'host/pairing_registry_delegate_mac.cc',
'host/pairing_registry_delegate_win.cc',
+ 'host/pairing_registry_delegate_win.h',
'host/pam_authorization_factory_posix.cc',
'host/pam_authorization_factory_posix.h',
'host/pin_hash.cc',
@@ -2416,6 +2417,8 @@
'base/rsa_key_pair.h',
'base/running_average.cc',
'base/running_average.h',
+ 'base/scoped_reg_key_win.h',
+ 'base/scoped_sc_handle_win.h',
'base/socket_reader.cc',
'base/socket_reader.h',
'base/typed_buffer.h',
@@ -2746,6 +2749,7 @@
'host/local_input_monitor_unittest.cc',
'host/log_to_server_unittest.cc',
'host/pairing_registry_delegate_linux_unittest.cc',
+ 'host/pairing_registry_delegate_win_unittest.cc',
'host/pin_hash_unittest.cc',
'host/policy_hack/fake_policy_watcher.cc',
'host/policy_hack/fake_policy_watcher.h',