text: // Copyright (c) 2012 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.
  
  #include "cryptohome/dbus_service.h"
  #include "cryptohome/service.h"
  
  #include <cstdlib>
  #include <string>
  
  #include <base/at_exit.h>
  #include <base/command_line.h>
  #include <base/files/file_path.h>
  #include <base/files/file_util.h>
  #include <base/logging.h>
  #include <base/memory/ptr_util.h>
  #include <base/strings/string_number_conversions.h>
  #include <chaps/pkcs11/cryptoki.h>
  #include <brillo/syslog_logging.h>
  #include <dbus/dbus.h>
  #include <glib.h>
  #include <openssl/evp.h>
  
  #include "cryptohome/cryptohome_metrics.h"
  #include "cryptohome/cryptolib.h"
  #include "cryptohome/homedirs.h"
  #include "cryptohome/platform.h"
  #include "cryptohome/userdataauth.h"
  
  namespace switches {
  static const char* kAttestationMode = "attestation_mode";
  static const char* kDistributedModeOption = "dbus";
  // Keeps std* open for debugging.
  static const char* kNoCloseOnDaemonize = "noclose";
  static const char* kNoLegacyMount = "nolegacymount";
  static const char* kDirEncryption = "direncryption";
  static const char* kNoDaemonize = "nodaemonize";
  static const char* kUserDataAuthInterface = "user_data_auth_interface";
  static const char* kCleanupThreshold = "cleanup_threshold";
  static const char* kAggressiveThreshold = "aggressive_cleanup_threshold";
  static const char* kTargetFreeSpace = "target_free_space";
  
  }  // namespace switches
  
  uint64_t ReadCleanupThreshold(const base::CommandLine* cl,
                                const char* switch_name,
                                uint64_t default_value) {
    std::string value = cl->GetSwitchValueASCII(switch_name);
  
    if (value.size() == 0) {
      return default_value;
    }
  
    uint64_t parsed_value;
    if (!base::StringToUint64(value, &parsed_value)) {
      LOG(ERROR) << "Failed to parse " << switch_name << "; using defaults";
      return default_value;
    }
  
    return parsed_value;
  }
  
  int main(int argc, char** argv) {
    // Initialize command line configuration early, as logging will require
    // command line to be initialized
    base::CommandLine::Init(argc, argv);
  
    brillo::InitLog(brillo::kLogToSyslog | brillo::kLogToStderr);
  
    // Read the file before we daemonize so it can be deleted as soon as we exit.
    cryptohome::Platform platform;
  
    base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
    // Validity check of attestation mode. Historically we had monolithic and
    // distributed mode, and now the monolithic mode has been obsoleted, so we
    // expect either the switch is missing or explicitly set to distributed mode.
    if (cl->HasSwitch(switches::kAttestationMode) &&
        cl->GetSwitchValueASCII(switches::kAttestationMode) !=
            switches::kDistributedModeOption) {
      LOG(FATAL) << "Unrecognized or obsoleted " << switches::kAttestationMode
                 << " option: "
                 << cl->GetSwitchValueASCII(switches::kAttestationMode);
    }
    int noclose = cl->HasSwitch(switches::kNoCloseOnDaemonize);
    bool nolegacymount = cl->HasSwitch(switches::kNoLegacyMount);
    bool direncryption = cl->HasSwitch(switches::kDirEncryption);
    bool daemonize = !cl->HasSwitch(switches::kNoDaemonize);
    bool use_new_dbus_interface = cl->HasSwitch(switches::kUserDataAuthInterface);
    uint64_t cleanup_threshold =
        ReadCleanupThreshold(cl, switches::kCleanupThreshold,
                             cryptohome::kFreeSpaceThresholdToTriggerCleanup);
    uint64_t aggressive_cleanup_threshold = ReadCleanupThreshold(
        cl, switches::kAggressiveThreshold,
        cryptohome::kFreeSpaceThresholdToTriggerAggressiveCleanup);
    uint64_t target_free_space = ReadCleanupThreshold(
        cl, switches::kTargetFreeSpace, cryptohome::kTargetFreeSpaceAfterCleanup);
  
    if (daemonize) {
      PLOG_IF(FATAL, daemon(0, noclose) == -1) << "Failed to daemonize";
    }
  
    // Initialize OpenSSL.
    OpenSSL_add_all_algorithms();
  
    // Initialize cryptohome metrics
    // Because mount thread may use metrics after main scope, don't
    // TearDownMetrics after main finished.
    cryptohome::InitializeMetrics();
  
    // Make sure scrypt parameters are correct.
    cryptohome::CryptoLib::AssertProductionScryptParams();
  
    if (use_new_dbus_interface) {
      // Note that there's an AtExitManager in the constructor of
      // UserDataAuthDaemon
      cryptohome::UserDataAuthDaemon user_data_auth_daemon;
  
      // Set options on whether we are going to use legacy mount. See comments on
      // Mount::MountLegacyHome() for more information.
      user_data_auth_daemon.GetUserDataAuth()->set_legacy_mount(!nolegacymount);
  
      // Set options on whether we are going to use ext4 directory encryption or
      // eCryptfs.
      user_data_auth_daemon.GetUserDataAuth()->set_force_ecryptfs(!direncryption);
  
      // Set automatic cleanup thresholds.
      user_data_auth_daemon.GetUserDataAuth()->set_cleanup_threshold(
          cleanup_threshold);
      user_data_auth_daemon.GetUserDataAuth()->set_aggressive_cleanup_threshold(
          aggressive_cleanup_threshold);
      user_data_auth_daemon.GetUserDataAuth()->set_target_free_space(
          target_free_space);
  
      // Note the startup sequence is as following:
      // 1. UserDataAuthDaemon constructor => UserDataAuth constructor
      // 2. UserDataAuthDaemon::OnInit() (called by Daemon::Run())
      // 3. UserDataAuthDaemon::RegisterDBusObjectsAsync() (called by 2.)
      // 4. UserDataAuth::Initialize() (called by 3.)
      // 5. UserDataAuth::PostDBusInitialize() (called by 3.)
      // Daemon::OnInit() needs to be called before Initialize(), because
      // Initialize() create threads, and thus mess with Daemon's
      // AsynchronousSignalHandler.
  
      // Start UserDataAuth daemon if the option is selected
      user_data_auth_daemon.Run();
    } else {
      // Start the old interface if nothing is selected
  
      // Setup threading. This needs to be called before other calls into glib and
      // before multiple threads are created that access dbus.
      dbus_threads_init_default();
  
      // Create an AtExitManager
      base::AtExitManager exit_manager;
  
      cryptohome::Service* service = cryptohome::Service::CreateDefault();
  
      service->set_legacy_mount(!nolegacymount);
      service->set_force_ecryptfs(!direncryption);
      service->set_cleanup_threshold(cleanup_threshold);
      service->set_aggressive_cleanup_threshold(aggressive_cleanup_threshold);
      service->set_target_free_space(target_free_space);
  
      if (!service->Initialize()) {
        LOG(FATAL) << "Service initialization failed";
        return 1;
      }
  
      if (!service->Register(brillo::dbus::GetSystemBusConnection())) {
        LOG(FATAL) << "DBUS service registration failed";
        return 1;
      }
  
      if (!service->Run()) {
        LOG(FATAL) << "Service run failed.";
        return 1;
      }
    }
    // If PKCS #11 was initialized, this will tear it down.
    C_Finalize(NULL);
  
    return 0;
  }
  