cryptohome: Add ecryptfs container

Add ecryptfs container to encapsulate key management for
ecryptfs mounts. EcryptfsContainer manages the addition
and clearing of EcryptfsAuthTokens which are used as arguments
to mount() for setting up ecryptfs mounts.

BUG=b:172344853
TEST=unittests

Change-Id: I0e1292eff3129c5f138346890a41c557a10e5c5f
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2525833
Reviewed-by: Leo Lai <cylai@google.com>
Reviewed-by: Daniil Lunev <dlunev@chromium.org>
Tested-by: Sarthak Kukreti <sarthakkukreti@chromium.org>
Commit-Queue: Sarthak Kukreti <sarthakkukreti@chromium.org>
diff --git a/cryptohome/BUILD.gn b/cryptohome/BUILD.gn
index 5145791..2e41bba 100644
--- a/cryptohome/BUILD.gn
+++ b/cryptohome/BUILD.gn
@@ -467,6 +467,7 @@
       "storage/arc_disk_quota_unittest.cc",
       "storage/disk_cleanup_routines_unittest.cc",
       "storage/disk_cleanup_unittest.cc",
+      "storage/encrypted_container/ecryptfs_container_test.cc",
       "storage/encrypted_container/fscrypt_container_test.cc",
       "storage/homedirs_unittest.cc",
       "storage/mock_disk_cleanup.cc",
diff --git a/cryptohome/libs/BUILD.gn b/cryptohome/libs/BUILD.gn
index 63e18d5..0fee3b0 100644
--- a/cryptohome/libs/BUILD.gn
+++ b/cryptohome/libs/BUILD.gn
@@ -144,6 +144,7 @@
     "../dircrypto_util.cc",
     "../libscrypt_compat.cc",
     "../platform.cc",
+    "../storage/encrypted_container/ecryptfs_container.cc",
     "../storage/encrypted_container/encrypted_container.cc",
     "../storage/encrypted_container/fscrypt_container.cc",
   ]
diff --git a/cryptohome/storage/encrypted_container/ecryptfs_container.cc b/cryptohome/storage/encrypted_container/ecryptfs_container.cc
new file mode 100644
index 0000000..e4db9b1
--- /dev/null
+++ b/cryptohome/storage/encrypted_container/ecryptfs_container.cc
@@ -0,0 +1,64 @@
+// Copyright 2020 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 "cryptohome/storage/encrypted_container/ecryptfs_container.h"
+
+#include <base/files/file_path.h>
+
+#include "cryptohome/cryptolib.h"
+#include "cryptohome/platform.h"
+#include "cryptohome/storage/encrypted_container/filesystem_key.h"
+
+namespace cryptohome {
+
+EcryptfsContainer::EcryptfsContainer(
+    const base::FilePath& backing_dir,
+    const FileSystemKeyReference& key_reference,
+    Platform* platform)
+    : backing_dir_(backing_dir),
+      key_reference_(key_reference),
+      platform_(platform) {}
+
+bool EcryptfsContainer::Purge() {
+  return platform_->DeletePathRecursively(backing_dir_);
+}
+
+bool EcryptfsContainer::Setup(const FileSystemKey& encryption_key,
+                              bool create) {
+  if (create) {
+    if (!platform_->CreateDirectory(backing_dir_)) {
+      LOG(ERROR) << "Failed to create backing directory";
+      return false;
+    }
+  }
+
+  // Add the File Encryption key (FEK) from the vault keyset.  This is the key
+  // that is used to encrypt the file contents when the file is persisted to the
+  // lower filesystem by eCryptfs.
+  auto key_signature = CryptoLib::SecureBlobToHex(key_reference_.fek_sig);
+  if (!platform_->AddEcryptfsAuthToken(encryption_key.fek, key_signature,
+                                       encryption_key.fek_salt)) {
+    LOG(ERROR) << "Couldn't add eCryptfs file encryption key to keyring.";
+    return false;
+  }
+
+  // Add the File Name Encryption Key (FNEK) from the vault keyset.  This is the
+  // key that is used to encrypt the file name when the file is persisted to the
+  // lower filesystem by eCryptfs.
+  auto filename_key_signature =
+      CryptoLib::SecureBlobToHex(key_reference_.fnek_sig);
+  if (!platform_->AddEcryptfsAuthToken(encryption_key.fnek,
+                                       filename_key_signature,
+                                       encryption_key.fnek_salt)) {
+    LOG(ERROR) << "Couldn't add eCryptfs filename encryption key to keyring.";
+    return false;
+  }
+  return true;
+}
+
+bool EcryptfsContainer::Teardown() {
+  return platform_->ClearUserKeyring();
+}
+
+}  // namespace cryptohome
diff --git a/cryptohome/storage/encrypted_container/ecryptfs_container.h b/cryptohome/storage/encrypted_container/ecryptfs_container.h
new file mode 100644
index 0000000..afb71bc
--- /dev/null
+++ b/cryptohome/storage/encrypted_container/ecryptfs_container.h
@@ -0,0 +1,41 @@
+// Copyright 2020 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.
+
+#ifndef CRYPTOHOME_STORAGE_ENCRYPTED_CONTAINER_ECRYPTFS_CONTAINER_H_
+#define CRYPTOHOME_STORAGE_ENCRYPTED_CONTAINER_ECRYPTFS_CONTAINER_H_
+
+#include "cryptohome/storage/encrypted_container/encrypted_container.h"
+
+#include <base/files/file_path.h>
+
+#include "cryptohome/platform.h"
+#include "cryptohome/storage/encrypted_container/filesystem_key.h"
+
+namespace cryptohome {
+
+// `EcryptfsContainer` is a file-level encrypted container which uses eCryptFs
+// to encrypted the `backing_dir_`.
+class EcryptfsContainer : public EncryptedContainer {
+ public:
+  EcryptfsContainer(const base::FilePath& backing_dir,
+                    const FileSystemKeyReference& key_reference,
+                    Platform* platform);
+  ~EcryptfsContainer() = default;
+
+  bool Setup(const FileSystemKey& encryption_key, bool create) override;
+  bool Teardown() override;
+  bool Purge() override;
+  EncryptedContainerType GetType() override {
+    return EncryptedContainerType::kEcryptfs;
+  }
+
+ private:
+  const base::FilePath backing_dir_;
+  const FileSystemKeyReference key_reference_;
+  Platform* platform_;
+};
+
+}  // namespace cryptohome
+
+#endif  // CRYPTOHOME_STORAGE_ENCRYPTED_CONTAINER_ECRYPTFS_CONTAINER_H_
diff --git a/cryptohome/storage/encrypted_container/ecryptfs_container_test.cc b/cryptohome/storage/encrypted_container/ecryptfs_container_test.cc
new file mode 100644
index 0000000..7420c3e
--- /dev/null
+++ b/cryptohome/storage/encrypted_container/ecryptfs_container_test.cc
@@ -0,0 +1,85 @@
+// Copyright 2020 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 "cryptohome/storage/encrypted_container/ecryptfs_container.h"
+
+#include <memory>
+
+#include <base/files/file_path.h>
+#include <brillo/secure_blob.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "cryptohome/mock_platform.h"
+#include "cryptohome/storage/encrypted_container/filesystem_key.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace cryptohome {
+
+class EcryptfsContainerTest : public ::testing::Test {
+ public:
+  EcryptfsContainerTest()
+      : backing_dir_(base::FilePath("/a/b/c")),
+        key_reference_({.fek_sig = brillo::SecureBlob("random_keysig"),
+                        .fnek_sig = brillo::SecureBlob("random_fnek_sig")}),
+        key_({.fek = brillo::SecureBlob("random key"),
+              .fnek = brillo::SecureBlob("random_fnek"),
+              .fek_salt = brillo::SecureBlob("random_fek_salt"),
+              .fnek_salt = brillo::SecureBlob("random_fnek_salt")}),
+        container_(
+            EncryptedContainer::Generate(EncryptedContainerType::kEcryptfs,
+                                         backing_dir_,
+                                         key_reference_,
+                                         &platform_)) {}
+  ~EcryptfsContainerTest() override = default;
+
+ protected:
+  base::FilePath backing_dir_;
+  FileSystemKeyReference key_reference_;
+  FileSystemKey key_;
+  MockPlatform platform_;
+  std::unique_ptr<EncryptedContainer> container_;
+};
+
+// Tests the creation path for an eCryptFs container.
+TEST_F(EcryptfsContainerTest, SetupCreateCheck) {
+  EXPECT_CALL(platform_,
+              AddEcryptfsAuthToken(_, testing::MatchesRegex("[0-9a-z]*"), _))
+      .Times(2)
+      .WillRepeatedly(Return(true));
+
+  EXPECT_TRUE(container_->Setup(key_, true));
+  EXPECT_TRUE(platform_.DirectoryExists(backing_dir_));
+}
+
+// Tests the setup path for an existing eCryptFs container.
+TEST_F(EcryptfsContainerTest, SetupNoCreateCheck) {
+  EXPECT_CALL(platform_,
+              AddEcryptfsAuthToken(_, testing::MatchesRegex("[0-9a-z]*"), _))
+      .Times(2)
+      .WillRepeatedly(Return(true));
+
+  EXPECT_TRUE(container_->Setup(key_, false));
+}
+
+// Tests the failure path on failing to add the eCryptFs auth token to the
+// user keyring.
+TEST_F(EcryptfsContainerTest, SetupFailedEncryptionKeyAdd) {
+  EXPECT_CALL(platform_,
+              AddEcryptfsAuthToken(_, testing::MatchesRegex("[0-9a-z]*"), _))
+      .WillOnce(Return(false));
+
+  EXPECT_FALSE(container_->Setup(key_, false));
+}
+
+// Tests the failure path on failing to invalidate the user keyring.
+TEST_F(EcryptfsContainerTest, TeardownInvalidateKey) {
+  EXPECT_CALL(platform_, ClearUserKeyring()).WillOnce(Return(true));
+
+  EXPECT_TRUE(container_->Teardown());
+}
+
+}  // namespace cryptohome
diff --git a/cryptohome/storage/encrypted_container/encrypted_container.cc b/cryptohome/storage/encrypted_container/encrypted_container.cc
index 4437d39..3d7de77 100644
--- a/cryptohome/storage/encrypted_container/encrypted_container.cc
+++ b/cryptohome/storage/encrypted_container/encrypted_container.cc
@@ -9,6 +9,7 @@
 #include <base/files/file_path.h>
 
 #include "cryptohome/platform.h"
+#include "cryptohome/storage/encrypted_container/ecryptfs_container.h"
 #include "cryptohome/storage/encrypted_container/filesystem_key.h"
 #include "cryptohome/storage/encrypted_container/fscrypt_container.h"
 
@@ -24,6 +25,9 @@
     case EncryptedContainerType::kFscrypt:
       return std::make_unique<FscryptContainer>(backing_dir, key_reference,
                                                 platform);
+    case EncryptedContainerType::kEcryptfs:
+      return std::make_unique<EcryptfsContainer>(backing_dir, key_reference,
+                                                 platform);
     default:
       return nullptr;
   }
diff --git a/cryptohome/storage/encrypted_container/encrypted_container.h b/cryptohome/storage/encrypted_container/encrypted_container.h
index bd4347e..9d5be85 100644
--- a/cryptohome/storage/encrypted_container/encrypted_container.h
+++ b/cryptohome/storage/encrypted_container/encrypted_container.h
@@ -18,6 +18,7 @@
 enum class EncryptedContainerType {
   kUnknown = 0,
   kFscrypt,
+  kEcryptfs,
 };
 
 // An encrypted container is an abstract class that represents an encrypted