| // Copyright 2019 The Fuchsia 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 "encrypted-volume.h" |
| |
| #include <lib/fdio/fdio.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <stdio.h> |
| #include <zircon/status.h> |
| |
| #include "src/security/zxcrypt/fdio-volume.h" |
| |
| namespace devmgr { |
| |
| EncryptedVolume::EncryptedVolume(fbl::unique_fd fd, fbl::unique_fd devfs_root) |
| : fd_(std::move(fd)), devfs_root_(std::move(devfs_root)) {} |
| |
| zx_status_t EncryptedVolume::Unseal() { |
| zx_status_t rc; |
| std::unique_ptr<zxcrypt::FdioVolume> zxcrypt_volume; |
| rc = zxcrypt::FdioVolume::Init(fd_.duplicate(), devfs_root_.duplicate(), &zxcrypt_volume); |
| if (rc != ZX_OK) { |
| FX_LOGS(ERROR) << "couldn't open zxcrypt fdio volume: " << zx_status_get_string(rc); |
| return rc; |
| } |
| |
| zx::channel zxcrypt_volume_manager_chan; |
| rc = zxcrypt_volume->OpenManager(zx::sec(2), zxcrypt_volume_manager_chan.reset_and_get_address()); |
| if (rc != ZX_OK) { |
| FX_LOGS(ERROR) << "couldn't open zxcrypt manager device: " << zx_status_get_string(rc); |
| return rc; |
| } |
| |
| zxcrypt::FdioVolumeManager zxcrypt_volume_manager(std::move(zxcrypt_volume_manager_chan)); |
| uint8_t slot = 0; |
| rc = zxcrypt_volume_manager.UnsealWithDeviceKey(slot); |
| if (rc != ZX_OK) { |
| FX_LOGS(ERROR) << "couldn't unseal zxcrypt manager device: " << zx_status_get_string(rc); |
| return rc; |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t EncryptedVolume::Format() { |
| zx_status_t rc; |
| rc = zxcrypt::FdioVolume::CreateWithDeviceKey(fd_.duplicate(), devfs_root_.duplicate(), nullptr); |
| if (rc != ZX_OK) { |
| FX_LOGS(ERROR) << "couldn't format zxcrypt volume with device key: " |
| << zx_status_get_string(rc); |
| return rc; |
| } |
| |
| return ZX_OK; |
| } |
| |
| const int kUnsealTryCountBeforeWipe = 5; |
| |
| zx_status_t EncryptedVolumeInterface::EnsureUnsealedAndFormatIfNeeded() { |
| // Policy: first, unseal. If that fails, format, then unseal again. |
| zx_status_t rc; |
| int try_count = 0; |
| do { |
| rc = Unseal(); |
| try_count++; |
| } while (rc != ZX_OK && try_count < kUnsealTryCountBeforeWipe); |
| |
| if (rc == ZX_OK) { |
| // We successfully unsealed the volume. No need to wipe. Return success. |
| return ZX_OK; |
| } |
| |
| // Alas, we could not unseal the volume. Give up. If the error code suggests |
| // we just have the wrong key, try formatting the volume with the keys we |
| // have. Otherwise, just return the error we got from the last Unseal() |
| // attempt. |
| if (rc == ZX_ERR_ACCESS_DENIED) { |
| FX_LOGS(ERROR) << |
| "Failed repeatedly to unseal zxcrypt device with all available keys. " |
| "Destructively reformatting with new key to attempt to bring up an empty block volume " |
| "rather than none at all. Expect factory-reset-like behavior."; |
| rc = Format(); |
| if (rc != ZX_OK) { |
| FX_LOGS(ERROR) << "couldn't format encrypted volume: " << zx_status_get_string(rc); |
| return rc; |
| } |
| |
| // At this point, we had better be able to unseal the volume that we just |
| // formatted. |
| rc = Unseal(); |
| if (rc != ZX_OK) { |
| FX_LOGS(ERROR) << "formatted volume but couldn't unseal it thereafter: " |
| << zx_status_get_string(rc); |
| return rc; |
| } |
| |
| return ZX_OK; |
| } else { |
| FX_LOGS(ERROR) << "could not produce an unsealed volume for minfs: " |
| << zx_status_get_string(rc); |
| return rc; |
| } |
| } |
| |
| } // namespace devmgr |