| // 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 "login_manager/session_manager_impl.h" |
| |
| #include <errno.h> |
| #include <stdint.h> |
| #include <sys/socket.h> |
| |
| #include <algorithm> |
| #include <iterator> |
| #include <locale> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| |
| #include <base/base64.h> |
| #include <base/bind.h> |
| #include <base/callback_helpers.h> |
| #include <base/files/file_util.h> |
| #include <base/rand_util.h> |
| #include <base/run_loop.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/string_split.h> |
| #include <base/strings/string_tokenizer.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <base/threading/thread_task_runner_handle.h> |
| #include <base/time/default_tick_clock.h> |
| #include <base/time/time.h> |
| #include <brillo/cryptohome.h> |
| #include <brillo/dbus/dbus_object.h> |
| #include <brillo/dbus/utils.h> |
| #include <brillo/scoped_mount_namespace.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <crypto/scoped_nss_types.h> |
| #include <dbus/message.h> |
| #include <dbus/object_proxy.h> |
| #include <install_attributes/libinstallattributes.h> |
| #include <libpasswordprovider/password.h> |
| #include <libpasswordprovider/password_provider.h> |
| |
| #include "bindings/chrome_device_policy.pb.h" |
| #include "bindings/device_management_backend.pb.h" |
| #include "login_manager/arc_sideload_status_interface.h" |
| #include "login_manager/blob_util.h" |
| #include "login_manager/crossystem.h" |
| #include "login_manager/dbus_util.h" |
| #include "login_manager/device_local_account_manager.h" |
| #include "login_manager/device_policy_service.h" |
| #include "login_manager/init_daemon_controller.h" |
| #include "login_manager/key_generator.h" |
| #include "login_manager/login_metrics.h" |
| #include "login_manager/nss_util.h" |
| #include "login_manager/policy_key.h" |
| #include "login_manager/policy_service.h" |
| #include "login_manager/process_manager_service_interface.h" |
| #include "login_manager/proto_bindings/arc.pb.h" |
| #include "login_manager/proto_bindings/login_screen_storage.pb.h" |
| #include "login_manager/proto_bindings/policy_descriptor.pb.h" |
| #include "login_manager/regen_mitigator.h" |
| #include "login_manager/secret_util.h" |
| #include "login_manager/system_utils.h" |
| #include "login_manager/user_policy_service_factory.h" |
| #include "login_manager/validator_utils.h" |
| #include "login_manager/vpd_process.h" |
| |
| using base::FilePath; |
| using brillo::cryptohome::home::GetHashedUserPath; |
| using brillo::cryptohome::home::GetUserPath; |
| using brillo::cryptohome::home::kGuestUserName; |
| using brillo::cryptohome::home::SanitizeUserName; |
| |
| namespace login_manager { // NOLINT |
| |
| constexpr char SessionManagerImpl::kStarted[] = "started"; |
| constexpr char SessionManagerImpl::kStopping[] = "stopping"; |
| constexpr char SessionManagerImpl::kStopped[] = "stopped"; |
| |
| constexpr char SessionManagerImpl::kLoggedInFlag[] = |
| "/run/session_manager/logged_in"; |
| constexpr char SessionManagerImpl::kResetFile[] = |
| "/mnt/stateful_partition/factory_install_reset"; |
| |
| constexpr char SessionManagerImpl::kTPMFirmwareUpdateLocationFile[] = |
| "/run/tpm_firmware_update_location"; |
| constexpr char SessionManagerImpl::kTPMFirmwareUpdateSRKVulnerableROCAFile[] = |
| "/run/tpm_firmware_update_srk_vulnerable_roca"; |
| constexpr char SessionManagerImpl::kTPMFirmwareUpdateRequestFlagFile[] = |
| "/mnt/stateful_partition/unencrypted/preserve/tpm_firmware_update_request"; |
| constexpr char SessionManagerImpl::kStatefulPreservationRequestFile[] = |
| "/mnt/stateful_partition/preservation_request"; |
| |
| constexpr char SessionManagerImpl::kStartUserSessionImpulse[] = |
| "start-user-session"; |
| |
| // ARC related impulse (systemd unit start or Upstart signal). |
| constexpr char SessionManagerImpl::kStartArcInstanceImpulse[] = |
| "start-arc-instance"; |
| constexpr char SessionManagerImpl::kStopArcInstanceImpulse[] = |
| "stop-arc-instance"; |
| constexpr char SessionManagerImpl::kContinueArcBootImpulse[] = |
| "continue-arc-boot"; |
| constexpr char SessionManagerImpl::kArcBootedImpulse[] = "arc-booted"; |
| |
| // Lock state related impulse (systemd unit start or Upstart signal). |
| constexpr char SessionManagerImpl::kScreenLockedImpulse[] = "screen-locked"; |
| constexpr char SessionManagerImpl::kScreenUnlockedImpulse[] = "screen-unlocked"; |
| |
| // TODO(b:66919195): Optimize Android container shutdown time. It |
| // needs as long as 3s on kevin to perform graceful shutdown. |
| constexpr base::TimeDelta SessionManagerImpl::kContainerTimeout = |
| base::TimeDelta::FromSeconds(3); |
| |
| constexpr base::TimeDelta SessionManagerImpl::kCrashBeforeSuspendInterval = |
| base::TimeDelta::FromSeconds(5); |
| constexpr base::TimeDelta SessionManagerImpl::kCrashAfterSuspendInterval = |
| base::TimeDelta::FromSeconds(5); |
| |
| namespace { |
| |
| // Because the cheets logs are huge, we set the D-Bus timeout to 1 minute. |
| const base::TimeDelta kBackupArcBugReportTimeout = |
| base::TimeDelta::FromMinutes(1); |
| |
| // The flag to pass to chrome to open a named socket for testing. |
| const char kTestingChannelFlag[] = "--testing-channel=NamedTestingInterface:"; |
| |
| // Device-local account state directory. |
| const char kDeviceLocalAccountStateDir[] = "/var/lib/device_local_accounts"; |
| |
| #if USE_CHEETS |
| // To launch ARC, certain amount of free disk space is needed. |
| // Path and the amount for the check. |
| constexpr char kArcDiskCheckPath[] = "/home"; |
| constexpr int64_t kArcCriticalDiskFreeBytes = 64 << 20; // 64MB |
| |
| // To set the CPU limits of the Android container. |
| const char kCpuSharesFile[] = |
| "/sys/fs/cgroup/cpu/session_manager_containers/cpu.shares"; |
| const unsigned int kCpuSharesForeground = 1024; |
| const unsigned int kCpuSharesBackground = 64; |
| #endif |
| |
| // The interval used to periodically check if time sync was done by tlsdated. |
| constexpr base::TimeDelta kSystemClockLastSyncInfoRetryDelay = |
| base::TimeDelta::FromMilliseconds(1000); |
| |
| // TPM firmware update modes. |
| constexpr char kTPMFirmwareUpdateModeFirstBoot[] = "first_boot"; |
| constexpr char kTPMFirmwareUpdateModePreserveStateful[] = "preserve_stateful"; |
| constexpr char kTPMFirmwareUpdateModeCleanup[] = "cleanup"; |
| |
| // Policy storage constants. |
| constexpr char kSigEncodeFailMessage[] = "Failed to retrieve policy data."; |
| |
| // Default path of symlink to log file where stdout and stderr from |
| // session_manager and Chrome are redirected. |
| constexpr char kDefaultUiLogSymlinkPath[] = "/var/log/ui/ui.LATEST"; |
| |
| // A path of the directory that contains all the key-value pairs stored to the |
| // pesistent login screen storage. |
| const char kLoginScreenStoragePath[] = "/var/lib/login_screen_storage"; |
| |
| const char* ToSuccessSignal(bool success) { |
| return success ? "success" : "failure"; |
| } |
| |
| #if USE_CHEETS |
| bool IsDevMode(SystemUtils* system) { |
| // When GetDevModeState() returns UNKNOWN, return true. |
| return system->GetDevModeState() != DevModeState::DEV_MODE_OFF; |
| } |
| |
| bool IsInsideVm(SystemUtils* system) { |
| // When GetVmState() returns UNKNOWN, return false. |
| return system->GetVmState() == VmState::INSIDE_VM; |
| } |
| #endif |
| |
| // Parses |descriptor_blob| into |descriptor| and validates it assuming the |
| // given |usage|. Returns true and sets |descriptor| on success. Returns false |
| // and sets |error| on failure. |
| bool ParseAndValidatePolicyDescriptor( |
| const std::vector<uint8_t>& descriptor_blob, |
| PolicyDescriptorUsage usage, |
| PolicyDescriptor* descriptor, |
| brillo::ErrorPtr* error) { |
| DCHECK(descriptor); |
| DCHECK(error); |
| if (!descriptor->ParseFromArray(descriptor_blob.data(), |
| descriptor_blob.size())) { |
| *error = CreateError(DBUS_ERROR_INVALID_ARGS, |
| "PolicyDescriptor parsing failed."); |
| return false; |
| } |
| |
| if (!ValidatePolicyDescriptor(*descriptor, usage)) { |
| *error = CreateError(DBUS_ERROR_INVALID_ARGS, "PolicyDescriptor invalid."); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Handles the result of an attempt to connect to a D-Bus signal, logging an |
| // error on failure. |
| void HandleDBusSignalConnected(const std::string& interface, |
| const std::string& signal, |
| bool success) { |
| if (!success) { |
| LOG(ERROR) << "Failed to connect to D-Bus signal " << interface << "." |
| << signal; |
| } |
| } |
| |
| // Replaces the log file that |symlink_path| (typically /var/log/ui/ui.LATEST) |
| // points to with a new file containing the same contents. This is used to |
| // disconnect Chrome's stderr and stdout after a user logs in: |
| // https://crbug.com/904850 |
| void DisconnectLogFile(const base::FilePath& symlink_path) { |
| base::FilePath log_path; |
| if (!base::ReadSymbolicLink(symlink_path, &log_path)) |
| return; |
| |
| if (!log_path.IsAbsolute()) |
| log_path = symlink_path.DirName().Append(log_path); |
| |
| // Perform a basic safety check. |
| if (log_path.DirName() != symlink_path.DirName()) { |
| LOG(WARNING) << "Log file " << log_path.value() << " isn't in same " |
| << "directory as symlink " << symlink_path.value() |
| << "; not disconnecting it"; |
| return; |
| } |
| |
| // Copy the contents to a temp file and then move it over the original path. |
| base::FilePath temp_path; |
| if (!base::CreateTemporaryFileInDir(log_path.DirName(), &temp_path)) { |
| PLOG(WARNING) << "Failed to create temp file in " |
| << log_path.DirName().value(); |
| return; |
| } |
| if (!base::CopyFile(log_path, temp_path)) { |
| PLOG(WARNING) << "Failed to copy " << log_path.value() << " to " |
| << temp_path.value(); |
| return; |
| } |
| |
| // Try to to copy permissions so the new file isn't 0600, which makes it hard |
| // to investigate issues on non-dev devices. |
| int mode = 0; |
| if (!base::GetPosixFilePermissions(log_path, &mode) || |
| !base::SetPosixFilePermissions(temp_path, mode)) { |
| PLOG(WARNING) << "Failed to copy permissions from " << log_path.value() |
| << " to " << temp_path.value(); |
| } |
| |
| if (!base::ReplaceFile(temp_path, log_path, nullptr /* error */)) { |
| PLOG(WARNING) << "Failed to rename " << temp_path.value() << " to " |
| << log_path.value(); |
| } |
| } |
| |
| } // namespace |
| |
| // Tracks D-Bus service running. |
| // Create*Callback functions return a callback adaptor from given |
| // DBusMethodResponse. These cancel in-progress operations when the instance is |
| // deleted. |
| class SessionManagerImpl::DBusService { |
| public: |
| explicit DBusService(org::chromium::SessionManagerInterfaceAdaptor* adaptor) |
| : adaptor_(adaptor), weak_ptr_factory_(this) {} |
| ~DBusService() = default; |
| |
| bool Start(const scoped_refptr<dbus::Bus>& bus) { |
| DCHECK(!dbus_object_); |
| |
| // Registers the SessionManagerInterface D-Bus methods and signals. |
| dbus_object_ = std::make_unique<brillo::dbus_utils::DBusObject>( |
| nullptr, bus, |
| org::chromium::SessionManagerInterfaceAdaptor::GetObjectPath()); |
| adaptor_->RegisterWithDBusObject(dbus_object_.get()); |
| dbus_object_->RegisterAndBlock(); |
| |
| // Note that this needs to happen *after* all methods are exported |
| // (http://crbug.com/331431). |
| // This should pass dbus::Bus::REQUIRE_PRIMARY once on the new libchrome. |
| return bus->RequestOwnershipAndBlock(kSessionManagerServiceName, |
| dbus::Bus::REQUIRE_PRIMARY); |
| } |
| |
| // Adaptor from DBusMethodResponse to PolicyService::Completion callback. |
| PolicyService::Completion CreatePolicyServiceCompletionCallback( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> response) { |
| return base::Bind(&DBusService::HandlePolicyServiceCompletion, |
| weak_ptr_factory_.GetWeakPtr(), base::Passed(&response)); |
| } |
| |
| // Adaptor from DBusMethodResponse to |
| // ServerBackedStateKeyGenerator::StateKeyCallback callback. |
| ServerBackedStateKeyGenerator::StateKeyCallback CreateStateKeyCallback( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse< |
| std::vector<std::vector<uint8_t>>>> response) { |
| return base::Bind(&DBusService::HandleStateKeyCallback, |
| weak_ptr_factory_.GetWeakPtr(), base::Passed(&response)); |
| } |
| |
| private: |
| void HandlePolicyServiceCompletion( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> response, |
| brillo::ErrorPtr error) { |
| if (error) { |
| response->ReplyWithError(error.get()); |
| return; |
| } |
| |
| response->Return(); |
| } |
| |
| void HandleStateKeyCallback( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse< |
| std::vector<std::vector<uint8_t>>>> response, |
| const std::vector<std::vector<uint8_t>>& state_key) { |
| response->Return(std::move(state_key)); |
| } |
| |
| org::chromium::SessionManagerInterfaceAdaptor* const adaptor_; |
| std::unique_ptr<brillo::dbus_utils::DBusObject> dbus_object_; |
| |
| base::WeakPtrFactory<DBusService> weak_ptr_factory_; |
| DISALLOW_COPY_AND_ASSIGN(DBusService); |
| }; |
| |
| struct SessionManagerImpl::UserSession { |
| public: |
| UserSession(const std::string& username, |
| const std::string& userhash, |
| bool is_incognito, |
| ScopedPK11SlotDescriptor desc, |
| std::unique_ptr<PolicyService> policy_service) |
| : username(username), |
| userhash(userhash), |
| is_incognito(is_incognito), |
| descriptor(std::move(desc)), |
| policy_service(std::move(policy_service)) {} |
| ~UserSession() {} |
| |
| const std::string username; |
| const std::string userhash; |
| const bool is_incognito; |
| ScopedPK11SlotDescriptor descriptor; |
| std::unique_ptr<PolicyService> policy_service; |
| }; |
| |
| SessionManagerImpl::SessionManagerImpl( |
| Delegate* delegate, |
| std::unique_ptr<InitDaemonController> init_controller, |
| const scoped_refptr<dbus::Bus>& bus, |
| KeyGenerator* key_gen, |
| ServerBackedStateKeyGenerator* state_key_generator, |
| ProcessManagerServiceInterface* manager, |
| LoginMetrics* metrics, |
| NssUtil* nss, |
| base::Optional<base::FilePath> ns_path, |
| SystemUtils* utils, |
| Crossystem* crossystem, |
| VpdProcess* vpd_process, |
| PolicyKey* owner_key, |
| ContainerManagerInterface* android_container, |
| InstallAttributesReader* install_attributes_reader, |
| dbus::ObjectProxy* powerd_proxy, |
| dbus::ObjectProxy* system_clock_proxy, |
| dbus::ObjectProxy* debugd_proxy, |
| ArcSideloadStatusInterface* arc_sideload_status) |
| : init_controller_(std::move(init_controller)), |
| system_clock_last_sync_info_retry_delay_( |
| kSystemClockLastSyncInfoRetryDelay), |
| tick_clock_(std::make_unique<base::DefaultTickClock>()), |
| bus_(bus), |
| adaptor_(this), |
| delegate_(delegate), |
| key_gen_(key_gen), |
| state_key_generator_(state_key_generator), |
| manager_(manager), |
| login_metrics_(metrics), |
| nss_(nss), |
| chrome_mount_ns_path_(ns_path), |
| system_(utils), |
| crossystem_(crossystem), |
| vpd_process_(vpd_process), |
| owner_key_(owner_key), |
| android_container_(android_container), |
| install_attributes_reader_(install_attributes_reader), |
| powerd_proxy_(powerd_proxy), |
| system_clock_proxy_(system_clock_proxy), |
| debugd_proxy_(debugd_proxy), |
| arc_sideload_status_(arc_sideload_status), |
| mitigator_(key_gen), |
| ui_log_symlink_path_(kDefaultUiLogSymlinkPath), |
| password_provider_( |
| std::make_unique<password_provider::PasswordProvider>()), |
| login_screen_storage_(std::make_unique<LoginScreenStorage>( |
| base::FilePath(kLoginScreenStoragePath), |
| std::make_unique<secret_util::SharedMemoryUtil>())), |
| weak_ptr_factory_(this) { |
| DCHECK(delegate_); |
| } |
| |
| SessionManagerImpl::~SessionManagerImpl() { |
| device_policy_->set_delegate(nullptr); // Could use WeakPtr instead? |
| } |
| |
| void SessionManagerImpl::SetPolicyServicesForTesting( |
| std::unique_ptr<DevicePolicyService> device_policy, |
| std::unique_ptr<UserPolicyServiceFactory> user_policy_factory, |
| std::unique_ptr<DeviceLocalAccountManager> device_local_account_manager) { |
| device_policy_ = std::move(device_policy); |
| user_policy_factory_ = std::move(user_policy_factory); |
| device_local_account_manager_ = std::move(device_local_account_manager); |
| } |
| |
| void SessionManagerImpl::SetTickClockForTesting( |
| std::unique_ptr<base::TickClock> clock) { |
| tick_clock_ = std::move(clock); |
| } |
| |
| void SessionManagerImpl::SetUiLogSymlinkPathForTesting( |
| const base::FilePath& path) { |
| ui_log_symlink_path_ = path; |
| } |
| |
| void SessionManagerImpl::SetLoginScreenStorageForTesting( |
| std::unique_ptr<LoginScreenStorage> login_screen_storage) { |
| login_screen_storage_ = std::move(login_screen_storage); |
| } |
| |
| void SessionManagerImpl::AnnounceSessionStoppingIfNeeded() { |
| if (session_started_) { |
| session_stopping_ = true; |
| DLOG(INFO) << "Emitting D-Bus signal SessionStateChanged: " << kStopping; |
| adaptor_.SendSessionStateChangedSignal(kStopping); |
| } |
| } |
| |
| void SessionManagerImpl::AnnounceSessionStopped() { |
| session_stopping_ = session_started_ = false; |
| DLOG(INFO) << "Emitting D-Bus signal SessionStateChanged: " << kStopped; |
| adaptor_.SendSessionStateChangedSignal(kStopped); |
| } |
| |
| bool SessionManagerImpl::ShouldEndSession(std::string* reason_out) { |
| auto set_reason = [&](const std::string& reason) { |
| if (reason_out) |
| *reason_out = reason; |
| }; |
| |
| if (screen_locked_) { |
| set_reason("screen is locked"); |
| return true; |
| } |
| |
| if (supervised_user_creation_ongoing_) { |
| set_reason("supervised user creation ongoing"); |
| return true; |
| } |
| |
| if (suspend_ongoing_) { |
| set_reason("suspend ongoing"); |
| return true; |
| } |
| |
| if (!last_suspend_done_time_.is_null()) { |
| const base::TimeDelta time_since_suspend = |
| tick_clock_->NowTicks() - last_suspend_done_time_; |
| if (time_since_suspend <= kCrashAfterSuspendInterval) { |
| set_reason("suspend completed recently"); |
| return true; |
| } |
| } |
| |
| set_reason(""); |
| return false; |
| } |
| |
| bool SessionManagerImpl::Initialize() { |
| key_gen_->set_delegate(this); |
| |
| powerd_proxy_->ConnectToSignal( |
| power_manager::kPowerManagerInterface, |
| power_manager::kSuspendImminentSignal, |
| base::Bind(&SessionManagerImpl::OnSuspendImminent, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&HandleDBusSignalConnected)); |
| powerd_proxy_->ConnectToSignal(power_manager::kPowerManagerInterface, |
| power_manager::kSuspendDoneSignal, |
| base::Bind(&SessionManagerImpl::OnSuspendDone, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&HandleDBusSignalConnected)); |
| |
| system_clock_proxy_->WaitForServiceToBeAvailable( |
| base::Bind(&SessionManagerImpl::OnSystemClockServiceAvailable, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| // Note: If SetPolicyServicesForTesting has been called, all services have |
| // already been set and initialized. |
| if (!device_policy_) { |
| device_policy_ = DevicePolicyService::Create( |
| owner_key_, login_metrics_, &mitigator_, nss_, crossystem_, |
| vpd_process_, install_attributes_reader_); |
| // Thinking about combining set_delegate() with the 'else' block below and |
| // moving it down? Note that device_policy_->Initialize() might call |
| // OnKeyPersisted() on the delegate, so be sure it's safe. |
| device_policy_->set_delegate(this); |
| if (!device_policy_->Initialize()) |
| return false; |
| |
| DCHECK(!user_policy_factory_); |
| user_policy_factory_ = |
| std::make_unique<UserPolicyServiceFactory>(nss_, system_); |
| |
| device_local_account_manager_ = std::make_unique<DeviceLocalAccountManager>( |
| base::FilePath(kDeviceLocalAccountStateDir), owner_key_), |
| device_local_account_manager_->UpdateDeviceSettings( |
| device_policy_->GetSettings()); |
| if (device_policy_->MayUpdateSystemSettings()) |
| device_policy_->UpdateSystemSettings(PolicyService::Completion()); |
| } else { |
| device_policy_->set_delegate(this); |
| } |
| |
| arc_sideload_status_->Initialize(); |
| |
| return true; |
| } |
| |
| void SessionManagerImpl::Finalize() { |
| // Reset the SessionManagerDBusAdaptor first to ensure that it'll permit |
| // any outstanding DBusMethodCompletion objects to be abandoned without |
| // having been run (http://crbug.com/638774, http://crbug.com/725734). |
| dbus_service_.reset(); |
| |
| device_policy_->PersistAllPolicy(); |
| for (const auto& kv : user_sessions_) { |
| if (kv.second) |
| kv.second->policy_service->PersistAllPolicy(); |
| } |
| device_local_account_manager_->PersistAllPolicy(); |
| |
| // We want to stop all running containers and VMs. Containers and VMs are |
| // per-session and cannot persist across sessions. |
| android_container_->RequestJobExit( |
| ArcContainerStopReason::SESSION_MANAGER_SHUTDOWN); |
| android_container_->EnsureJobExit(kContainerTimeout); |
| |
| arc_sideload_status_.reset(); |
| } |
| |
| bool SessionManagerImpl::StartDBusService() { |
| DCHECK(!dbus_service_); |
| auto dbus_service = std::make_unique<DBusService>(&adaptor_); |
| if (!dbus_service->Start(bus_)) |
| return false; |
| |
| dbus_service_ = std::move(dbus_service); |
| return true; |
| } |
| |
| void SessionManagerImpl::EmitLoginPromptVisible() { |
| login_metrics_->RecordStats("login-prompt-visible"); |
| adaptor_.SendLoginPromptVisibleSignal(); |
| init_controller_->TriggerImpulse("login-prompt-visible", {}, |
| InitDaemonController::TriggerMode::ASYNC); |
| } |
| |
| void SessionManagerImpl::EmitAshInitialized() { |
| init_controller_->TriggerImpulse("ash-initialized", {}, |
| InitDaemonController::TriggerMode::ASYNC); |
| } |
| |
| bool SessionManagerImpl::EnableChromeTesting( |
| brillo::ErrorPtr* error, |
| bool in_force_relaunch, |
| const std::vector<std::string>& in_test_arguments, |
| const std::vector<std::string>& in_test_environment_variables, |
| std::string* out_filepath) { |
| // Check to see if we already have Chrome testing enabled. |
| bool already_enabled = !chrome_testing_path_.empty(); |
| |
| if (!already_enabled) { |
| base::FilePath temp_file_path; // So we don't clobber chrome_testing_path_; |
| if (!system_->GetUniqueFilenameInWriteOnlyTempDir(&temp_file_path)) { |
| *error = CreateError(dbus_error::kTestingChannelError, |
| "Could not create testing channel filename."); |
| return false; |
| } |
| chrome_testing_path_ = temp_file_path; |
| } |
| |
| if (!already_enabled || in_force_relaunch) { |
| // Delete testing channel file if it already exists. |
| system_->RemoveFile(chrome_testing_path_); |
| |
| // Add testing channel argument to arguments. |
| std::string testing_argument = kTestingChannelFlag; |
| testing_argument.append(chrome_testing_path_.value()); |
| std::vector<std::string> test_args = in_test_arguments; |
| test_args.push_back(testing_argument); |
| manager_->SetBrowserTestArgs(test_args); |
| manager_->SetBrowserAdditionalEnvironmentalVariables( |
| in_test_environment_variables); |
| manager_->RestartBrowser(); |
| } |
| *out_filepath = chrome_testing_path_.value(); |
| return true; |
| } |
| |
| bool SessionManagerImpl::LoginScreenStorageListKeys( |
| brillo::ErrorPtr* error, std::vector<std::string>* out_keys) { |
| *out_keys = login_screen_storage_->ListKeys(); |
| return true; |
| } |
| |
| void SessionManagerImpl::LoginScreenStorageDelete(const std::string& in_key) { |
| login_screen_storage_->Delete(in_key); |
| } |
| |
| bool SessionManagerImpl::StartSession(brillo::ErrorPtr* error, |
| const std::string& in_account_id, |
| const std::string& in_unique_identifier) { |
| std::string actual_account_id; |
| if (!NormalizeAccountId(in_account_id, &actual_account_id, error)) { |
| DCHECK(*error); |
| return false; |
| } |
| |
| // Check if this user already started a session. |
| if (user_sessions_.count(actual_account_id) > 0) { |
| *error = |
| CREATE_ERROR_AND_LOG(dbus_error::kSessionExists, |
| "Provided user id already started a session."); |
| return false; |
| } |
| |
| // Create a UserSession object for this user. |
| const bool is_incognito = IsIncognitoAccountId(actual_account_id); |
| const OptionalFilePath ns_mnt_path = is_incognito || IsolateUserSession() |
| ? chrome_mount_ns_path_ |
| : base::nullopt; |
| auto user_session = |
| CreateUserSession(actual_account_id, ns_mnt_path, is_incognito, error); |
| if (!user_session) { |
| DCHECK(*error); |
| return false; |
| } |
| |
| // Check whether the current user is the owner, and if so make sure they are |
| // whitelisted and have an owner key. |
| bool user_is_owner = false; |
| if (!device_policy_->CheckAndHandleOwnerLogin(user_session->username, |
| user_session->descriptor.get(), |
| &user_is_owner, error)) { |
| DCHECK(*error); |
| return false; |
| } |
| |
| // If all previous sessions were incognito (or no previous sessions exist). |
| bool is_first_real_user = AllSessionsAreIncognito() && !is_incognito; |
| |
| // Send each user login event to UMA (right before we start session |
| // since the metrics library does not log events in guest mode). |
| const DevModeState dev_mode_state = system_->GetDevModeState(); |
| if (dev_mode_state != DevModeState::DEV_MODE_UNKNOWN) { |
| login_metrics_->SendLoginUserType( |
| dev_mode_state != DevModeState::DEV_MODE_OFF, is_incognito, |
| user_is_owner); |
| } |
| |
| // Make sure that Chrome's stdout and stderr, which may contain log messages |
| // with user-specific data, don't get saved after the first user logs in: |
| // https://crbug.com/904850 |
| if (user_sessions_.empty()) |
| DisconnectLogFile(ui_log_symlink_path_); |
| |
| init_controller_->TriggerImpulse(kStartUserSessionImpulse, |
| {"CHROMEOS_USER=" + actual_account_id}, |
| InitDaemonController::TriggerMode::ASYNC); |
| LOG(INFO) << "Starting user session"; |
| manager_->SetBrowserSessionForUser(actual_account_id, user_session->userhash); |
| session_started_ = true; |
| user_sessions_[actual_account_id] = std::move(user_session); |
| if (is_first_real_user) { |
| DCHECK(primary_user_account_id_.empty()); |
| primary_user_account_id_ = actual_account_id; |
| } |
| DLOG(INFO) << "Emitting D-Bus signal SessionStateChanged: " << kStarted; |
| adaptor_.SendSessionStateChangedSignal(kStarted); |
| |
| // Active Directory managed devices are not expected to have a policy key. |
| // Don't create one for them. |
| const bool is_active_directory = |
| install_attributes_reader_->GetAttribute( |
| InstallAttributesReader::kAttrMode) == |
| InstallAttributesReader::kDeviceModeEnterpriseAD; |
| if (device_policy_->KeyMissing() && !is_active_directory && |
| !device_policy_->Mitigating() && is_first_real_user) { |
| // This is the first sign-in on this unmanaged device. Take ownership. |
| key_gen_->Start(actual_account_id, ns_mnt_path); |
| } |
| |
| DeleteArcBugReportBackup(actual_account_id); |
| |
| // Record that a login has successfully completed on this boot. |
| system_->AtomicFileWrite(base::FilePath(kLoggedInFlag), "1"); |
| return true; |
| } |
| |
| bool SessionManagerImpl::SaveLoginPassword( |
| brillo::ErrorPtr* error, const base::ScopedFD& in_password_fd) { |
| if (!secret_util::SaveSecretFromPipe(password_provider_.get(), |
| in_password_fd)) { |
| LOG(ERROR) << "Could not save password."; |
| return false; |
| } |
| return true; |
| } |
| |
| bool SessionManagerImpl::LoginScreenStorageStore( |
| brillo::ErrorPtr* error, |
| const std::string& in_key, |
| const std::vector<uint8_t>& in_metadata, |
| uint64_t in_value_size, |
| const base::ScopedFD& in_value_fd) { |
| LoginScreenStorageMetadata metadata; |
| if (!metadata.ParseFromArray(in_metadata.data(), in_metadata.size())) { |
| *error = CreateError(DBUS_ERROR_INVALID_ARGS, "metadata parsing failed."); |
| return false; |
| } |
| |
| if (!metadata.clear_on_session_exit() && !user_sessions_.empty()) { |
| *error = CreateError(DBUS_ERROR_FAILED, |
| "can't store persistent login screen data while there " |
| "are active user sessions."); |
| return false; |
| } |
| |
| return login_screen_storage_->Store(error, in_key, metadata, in_value_size, |
| in_value_fd); |
| } |
| |
| bool SessionManagerImpl::LoginScreenStorageRetrieve( |
| brillo::ErrorPtr* error, |
| const std::string& in_key, |
| uint64_t* out_value_size, |
| brillo::dbus_utils::FileDescriptor* out_value_fd) { |
| base::ScopedFD value_fd; |
| bool success = |
| login_screen_storage_->Retrieve(error, in_key, out_value_size, &value_fd); |
| *out_value_fd = std::move(value_fd); |
| return success; |
| } |
| |
| void SessionManagerImpl::StopSession(const std::string& in_unique_identifier) { |
| StopSessionWithReason( |
| static_cast<uint32_t>(SessionStopReason::REQUEST_FROM_SESSION_MANAGER)); |
| } |
| |
| void SessionManagerImpl::StopSessionWithReason(uint32_t reason) { |
| LOG(INFO) << "Stopping all sessions reason = " << reason; |
| // Most calls to StopSession() will log the reason for the call. |
| // If you don't see a log message saying the reason for the call, it is |
| // likely a D-Bus message. |
| manager_->ScheduleShutdown(); |
| // TODO(cmasone): re-enable these when we try to enable logout without exiting |
| // the session manager |
| // browser_.job->StopSession(); |
| // user_policy_.reset(); |
| // session_started_ = false; |
| |
| password_provider_->DiscardPassword(); |
| } |
| |
| void SessionManagerImpl::StorePolicyEx( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> response, |
| const std::vector<uint8_t>& in_descriptor_blob, |
| const std::vector<uint8_t>& in_policy_blob) { |
| StorePolicyInternalEx(in_descriptor_blob, in_policy_blob, |
| SignatureCheck::kEnabled, std::move(response)); |
| } |
| |
| void SessionManagerImpl::StoreUnsignedPolicyEx( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> response, |
| const std::vector<uint8_t>& in_descriptor_blob, |
| const std::vector<uint8_t>& in_policy_blob) { |
| brillo::ErrorPtr error = VerifyUnsignedPolicyStore(); |
| if (error) { |
| response->ReplyWithError(error.get()); |
| return; |
| } |
| StorePolicyInternalEx(in_descriptor_blob, in_policy_blob, |
| SignatureCheck::kDisabled, std::move(response)); |
| } |
| |
| bool SessionManagerImpl::RetrievePolicyEx( |
| brillo::ErrorPtr* error, |
| const std::vector<uint8_t>& in_descriptor_blob, |
| std::vector<uint8_t>* out_policy_blob) { |
| PolicyDescriptor descriptor; |
| if (!ParseAndValidatePolicyDescriptor(in_descriptor_blob, |
| PolicyDescriptorUsage::kRetrieve, |
| &descriptor, error)) { |
| return false; |
| } |
| |
| std::unique_ptr<PolicyService> storage; |
| PolicyService* policy_service = GetPolicyService(descriptor, &storage, error); |
| if (!policy_service) |
| return false; |
| |
| PolicyNamespace ns(descriptor.domain(), descriptor.component_id()); |
| |
| if (!policy_service->Retrieve(ns, out_policy_blob)) { |
| LOG(ERROR) << kSigEncodeFailMessage; |
| *error = CreateError(dbus_error::kSigEncodeFail, kSigEncodeFailMessage); |
| return false; |
| } |
| return true; |
| } |
| |
| bool SessionManagerImpl::ListStoredComponentPolicies( |
| brillo::ErrorPtr* error, |
| const std::vector<uint8_t>& in_descriptor_blob, |
| std::vector<std::string>* out_component_ids) { |
| PolicyDescriptor descriptor; |
| if (!ParseAndValidatePolicyDescriptor(in_descriptor_blob, |
| PolicyDescriptorUsage::kList, |
| &descriptor, error)) { |
| return false; |
| } |
| |
| std::unique_ptr<PolicyService> storage; |
| PolicyService* policy_service = GetPolicyService(descriptor, &storage, error); |
| if (!policy_service) |
| return false; |
| |
| *out_component_ids = policy_service->ListComponentIds(descriptor.domain()); |
| return true; |
| } |
| |
| std::string SessionManagerImpl::RetrieveSessionState() { |
| if (!session_started_) |
| return kStopped; |
| if (session_stopping_) |
| return kStopping; |
| return kStarted; |
| } |
| |
| std::map<std::string, std::string> |
| SessionManagerImpl::RetrieveActiveSessions() { |
| std::map<std::string, std::string> result; |
| for (const auto& entry : user_sessions_) { |
| if (!entry.second) |
| continue; |
| result[entry.second->username] = entry.second->userhash; |
| } |
| return result; |
| } |
| |
| void SessionManagerImpl::RetrievePrimarySession( |
| std::string* out_username, std::string* out_sanitized_username) { |
| out_username->clear(); |
| out_sanitized_username->clear(); |
| if (user_sessions_.count(primary_user_account_id_) > 0) { |
| out_username->assign(user_sessions_[primary_user_account_id_]->username); |
| out_sanitized_username->assign( |
| user_sessions_[primary_user_account_id_]->userhash); |
| } |
| } |
| |
| bool SessionManagerImpl::IsGuestSessionActive() { |
| return !user_sessions_.empty() && AllSessionsAreIncognito(); |
| } |
| |
| void SessionManagerImpl::HandleSupervisedUserCreationStarting() { |
| supervised_user_creation_ongoing_ = true; |
| } |
| |
| void SessionManagerImpl::HandleSupervisedUserCreationFinished() { |
| supervised_user_creation_ongoing_ = false; |
| } |
| |
| bool SessionManagerImpl::LockScreen(brillo::ErrorPtr* error) { |
| if (!session_started_) { |
| *error = CREATE_WARNING_AND_LOG( |
| dbus_error::kSessionDoesNotExist, |
| "Attempt to lock screen outside of user session."); |
| return false; |
| } |
| // If all sessions are incognito, then locking is not allowed. |
| if (AllSessionsAreIncognito()) { |
| *error = |
| CREATE_WARNING_AND_LOG(dbus_error::kSessionExists, |
| "Attempt to lock screen during Guest session."); |
| return false; |
| } |
| if (!screen_locked_) { |
| screen_locked_ = true; |
| init_controller_->TriggerImpulse(kScreenLockedImpulse, {}, |
| InitDaemonController::TriggerMode::ASYNC); |
| delegate_->LockScreen(); |
| } |
| LOG(INFO) << "LockScreen() method called."; |
| return true; |
| } |
| |
| void SessionManagerImpl::HandleLockScreenShown() { |
| LOG(INFO) << "HandleLockScreenShown() method called."; |
| adaptor_.SendScreenIsLockedSignal(); |
| } |
| |
| void SessionManagerImpl::HandleLockScreenDismissed() { |
| screen_locked_ = false; |
| init_controller_->TriggerImpulse(kScreenUnlockedImpulse, {}, |
| InitDaemonController::TriggerMode::ASYNC); |
| LOG(INFO) << "HandleLockScreenDismissed() method called."; |
| adaptor_.SendScreenIsUnlockedSignal(); |
| } |
| |
| bool SessionManagerImpl::IsScreenLocked() { |
| return screen_locked_; |
| } |
| |
| bool SessionManagerImpl::RestartJob(brillo::ErrorPtr* error, |
| const base::ScopedFD& in_cred_fd, |
| const std::vector<std::string>& in_argv) { |
| struct ucred ucred = {0}; |
| socklen_t len = sizeof(struct ucred); |
| if (!in_cred_fd.is_valid() || getsockopt(in_cred_fd.get(), SOL_SOCKET, |
| SO_PEERCRED, &ucred, &len) == -1) { |
| PLOG(ERROR) << "Can't get peer creds"; |
| *error = CreateError("GetPeerCredsFailed", strerror(errno)); |
| return false; |
| } |
| |
| if (!manager_->IsBrowser(ucred.pid)) { |
| *error = CREATE_ERROR_AND_LOG(dbus_error::kUnknownPid, |
| "Provided pid is unknown."); |
| return false; |
| } |
| |
| // To set "logged-in" state for BWSI mode. |
| if (!StartSession(error, kGuestUserName, "")) { |
| DCHECK(*error); |
| return false; |
| } |
| |
| manager_->SetBrowserArgs(in_argv); |
| manager_->RestartBrowser(); |
| return true; |
| } |
| |
| bool SessionManagerImpl::StartDeviceWipe(brillo::ErrorPtr* error) { |
| if (system_->Exists(base::FilePath(kLoggedInFlag))) { |
| *error = CREATE_ERROR_AND_LOG(dbus_error::kSessionExists, |
| "A user has already logged in this boot."); |
| return false; |
| } |
| |
| InitiateDeviceWipe("session_manager_dbus_request"); |
| return true; |
| } |
| |
| bool SessionManagerImpl::StartRemoteDeviceWipe( |
| brillo::ErrorPtr* error, const std::vector<uint8_t>& in_signed_command) { |
| if (!device_policy_->ValidateRemoteDeviceWipeCommand(in_signed_command)) { |
| *error = CreateError(dbus_error::kInvalidParameter, |
| "Remote wipe command validation failed, aborting."); |
| return false; |
| } |
| |
| InitiateDeviceWipe("remote_wipe_request"); |
| return true; |
| } |
| |
| void SessionManagerImpl::ClearForcedReEnrollmentVpd( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> response) { |
| device_policy_->ClearForcedReEnrollmentFlags( |
| dbus_service_->CreatePolicyServiceCompletionCallback( |
| std::move(response))); |
| } |
| |
| bool SessionManagerImpl::StartTPMFirmwareUpdate( |
| brillo::ErrorPtr* error, const std::string& update_mode) { |
| // Make sure |update_mode| is supported. |
| if (update_mode != kTPMFirmwareUpdateModeFirstBoot && |
| update_mode != kTPMFirmwareUpdateModePreserveStateful && |
| update_mode != kTPMFirmwareUpdateModeCleanup) { |
| *error = |
| CREATE_ERROR_AND_LOG(dbus_error::kInvalidParameter, "Bad update mode."); |
| return false; |
| } |
| |
| // Verify that we haven't seen a user log in since boot. |
| if (system_->Exists(base::FilePath(kLoggedInFlag))) { |
| *error = CREATE_ERROR_AND_LOG(dbus_error::kSessionExists, |
| "A user has already logged in since boot."); |
| return false; |
| } |
| |
| // For remotely managed devices, make sure the requested update mode matches |
| // the admin-configured one in device policy. |
| // Cast value to C string and back to remove trailing zero. |
| const std::string mode(install_attributes_reader_ |
| ->GetAttribute(InstallAttributesReader::kAttrMode) |
| .c_str()); |
| if (mode == InstallAttributesReader::kDeviceModeEnterprise) { |
| const enterprise_management::TPMFirmwareUpdateSettingsProto& settings = |
| device_policy_->GetSettings().tpm_firmware_update_settings(); |
| std::set<std::string> allowed_modes; |
| if (settings.allow_user_initiated_powerwash()) { |
| allowed_modes.insert(kTPMFirmwareUpdateModeFirstBoot); |
| } |
| if (settings.allow_user_initiated_preserve_device_state()) { |
| allowed_modes.insert(kTPMFirmwareUpdateModePreserveStateful); |
| } |
| |
| // See whether the requested mode is allowed. Cleanup is permitted when at |
| // least one of the actual modes are allowed. |
| bool allowed = (update_mode == kTPMFirmwareUpdateModeCleanup) |
| ? !allowed_modes.empty() |
| : allowed_modes.count(update_mode) > 0; |
| if (!allowed) { |
| *error = CreateError(dbus_error::kNotAvailable, |
| "Policy doesn't allow TPM firmware update."); |
| return false; |
| } |
| } |
| |
| // Validate that a firmware update is actually available to make sure |
| // enterprise users can't abuse TPM firmware update to trigger powerwash. |
| bool available = false; |
| if (update_mode == kTPMFirmwareUpdateModeFirstBoot || |
| update_mode == kTPMFirmwareUpdateModePreserveStateful) { |
| std::string update_location; |
| available = |
| system_->ReadFileToString( |
| base::FilePath(kTPMFirmwareUpdateLocationFile), &update_location) && |
| update_location.size(); |
| } else if (update_mode == kTPMFirmwareUpdateModeCleanup) { |
| available = system_->Exists( |
| base::FilePath(kTPMFirmwareUpdateSRKVulnerableROCAFile)); |
| } |
| |
| if (!available) { |
| *error = |
| CREATE_ERROR_AND_LOG(dbus_error::kNotAvailable, "No update available."); |
| return false; |
| } |
| |
| // Put the update request into place. |
| if (!system_->AtomicFileWrite( |
| base::FilePath(kTPMFirmwareUpdateRequestFlagFile), update_mode)) { |
| *error = CREATE_ERROR_AND_LOG(dbus_error::kNotAvailable, |
| "Failed to persist update request."); |
| return false; |
| } |
| |
| if (update_mode == kTPMFirmwareUpdateModeFirstBoot || |
| update_mode == kTPMFirmwareUpdateModeCleanup) { |
| InitiateDeviceWipe("tpm_firmware_update_" + update_mode); |
| } else if (update_mode == kTPMFirmwareUpdateModePreserveStateful) { |
| // This flag file indicates that encrypted stateful should be preserved. |
| if (!system_->AtomicFileWrite( |
| base::FilePath(kStatefulPreservationRequestFile), update_mode)) { |
| *error = CREATE_ERROR_AND_LOG(dbus_error::kNotAvailable, |
| "Failed to request stateful preservation."); |
| return false; |
| } |
| |
| if (crossystem_->VbSetSystemPropertyInt(Crossystem::kClearTpmOwnerRequest, |
| 1) != 0) { |
| *error = CREATE_ERROR_AND_LOG(dbus_error::kNotAvailable, |
| "Failed to request TPM clear."); |
| return false; |
| } |
| |
| RestartDevice("tpm_firmware_update " + update_mode); |
| } else { |
| NOTREACHED(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void SessionManagerImpl::SetFlagsForUser( |
| const std::string& in_account_id, |
| const std::vector<std::string>& in_flags) { |
| manager_->SetFlagsForUser(in_account_id, in_flags); |
| } |
| |
| void SessionManagerImpl::GetServerBackedStateKeys( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse< |
| std::vector<std::vector<uint8_t>>>> response) { |
| DCHECK(dbus_service_); |
| ServerBackedStateKeyGenerator::StateKeyCallback callback = |
| dbus_service_->CreateStateKeyCallback(std::move(response)); |
| if (system_clock_synchronized_) { |
| state_key_generator_->RequestStateKeys(callback); |
| } else { |
| pending_state_key_callbacks_.push_back(callback); |
| } |
| } |
| |
| void SessionManagerImpl::OnSuspendImminent(dbus::Signal* signal) { |
| suspend_ongoing_ = true; |
| |
| // If Chrome crashed recently, it might've missed this SuspendImminent signal |
| // and failed to lock the screen. Stop the session as a precaution: |
| // https://crbug.com/867970 |
| const base::TimeTicks start_time = manager_->GetLastBrowserRestartTime(); |
| if (!start_time.is_null() && |
| tick_clock_->NowTicks() - start_time <= kCrashBeforeSuspendInterval) { |
| LOG(INFO) << "Stopping session for suspend after recent browser restart"; |
| StopSessionWithReason( |
| static_cast<uint32_t>(SessionStopReason::SUSPEND_AFTER_RESTART)); |
| } |
| } |
| |
| void SessionManagerImpl::OnSuspendDone(dbus::Signal* signal) { |
| suspend_ongoing_ = false; |
| last_suspend_done_time_ = tick_clock_->NowTicks(); |
| } |
| |
| void SessionManagerImpl::OnSystemClockServiceAvailable(bool service_available) { |
| if (!service_available) { |
| LOG(ERROR) << "Failed to listen for tlsdated service start"; |
| return; |
| } |
| |
| GetSystemClockLastSyncInfo(); |
| } |
| |
| void SessionManagerImpl::GetSystemClockLastSyncInfo() { |
| dbus::MethodCall method_call(system_clock::kSystemClockInterface, |
| system_clock::kSystemLastSyncInfo); |
| system_clock_proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::Bind(&SessionManagerImpl::OnGotSystemClockLastSyncInfo, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void SessionManagerImpl::OnGotSystemClockLastSyncInfo( |
| dbus::Response* response) { |
| if (!response) { |
| LOG(ERROR) << system_clock::kSystemClockInterface << "." |
| << system_clock::kSystemLastSyncInfo << " request failed."; |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&SessionManagerImpl::GetSystemClockLastSyncInfo, |
| weak_ptr_factory_.GetWeakPtr()), |
| system_clock_last_sync_info_retry_delay_); |
| return; |
| } |
| |
| dbus::MessageReader reader(response); |
| bool network_synchronized = false; |
| if (!reader.PopBool(&network_synchronized)) { |
| LOG(ERROR) << system_clock::kSystemClockInterface << "." |
| << system_clock::kSystemLastSyncInfo |
| << " response lacks network-synchronized argument"; |
| return; |
| } |
| |
| if (network_synchronized) { |
| system_clock_synchronized_ = true; |
| for (const auto& callback : pending_state_key_callbacks_) |
| state_key_generator_->RequestStateKeys(callback); |
| pending_state_key_callbacks_.clear(); |
| } else { |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&SessionManagerImpl::GetSystemClockLastSyncInfo, |
| weak_ptr_factory_.GetWeakPtr()), |
| system_clock_last_sync_info_retry_delay_); |
| } |
| } |
| |
| bool SessionManagerImpl::InitMachineInfo(brillo::ErrorPtr* error, |
| const std::string& in_data) { |
| std::map<std::string, std::string> params; |
| if (!ServerBackedStateKeyGenerator::ParseMachineInfo(in_data, ¶ms)) { |
| *error = CreateError(dbus_error::kInitMachineInfoFail, "Parse failure."); |
| return false; |
| } |
| |
| if (!state_key_generator_->InitMachineInfo(params)) { |
| *error = |
| CreateError(dbus_error::kInitMachineInfoFail, "Missing parameters."); |
| return false; |
| } |
| return true; |
| } |
| |
| bool SessionManagerImpl::StartArcMiniContainer( |
| brillo::ErrorPtr* error, const std::vector<uint8_t>& in_request) { |
| #if USE_CHEETS |
| StartArcMiniContainerRequest request; |
| if (!request.ParseFromArray(in_request.data(), in_request.size())) { |
| *error = CreateError(DBUS_ERROR_INVALID_ARGS, |
| "StartArcMiniContainerRequest parsing failed."); |
| return false; |
| } |
| std::vector<std::string> env_vars = { |
| base::StringPrintf("CHROMEOS_DEV_MODE=%d", IsDevMode(system_)), |
| base::StringPrintf("CHROMEOS_INSIDE_VM=%d", IsInsideVm(system_)), |
| base::StringPrintf("NATIVE_BRIDGE_EXPERIMENT=%d", |
| request.native_bridge_experiment()), |
| base::StringPrintf("ARC_FILE_PICKER_EXPERIMENT=%d", |
| request.arc_file_picker_experiment()), |
| base::StringPrintf("ARC_CUSTOM_TABS_EXPERIMENT=%d", |
| request.arc_custom_tabs_experiment()), |
| base::StringPrintf("DISABLE_SYSTEM_DEFAULT_APP=%d", |
| request.disable_system_default_app())}; |
| if (request.lcd_density() > 0) { |
| env_vars.push_back( |
| base::StringPrintf("ARC_LCD_DENSITY=%d", request.lcd_density())); |
| } |
| |
| switch (request.play_store_auto_update()) { |
| case StartArcMiniContainerRequest_PlayStoreAutoUpdate_AUTO_UPDATE_DEFAULT: |
| break; |
| case StartArcMiniContainerRequest_PlayStoreAutoUpdate_AUTO_UPDATE_ON: |
| env_vars.emplace_back("PLAY_STORE_AUTO_UPDATE=1"); |
| break; |
| case StartArcMiniContainerRequest_PlayStoreAutoUpdate_AUTO_UPDATE_OFF: |
| env_vars.emplace_back("PLAY_STORE_AUTO_UPDATE=0"); |
| break; |
| default: |
| NOTREACHED() << "Unhandled play store auto-update mode: " |
| << request.play_store_auto_update() << "."; |
| } |
| |
| if (!StartArcContainer(env_vars, error)) { |
| DCHECK(*error); |
| return false; |
| } |
| return true; |
| #else |
| *error = CreateError(dbus_error::kNotAvailable, "ARC not supported."); |
| return false; |
| #endif // !USE_CHEETS |
| } |
| |
| bool SessionManagerImpl::UpgradeArcContainer( |
| brillo::ErrorPtr* error, const std::vector<uint8_t>& in_request) { |
| #if USE_CHEETS |
| // Stop the existing instance if it fails to continue to boot an existing |
| // container. Using Unretained() is okay because the closure will be called |
| // before this function returns. If container was not running, this is no op. |
| base::ScopedClosureRunner scoped_runner(base::Bind( |
| &SessionManagerImpl::OnContinueArcBootFailed, base::Unretained(this))); |
| |
| UpgradeArcContainerRequest request; |
| if (!request.ParseFromArray(in_request.data(), in_request.size())) { |
| *error = CreateError(DBUS_ERROR_INVALID_ARGS, |
| "UpgradeArcContainerRequest parsing failed."); |
| return false; |
| } |
| |
| pid_t pid = 0; |
| if (!android_container_->GetContainerPID(&pid)) { |
| *error = CREATE_ERROR_AND_LOG(dbus_error::kArcContainerNotFound, |
| "Failed to find mini-container for upgrade."); |
| return false; |
| } |
| LOG(INFO) << "Android container is running with PID " << pid; |
| |
| // |arc_start_time_| is initialized when the container is upgraded (rather |
| // than when the mini-container starts) since we are interested in measuring |
| // time from when the user logs in until the system is ready to be interacted |
| // with. |
| arc_start_time_ = tick_clock_->NowTicks(); |
| |
| // To upgrade the ARC mini-container, a certain amount of disk space is |
| // needed under /home. We first check it. |
| if (system_->AmountOfFreeDiskSpace(base::FilePath(kArcDiskCheckPath)) < |
| kArcCriticalDiskFreeBytes) { |
| *error = CREATE_ERROR_AND_LOG(dbus_error::kLowFreeDisk, |
| "Low free disk under /home"); |
| StopArcInstanceInternal(ArcContainerStopReason::LOW_DISK_SPACE); |
| ignore_result(scoped_runner.Release()); |
| return false; |
| } |
| |
| std::string account_id; |
| if (!NormalizeAccountId(request.account_id(), &account_id, error)) { |
| DCHECK(*error); |
| return false; |
| } |
| if (user_sessions_.count(account_id) == 0) { |
| // This path can be taken if a forged D-Bus message for starting a full |
| // (stateful) container is sent to session_manager before the actual |
| // user's session has started. Do not remove the |account_id| check to |
| // prevent such a container from starting on login screen. |
| *error = CREATE_ERROR_AND_LOG(dbus_error::kSessionDoesNotExist, |
| "Provided user ID does not have a session."); |
| return false; |
| } |
| |
| android_container_->SetStatefulMode(StatefulMode::STATEFUL); |
| auto env_vars = CreateUpgradeArcEnvVars(request, account_id, pid); |
| if (!init_controller_->TriggerImpulse( |
| kContinueArcBootImpulse, env_vars, |
| InitDaemonController::TriggerMode::SYNC)) { |
| *error = CREATE_ERROR_AND_LOG(dbus_error::kEmitFailed, |
| "Emitting continue-arc-boot impulse failed."); |
| |
| BackupArcBugReport(account_id); |
| return false; |
| } |
| |
| login_metrics_->StartTrackingArcUseTime(); |
| |
| ignore_result(scoped_runner.Release()); |
| DeleteArcBugReportBackup(account_id); |
| |
| return true; |
| #else |
| *error = CreateError(dbus_error::kNotAvailable, "ARC not supported."); |
| return false; |
| #endif // !USE_CHEETS |
| } |
| |
| bool SessionManagerImpl::StopArcInstance(brillo::ErrorPtr* error, |
| const std::string& account_id, |
| bool should_backup_log) { |
| #if USE_CHEETS |
| if (should_backup_log && !account_id.empty()) { |
| std::string actual_account_id; |
| if (!NormalizeAccountId(account_id, &actual_account_id, error)) { |
| DCHECK(*error); |
| return false; |
| } |
| |
| BackupArcBugReport(actual_account_id); |
| } |
| |
| if (!StopArcInstanceInternal(ArcContainerStopReason::USER_REQUEST)) { |
| *error = CREATE_ERROR_AND_LOG(dbus_error::kContainerShutdownFail, |
| "Error getting Android container pid."); |
| return false; |
| } |
| |
| return true; |
| #else |
| *error = CreateError(dbus_error::kNotAvailable, "ARC not supported."); |
| return false; |
| #endif // USE_CHEETS |
| } |
| |
| bool SessionManagerImpl::SetArcCpuRestriction(brillo::ErrorPtr* error, |
| uint32_t in_restriction_state) { |
| #if USE_CHEETS |
| std::string shares_out; |
| switch (static_cast<ContainerCpuRestrictionState>(in_restriction_state)) { |
| case CONTAINER_CPU_RESTRICTION_FOREGROUND: |
| shares_out = std::to_string(kCpuSharesForeground); |
| break; |
| case CONTAINER_CPU_RESTRICTION_BACKGROUND: |
| shares_out = std::to_string(kCpuSharesBackground); |
| break; |
| default: |
| *error = CREATE_ERROR_AND_LOG(dbus_error::kArcCpuCgroupFail, |
| "Invalid CPU restriction state specified."); |
| return false; |
| } |
| if (base::WriteFile(base::FilePath(kCpuSharesFile), shares_out.c_str(), |
| shares_out.length()) != shares_out.length()) { |
| *error = |
| CREATE_ERROR_AND_LOG(dbus_error::kArcCpuCgroupFail, |
| "Error updating Android container's cgroups."); |
| return false; |
| } |
| return true; |
| #else |
| *error = CreateError(dbus_error::kNotAvailable, "ARC not supported."); |
| return false; |
| #endif |
| } |
| |
| bool SessionManagerImpl::EmitArcBooted(brillo::ErrorPtr* error, |
| const std::string& in_account_id) { |
| #if USE_CHEETS |
| std::vector<std::string> env_vars; |
| if (!in_account_id.empty()) { |
| std::string actual_account_id; |
| if (!NormalizeAccountId(in_account_id, &actual_account_id, error)) { |
| DCHECK(*error); |
| return false; |
| } |
| env_vars.emplace_back("CHROMEOS_USER=" + actual_account_id); |
| } |
| |
| init_controller_->TriggerImpulse(kArcBootedImpulse, env_vars, |
| InitDaemonController::TriggerMode::ASYNC); |
| return true; |
| #else |
| *error = CreateError(dbus_error::kNotAvailable, "ARC not supported."); |
| return false; |
| #endif |
| } |
| |
| bool SessionManagerImpl::GetArcStartTimeTicks(brillo::ErrorPtr* error, |
| int64_t* out_start_time) { |
| #if USE_CHEETS |
| if (arc_start_time_.is_null()) { |
| *error = CreateError(dbus_error::kNotStarted, "ARC is not started yet."); |
| return false; |
| } |
| *out_start_time = arc_start_time_.ToInternalValue(); |
| return true; |
| #else |
| *error = CreateError(dbus_error::kNotAvailable, "ARC not supported."); |
| return false; |
| #endif // !USE_CHEETS |
| } |
| |
| void SessionManagerImpl::EnableAdbSideloadCallbackAdaptor( |
| brillo::dbus_utils::DBusMethodResponse<bool>* response, |
| ArcSideloadStatusInterface::Status status, |
| const char* error) { |
| if (error != nullptr) { |
| brillo::ErrorPtr dbus_error = CreateError(DBUS_ERROR_FAILED, error); |
| response->ReplyWithError(dbus_error.get()); |
| return; |
| } |
| |
| if (status == ArcSideloadStatusInterface::Status::NEED_POWERWASH) { |
| brillo::ErrorPtr dbus_error = CreateError(DBUS_ERROR_NOT_SUPPORTED, error); |
| response->ReplyWithError(dbus_error.get()); |
| return; |
| } |
| |
| response->Return(status == ArcSideloadStatusInterface::Status::ENABLED); |
| } |
| |
| void SessionManagerImpl::EnableAdbSideload( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<bool>> response) { |
| if (system_->Exists(base::FilePath(kLoggedInFlag))) { |
| auto error = CREATE_ERROR_AND_LOG( |
| dbus_error::kSessionExists, |
| "EnableAdbSideload is not allowed once a user logged in this boot."); |
| response->ReplyWithError(error.get()); |
| return; |
| } |
| |
| arc_sideload_status_->EnableAdbSideload(base::Bind( |
| &SessionManagerImpl::EnableAdbSideloadCallbackAdaptor, |
| weak_ptr_factory_.GetWeakPtr(), base::Owned(response.release()))); |
| } |
| |
| void SessionManagerImpl::QueryAdbSideloadCallbackAdaptor( |
| brillo::dbus_utils::DBusMethodResponse<bool>* response, |
| ArcSideloadStatusInterface::Status status) { |
| if (status == ArcSideloadStatusInterface::Status::NEED_POWERWASH) { |
| brillo::ErrorPtr dbus_error = |
| CreateError(DBUS_ERROR_NOT_SUPPORTED, "Need powerwash"); |
| response->ReplyWithError(dbus_error.get()); |
| return; |
| } |
| response->Return(status == ArcSideloadStatusInterface::Status::ENABLED); |
| } |
| |
| void SessionManagerImpl::QueryAdbSideload( |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<bool>> response) { |
| arc_sideload_status_->QueryAdbSideload(base::Bind( |
| &SessionManagerImpl::QueryAdbSideloadCallbackAdaptor, |
| weak_ptr_factory_.GetWeakPtr(), base::Owned(response.release()))); |
| } |
| |
| void SessionManagerImpl::OnPolicyPersisted(bool success) { |
| device_local_account_manager_->UpdateDeviceSettings( |
| device_policy_->GetSettings()); |
| adaptor_.SendPropertyChangeCompleteSignal(ToSuccessSignal(success)); |
| } |
| |
| void SessionManagerImpl::OnKeyPersisted(bool success) { |
| adaptor_.SendSetOwnerKeyCompleteSignal(ToSuccessSignal(success)); |
| } |
| |
| void SessionManagerImpl::OnKeyGenerated(const std::string& username, |
| const base::FilePath& temp_key_file) { |
| ImportValidateAndStoreGeneratedKey(username, temp_key_file); |
| } |
| |
| void SessionManagerImpl::ImportValidateAndStoreGeneratedKey( |
| const std::string& username, const base::FilePath& temp_key_file) { |
| DLOG(INFO) << "Processing generated key at " << temp_key_file.value(); |
| std::string key; |
| |
| // The temp key file will only exist in the user's mount namespace. |
| OptionalFilePath ns_mnt_path = |
| user_sessions_[username]->descriptor->ns_mnt_path; |
| std::unique_ptr<brillo::ScopedMountNamespace> ns_mnt; |
| if (ns_mnt_path) { |
| ns_mnt = brillo::ScopedMountNamespace::CreateFromPath(ns_mnt_path.value()); |
| } |
| base::ReadFileToString(temp_key_file, &key); |
| PLOG_IF(WARNING, !base::DeleteFile(temp_key_file, false)) |
| << "Can't delete " << temp_key_file.value(); |
| ns_mnt.reset(); |
| |
| device_policy_->ValidateAndStoreOwnerKey( |
| username, StringToBlob(key), user_sessions_[username]->descriptor.get()); |
| } |
| |
| void SessionManagerImpl::InitiateDeviceWipe(const std::string& reason) { |
| // The log string must not be confused with other clobbers-state parameters. |
| // Sanitize by replacing all non-alphanumeric characters with underscores and |
| // clamping size to 50 characters. |
| std::string sanitized_reason(reason.substr(0, 50)); |
| std::locale locale("C"); |
| std::replace_if( |
| sanitized_reason.begin(), sanitized_reason.end(), |
| [&locale](const std::string::value_type character) { |
| return !std::isalnum(character, locale); |
| }, |
| '_'); |
| const base::FilePath reset_path(kResetFile); |
| system_->AtomicFileWrite(reset_path, |
| "fast safe keepimg reason=" + sanitized_reason); |
| RestartDevice(sanitized_reason); |
| } |
| |
| // static |
| bool SessionManagerImpl::NormalizeAccountId(const std::string& account_id, |
| std::string* actual_account_id_out, |
| brillo::ErrorPtr* error_out) { |
| if (ValidateAccountId(account_id, actual_account_id_out)) { |
| DCHECK(!actual_account_id_out->empty()); |
| return true; |
| } |
| |
| // TODO(alemate): adjust this error message after ChromeOS will stop using |
| // email as cryptohome identifier. |
| *error_out = |
| CREATE_ERROR_AND_LOG(dbus_error::kInvalidAccount, |
| "Provided email address is not valid. ASCII only."); |
| DCHECK(actual_account_id_out->empty()); |
| return false; |
| } |
| |
| bool SessionManagerImpl::AllSessionsAreIncognito() { |
| size_t incognito_count = 0; |
| for (UserSessionMap::const_iterator it = user_sessions_.begin(); |
| it != user_sessions_.end(); ++it) { |
| if (it->second) |
| incognito_count += it->second->is_incognito; |
| } |
| return incognito_count == user_sessions_.size(); |
| } |
| |
| std::unique_ptr<SessionManagerImpl::UserSession> |
| SessionManagerImpl::CreateUserSession(const std::string& username, |
| const OptionalFilePath& ns_mnt_path, |
| bool is_incognito, |
| brillo::ErrorPtr* error) { |
| std::unique_ptr<PolicyService> user_policy = |
| user_policy_factory_->Create(username); |
| if (!user_policy) { |
| LOG(ERROR) << "User policy failed to initialize."; |
| *error = CreateError(dbus_error::kPolicyInitFail, "Can't create session."); |
| return nullptr; |
| } |
| |
| ScopedPK11SlotDescriptor desc( |
| nss_->OpenUserDB(GetUserPath(username), ns_mnt_path)); |
| |
| if (!desc->slot) { |
| LOG(ERROR) << "Could not open the current user's NSS database."; |
| *error = CreateError(dbus_error::kNoUserNssDb, "Can't create session."); |
| return nullptr; |
| } |
| |
| return std::make_unique<UserSession>(username, SanitizeUserName(username), |
| is_incognito, std::move(desc), |
| std::move(user_policy)); |
| } |
| |
| brillo::ErrorPtr SessionManagerImpl::VerifyUnsignedPolicyStore() { |
| // Unsigned policy store D-Bus call is allowed only in enterprise_ad mode. |
| const std::string& mode = install_attributes_reader_->GetAttribute( |
| InstallAttributesReader::kAttrMode); |
| if (mode != InstallAttributesReader::kDeviceModeEnterpriseAD) { |
| return CREATE_ERROR_AND_LOG(dbus_error::kPolicySignatureRequired, |
| "Device mode doesn't permit unsigned policy."); |
| } |
| |
| return nullptr; |
| } |
| |
| PolicyService* SessionManagerImpl::GetPolicyService( |
| const PolicyDescriptor& descriptor, |
| std::unique_ptr<PolicyService>* storage, |
| brillo::ErrorPtr* error) { |
| DCHECK(storage); |
| DCHECK(error); |
| PolicyService* policy_service = nullptr; |
| switch (descriptor.account_type()) { |
| case ACCOUNT_TYPE_DEVICE: { |
| policy_service = device_policy_.get(); |
| break; |
| } |
| case ACCOUNT_TYPE_USER: { |
| UserSessionMap::const_iterator it = |
| user_sessions_.find(descriptor.account_id()); |
| policy_service = it != user_sessions_.end() |
| ? it->second->policy_service.get() |
| : nullptr; |
| break; |
| } |
| case ACCOUNT_TYPE_SESSIONLESS_USER: { |
| // Special case, different lifetime management than all other cases |
| // (unique_ptr vs plain ptr). TODO(crbug.com/771638): Clean this up when |
| // the bug is fixed and sessionless users are handled differently. |
| *storage = user_policy_factory_->CreateForHiddenUserHome( |
| descriptor.account_id()); |
| policy_service = storage->get(); |
| break; |
| } |
| case ACCOUNT_TYPE_DEVICE_LOCAL_ACCOUNT: { |
| policy_service = device_local_account_manager_->GetPolicyService( |
| descriptor.account_id()); |
| break; |
| } |
| } |
| if (policy_service) |
| return policy_service; |
| |
| // Error case |
| const std::string message = |
| base::StringPrintf("Cannot get policy service for account type %i", |
| static_cast<int>(descriptor.account_type())); |
| LOG(ERROR) << message; |
| *error = CreateError(dbus_error::kGetServiceFail, message); |
| return nullptr; |
| } |
| |
| int SessionManagerImpl::GetKeyInstallFlags(const PolicyDescriptor& descriptor) { |
| switch (descriptor.account_type()) { |
| case ACCOUNT_TYPE_DEVICE: { |
| int flags = PolicyService::KEY_ROTATE; |
| if (!session_started_) |
| flags |= PolicyService::KEY_INSTALL_NEW | PolicyService::KEY_CLOBBER; |
| return flags; |
| } |
| case ACCOUNT_TYPE_USER: |
| return PolicyService::KEY_INSTALL_NEW | PolicyService::KEY_ROTATE; |
| case ACCOUNT_TYPE_SESSIONLESS_USER: { |
| // Only supports retrieval, not storage. |
| NOTREACHED(); |
| return PolicyService::KEY_NONE; |
| } |
| case ACCOUNT_TYPE_DEVICE_LOCAL_ACCOUNT: |
| return PolicyService::KEY_NONE; |
| } |
| } |
| |
| void SessionManagerImpl::StorePolicyInternalEx( |
| const std::vector<uint8_t>& descriptor_blob, |
| const std::vector<uint8_t>& policy_blob, |
| SignatureCheck signature_check, |
| std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<>> response) { |
| brillo::ErrorPtr error; |
| PolicyDescriptor descriptor; |
| if (!ParseAndValidatePolicyDescriptor(descriptor_blob, |
| PolicyDescriptorUsage::kStore, |
| &descriptor, &error)) { |
| response->ReplyWithError(error.get()); |
| return; |
| } |
| |
| std::unique_ptr<PolicyService> storage; |
| PolicyService* policy_service = |
| GetPolicyService(descriptor, &storage, &error); |
| if (!policy_service) { |
| response->ReplyWithError(error.get()); |
| return; |
| } |
| |
| int key_flags = GetKeyInstallFlags(descriptor); |
| PolicyNamespace ns(descriptor.domain(), descriptor.component_id()); |
| |
| // If the blob is empty, delete the policy. |
| DCHECK(dbus_service_); |
| if (policy_blob.empty()) { |
| if (!policy_service->Delete(ns, signature_check)) { |
| auto error = |
| CreateError(dbus_error::kDeleteFail, "Failed to delete policy"); |
| response->ReplyWithError(error.get()); |
| return; |
| } |
| response->Return(); |
| } else { |
| policy_service->Store(ns, policy_blob, key_flags, signature_check, |
| dbus_service_->CreatePolicyServiceCompletionCallback( |
| std::move(response))); |
| } |
| } |
| |
| void SessionManagerImpl::RestartDevice(const std::string& reason) { |
| delegate_->RestartDevice("session_manager (" + reason + ")"); |
| } |
| |
| void SessionManagerImpl::BackupArcBugReport(const std::string& account_id) { |
| if (user_sessions_.count(account_id) == 0) { |
| LOG(ERROR) << "Cannot back up ARC bug report for inactive user."; |
| return; |
| } |
| |
| const base::TimeTicks arc_bug_report_backup_time = base::TimeTicks::Now(); |
| |
| dbus::MethodCall method_call(debugd::kDebugdInterface, |
| debugd::kBackupArcBugReport); |
| dbus::MessageWriter writer(&method_call); |
| |
| writer.AppendString(account_id); |
| |
| std::unique_ptr<dbus::Response> response(debugd_proxy_->CallMethodAndBlock( |
| &method_call, kBackupArcBugReportTimeout.InMilliseconds())); |
| |
| if (response) { |
| login_metrics_->SendArcBugReportBackupTime(base::TimeTicks::Now() - |
| arc_bug_report_backup_time); |
| } else { |
| LOG(ERROR) << "Error contacting debugd to back up ARC bug report."; |
| } |
| } |
| |
| void SessionManagerImpl::DeleteArcBugReportBackup( |
| const std::string& account_id) { |
| dbus::MethodCall method_call(debugd::kDebugdInterface, |
| debugd::kDeleteArcBugReportBackup); |
| dbus::MessageWriter writer(&method_call); |
| |
| writer.AppendString(account_id); |
| |
| std::unique_ptr<dbus::Response> response(debugd_proxy_->CallMethodAndBlock( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); |
| |
| if (!response) { |
| LOG(ERROR) << "Error contacting debugd to delete ARC bug report backup."; |
| } |
| } |
| |
| #if USE_CHEETS |
| bool SessionManagerImpl::StartArcContainer( |
| const std::vector<std::string>& env_vars, brillo::ErrorPtr* error_out) { |
| init_controller_->TriggerImpulse(kStartArcInstanceImpulse, env_vars, |
| InitDaemonController::TriggerMode::ASYNC); |
| |
| // Pass in the same environment variables that were passed to arc-setup |
| // (through init, above) into the container invocation as environment values. |
| // When the container is started with run_oci, this allows for it to correctly |
| // propagate some information (such as the CHROMEOS_USER) to the hooks so it |
| // can set itself up. |
| if (!android_container_->StartContainer( |
| env_vars, base::Bind(&SessionManagerImpl::OnAndroidContainerStopped, |
| weak_ptr_factory_.GetWeakPtr()))) { |
| // Failed to start container. Thus, trigger stop-arc-instance impulse |
| // manually for cleanup. |
| init_controller_->TriggerImpulse(kStopArcInstanceImpulse, {}, |
| InitDaemonController::TriggerMode::SYNC); |
| *error_out = CREATE_ERROR_AND_LOG(dbus_error::kContainerStartupFail, |
| "Starting Android container failed."); |
| return false; |
| } |
| |
| pid_t pid = 0; |
| android_container_->GetContainerPID(&pid); |
| LOG(INFO) << "Started Android container with PID " << pid; |
| return true; |
| } |
| |
| std::vector<std::string> SessionManagerImpl::CreateUpgradeArcEnvVars( |
| const UpgradeArcContainerRequest& request, |
| const std::string& account_id, |
| pid_t pid) { |
| // Only allow for managed account if the policies allow it. |
| bool is_adb_sideloading_allowed_for_request = |
| !request.is_account_managed() || |
| request.is_managed_adb_sideloading_allowed(); |
| |
| std::vector<std::string> env_vars = { |
| base::StringPrintf("CHROMEOS_DEV_MODE=%d", IsDevMode(system_)), |
| base::StringPrintf("CHROMEOS_INSIDE_VM=%d", IsInsideVm(system_)), |
| "CHROMEOS_USER=" + account_id, |
| base::StringPrintf("DISABLE_BOOT_COMPLETED_BROADCAST=%d", |
| request.skip_boot_completed_broadcast()), |
| base::StringPrintf("CONTAINER_PID=%d", pid), |
| "DEMO_SESSION_APPS_PATH=" + request.demo_session_apps_path(), |
| base::StringPrintf("IS_DEMO_SESSION=%d", request.is_demo_session()), |
| base::StringPrintf("SUPERVISION_TRANSITION=%d", |
| request.supervision_transition()), |
| base::StringPrintf("ENABLE_ADB_SIDELOAD=%d", |
| arc_sideload_status_->IsAdbSideloadAllowed() && |
| is_adb_sideloading_allowed_for_request)}; |
| |
| switch (request.packages_cache_mode()) { |
| case UpgradeArcContainerRequest_PackageCacheMode_SKIP_SETUP_COPY_ON_INIT: |
| env_vars.emplace_back("SKIP_PACKAGES_CACHE_SETUP=1"); |
| env_vars.emplace_back("COPY_PACKAGES_CACHE=1"); |
| break; |
| case UpgradeArcContainerRequest_PackageCacheMode_COPY_ON_INIT: |
| env_vars.emplace_back("SKIP_PACKAGES_CACHE_SETUP=0"); |
| env_vars.emplace_back("COPY_PACKAGES_CACHE=1"); |
| break; |
| case UpgradeArcContainerRequest_PackageCacheMode_DEFAULT: |
| env_vars.emplace_back("SKIP_PACKAGES_CACHE_SETUP=0"); |
| env_vars.emplace_back("COPY_PACKAGES_CACHE=0"); |
| break; |
| default: |
| NOTREACHED() << "Wrong packages cache mode: " |
| << request.packages_cache_mode() << "."; |
| } |
| |
| if (request.skip_gms_core_cache()) { |
| env_vars.emplace_back("SKIP_GMS_CORE_CACHE_SETUP=1"); |
| } else { |
| env_vars.emplace_back("SKIP_GMS_CORE_CACHE_SETUP=0"); |
| } |
| |
| DCHECK(request.has_locale()); |
| env_vars.emplace_back("LOCALE=" + request.locale()); |
| |
| std::string preferred_languages; |
| for (int i = 0; i < request.preferred_languages_size(); ++i) { |
| if (i != 0) |
| preferred_languages += ","; |
| preferred_languages += request.preferred_languages(i); |
| } |
| env_vars.emplace_back("PREFERRED_LANGUAGES=" + preferred_languages); |
| |
| return env_vars; |
| } |
| |
| void SessionManagerImpl::OnContinueArcBootFailed() { |
| LOG(ERROR) << "Failed to continue ARC boot. Stopping the container."; |
| StopArcInstanceInternal(ArcContainerStopReason::UPGRADE_FAILURE); |
| } |
| |
| bool SessionManagerImpl::StopArcInstanceInternal( |
| ArcContainerStopReason reason) { |
| pid_t pid; |
| if (!android_container_->GetContainerPID(&pid)) |
| return false; |
| |
| android_container_->RequestJobExit(reason); |
| android_container_->EnsureJobExit(kContainerTimeout); |
| return true; |
| } |
| |
| void SessionManagerImpl::OnAndroidContainerStopped( |
| pid_t pid, ArcContainerStopReason reason) { |
| if (reason == ArcContainerStopReason::CRASH) { |
| LOG(ERROR) << "Android container with PID " << pid << " crashed"; |
| } else { |
| LOG(INFO) << "Android container with PID " << pid << " stopped"; |
| } |
| |
| login_metrics_->StopTrackingArcUseTime(); |
| if (!init_controller_->TriggerImpulse( |
| kStopArcInstanceImpulse, {}, |
| InitDaemonController::TriggerMode::SYNC)) { |
| LOG(ERROR) << "Emitting stop-arc-instance impulse failed."; |
| } |
| |
| adaptor_.SendArcInstanceStoppedSignal(static_cast<uint32_t>(reason)); |
| } |
| #endif // USE_CHEETS |
| } // namespace login_manager |