Wire up server-backed state key generation DBus API.

This implements a DBus call to generate server-backed state keys. This
is intended for Chrome to call when it needs state keys for
communicating with the device management server.

BUG=chromium:358213
TEST=None

Change-Id: I8fdfba3f25e032d1942a74a835077814c31305a2
Reviewed-on: https://chromium-review.googlesource.com/194313
Tested-by: Mattias Nissler <mnissler@chromium.org>
Reviewed-by: Richard Barnette <jrbarnette@chromium.org>
Reviewed-by: Chris Masone <cmasone@chromium.org>
Commit-Queue: Mattias Nissler <mnissler@chromium.org>
diff --git a/SessionManager.conf b/SessionManager.conf
index 938b50d..f09cd37 100644
--- a/SessionManager.conf
+++ b/SessionManager.conf
@@ -9,6 +9,11 @@
   <policy user="root">
     <allow own="org.chromium.SessionManager" />
     <allow send_destination="org.chromium.SessionManager" />
+
+    <!-- Only root should be allowed to call InitMachineInfo. -->
+    <allow send_destination="org.chromium.SessionManager"
+           send_interface="org.chromium.SessionManagerInterface"
+           send_member="InitMachineInfo"/>
   </policy>
 
   <policy user="chronos">
@@ -72,6 +77,9 @@
     <allow send_destination="org.chromium.SessionManager"
            send_interface="org.chromium.SessionManagerInterface"
            send_member="SetFlagsForUser"/>
+    <allow send_destination="org.chromium.SessionManager"
+           send_interface="org.chromium.SessionManagerInterface"
+           send_member="GetServerBackedStateKeys"/>
   </policy>
 
   <policy user="power">
diff --git a/dbus_error_types.h b/dbus_error_types.h
index e219b38..e17c28a 100644
--- a/dbus_error_types.h
+++ b/dbus_error_types.h
@@ -10,6 +10,7 @@
 #define INTERFACE "org.chromium.SessionManagerInterface"
 
 static const char kEmitFailed[] = INTERFACE ".EmitFailed";
+static const char kInitMachineInfoFail[] = INTERFACE ".InitMachineInfoFail";
 static const char kInvalidAccount[] = INTERFACE ".InvalidAccount";
 static const char kNoOwnerKey[] = INTERFACE ".NoOwnerKey";
 static const char kNoUserNssDb[] = INTERFACE ".NoUserNssDb";
@@ -17,8 +18,8 @@
 static const char kPubkeySetIllegal[] = INTERFACE ".PubkeySetIllegal";
 static const char kSessionDoesNotExist[] = INTERFACE ".SessionDoesNotExist";
 static const char kSessionExists[] = INTERFACE ".SessionExists";
-static const char kSigEncodeFail[] = INTERFACE ".SigEncodeFail";
 static const char kSigDecodeFail[] = INTERFACE ".SigDecodeFail";
+static const char kSigEncodeFail[] = INTERFACE ".SigEncodeFail";
 static const char kTestingChannelError[] = INTERFACE ".TestingChannelError";
 static const char kUnknownPid[] = INTERFACE ".UnknownPid";
 static const char kVerifyFail[] = INTERFACE ".VerifyFail";
diff --git a/init/ui-collect-machine-info.conf b/init/ui-collect-machine-info.conf
new file mode 100644
index 0000000..c283a6d
--- /dev/null
+++ b/init/ui-collect-machine-info.conf
@@ -0,0 +1,37 @@
+# Copyright 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+description   "Collect machine information for session_manager"
+author        "chromium-os-dev@chromium.org"
+
+# This job collects machine information and places it in a file. It is
+# then pushed to session_manager by ui-init-late.
+
+# Start after the system is mostly up and running, which also implies
+# that the VPD has been read and the udev database is fully initialized.
+start on started system-services
+
+env UI_MACHINE_INFO_FILE=/var/run/session_manager/machine-info
+
+script
+  (
+    # Just continue if one of the commands below fails.
+    set +e
+
+    # Grab full VPD data from dump_vpd_log, which contains the machine
+    # serial number and echo group code.
+    #
+    # TODO(mnissler): As things stand, dump_vpd_log prints error
+    # messages on stdout in some situations, so only emit its output on
+    # clean termination. Simplify this after http://crbug.com/369188 is
+    # fixed.
+    VPD_DATA=$(dump_vpd_log --full --stdout)
+    [ $? -eq 0 ] && echo "${VPD_DATA}"
+
+    # Grab the disk serial number from the udev database.
+    ROOTDEV=$(rootdev -s -d || true)
+    udevadm info --query=property --name="${ROOTDEV}" |
+        awk -F = '/^ID_SERIAL=/ { print "root_disk_serial_number=" $2 }'
+  ) > "${UI_MACHINE_INFO_FILE}"
+end script
diff --git a/init/ui-init-late.conf b/init/ui-init-late.conf
new file mode 100644
index 0000000..90c5e77
--- /dev/null
+++ b/init/ui-init-late.conf
@@ -0,0 +1,30 @@
+# Copyright 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+description   "Supplemental session_manager initialization after boot"
+author        "chromium-os-dev@chromium.org"
+
+# This job forwards machine information to session_manager over DBus.
+# The data is used by session_manager eventually, but is not critical to
+# starting the UI.
+
+# Machine information needs to be pushed to session_manager every time
+# it starts. However, the data may not be available yet. In that case
+# this job runs again after ui-collect-machine-info has collected the
+# data.
+start on login-prompt-visible or stopped ui-collect-machine-info
+
+env UI_MACHINE_INFO_FILE=/var/run/session_manager/machine-info
+
+script
+  # Bail out if data collection is still pending.
+  [ -e "${UI_MACHINE_INFO_FILE}" ] || exit 0
+
+  dbus-send --system --dest=org.chromium.SessionManager \
+            --type=method_call /org/chromium/SessionManager \
+            org.chromium.SessionManagerInterface.InitMachineInfo \
+            string:"$(cat "${UI_MACHINE_INFO_FILE}")" ||
+      logger -t "${UPSTART_JOB}" \
+             "Failed to pass machine info to session_manager!"
+end script
diff --git a/org.chromium.SessionManagerInterface.xml b/org.chromium.SessionManagerInterface.xml
index d42e6c3..ec5ce78 100644
--- a/org.chromium.SessionManagerInterface.xml
+++ b/org.chromium.SessionManagerInterface.xml
@@ -306,6 +306,37 @@
       <arg type="s" name="user_email" direction="in" />
       <arg type="as" name="flags" direction="in" />
     </method>
+
+    <!--
+        GetServerBackedStateKeys:
+        @state_keys: The array of currently valid state keys.
+
+        Requests server-backed state keys to be computed and returned. A
+        server-backed state key is an opaque client-determined identifier
+        that's used to stage state in a server to be retrieved after device
+        recovery. These are used to figure out device state such as previous
+        enrollment domain and whether the device got marked as stolen by its
+        owner. The keys are time-dependent, with each key being valid only for
+        a window of time, and this call returns the currently valid state key
+        plus a number of subsequent state keys that span a year of time in
+        coverage.
+    -->
+    <method name="GetServerBackedStateKeys">
+      <arg type="aay" name="state_keys" direction="out" />
+    </method>
+
+    <!--
+        InitMachineInfo:
+        @data: A string containing newline-separated key=value pairs.
+
+        Initializes supplemental machine information for use by session manager
+        that has be asynchronously determined in the boot process after
+        starting session_manager. This method gets invoked by the ui-init-late
+        init job; nothing else should call this method.
+    -->
+    <method name="InitMachineInfo">
+      <arg type="s" name="data" direction="in" />
+    </method>
   </interface>
   <!-- ********************************************************************* -->
 </node>
diff --git a/session_manager_dbus_adaptor.cc b/session_manager_dbus_adaptor.cc
index b944f84..ae13332 100644
--- a/session_manager_dbus_adaptor.cc
+++ b/session_manager_dbus_adaptor.cc
@@ -14,11 +14,12 @@
 #include <base/file_util.h>
 #include <base/memory/scoped_ptr.h>
 #include <base/stl_util.h>
+#include <chromeos/dbus/service_constants.h>
 #include <dbus/exported_object.h>
 #include <dbus/message.h>
 
-#include "login_manager/session_manager_impl.h"
 #include "login_manager/policy_service.h"
+#include "login_manager/session_manager_impl.h"
 
 namespace login_manager {
 namespace {
@@ -103,6 +104,26 @@
   }
   return response.Pass();
 }
+
+// Handles completion of a server-backed state key retrieval operation and
+// passes the response back to the waiting DBus invocation context.
+void HandleGetServerBackedStateKeysCompletion(
+    dbus::MethodCall* call,
+    const dbus::ExportedObject::ResponseSender& sender,
+    const std::vector<std::vector<uint8> >& state_keys) {
+  scoped_ptr<dbus::Response> response(dbus::Response::FromMethodCall(call));
+  dbus::MessageWriter writer(response.get());
+  dbus::MessageWriter array_writer(NULL);
+  writer.OpenArray("ay", &array_writer);
+  for (std::vector<std::vector<uint8> >::const_iterator
+           state_key(state_keys.begin());
+       state_key != state_keys.end(); ++state_key) {
+    array_writer.AppendArrayOfBytes(state_key->data(), state_key->size());
+  }
+  writer.CloseContainer(&array_writer);
+  sender.Run(response.Pass());
+}
+
 }  // namespace
 
 // PolicyService::Completion implementation that forwards the result to a DBus
@@ -212,6 +233,11 @@
   ExportSyncDBusMethod(object, kSessionManagerSetFlagsForUser,
                        &SessionManagerDBusAdaptor::SetFlagsForUser);
 
+  ExportAsyncDBusMethod(object, kSessionManagerGetServerBackedStateKeys,
+                        &SessionManagerDBusAdaptor::GetServerBackedStateKeys);
+  ExportSyncDBusMethod(object, kSessionManagerInitMachineInfo,
+                       &SessionManagerDBusAdaptor::InitMachineInfo);
+
   CHECK(object->ExportMethodAndBlock(
       kDBusIntrospectableInterface, kDBusIntrospectMethod,
       base::Bind(&HandleSynchronousDBusMethodCall,
@@ -440,6 +466,28 @@
   return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
 }
 
+void SessionManagerDBusAdaptor::GetServerBackedStateKeys(
+    dbus::MethodCall* call,
+    dbus::ExportedObject::ResponseSender sender) {
+  std::vector<std::vector<uint8> > state_keys;
+  impl_->RequestServerBackedStateKeys(
+      base::Bind(&HandleGetServerBackedStateKeysCompletion, call, sender));
+}
+
+scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::InitMachineInfo(
+    dbus::MethodCall* call) {
+  dbus::MessageReader reader(call);
+  std::string data;
+  if (!reader.PopString(&data))
+    return CreateInvalidArgsError(call, call->GetSignature());
+
+  SessionManagerImpl::Error error;
+  impl_->InitMachineInfo(data, &error);
+  if (error.is_set())
+    return CreateError(call, error.name(), error.message());
+  return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
+}
+
 scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::Introspect(
     dbus::MethodCall* call) {
   std::string output;
diff --git a/session_manager_dbus_adaptor.h b/session_manager_dbus_adaptor.h
index dfacced..0fa3e8f 100644
--- a/session_manager_dbus_adaptor.h
+++ b/session_manager_dbus_adaptor.h
@@ -71,6 +71,11 @@
   scoped_ptr<dbus::Response> StartDeviceWipe(dbus::MethodCall* call);
   scoped_ptr<dbus::Response> SetFlagsForUser(dbus::MethodCall* call);
 
+  // Asynchronous.
+  void GetServerBackedStateKeys(dbus::MethodCall* call,
+                                dbus::ExportedObject::ResponseSender sender);
+  scoped_ptr<dbus::Response> InitMachineInfo(dbus::MethodCall* call);
+
   scoped_ptr<dbus::Response> Introspect(dbus::MethodCall* call);
 
  private:
diff --git a/session_manager_impl.cc b/session_manager_impl.cc
index d9f4d32..fcc0a02 100644
--- a/session_manager_impl.cc
+++ b/session_manager_impl.cc
@@ -39,9 +39,9 @@
 #include "login_manager/user_policy_service_factory.h"
 
 using base::FilePath;
-using chromeos::cryptohome::home::kGuestUserName;
 using chromeos::cryptohome::home::GetUserPath;
 using chromeos::cryptohome::home::SanitizeUserName;
+using chromeos::cryptohome::home::kGuestUserName;
 
 namespace login_manager {  // NOLINT
 
@@ -119,6 +119,7 @@
     base::Closure lock_screen_closure,
     base::Closure restart_device_closure,
     KeyGenerator* key_gen,
+    ServerBackedStateKeyGenerator* state_key_generator,
     ProcessManagerServiceInterface* manager,
     LoginMetrics* metrics,
     NssUtil* nss,
@@ -131,13 +132,13 @@
       restart_device_closure_(restart_device_closure),
       dbus_emitter_(dbus_emitter),
       key_gen_(key_gen),
+      state_key_generator_(state_key_generator),
       manager_(manager),
       login_metrics_(metrics),
       nss_(nss),
       system_(utils),
       owner_key_(nss->GetOwnerKeyFilePath(), nss),
-      mitigator_(key_gen) {
-}
+      mitigator_(key_gen) {}
 
 SessionManagerImpl::~SessionManagerImpl() {
   STLDeleteValues(&user_sessions_);
@@ -524,6 +525,21 @@
   manager_->SetFlagsForUser(user_email, session_user_flags);
 }
 
+void SessionManagerImpl::RequestServerBackedStateKeys(
+    const ServerBackedStateKeyGenerator::StateKeyCallback& callback) {
+  state_key_generator_->RequestStateKeys(callback);
+}
+
+void SessionManagerImpl::InitMachineInfo(const std::string& data,
+                                         Error* error) {
+  std::map<std::string, std::string> params;
+  if (!ServerBackedStateKeyGenerator::ParseMachineInfo(data, &params))
+    error->Set(dbus_error::kInitMachineInfoFail, "Parse failure.");
+
+  if (!state_key_generator_->InitMachineInfo(params))
+    error->Set(dbus_error::kInitMachineInfoFail, "Missing parameters.");
+}
+
 void SessionManagerImpl::OnPolicyPersisted(bool success) {
   dbus_emitter_->EmitSignalWithSuccessFailure(kPropertyChangeCompleteSignal,
                                               success);
diff --git a/session_manager_impl.h b/session_manager_impl.h
index f5fae68..0ca8056 100644
--- a/session_manager_impl.h
+++ b/session_manager_impl.h
@@ -21,6 +21,7 @@
 #include "login_manager/policy_key.h"
 #include "login_manager/policy_service.h"
 #include "login_manager/regen_mitigator.h"
+#include "login_manager/server_backed_state_key_generator.h"
 
 namespace login_manager {
 class DBusSignalEmitterInterface;
@@ -79,6 +80,7 @@
                      base::Closure lock_screen_closure,
                      base::Closure restart_device_closure,
                      KeyGenerator* key_gen,
+                     ServerBackedStateKeyGenerator* state_key_generator,
                      ProcessManagerServiceInterface* manager,
                      LoginMetrics* metrics,
                      NssUtil* nss,
@@ -152,6 +154,10 @@
   void SetFlagsForUser(const std::string&user_email,
                        const std::vector<std::string>& session_user_flags);
 
+  void RequestServerBackedStateKeys(
+      const ServerBackedStateKeyGenerator::StateKeyCallback& callback);
+  void InitMachineInfo(const std::string& data, Error* error);
+
   // PolicyService::Delegate implementation:
   virtual void OnPolicyPersisted(bool success) OVERRIDE;
   virtual void OnKeyPersisted(bool success) OVERRIDE;
@@ -199,6 +205,7 @@
 
   DBusSignalEmitterInterface* dbus_emitter_;  // Owned by the caller.
   KeyGenerator* key_gen_;  // Owned by the caller.
+  ServerBackedStateKeyGenerator* state_key_generator_;  // Owned by the caller.
   ProcessManagerServiceInterface* manager_;  // Owned by the caller.
   LoginMetrics* login_metrics_;  // Owned by the caller.
   NssUtil* nss_;  // Owned by the caller.
diff --git a/session_manager_impl_unittest.cc b/session_manager_impl_unittest.cc
index 99da7f5..16a7b29 100644
--- a/session_manager_impl_unittest.cc
+++ b/session_manager_impl_unittest.cc
@@ -16,6 +16,7 @@
 #include <base/callback.h>
 #include <base/command_line.h>
 #include <base/file_util.h>
+#include <base/files/file_path.h>
 #include <base/files/scoped_temp_dir.h>
 #include <base/memory/ref_counted.h>
 #include <base/message_loop/message_loop.h>
@@ -42,6 +43,7 @@
 #include "login_manager/mock_process_manager_service.h"
 #include "login_manager/mock_system_utils.h"
 #include "login_manager/mock_user_policy_service_factory.h"
+#include "login_manager/server_backed_state_key_generator.h"
 #include "login_manager/stub_upstart_signal_emitter.h"
 
 using ::testing::AnyNumber;
@@ -57,9 +59,9 @@
 using ::testing::StrEq;
 using ::testing::_;
 
-using chromeos::cryptohome::home::kGuestUserName;
 using chromeos::cryptohome::home::SanitizeUserName;
 using chromeos::cryptohome::home::SetSystemSalt;
+using chromeos::cryptohome::home::kGuestUserName;
 
 using std::map;
 using std::string;
@@ -71,6 +73,7 @@
  public:
   SessionManagerImplTest()
       : device_policy_service_(new MockDevicePolicyService),
+        state_key_generator_(&utils_),
         impl_(scoped_ptr<UpstartSignalEmitter>(new StubUpstartSignalEmitter),
               &dbus_emitter_,
               base::Bind(&SessionManagerImplTest::FakeLockScreen,
@@ -78,6 +81,7 @@
               base::Bind(&SessionManagerImplTest::FakeRestartDevice,
                          base::Unretained(this)),
               &key_gen_,
+              &state_key_generator_,
               &manager_,
               &metrics_,
               &nss_,
@@ -86,8 +90,7 @@
         actual_locks_(0),
         expected_locks_(0),
         actual_restarts_(0),
-        expected_restarts_(0) {
-  }
+        expected_restarts_(0) {}
 
   virtual ~SessionManagerImplTest() {}
 
@@ -190,6 +193,7 @@
 
   MockDBusSignalEmitter dbus_emitter_;
   MockKeyGenerator key_gen_;
+  ServerBackedStateKeyGenerator state_key_generator_;
   MockProcessManagerService manager_;
   MockMetrics metrics_;
   MockNssUtil nss_;
diff --git a/session_manager_service.cc b/session_manager_service.cc
index 8fca7fd..ce1ce17 100644
--- a/session_manager_service.cc
+++ b/session_manager_service.cc
@@ -14,8 +14,8 @@
 #include <base/bind.h>
 #include <base/callback.h>
 #include <base/command_line.h>
-#include <base/files/file_path.h>
 #include <base/file_util.h>
+#include <base/files/file_path.h>
 #include <base/logging.h>
 #include <base/memory/scoped_ptr.h>
 #include <base/message_loop/message_loop.h>
@@ -50,6 +50,7 @@
 namespace login_manager {
 
 namespace {
+
 // I need a do-nothing action for SIGALRM, or using alarm() will kill me.
 void DoNothing(int signal) {}
 
@@ -108,6 +109,7 @@
       system_(utils),
       nss_(NssUtil::Create()),
       key_gen_(uid, utils),
+      state_key_generator_(utils),
       enable_browser_abort_on_hang_(enable_browser_abort_on_hang),
       liveness_checking_interval_(hang_detection_interval),
       child_exit_handler_(utils),
@@ -163,6 +165,7 @@
                  power_manager::kPowerManagerInterface,
                  power_manager::kRequestRestartMethod),
       &key_gen_,
+      &state_key_generator_,
       this,
       login_metrics_,
       nss_.get(),
diff --git a/session_manager_service.h b/session_manager_service.h
index 1f8c4ea..45a1613 100644
--- a/session_manager_service.h
+++ b/session_manager_service.h
@@ -22,6 +22,7 @@
 #include "login_manager/key_generator.h"
 #include "login_manager/liveness_checker.h"
 #include "login_manager/process_manager_service_interface.h"
+#include "login_manager/server_backed_state_key_generator.h"
 #include "login_manager/session_manager_interface.h"
 #include "login_manager/termination_handler.h"
 
@@ -232,6 +233,7 @@
 
   scoped_ptr<NssUtil> nss_;
   KeyGenerator key_gen_;
+  ServerBackedStateKeyGenerator state_key_generator_;
   scoped_ptr<DBusSignalEmitterInterface> dbus_emitter_;
   scoped_ptr<LivenessChecker> liveness_checker_;
   const bool enable_browser_abort_on_hang_;