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 33 using base::FilePath; 34 35 namespace { 36 37 const char kDatabasePath[] = 38 "/mnt/stateful_partition/unencrypted/preserve/attestation.epb"; 39 const mode_t kDatabasePermissions = 0600; 40 41 // A base::FilePathWatcher::Callback that just relays to |callback|. 42 void FileWatcherCallback(const base::Closure& callback, const FilePath&, bool) { 43 callback.Run(); 44 } 45 46 } // namespace 47 48 namespace attestation { 49 50 DatabaseImpl::DatabaseImpl(CryptoUtility* crypto) : io_(this), crypto_(crypto) { 51 } 52 53 DatabaseImpl::~DatabaseImpl() { 54 brillo::SecureMemset(string_as_array(&database_key_), 0, 55 database_key_.size()); 56 } 57 58 void 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 69 const AttestationDatabase& DatabaseImpl::GetProtobuf() const { 70 DCHECK(thread_checker_.CalledOnValidThread()); 71 return protobuf_; 72 } 73 74 AttestationDatabase* DatabaseImpl::GetMutableProtobuf() { 75 DCHECK(thread_checker_.CalledOnValidThread()); 76 return &protobuf_; 77 } 78 79 bool 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 88 bool 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 98 bool 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 114 bool 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 151 void 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 159 bool 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 179 bool 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