blob: 6b119b5a3566086e91a2658b87d255c97fbe7084 [file] [log] [blame]
// Copyright 2021 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 "libhwsec-foundation/tpm_error/handle_auth_failure.h"
#include <stddef.h>
#include <sys/wait.h>
#include <algorithm>
#include <string>
#include <vector>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_split.h>
#include <re2/re2.h>
#include "libhwsec-foundation/da_reset/da_resetter.h"
#include "libhwsec-foundation/tpm_error/auth_failure_analysis.h"
#include "libhwsec-foundation/tpm_error/tpm_error_data.h"
#include "libhwsec-foundation/tpm_error/tpm_error_uma_reporter.h"
namespace {
constexpr int64_t kLogMaxSize = 20'000;
constexpr int64_t kLogRemainingSize = 10'000;
char lastError[256] = {'\0'};
base::FilePath logFile;
base::FilePath permanentLogFile;
// Set the error in order to let consumer, e.g tcsd, fetch the error by
// FetchAuthFailureError().
void SetLastError(const std::string& msg) {
std::string error_msg = msg + ": " + strerror(errno);
strncpy(lastError, error_msg.c_str(), sizeof(lastError) - 1);
}
// Append |msg| to |log_path|, and limit the size of log to |kLogMaxSize|;
bool AppendMessage(const base::FilePath& log_path, const std::string& msg) {
if (!base::PathExists(log_path)) {
return base::WriteFile(log_path, msg);
}
if (!base::AppendToFile(log_path, msg)) {
return false;
}
int64_t file_size;
if (!base::GetFileSize(log_path, &file_size)) {
return false;
}
if (file_size >= kLogMaxSize) {
std::string contents;
if (!base::ReadFileToString(log_path, &contents)) {
return false;
}
// Truncate log size to |kLogRemainingSize|.
int64_t truncate_size = (int64_t)contents.size() - kLogRemainingSize;
contents.erase(0, truncate_size);
return base::WriteFile(log_path, contents);
}
return true;
}
// Handle any log message in this file, and send them to |logFile| and
// |permanentLogFile| which is set by InitializeAuthFailureLogging().
bool LogMessageHandler(int severity,
const char* file,
int line,
size_t message_start,
const std::string& str) {
// Skip if the message is not genenrated by this file.
if (strncmp(file, __FILE__, sizeof(__FILE__)) != 0) {
return false;
}
if (!AppendMessage(logFile, str) || !AppendMessage(permanentLogFile, str)) {
SetLastError("error logging");
}
return severity != logging::LOGGING_FATAL;
}
// This will log command to the file set by InitializeAuthFailureLogging().
void LogAuthFailureCommand(const struct TpmErrorData& data) {
LOG(WARNING) << "auth failure: command " << data.command << ", response "
<< data.response;
}
constexpr LazyRE2 auth_failure_command = {
R"(auth failure: command (\d+), response (\d+))"};
uint32_t GetCommandHash(const base::FilePath& log_path) {
std::string contents;
if (!base::ReadFileToString(log_path, &contents)) {
return 0;
}
auto lines = base::SplitString(contents, "\n", base::KEEP_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
// Parse TpmErrorData from auth failure log.
std::vector<struct TpmErrorData> data_set;
for (const std::string& line : lines) {
struct TpmErrorData data;
if (!RE2::PartialMatch(line, *auth_failure_command, &data.command,
&data.response)) {
continue;
}
data_set.push_back(data);
}
// Uniquify collcection of TpmErrorData.
std::sort(data_set.begin(), data_set.end());
auto it = std::unique(data_set.begin(), data_set.end());
data_set.resize(std::distance(data_set.begin(), it));
return GetHashFromTpmDataSet(data_set);
}
} // namespace
extern "C" int FetchAuthFailureError(char out[], size_t size) {
if (size <= 1)
return 0;
if (lastError[0] == '\0')
return 0;
strncpy(out, lastError, size - 1);
out[size - 1] = '\0';
lastError[0] = '\0';
return 1;
}
extern "C" void InitializeAuthFailureLogging(const char* log_path,
const char* permanent_log_path) {
CHECK(logging::GetLogMessageHandler() == nullptr)
<< "LogMessageHandler has already been set";
logFile = base::FilePath(log_path);
permanentLogFile = base::FilePath(permanent_log_path);
logging::SetLogMessageHandler(LogMessageHandler);
}
extern "C" int CheckAuthFailureHistory(const char* current_path,
const char* previous_path,
size_t* auth_failure_hash) {
base::FilePath current_log(current_path);
base::FilePath previous_log(previous_path);
if (!base::PathExists(current_log)) {
return 0;
}
int64_t size;
if (!base::GetFileSize(current_log, &size)) {
SetLastError("error checking file size");
return 0;
}
// If there is no failure log in |current_log|, nothing to do here.
if (size == 0) {
return 0;
}
if (!base::Move(current_log, previous_log)) {
SetLastError("error moving file");
return 0;
}
if (auth_failure_hash) {
*auth_failure_hash = GetCommandHash(previous_log);
}
return 1;
}
extern "C" int HandleAuthFailure(const struct TpmErrorData* data) {
if (!hwsec_foundation::DoesCauseDAIncrease(*data)) {
return true;
}
LogAuthFailureCommand(*data);
hwsec_foundation::TpmErrorUmaReporter reporter;
reporter.Report(*data);
hwsec_foundation::DAResetter resetter;
return resetter.ResetDictionaryAttackLock();
}