database_impl.cc revision 073ef82ad13d48cdc9c04425a39b0a4c1f2657db
1// 2// Copyright (C) 2015 The Android Open Source Project 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15// 16 17#include "attestation/server/database_impl.h" 18 19#include <fcntl.h> 20#include <sys/stat.h> 21#include <sys/types.h> 22#include <unistd.h> 23 24#include <string> 25 26#include <base/files/file_path.h> 27#include <base/files/file_util.h> 28#include <base/files/important_file_writer.h> 29#include <base/logging.h> 30#include <base/stl_util.h> 31#include <brillo/secure_blob.h> 32 33using base::FilePath; 34 35namespace { 36 37const char kDatabasePath[] = 38 "/mnt/stateful_partition/unencrypted/preserve/attestation.epb"; 39const mode_t kDatabasePermissions = 0600; 40 41// A base::FilePathWatcher::Callback that just relays to |callback|. 42void FileWatcherCallback(const base::Closure& callback, const FilePath&, bool) { 43 callback.Run(); 44} 45 46} // namespace 47 48namespace attestation { 49 50DatabaseImpl::DatabaseImpl(CryptoUtility* crypto) : io_(this), crypto_(crypto) { 51} 52 53DatabaseImpl::~DatabaseImpl() { 54 brillo::SecureMemset(string_as_array(&database_key_), 0, 55 database_key_.size()); 56} 57 58void DatabaseImpl::Initialize() { 59 // Start thread-checking now. 60 thread_checker_.DetachFromThread(); 61 DCHECK(thread_checker_.CalledOnValidThread()); 62 io_->Watch(base::Bind(base::IgnoreResult(&DatabaseImpl::Reload), 63 base::Unretained(this))); 64 if (!Reload()) { 65 LOG(WARNING) << "Creating new attestation database."; 66 } 67} 68 69const AttestationDatabase& DatabaseImpl::GetProtobuf() const { 70 DCHECK(thread_checker_.CalledOnValidThread()); 71 return protobuf_; 72} 73 74AttestationDatabase* DatabaseImpl::GetMutableProtobuf() { 75 DCHECK(thread_checker_.CalledOnValidThread()); 76 return &protobuf_; 77} 78 79bool DatabaseImpl::SaveChanges() { 80 DCHECK(thread_checker_.CalledOnValidThread()); 81 std::string buffer; 82 if (!EncryptProtobuf(&buffer)) { 83 return false; 84 } 85 return io_->Write(buffer); 86} 87 88bool DatabaseImpl::Reload() { 89 DCHECK(thread_checker_.CalledOnValidThread()); 90 LOG(INFO) << "Loading attestation database."; 91 std::string buffer; 92 if (!io_->Read(&buffer)) { 93 return false; 94 } 95 return DecryptProtobuf(buffer); 96} 97 98bool DatabaseImpl::Read(std::string* data) { 99 const int kMask = base::FILE_PERMISSION_OTHERS_MASK; 100 FilePath path(kDatabasePath); 101 int permissions = 0; 102 if (base::GetPosixFilePermissions(path, &permissions) && 103 (permissions & kMask) != 0) { 104 LOG(WARNING) << "Attempting to fix permissions on attestation database."; 105 base::SetPosixFilePermissions(path, permissions & ~kMask); 106 } 107 if (!base::ReadFileToString(path, data)) { 108 PLOG(ERROR) << "Failed to read attestation database"; 109 return false; 110 } 111 return true; 112} 113 114bool DatabaseImpl::Write(const std::string& data) { 115 FilePath file_path(kDatabasePath); 116 if (!base::CreateDirectory(file_path.DirName())) { 117 LOG(ERROR) << "Cannot create directory: " << file_path.DirName().value(); 118 return false; 119 } 120 if (!base::ImportantFileWriter::WriteFileAtomically(file_path, data)) { 121 LOG(ERROR) << "Failed to write file: " << file_path.value(); 122 return false; 123 } 124 if (!base::SetPosixFilePermissions(file_path, kDatabasePermissions)) { 125 LOG(ERROR) << "Failed to set permissions for file: " << file_path.value(); 126 return false; 127 } 128 // Sync the parent directory. 129 std::string dir_name = file_path.DirName().value(); 130 int dir_fd = HANDLE_EINTR(open(dir_name.c_str(), O_RDONLY|O_DIRECTORY)); 131 if (dir_fd < 0) { 132 PLOG(WARNING) << "Could not open " << dir_name << " for syncing"; 133 return false; 134 } 135 // POSIX specifies EINTR as a possible return value of fsync(). 136 int result = HANDLE_EINTR(fsync(dir_fd)); 137 if (result < 0) { 138 PLOG(WARNING) << "Failed to sync " << dir_name; 139 close(dir_fd); 140 return false; 141 } 142 // close() may not be retried on error. 143 result = IGNORE_EINTR(close(dir_fd)); 144 if (result < 0) { 145 PLOG(WARNING) << "Failed to close after sync " << dir_name; 146 return false; 147 } 148 return true; 149} 150 151void DatabaseImpl::Watch(const base::Closure& callback) { 152 if (!file_watcher_) { 153 file_watcher_.reset(new base::FilePathWatcher()); 154 file_watcher_->Watch(FilePath(kDatabasePath), false, 155 base::Bind(&FileWatcherCallback, callback)); 156 } 157} 158 159bool DatabaseImpl::EncryptProtobuf(std::string* encrypted_output) { 160 std::string serial_proto; 161 if (!protobuf_.SerializeToString(&serial_proto)) { 162 LOG(ERROR) << "Failed to serialize db."; 163 return false; 164 } 165 if (database_key_.empty() || sealed_database_key_.empty()) { 166 if (!crypto_->CreateSealedKey(&database_key_, &sealed_database_key_)) { 167 LOG(ERROR) << "Failed to generate database key."; 168 return false; 169 } 170 } 171 if (!crypto_->EncryptData(serial_proto, database_key_, sealed_database_key_, 172 encrypted_output)) { 173 LOG(ERROR) << "Attestation: Failed to encrypt database."; 174 return false; 175 } 176 return true; 177} 178 179bool DatabaseImpl::DecryptProtobuf(const std::string& encrypted_input) { 180 if (!crypto_->UnsealKey(encrypted_input, &database_key_, 181 &sealed_database_key_)) { 182 LOG(ERROR) << "Attestation: Could not unseal decryption key."; 183 return false; 184 } 185 std::string serial_proto; 186 if (!crypto_->DecryptData(encrypted_input, database_key_, &serial_proto)) { 187 LOG(ERROR) << "Attestation: Failed to decrypt database."; 188 return false; 189 } 190 if (!protobuf_.ParseFromString(serial_proto)) { 191 // Previously the DB was encrypted with CryptoLib::AesEncrypt which appends 192 // a SHA-1. This can be safely ignored. 193 const size_t kLegacyJunkSize = 20; 194 if (serial_proto.size() < kLegacyJunkSize || 195 !protobuf_.ParseFromArray(serial_proto.data(), 196 serial_proto.length() - kLegacyJunkSize)) { 197 LOG(ERROR) << "Failed to parse database."; 198 return false; 199 } 200 } 201 return true; 202} 203 204} // namespace attestation 205