Home | History | Annotate | Download | only in server
      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