| // Copyright 2017 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 "smbprovider/mount_manager.h" |
| |
| #include <algorithm> |
| |
| #include <base/check.h> |
| #include <base/check_op.h> |
| #include <base/files/file.h> |
| #include <base/logging.h> |
| #include <base/strings/string_util.h> |
| #include <base/time/tick_clock.h> |
| |
| #include "smbprovider/smb_credential.h" |
| #include "smbprovider/smbprovider_helper.h" |
| |
| namespace smbprovider { |
| |
| namespace { |
| |
| // Returns true if |buffer_length| is large enough to contain |str|. |
| bool CanBufferHoldString(const std::string& str, int32_t buffer_length) { |
| return static_cast<int32_t>(str.size()) + 1 <= buffer_length; |
| } |
| |
| // Returns true if |buffer_length| is large enough to contain |password|. |
| bool CanBufferHoldPassword( |
| const std::unique_ptr<password_provider::Password>& password, |
| int32_t buffer_length) { |
| DCHECK(password); |
| return static_cast<int32_t>(password->size()) + 1 <= buffer_length; |
| } |
| |
| // Sets the first element in the buffer to be a null terminator. |
| void SetBufferEmpty(char* buffer) { |
| DCHECK(buffer); |
| buffer[0] = '\0'; |
| } |
| |
| // Copies |str| to |buffer| and adds a null terminator at the end. |
| void CopyStringToBuffer(const std::string& str, char* buffer) { |
| DCHECK(buffer); |
| strncpy(buffer, str.c_str(), str.size()); |
| buffer[str.size()] = '\0'; |
| } |
| |
| // Copies |password| to |buffer| and adds a null terminator at the end. |
| void CopyPasswordToBuffer( |
| const std::unique_ptr<password_provider::Password>& password, |
| char* buffer) { |
| DCHECK(password); |
| DCHECK(buffer); |
| |
| strncpy(buffer, password->GetRaw(), password->size()); |
| buffer[password->size()] = '\0'; |
| } |
| |
| // Checks that the credential can be inputted given the buffer sizes. Returns |
| // false if the buffers are too small or if the credential is empty. |
| bool CanInputCredential(int32_t workgroup_length, |
| int32_t username_length, |
| int32_t password_length, |
| const SmbCredential& credential) { |
| if (!CanBufferHoldString(credential.workgroup, workgroup_length) || |
| !CanBufferHoldString(credential.username, username_length)) { |
| LOG(ERROR) << "Credential buffers are too small for input"; |
| return false; |
| } |
| |
| if (credential.password && |
| !CanBufferHoldPassword(credential.password, password_length)) { |
| LOG(ERROR) << "Password buffer is too small for input"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Populates the |credential| into the specified buffers. CanInputCredential() |
| // should be called first in order to verify the buffers can contain the |
| // credential. |
| void PopulateCredential(const SmbCredential& credential, |
| char* workgroup_buffer, |
| char* username_buffer, |
| char* password_buffer) { |
| DCHECK(workgroup_buffer); |
| DCHECK(username_buffer); |
| DCHECK(password_buffer); |
| |
| CopyStringToBuffer(credential.workgroup, workgroup_buffer); |
| CopyStringToBuffer(credential.username, username_buffer); |
| |
| const bool empty_password = !credential.password; |
| if (empty_password) { |
| SetBufferEmpty(password_buffer); |
| } else { |
| CopyPasswordToBuffer(credential.password, password_buffer); |
| } |
| } |
| |
| } // namespace |
| |
| std::unique_ptr<password_provider::Password> GetPassword( |
| const base::ScopedFD& password_fd) { |
| size_t password_length = 0; |
| |
| // Read sizeof(size_t) bytes from the file to get the password length. |
| if (!base::ReadFromFD(password_fd.get(), |
| reinterpret_cast<char*>(&password_length), |
| sizeof(password_length))) { |
| LOG(ERROR) << "Cannot read password from file"; |
| return nullptr; |
| } |
| |
| if (password_length == 0) { |
| // Return empty password since there is no password. |
| return nullptr; |
| } |
| |
| return password_provider::Password::CreateFromFileDescriptor( |
| password_fd.get(), password_length); |
| } |
| |
| MountManager::MountManager(std::unique_ptr<MountTracker> mount_tracker, |
| SambaInterfaceFactory samba_interface_factory) |
| : mount_tracker_(std::move(mount_tracker)), |
| samba_interface_factory_(samba_interface_factory), |
| system_samba_interface_( |
| CreateSambaInterface(MountConfig(false /* enable_ntlm */))) {} |
| |
| MountManager::~MountManager() = default; |
| |
| bool MountManager::IsAlreadyMounted(int32_t mount_id) const { |
| DCHECK_GE(mount_id, 0); |
| return mount_tracker_->IsAlreadyMounted(mount_id); |
| } |
| |
| void MountManager::AddMount(const std::string& mount_root, |
| SmbCredential credential, |
| const MountConfig& mount_config, |
| int32_t* mount_id) { |
| DCHECK(mount_id); |
| mount_tracker_->AddMount(mount_root, std::move(credential), |
| CreateSambaInterface(mount_config), mount_id); |
| } |
| |
| bool MountManager::RemoveMount(int32_t mount_id) { |
| DCHECK_GE(mount_id, 0); |
| return mount_tracker_->RemoveMount(mount_id); |
| } |
| |
| bool MountManager::GetFullPath(int32_t mount_id, |
| const std::string& entry_path, |
| std::string* full_path) const { |
| DCHECK(full_path); |
| return mount_tracker_->GetFullPath(mount_id, entry_path, full_path); |
| } |
| |
| bool MountManager::GetMetadataCache(int32_t mount_id, |
| MetadataCache** cache) const { |
| DCHECK(cache); |
| return mount_tracker_->GetMetadataCache(mount_id, cache); |
| } |
| |
| std::string MountManager::GetRelativePath(int32_t mount_id, |
| const std::string& full_path) const { |
| return mount_tracker_->GetRelativePath(mount_id, full_path); |
| } |
| |
| bool MountManager::GetSambaInterface(int32_t mount_id, |
| SambaInterface** samba_interface) const { |
| DCHECK(samba_interface); |
| return mount_tracker_->GetSambaInterface(mount_id, samba_interface); |
| } |
| |
| SambaInterface* MountManager::GetSystemSambaInterface() const { |
| return system_samba_interface_.get(); |
| } |
| |
| std::unique_ptr<SambaInterface> MountManager::CreateSambaInterface( |
| const MountConfig& mount_config) { |
| return samba_interface_factory_.Run(this, mount_config); |
| } |
| |
| bool MountManager::GetAuthentication( |
| SambaInterface::SambaInterfaceId samba_interface_id, |
| const std::string& share_path, |
| char* workgroup, |
| int32_t workgroup_length, |
| char* username, |
| int32_t username_length, |
| char* password, |
| int32_t password_length) const { |
| DCHECK_GT(workgroup_length, 0); |
| DCHECK_GT(username_length, 0); |
| DCHECK_GT(password_length, 0); |
| |
| if (!mount_tracker_->IsAlreadyMounted(samba_interface_id)) { |
| LOG(ERROR) << "Credential not found for SambaInterfaceId: " |
| << samba_interface_id; |
| |
| SetBufferEmpty(workgroup); |
| SetBufferEmpty(username); |
| SetBufferEmpty(password); |
| return false; |
| } |
| |
| const SmbCredential& credential = |
| mount_tracker_->GetCredential(samba_interface_id); |
| |
| if (!CanInputCredential(workgroup_length, username_length, password_length, |
| credential)) { |
| LOG(ERROR) << "Buffers cannot support a credential for SambaInterfaceId: " |
| << samba_interface_id; |
| |
| SetBufferEmpty(workgroup); |
| SetBufferEmpty(username); |
| SetBufferEmpty(password); |
| return false; |
| } |
| |
| PopulateCredential(credential, workgroup, username, password); |
| return true; |
| } |
| |
| SambaInterface::SambaInterfaceId MountManager::GetSystemSambaInterfaceId() { |
| return system_samba_interface_->GetSambaInterfaceId(); |
| } |
| |
| bool MountManager::UpdateMountCredential(int32_t mount_id, |
| SmbCredential credential) { |
| return mount_tracker_->UpdateCredential(mount_id, std::move(credential)); |
| } |
| |
| bool MountManager::UpdateSharePath(int32_t mount_id, |
| const std::string& share_path) { |
| return mount_tracker_->UpdateSharePath(mount_id, share_path); |
| } |
| |
| bool MountManager::SavePasswordToFile(int32_t mount_id) { |
| SambaInterface* samba_interface = nullptr; |
| if (!GetSambaInterface(mount_id, &samba_interface)) { |
| LOG(ERROR) << "Unable to find mount id " << mount_id; |
| return false; |
| } |
| |
| const SmbCredential& credential = |
| mount_tracker_->GetCredential(samba_interface->GetSambaInterfaceId()); |
| DCHECK(!credential.password_file.empty()); |
| |
| base::File password_file( |
| credential.password_file, |
| base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |
| if (!password_file.IsValid()) { |
| LOG(ERROR) << "Unable to open password file for write with error: " |
| << password_file.error_details(); |
| return false; |
| } |
| |
| size_t password_length = 0; |
| if (credential.password) { |
| password_length = credential.password->size(); |
| } |
| int written = password_file.WriteAtCurrentPos( |
| reinterpret_cast<const char*>(&password_length), sizeof(password_length)); |
| if (written != sizeof(password_length)) { |
| LOG(ERROR) << "Unable to write password length"; |
| return false; |
| } |
| if (password_length > 0) { |
| written = password_file.WriteAtCurrentPos(credential.password->GetRaw(), |
| password_length); |
| if (written != password_length) { |
| LOG(ERROR) << "Unable to write password"; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool MountManager::ErasePasswordFile(int32_t mount_id) { |
| SambaInterface* samba_interface = nullptr; |
| if (!GetSambaInterface(mount_id, &samba_interface)) { |
| LOG(ERROR) << "Unable to find mount id " << mount_id; |
| return false; |
| } |
| |
| const SmbCredential& credential = |
| mount_tracker_->GetCredential(samba_interface->GetSambaInterfaceId()); |
| if (credential.password_file.empty() || |
| !base::PathExists(credential.password_file)) { |
| // No password file exists. |
| return true; |
| } |
| if (!base::DeleteFile(credential.password_file)) { |
| PLOG(ERROR) << "Unable to erase password file"; |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace smbprovider |