diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-24 18:24:26 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-24 18:24:26 +0000 |
commit | 26d4accf41d14c363842af2422688246b07aba23 (patch) | |
tree | 59388d8b0a9c8ffeca982ee53896684241a7e9a4 /remoting | |
parent | afefc060a6eac61aec0e4e538502fe8bb1a1c436 (diff) | |
download | chromium_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.cc | 3 | ||||
-rw-r--r-- | remoting/host/host_config.h | 4 | ||||
-rw-r--r-- | remoting/host/remoting_me2me_host.cc | 35 | ||||
-rw-r--r-- | remoting/host/simple_host_process.cc | 17 | ||||
-rw-r--r-- | remoting/protocol/me2me_host_authenticator_factory.cc | 43 | ||||
-rw-r--r-- | remoting/protocol/me2me_host_authenticator_factory.h | 23 | ||||
-rwxr-xr-x | remoting/tools/me2me_virtual_host.py | 45 | ||||
-rwxr-xr-x | remoting/tools/register_host.py | 56 |
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 |