Commit 26d4accf authored by sergeyu@chromium.org's avatar sergeyu@chromium.org

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
parent afefc060
// 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
// 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[];
......
......@@ -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_;
......
......@@ -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_;
......
......@@ -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
......
......@@ -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);
};
......
......@@ -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
......
#!/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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment