| // Copyright 2018 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/kerberos_artifact_synchronizer.h" |
| |
| #include <utility> |
| |
| #include <base/bind.h> |
| #include <base/files/file_path.h> |
| #include <base/files/important_file_writer.h> |
| #include <base/files/file_util.h> |
| #include <dbus/authpolicy/dbus-constants.h> |
| #include <dbus/message.h> |
| |
| namespace smbprovider { |
| |
| KerberosArtifactSynchronizer::KerberosArtifactSynchronizer( |
| const std::string& krb5_conf_path, |
| const std::string& krb5_ccache_path, |
| std::unique_ptr<KerberosArtifactClientInterface> client, |
| bool allow_credentials_update) |
| : krb5_conf_path_(krb5_conf_path), |
| krb5_ccache_path_(krb5_ccache_path), |
| client_(std::move(client)), |
| allow_credentials_update_(allow_credentials_update) {} |
| |
| void KerberosArtifactSynchronizer::SetupKerberos( |
| const std::string& account_identifier, SetupKerberosCallback callback) { |
| if (!allow_credentials_update_) { |
| if (account_identifier.empty()) { |
| LOG(ERROR) << "Kerberos user account identifier is empty"; |
| std::move(callback).Run(false /* success */); |
| return; |
| } |
| if (!account_identifier_.empty() && |
| account_identifier_ != account_identifier) { |
| LOG(ERROR) << "Kerberos is already set up for a different user"; |
| std::move(callback).Run(false /* success */); |
| return; |
| } |
| } |
| |
| if (is_kerberos_setup_ && account_identifier_ == account_identifier) { |
| LOG(WARNING) << "Kerberos already set up the user"; |
| std::move(callback).Run(true /* success */); |
| return; |
| } |
| |
| account_identifier_ = account_identifier; |
| |
| if (account_identifier_.empty()) { |
| if (is_kerberos_setup_) { |
| // Empty account identifier means there is no ticket available. |
| // If Kerberos was already set up, remove existing credential files. |
| RemoveFiles(std::move(callback)); |
| } else { |
| // Credential files were not created yet, so just return with success. |
| std::move(callback).Run(true /* success */); |
| } |
| return; |
| } |
| |
| GetFiles(std::move(callback)); |
| } |
| |
| void KerberosArtifactSynchronizer::GetFiles(SetupKerberosCallback callback) { |
| client_->GetUserKerberosFiles( |
| account_identifier_, |
| base::Bind(&KerberosArtifactSynchronizer::OnGetFilesResponse, |
| base::Unretained(this), std::move(callback))); |
| } |
| |
| void KerberosArtifactSynchronizer::OnGetFilesResponse( |
| SetupKerberosCallback callback, |
| bool success, |
| const std::string& krb5_ccache, |
| const std::string& krb5_conf) { |
| DCHECK(callback); |
| |
| if (!success) { |
| LOG(ERROR) << "KerberosArtifactSynchronizer failed to get Kerberos files"; |
| std::move(callback).Run(false /* setup_success */); |
| return; |
| } |
| |
| WriteFiles(krb5_ccache, krb5_conf, std::move(callback)); |
| } |
| |
| void KerberosArtifactSynchronizer::WriteFiles(const std::string& krb5_ccache, |
| const std::string& krb5_conf, |
| SetupKerberosCallback callback) { |
| DCHECK(callback); |
| |
| bool success = WriteFile(krb5_conf_path_, krb5_conf) && |
| WriteFile(krb5_ccache_path_, krb5_ccache); |
| |
| if (is_kerberos_setup_) { |
| // Signal is already setup. |
| if (!success) { |
| LOG(ERROR) << "KerberosArtifactSynchronizer: failed to write updated " |
| "Kerberos Files"; |
| std::move(callback).Run(false /* setup_success */); |
| return; |
| } |
| // If credentials update is allowed, this happens if we call setup more |
| // than once to update credentials or GetFiles is triggered by |
| // KerberosFilesChanged signal. Otherwise, if credentials update is not |
| // allowed, this is rare case where the browser restarted and |
| // SetupKerberos() was called twice in quick succession. If |
| // |is_kerberos_setup_| is true, then the first call to SetupKerberos() |
| // succeeded, so treat this as a success. |
| std::move(callback).Run(true /* setup_success */); |
| return; |
| } |
| |
| if (!success) { |
| // Failed to write the Kerberos files so return error to caller. |
| LOG(ERROR) << "KerberosArtifactSynchronizer: failed to write initial " |
| "Kerberos Files"; |
| std::move(callback).Run(false /* setup_success */); |
| return; |
| } |
| |
| // Sets is_kerberos_setup_ to true on successful signal connection. |
| ConnectToKerberosFilesChangedSignal(std::move(callback)); |
| } |
| |
| void KerberosArtifactSynchronizer::ConnectToKerberosFilesChangedSignal( |
| SetupKerberosCallback callback) { |
| client_->ConnectToKerberosFilesChangedSignal( |
| base::Bind(&KerberosArtifactSynchronizer::OnKerberosFilesChanged, |
| base::Unretained(this)), |
| base::Bind( |
| &KerberosArtifactSynchronizer::OnKerberosFilesChangedSignalConnected, |
| base::Unretained(this), std::move(callback))); |
| } |
| |
| void KerberosArtifactSynchronizer::OnKerberosFilesChanged( |
| dbus::Signal* signal) { |
| DCHECK(signal); |
| |
| // Call GetFiles with empty callback, since after files are retrieved and |
| // stored there is no further action needed. Normally we would use |
| // base::DoNothing, but it's not working with a parameter. |
| // TODO(tomdobro): switch to base::DoNothing once libchrome is updated. |
| auto files_stored_callback = [](bool /* setup_success */) {}; |
| GetFiles(base::Bind(files_stored_callback)); |
| } |
| |
| void KerberosArtifactSynchronizer::OnKerberosFilesChangedSignalConnected( |
| SetupKerberosCallback callback, |
| const std::string& interface_name, |
| const std::string& signal_name, |
| bool success) { |
| DCHECK(success); |
| |
| if (is_kerberos_setup_) { |
| // If SetupKerberos() was called twice in quick succession (i.e. if the |
| // browser restarted on login), it's possible for this change signal to be |
| // registered twice. The change handler will be run twice, but this |
| // shouldn't be an issue. |
| LOG(ERROR) << "Duplicate Kerberos file change signals registered"; |
| } |
| is_kerberos_setup_ = true; |
| std::move(callback).Run(true /* setup_success */); |
| } |
| |
| bool KerberosArtifactSynchronizer::WriteFile(const std::string& path, |
| const std::string& blob) { |
| const base::FilePath file_path(path); |
| if (!base::ImportantFileWriter::WriteFileAtomically(file_path, blob)) { |
| LOG(ERROR) << "Failed to write file " << file_path.value(); |
| return false; |
| } |
| return true; |
| } |
| |
| void KerberosArtifactSynchronizer::RemoveFiles(SetupKerberosCallback callback) { |
| bool success = RemoveFile(krb5_conf_path_) && RemoveFile(krb5_ccache_path_); |
| LOG_IF(ERROR, !success) |
| << "KerberosArtifactSynchronizer failed to remove Kerberos files"; |
| std::move(callback).Run(success); |
| } |
| |
| bool KerberosArtifactSynchronizer::RemoveFile(const std::string& path) { |
| if (!base::DeleteFile(base::FilePath(path), false /* recursive */)) { |
| LOG(ERROR) << "Failed to delete file " << path; |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace smbprovider |