summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorsergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-24 18:24:26 +0000
committersergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-24 18:24:26 +0000
commit26d4accf41d14c363842af2422688246b07aba23 (patch)
tree59388d8b0a9c8ffeca982ee53896684241a7e9a4 /remoting
parentafefc060a6eac61aec0e4e538502fe8bb1a1c436 (diff)
downloadchromium_src-26d4accf41d14c363842af2422688246b07aba23.zip
chromium_src-26d4accf41d14c363842af2422688246b07aba23.tar.gz
chromium_src-26d4accf41d14c363842af2422688246b07aba23.tar.bz2
Enable V2 authentication for Me2Me host.
Updated both Me2me host and simple host to accept spake2_hmac auth. They still accept V1 authentication. Also the corresponding scripts are updated to ask for PIN BUG=107777 Review URL: http://codereview.chromium.org/9270031 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@118860 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r--remoting/host/host_config.cc3
-rw-r--r--remoting/host/host_config.h4
-rw-r--r--remoting/host/remoting_me2me_host.cc35
-rw-r--r--remoting/host/simple_host_process.cc17
-rw-r--r--remoting/protocol/me2me_host_authenticator_factory.cc43
-rw-r--r--remoting/protocol/me2me_host_authenticator_factory.h23
-rwxr-xr-xremoting/tools/me2me_virtual_host.py45
-rwxr-xr-xremoting/tools/register_host.py56
8 files changed, 161 insertions, 65 deletions
diff --git a/remoting/host/host_config.cc b/remoting/host/host_config.cc
index 09ba8e3..8841b8b 100644
--- a/remoting/host/host_config.cc
+++ b/remoting/host/host_config.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -12,6 +12,7 @@ const char kXmppAuthTokenConfigPath[] = "xmpp_auth_token";
const char kXmppAuthServiceConfigPath[] = "xmpp_auth_service";
const char kHostIdConfigPath[] = "host_id";
const char kHostNameConfigPath[] = "host_name";
+const char kHostSecretHashConfigPath[] = "host_secret_hash";
const char kPrivateKeyConfigPath[] = "private_key";
} // namespace remoting
diff --git a/remoting/host/host_config.h b/remoting/host/host_config.h
index bca937d..79b2060 100644
--- a/remoting/host/host_config.h
+++ b/remoting/host/host_config.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -26,6 +26,8 @@ extern const char kXmppAuthServiceConfigPath[];
extern const char kHostIdConfigPath[];
// Readable host name.
extern const char kHostNameConfigPath[];
+// Hash of the host secret used for authentication.
+extern const char kHostSecretHashConfigPath[];
// Private keys used for host authentication.
extern const char kPrivateKeyConfigPath[];
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index 5e17900..6c2b1cb 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -110,10 +110,10 @@ class HostProcess {
private:
// Read Host config from disk, returning true if successful.
bool LoadConfig(base::MessageLoopProxy* io_message_loop) {
- scoped_refptr<remoting::JsonHostConfig> host_config =
- new remoting::JsonHostConfig(host_config_path_, io_message_loop);
- scoped_refptr<remoting::JsonHostConfig> auth_config =
- new remoting::JsonHostConfig(auth_config_path_, io_message_loop);
+ scoped_refptr<JsonHostConfig> host_config =
+ new JsonHostConfig(host_config_path_, io_message_loop);
+ scoped_refptr<JsonHostConfig> auth_config =
+ new JsonHostConfig(auth_config_path_, io_message_loop);
std::string failed_path;
if (!host_config->Read()) {
@@ -135,6 +135,17 @@ class HostProcess {
return false;
}
+ std::string host_secret_hash_string;
+ if (!host_config->GetString(kHostSecretHashConfigPath,
+ &host_secret_hash_string)) {
+ host_secret_hash_string = "plain:";
+ }
+
+ if (!host_secret_hash_.Parse(host_secret_hash_string)) {
+ LOG(ERROR) << "Invalid host_secret_hash.";
+ return false;
+ }
+
// Use an XMPP connection to the Talk network for session signalling.
if (!auth_config->GetString(kXmppLoginConfigPath, &xmpp_login_) ||
!auth_config->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token_)) {
@@ -142,13 +153,13 @@ class HostProcess {
return false;
}
- if (!auth_config->GetString(remoting::kXmppAuthServiceConfigPath,
- &xmpp_auth_service_)) {
+ if (!auth_config->GetString(kXmppAuthServiceConfigPath,
+ &xmpp_auth_service_)) {
// For the me2me host, we assume we use the ClientLogin token for
// chromiumsync because we do not have an HTTP stack with which we can
// easily request an OAuth2 access token even if we had a RefreshToken for
// the account.
- xmpp_auth_service_ = remoting::kChromotingTokenDefaultServiceName;
+ xmpp_auth_service_ = kChromotingTokenDefaultServiceName;
}
return true;
@@ -183,19 +194,16 @@ class HostProcess {
host_->Start();
// Create authenticator factory.
- //
- // TODO(sergeyu): Currently empty PIN is used. This is a temporary
- // hack pending us adding a way to set a PIN. crbug.com/105214 .
scoped_ptr<protocol::AuthenticatorFactory> factory(
new protocol::Me2MeHostAuthenticatorFactory(
xmpp_login_, key_pair_.GenerateCertificate(),
- *key_pair_.private_key(), ""));
+ *key_pair_.private_key(), host_secret_hash_));
host_->SetAuthenticatorFactory(factory.Pass());
}
MessageLoop message_loop_;
base::Thread file_io_thread_;
- remoting::ChromotingHostContext context_;
+ ChromotingHostContext context_;
scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
FilePath auth_config_path_;
@@ -203,6 +211,7 @@ class HostProcess {
std::string host_id_;
HostKeyPair key_pair_;
+ protocol::SharedSecretHash host_secret_hash_;
std::string xmpp_login_;
std::string xmpp_auth_token_;
std::string xmpp_auth_service_;
@@ -210,7 +219,7 @@ class HostProcess {
scoped_ptr<SignalStrategy> signal_strategy_;
scoped_ptr<SignalingConnector> signaling_connector_;
scoped_ptr<DesktopEnvironment> desktop_environment_;
- scoped_ptr<remoting::HeartbeatSender> heartbeat_sender_;
+ scoped_ptr<HeartbeatSender> heartbeat_sender_;
scoped_ptr<LogToServer> log_to_server_;
scoped_ptr<HostEventLogger> host_event_logger_;
scoped_refptr<ChromotingHost> host_;
diff --git a/remoting/host/simple_host_process.cc b/remoting/host/simple_host_process.cc
index 053c394..d16e829 100644
--- a/remoting/host/simple_host_process.cc
+++ b/remoting/host/simple_host_process.cc
@@ -121,6 +121,17 @@ class SimpleHost {
return 1;
}
+ std::string host_secret_hash_string;
+ if (!config->GetString(kHostSecretHashConfigPath,
+ &host_secret_hash_string)) {
+ host_secret_hash_string = "plain:";
+ }
+
+ if (!host_secret_hash_.Parse(host_secret_hash_string)) {
+ LOG(ERROR) << "Invalid host_secret_hash.";
+ return false;
+ }
+
// Use an XMPP connection to the Talk network for session signalling.
if (!config->GetString(kXmppLoginConfigPath, &xmpp_login_) ||
!config->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token_)) {
@@ -242,14 +253,11 @@ class SimpleHost {
host_->Start();
// Create a Me2Me authenticator factory.
- //
- // TODO(sergeyu): Currently empty PIN is used. This is a temporary
- // hack pending us adding a way to set a PIN. crbug.com/105214 .
if (!is_it2me_) {
scoped_ptr<protocol::AuthenticatorFactory> factory(
new protocol::Me2MeHostAuthenticatorFactory(
xmpp_login_, key_pair_.GenerateCertificate(),
- *key_pair_.private_key(), ""));
+ *key_pair_.private_key(), host_secret_hash_));
host_->SetAuthenticatorFactory(factory.Pass());
}
}
@@ -267,6 +275,7 @@ class SimpleHost {
std::string host_id_;
HostKeyPair key_pair_;
+ protocol::SharedSecretHash host_secret_hash_;
std::string xmpp_login_;
std::string xmpp_auth_token_;
std::string xmpp_auth_service_;
diff --git a/remoting/protocol/me2me_host_authenticator_factory.cc b/remoting/protocol/me2me_host_authenticator_factory.cc
index db7cc4c..e3f6d98 100644
--- a/remoting/protocol/me2me_host_authenticator_factory.cc
+++ b/remoting/protocol/me2me_host_authenticator_factory.cc
@@ -4,6 +4,7 @@
#include "remoting/protocol/me2me_host_authenticator_factory.h"
+#include "base/base64.h"
#include "base/string_util.h"
#include "crypto/rsa_private_key.h"
#include "remoting/protocol/v1_authenticator.h"
@@ -12,14 +13,36 @@
namespace remoting {
namespace protocol {
+
+bool SharedSecretHash::Parse(const std::string& as_string) {
+ size_t separator = as_string.find(':');
+ if (separator == std::string::npos)
+ return false;
+
+ std::string function_name = as_string.substr(0, separator);
+ if (function_name == "plain") {
+ hash_function = AuthenticationMethod::NONE;
+ } else if (function_name == "hmac") {
+ hash_function = AuthenticationMethod::HMAC_SHA256;
+ } else {
+ return false;
+ }
+
+ if (!base::Base64Decode(as_string.substr(separator + 1), &value)) {
+ return false;
+ }
+
+ return true;
+}
+
Me2MeHostAuthenticatorFactory::Me2MeHostAuthenticatorFactory(
const std::string& local_jid,
const std::string& local_cert,
const crypto::RSAPrivateKey& local_private_key,
- const std::string& shared_secret)
+ const SharedSecretHash& shared_secret_hash)
: local_cert_(local_cert),
local_private_key_(local_private_key.Copy()),
- shared_secret_(shared_secret) {
+ shared_secret_hash_(shared_secret_hash) {
// Verify that |local_jid| is bare.
DCHECK_EQ(local_jid.find('/'), std::string::npos);
local_jid_prefix_ = local_jid + '/';
@@ -45,19 +68,15 @@ scoped_ptr<Authenticator> Me2MeHostAuthenticatorFactory::CreateAuthenticator(
return scoped_ptr<Authenticator>(NULL);
}
- // TODO(sergeyu): V2 authenticator is not finished yet. Enable it
- // here when it is finished. crbug.com/105214
- //
- // if (V2Authenticator::IsEkeMessage(first_message)) {
- // return V2Authenticator::CreateForHost(
- // local_cert_, local_private_key_.get(), shared_secret_);
- // }
+ if (V2Authenticator::IsEkeMessage(first_message)) {
+ return V2Authenticator::CreateForHost(
+ local_cert_, *local_private_key_, shared_secret_hash_.value);
+ }
// TODO(sergeyu): Old clients still use V1 auth protocol. Remove
- // this once we are done migrating to V2.
+ // this once we are done migrating to V2. crbug.com/110483 .
return scoped_ptr<Authenticator>(new V1HostAuthenticator(
- local_cert_, *local_private_key_,
- shared_secret_, remote_jid));
+ local_cert_, *local_private_key_, "", remote_jid));
}
} // namespace protocol
diff --git a/remoting/protocol/me2me_host_authenticator_factory.h b/remoting/protocol/me2me_host_authenticator_factory.h
index e20cea6..a2cf3e5 100644
--- a/remoting/protocol/me2me_host_authenticator_factory.h
+++ b/remoting/protocol/me2me_host_authenticator_factory.h
@@ -10,6 +10,7 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
+#include "remoting/protocol/authentication_method.h"
#include "remoting/protocol/authenticator.h"
namespace crypto {
@@ -19,13 +20,25 @@ class RSAPrivateKey;
namespace remoting {
namespace protocol {
+// SharedSecretHash stores hash of a host secret paired with the type
+// of the hashing function.
+struct SharedSecretHash {
+ AuthenticationMethod::HashFunction hash_function;
+ std::string value;
+
+ // Parse string representation of a shared secret hash. The |as_string|
+ // must be in form "<hash_function>:<hash_value_base64>".
+ bool Parse(const std::string& as_string);
+};
+
class Me2MeHostAuthenticatorFactory : public AuthenticatorFactory {
public:
// Doesn't take ownership of |local_private_key|.
- Me2MeHostAuthenticatorFactory(const std::string& local_jid,
- const std::string& local_cert,
- const crypto::RSAPrivateKey& local_private_key,
- const std::string& shared_secret);
+ Me2MeHostAuthenticatorFactory(
+ const std::string& local_jid,
+ const std::string& local_cert,
+ const crypto::RSAPrivateKey& local_private_key,
+ const SharedSecretHash& shared_secret_hash);
virtual ~Me2MeHostAuthenticatorFactory();
// AuthenticatorFactory interface.
@@ -37,7 +50,7 @@ class Me2MeHostAuthenticatorFactory : public AuthenticatorFactory {
std::string local_jid_prefix_;
std::string local_cert_;
scoped_ptr<crypto::RSAPrivateKey> local_private_key_;
- std::string shared_secret_;
+ SharedSecretHash shared_secret_hash_;
DISALLOW_COPY_AND_ASSIGN(Me2MeHostAuthenticatorFactory);
};
diff --git a/remoting/tools/me2me_virtual_host.py b/remoting/tools/me2me_virtual_host.py
index 5a742c2..0ffbee1 100755
--- a/remoting/tools/me2me_virtual_host.py
+++ b/remoting/tools/me2me_virtual_host.py
@@ -10,8 +10,10 @@
# process, running under an ordinary (non-root) user account.
import atexit
+import base64
import getpass
import hashlib
+import hmac
import json
import logging
import optparse
@@ -115,13 +117,13 @@ class Host:
Callers should instantiate a Host object (passing in a filename where the
config will be kept), then should call either of the methods:
- * create_config(auth): Create a new Host configuration and register it with
- the Directory Service (the "auth" parameter is used to authenticate with the
- Service).
+ * register(auth): Create a new Host configuration and register it
+ with the Directory Service (the "auth" parameter is used to
+ authenticate with the Service).
* load_config(): Load a config from disk, with details of an existing Host
registration.
- After calling create_config() (or making any config changes) the method
+ After calling register() (or making any config changes) the method
save_config() should be called to save the details to disk.
"""
@@ -130,11 +132,13 @@ class Host:
def __init__(self, config_file):
self.config_file = config_file
-
- def create_config(self, auth):
self.host_id = str(uuid.uuid1())
- logging.info("HostId: " + self.host_id)
self.host_name = socket.gethostname()
+ self.host_secret_hash = None
+ self.private_key = None
+
+ def register(self, auth):
+ logging.info("HostId: " + self.host_id)
logging.info("HostName: " + self.host_name)
logging.info("Generating RSA key pair...")
@@ -168,16 +172,31 @@ class Host:
sys.exit(1)
logging.info("Done")
+ def ask_pin(self):
+ while 1:
+ pin = getpass.getpass("Host PIN (can be empty): ")
+ if len(pin) > 0 and len(pin) < 4:
+ print "PIN must be at least 4 characters long."
+ continue
+ break
+ if pin == "":
+ self.host_secret_hash = None
+ else:
+ self.host_secret_hash = "hmac:" + base64.b64encode(
+ hmac.new(str(self.host_id), pin, hashlib.sha256).digest())
+
def load_config(self):
try:
settings_file = open(self.config_file, 'r')
data = json.load(settings_file)
settings_file.close()
- self.host_id = data["host_id"]
- self.host_name = data["host_name"]
- self.private_key = data["private_key"]
except:
+ logging.info("Failed to load: " + self.config_file)
return False
+ self.host_id = data["host_id"]
+ self.host_name = data["host_name"]
+ self.host_secret_hash = data.get("host_secret_hash")
+ self.private_key = data["private_key"]
return True
def save_config(self):
@@ -186,6 +205,9 @@ class Host:
"host_name": self.host_name,
"private_key": self.private_key,
}
+ if self.host_secret_hash:
+ data["host_secret_hash"] = self.host_secret_hash,
+
old_umask = os.umask(0066)
settings_file = open(self.config_file, 'w')
settings_file.write(json.dumps(data, indent=2))
@@ -503,7 +525,8 @@ def main():
host = Host(os.path.join(CONFIG_DIR, "host#%s.json" % host_hash))
if not host.load_config():
- host.create_config(auth)
+ host.ask_pin()
+ host.register(auth)
host.save_config()
global g_pidfile
diff --git a/remoting/tools/register_host.py b/remoting/tools/register_host.py
index 0b0b4ea..d59e6b5 100755
--- a/remoting/tools/register_host.py
+++ b/remoting/tools/register_host.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Copyright (c) 2012 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.
@@ -8,13 +8,17 @@
It asks for username/password and then writes these settings to config file.
"""
+import base64
import getpass
+import hashlib
+import hmac
+import json
import os
-import urllib
-import urllib2
import random
import socket
import sys
+import urllib
+import urllib2
import gaia_auth
import keygen
@@ -47,15 +51,26 @@ def main():
(private_key, public_key) = keygen.generateRSAKeyPair()
print "Done"
- params = ('{"data":{' +
- '"hostId": "%(hostId)s",' +
- '"hostName": "%(hostName)s",' +
- '"publicKey": "%(publicKey)s"}}') % \
- {'hostId': host_id, 'hostName': host_name,
- 'publicKey': public_key}
+ while 1:
+ pin = getpass.getpass("Host PIN (can be empty): ")
+ if len(pin) > 0 and len(pin) < 4:
+ print "PIN must be at least 4 characters long."
+ continue
+ break
+ if pin == "":
+ host_secret_hash = None
+ else:
+ host_secret_hash = "hmac:" + base64.b64encode(
+ hmac.new(str(host_id), pin, hashlib.sha256).digest())
+
+ params = { "data": {
+ "hostId": host_id,
+ "hostName": host_name,
+ "publicKey": public_key,
+ } }
headers = {"Authorization": "GoogleLogin auth=" + auth_token,
- "Content-Type": "application/json" }
- request = urllib2.Request(url, params, headers)
+ "Content-Type": "application/json" }
+ request = urllib2.Request(url, json.dumps(params), headers)
opener = urllib2.OpenerDirector()
opener.add_handler(urllib2.HTTPDefaultErrorHandler())
@@ -79,13 +94,18 @@ def main():
# Write settings file.
os.umask(0066) # Set permission mask for created file.
settings_file = open(settings_filepath, 'w')
- settings_file.write('{\n');
- settings_file.write(' "xmpp_login" : "' + email + '",\n')
- settings_file.write(' "xmpp_auth_token" : "' + auth_token + '",\n')
- settings_file.write(' "host_id" : "' + host_id + '",\n')
- settings_file.write(' "host_name" : "' + host_name + '",\n')
- settings_file.write(' "private_key" : "' + private_key + '",\n')
- settings_file.write('}\n')
+ config = {
+ "xmpp_login" : email,
+ "xmpp_auth_token" : auth_token,
+ "host_id" : host_id,
+ "host_name" : host_name,
+ "private_key" : private_key,
+ }
+
+ if host_secret_hash:
+ config["host_secret_hash"] = host_secret_hash;
+
+ settings_file.write(json.dumps(config, indent=2))
settings_file.close()
print 'Configuration saved in', settings_filepath