Big refactor for citadel installer

This commit is contained in:
Bruce Leidl 2018-12-31 18:27:17 -05:00
parent 109f007e33
commit 4099f19f4b
34 changed files with 2247 additions and 1957 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
**/target
**/*.rs.bk

View File

@ -6,11 +6,6 @@ dependencies = [
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arrayref"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "atty"
version = "0.2.11"
@ -47,25 +42,6 @@ name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "block-buffer"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "byte-tools"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cc"
version = "1.0.25"
@ -77,7 +53,7 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "citadel-mkimage"
name = "citadel-image"
version = "0.1.0"
dependencies = [
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -102,56 +78,6 @@ dependencies = [
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clear_on_drop"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "curve25519-dalek"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
"subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "digest"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ed25519-dalek"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"curve25519-dalek 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure"
version = "0.1.3"
@ -172,33 +98,6 @@ dependencies = [
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "generic-array"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "1.2.0"
@ -213,17 +112,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "libcitadel"
version = "0.1.0"
dependencies = [
"ed25519-dalek 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ring 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -254,91 +152,6 @@ dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_chacha"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand_hc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_pcg"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_xorshift"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "redox_syscall"
version = "0.1.43"
@ -352,6 +165,17 @@ dependencies = [
"redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ring"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-demangle"
version = "0.1.9"
@ -362,27 +186,6 @@ name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.82"
@ -398,27 +201,11 @@ dependencies = [
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sha2"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "strsim"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "subtle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.15.22"
@ -466,11 +253,6 @@ dependencies = [
"serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "typenum"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-width"
version = "0.1.5"
@ -481,6 +263,11 @@ name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "untrusted"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
version = "0.8.1"
@ -512,62 +299,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a"
"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab"
"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum curve25519-dalek 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3eacf6ff1b911e3170a8c400b402e10c86dc3cb166bd69034ebbc2b785fea4c2"
"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90"
"checksum ed25519-dalek 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cd66d8a16ef71c23cf5eeb2140d8d3cd293457c6c7fd6804b593397a933fcf1e"
"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7"
"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
"checksum nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "921f61dc817b379d0834e45d5ec45beaacfae97082090a49c2cf30dcbc30206f"
"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09"
"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c"
"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
"checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a"
"checksum rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a"
"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372"
"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05"
"checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3"
"checksum redox_syscall 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "679da7508e9a6390aeaf7fbd02a800fdc64b73fe2204dd2c8ae66d22d9d5ad5d"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum ring 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe642b9dd1ba0038d78c4a3999d1ee56178b4d415c1e1fbaba83b06dce012f0"
"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "6fa52f19aee12441d5ad11c9a00459122bd8f98707cadf9778c540674f1935b6"
"checksum serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "96a7f9496ac65a2db5929afa087b54f8fc5008dcfbe48a8874ed20049b0d6154"
"checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
"checksum syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)" = "ae8b29eb5210bc5cf63ed6149cbf9adfc82ac0be023d8735c176ee74a2db4da7"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"

View File

@ -1,5 +1,5 @@
[package]
name = "citadel-mkimage"
name = "citadel-image"
version = "0.1.0"
authors = ["Bruce Leidl <bruce@subgraph.com>"]
homepage = "https://github.com/subgraph/citadel"

View File

@ -1,17 +1,16 @@
use std::path::PathBuf;
use std::fs::OpenOptions;
use std::path::Path;
use std::fs::{self,File};
use std::io::{self,Write};
use failure::ResultExt;
use libcitadel::Result;
use libcitadel::{Result,ImageHeader,util,verity};
use BuildConfig;
use util;
pub struct UpdateBuilder {
config: BuildConfig,
target: PathBuf,
nblocks: Option<usize>,
shasum: Option<String>,
@ -28,18 +27,24 @@ fn align(sz: usize, n: usize) -> usize {
impl UpdateBuilder {
pub fn new(config: BuildConfig) -> Result<UpdateBuilder> {
let builder = UpdateBuilder {
config,
pub fn new(config: BuildConfig) -> UpdateBuilder {
let filename = UpdateBuilder::target_filename(&config);
let target = config.workdir_path(&filename);
UpdateBuilder {
config, target,
nblocks: None, shasum: None, verity_salt: None,
verity_root: None,
};
}
}
builder.copy_source_image()?;
Ok(builder)
fn target_filename(config: &BuildConfig) -> String {
format!("citadel-{}-{}-{:03}", config.img_name(), config.channel(), config.version())
}
pub fn build(&mut self) -> Result<()> {
info!("Copying source file to {}", self.target.display());
fs::copy(self.config.source(), &self.target)?;
self.pad_image()
.context("failed writing padding to image")?;
@ -55,27 +60,12 @@ impl UpdateBuilder {
Ok(())
}
fn target(&self, ext: Option<&str>) -> PathBuf {
match ext {
Some(s) => self.config.workdir_path(&format!("citadel-{}-{}-{:03}.{}", self.config.img_name(), self.config.channel(), self.config.version(), s)),
None => self.config.workdir_path(&format!("citadel-{}-{}-{:03}", self.config.img_name(), self.config.channel(), self.config.version()))
}
}
fn copy_source_image(&self) -> Result<()> {
sanity_check_source(self.config.source())?;
let target = self.target(None);
info!("Copying source file to {}", target.display());
let mut from = File::open(self.config.source())?;
let mut to = File::create(&target)?;
io::copy(&mut from, &mut to)?;
Ok(())
}
fn pad_image(&mut self) -> Result<()> {
let target = self.target(None);
let meta = target.metadata()?;
let meta = self.target.metadata()?;
let len = meta.len() as usize;
if len % 512 != 0 {
bail!("Image file size is not a multiple of sector size (512 bytes)");
}
let padlen = align(len, BLOCK_SIZE) - len;
if padlen > 0 {
@ -83,7 +73,7 @@ impl UpdateBuilder {
let zeros = vec![0u8; padlen];
let mut file = OpenOptions::new()
.append(true)
.open(&target)?;
.open(&self.target)?;
file.write_all(&zeros)?;
}
@ -95,7 +85,7 @@ impl UpdateBuilder {
}
fn calculate_shasum(&mut self) -> Result<()> {
let shasum = util::sha256(self.target(None))?;
let shasum = util::sha256(&self.target)?;
info!("Sha256 of image data is {}", shasum);
self.shasum = Some(shasum);
Ok(())
@ -105,7 +95,7 @@ impl UpdateBuilder {
let hashfile = self.config.workdir_path(&format!("verity-hash-{}-{:03}", self.config.image_type(), self.config.version()));
let outfile = self.config.workdir_path("verity-format.out");
let verity = util::verity_initial_hashtree(self.target(None), &hashfile)?;
let verity = verity::generate_initial_hashtree(&self.target, &hashfile)?;
fs::write(outfile, verity.output())
@ -132,59 +122,33 @@ impl UpdateBuilder {
fn compress_image(&self) -> Result<()> {
info!("Compressing image data");
util::xz_compress(self.target(None))
util::xz_compress(&self.target)
}
fn write_final_image(&self) -> Result<()> {
let header = self.generate_header()?;
let mut path = self.target(None);
let fname = path.file_name().unwrap().to_str().unwrap().to_owned();
path.set_file_name(fname + ".img");
let filename = format!("{}.img", UpdateBuilder::target_filename(&self.config));
let mut image_path = self.config.workdir_path(&filename);
let mut out = File::create(&path)
.context(format!("could not open output file {}", path.display()))?;
let mut out = File::create(&image_path)
.context(format!("could not open output file {}", image_path.display()))?;
out.write_all(&header)?;
header.write_header(&out)?;
path.set_extension("xz");
let mut data = File::open(&path)
.context(format!("could not open compressed image data file {}", path.display()))?;
image_path.set_extension("xz");
let mut data = File::open(&image_path)
.context(format!("could not open compressed image data file {}", image_path.display()))?;
io::copy(&mut data, &mut out)
.context("error copying image data to output file")?;
Ok(())
}
///
/// The Image Header structure is stored in a 4096 byte block at the start of
/// every resource image file. When an image is installed to a partition it
/// is stored at the last 4096 byte block of the block device for the partition.
///
/// The layout of this structure is the following:
///
/// field size (bytes) offset
/// ----- ------------ ------
///
/// magic 4 0
/// status 1 4
/// flags 1 5
/// length 2 6
///
/// metainfo <length> 8
///
/// signature 64 8 + length
///
fn generate_header(&self) -> Result<Vec<u8>> {
let mut hdr = vec![0u8; BLOCK_SIZE];
hdr[0..4].copy_from_slice(b"SGOS");
hdr[5] = 0x04; // FLAG_DATA_COMPRESSED
fn generate_header(&self) -> Result<ImageHeader> {
let hdr = ImageHeader::new();
hdr.set_flag(ImageHeader::FLAG_DATA_COMPRESSED);
let metainfo = self.generate_metainfo();
let metalen = metainfo.len();
hdr[6] = (metalen >> 8) as u8;
hdr[7] = metalen as u8;
hdr[8..8+metalen].copy_from_slice(&metainfo);
hdr.set_metainfo_bytes(&metainfo);
Ok(hdr)
}
@ -208,20 +172,3 @@ impl UpdateBuilder {
Ok(v)
}
}
fn sanity_check_source<P: AsRef<Path>>(src: P) -> Result<()> {
let src: &Path = src.as_ref();
let meta = match src.metadata() {
Ok(md) => md,
Err(e) => bail!("Could not load image file {}: {}", src.display(), e),
};
if !meta.file_type().is_file() {
bail!("Image file {} exists but is not a regular file", src.display());
}
if meta.len() % 512 != 0 {
bail!("Image file size is not a multiple of sector size (512 bytes)");
}
Ok(())
}

View File

@ -1,13 +1,11 @@
use std::path::{Path,PathBuf};
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use toml;
use libcitadel::Result;
#[derive(Deserialize)]
pub struct BuildConfig {
#[serde(rename = "image-type")]
@ -64,7 +62,10 @@ impl BuildConfig {
};
let src = Path::new(&self.source);
if !src.is_file() {
bail!("Source path '{}' does not exist or is not a regular file", src.display());
bail!(
"Source path '{}' does not exist or is not a regular file",
src.display()
);
}
if self.image_type == "modules" && self.kernel_version.is_none() {
bail!("Cannot build 'modules' image without kernel-version field");

173
citadel-image/src/main.rs Normal file
View File

@ -0,0 +1,173 @@
#[macro_use] extern crate libcitadel;
#[macro_use] extern crate failure;
#[macro_use] extern crate serde_derive;
extern crate clap;
extern crate toml;
use std::process::exit;
use std::path::Path;
use clap::{App,Arg,SubCommand,ArgMatches};
use clap::AppSettings::*;
use build::UpdateBuilder;
use config::BuildConfig;
use libcitadel::{Result,ResourceImage,set_verbose,format_error,Partition,KeyPair};
mod build;
mod config;
fn main() {
let app = App::new("citadel-image")
.about("Citadel update image builder")
.settings(&[ArgRequiredElseHelp,ColoredHelp, DisableHelpSubcommand, DisableVersion, DeriveDisplayOrder])
.subcommand(SubCommand::with_name("build")
.about("Build an update image specified by a configuration file")
.arg(Arg::with_name("build-file")
.required(true)
.help("Path to image build config file")))
.subcommand(SubCommand::with_name("metainfo")
.about("Display metainfo variables for an image file")
.arg(Arg::with_name("path")
.required(true)
.help("Path to image file")))
.subcommand(SubCommand::with_name("generate-verity")
.about("Generate dm-verity hash tree for an image file")
.arg(Arg::with_name("path")
.required(true)
.help("Path to image file")))
.subcommand(SubCommand::with_name("verify")
.about("Verify dm-verity hash tree for an image file")
.arg(Arg::with_name("path")
.required(true)
.help("Path to image file")))
.subcommand(SubCommand::with_name("install-rootfs")
.about("Install rootfs image file to a partition")
.arg(Arg::with_name("path")
.required(true)
.help("Path to image file")))
.subcommand(SubCommand::with_name("genkeys")
.about("Generate a pair of keys"))
.subcommand(SubCommand::with_name("decompress")
.about("Decompress a compressed image file")
.arg(Arg::with_name("path")
.required(true)
.help("Path to image file")));
set_verbose(true);
let matches = app.get_matches();
let result = match matches.subcommand() {
("build", Some(m)) => build_image(m),
("metainfo", Some(m)) => metainfo(m),
("generate-verity", Some(m)) => generate_verity(m),
("verify", Some(m)) => verify(m),
("sign-image", Some(m)) => sign_image(m),
("genkeys", Some(_)) => genkeys(),
("decompress", Some(m)) => decompress(m),
("install-rootfs", Some(m)) => install_rootfs(m),
_ => Ok(()),
};
if let Err(ref e) = result {
println!("Error: {}", format_error(e));
exit(1);
}
}
fn build_image(arg_matches: &ArgMatches) -> Result<()> {
let build_file = arg_matches.value_of("build-file").unwrap();
let config = BuildConfig::load(build_file)?;
let mut builder = UpdateBuilder::new(config);
builder.build()?;
Ok(())
}
fn metainfo(arg_matches: &ArgMatches) -> Result<()> {
let img = load_image(arg_matches)?;
print!("{}",String::from_utf8(img.header().metainfo_bytes())?);
Ok(())
}
fn generate_verity(arg_matches: &ArgMatches) -> Result<()> {
let img = load_image(arg_matches)?;
if img.has_verity_hashtree() {
info!("Image already has dm-verity hashtree appended, doing nothing.");
} else {
img.generate_verity_hashtree()?;
}
Ok(())
}
fn verify(arg_matches: &ArgMatches) -> Result<()> {
let img = load_image(arg_matches)?;
let ok = img.verify_verity()?;
if ok {
info!("Image verification succeeded");
} else {
warn!("Image verification FAILED!");
}
Ok(())
}
fn load_image(arg_matches: &ArgMatches) -> Result<ResourceImage> {
let path = arg_matches.value_of("path").expect("path argument missing");
if !Path::new(path).exists() {
bail!("Cannot load image {}: File does not exist", path);
}
let img = ResourceImage::from_path(path)?;
if !img.is_valid_image() {
bail!("File {} is not a valid image file", path);
}
Ok(img)
}
fn install_rootfs(arg_matches: &ArgMatches) -> Result<()> {
let img = load_image(arg_matches)?;
let partition =choose_install_partition()?;
img.write_to_partition(&partition)?;
Ok(())
}
fn sign_image(arg_matches: &ArgMatches) -> Result<()> {
let _img = load_image(arg_matches)?;
info!("Not implemented yet");
Ok(())
}
fn genkeys() -> Result<()> {
let keypair = KeyPair::generate()?;
println!("public-key = \"{}\"", keypair.public_key_hex());
println!("private-key = \"{}\"", keypair.private_key_hex());
Ok(())
}
fn decompress(arg_matches: &ArgMatches) -> Result<()> {
let img = load_image(arg_matches)?;
if !img.is_compressed() {
info!("Image is not compressed, not decompressing.");
} else {
img.decompress()?;
}
Ok(())
}
fn choose_install_partition() -> Result<Partition> {
let partitions = Partition::rootfs_partitions()?;
for p in &partitions {
if !p.is_mounted() && !p.is_initialized() {
return Ok(p.clone())
}
}
for p in &partitions {
if !p.is_mounted() {
return Ok(p.clone())
}
}
Err(format_err!("No suitable install partition found"))
}

188
citadel-install/Cargo.lock generated Normal file
View File

@ -0,0 +1,188 @@
[[package]]
name = "autocfg"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "backtrace"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "citadel-install"
version = "0.1.0"
dependencies = [
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"rpassword 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure_derive"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libc"
version = "0.2.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.4.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rpassword"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-demangle"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.15.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synstructure"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727"
"checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5"
"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6"
"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7"
"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09"
"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c"
"checksum rpassword 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37473170aedbe66ffa3ad3726939ba677d83c646ad4fd99e5b4bc38712f45ec"
"checksum rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "01b90379b8664dd83460d59bdc5dd1fd3172b8913788db483ed1325171eab2f7"
"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -0,0 +1,10 @@
[package]
name = "citadel-install"
version = "0.1.0"
authors = ["Bruce Leidl <bruce@subgraph.com>"]
homepage = "http://github.com/subgraph/citadel"
[dependencies]
failure = "0.1.3"
libc = "0.2"
rpassword = "2.1.0"

150
citadel-install/src/cli.rs Normal file
View File

@ -0,0 +1,150 @@
use std::io::{self,Write};
use std::path::Path;
use Result;
use util::Disk;
use rpassword;
use installer::Installer;
pub fn run_cli_install() -> Result<bool> {
let disk = match choose_disk()? {
Some(disk) => disk,
None => return Ok(false),
};
display_disk(&disk);
let passphrase = match read_passphrase()? {
Some(passphrase) => passphrase,
None => return Ok(false),
};
if !confirm_install(&disk)? {
return Ok(false);
}
run_install(disk, passphrase)?;
Ok(true)
}
pub fn run_cli_install_with<P: AsRef<Path>>(target: P) -> Result<bool> {
let disk = find_disk_by_path(target.as_ref())?;
display_disk(&disk);
let passphrase = match read_passphrase()? {
Some(passphrase) => passphrase,
None => return Ok(false),
};
if !confirm_install(&disk)? {
return Ok(false);
}
run_install(disk, passphrase)?;
Ok(true)
}
fn run_install(disk: Disk, passphrase: String) -> Result<()> {
let mut install = Installer::new();
install.set_target(disk.path().to_str().unwrap());
install.set_passphrase(&passphrase);
install.set_install_syslinux(true);
install.verify()?;
install.run()
}
fn display_disk(disk: &Disk) {
println!();
println!(" Device: {}", disk.path().display());
println!(" Size: {}", disk.size_str());
println!(" Model: {}", disk.model());
println!();
}
fn find_disk_by_path(path: &Path) -> Result<Disk> {
if !path.exists() {
bail!("Target disk path {} does not exist", path.display());
}
for disk in Disk::probe_all()? {
if disk.path() == path {
return Ok(disk.clone());
}
}
Err(format_err!("Installation target {} is not a valid disk", path.display()))
}
fn choose_disk() -> Result<Option<Disk>> {
let disks = Disk::probe_all()?;
if disks.is_empty() {
bail!("No disks found.");
}
loop {
prompt_choose_disk(&disks)?;
let line = read_line()?;
if line == "q" || line == "Q" {
return Ok(None);
}
if let Ok(n) = line.parse::<usize>() {
if n > 0 && n <= disks.len() {
return Ok(Some(disks[n-1].clone()));
}
}
}
}
fn prompt_choose_disk(disks: &[Disk]) -> Result<()> {
println!("Available disks:\n");
for idx in 0..disks.len() {
println!(" [{}]: {} Size: {} Model: {}", idx + 1, disks[idx].path().display(), disks[idx].size_str(), disks[idx].model());
}
print!("\nChoose a disk to install to (q to quit): ");
io::stdout().flush()?;
Ok(())
}
fn read_line() -> Result<String> {
let mut input = String::new();
io::stdin().read_line(&mut input)?;
if input.ends_with('\n') {
input.pop();
}
Ok(input)
}
fn read_passphrase() -> Result<Option<String>> {
loop {
println!("Enter a disk encryption passphrase (or 'q' to quit)");
println!();
let passphrase = rpassword::read_password_from_tty(Some(" Passphrase : "))?;
if passphrase.is_empty() {
println!("Passphrase cannot be empty");
continue;
}
if passphrase == "q" || passphrase == "Q" {
return Ok(None);
}
let confirm = rpassword::read_password_from_tty(Some(" Confirm : "))?;
if confirm == "q" || confirm == "Q" {
return Ok(None);
}
println!();
if passphrase == confirm {
return Ok(Some(passphrase));
}
println!("Passphrases do not match");
println!();
}
}
fn confirm_install(disk: &Disk) -> Result<bool> {
println!("Are you sure you want to completely erase this this device?");
println!();
println!(" Device: {}", disk.path().display());
println!(" Size: {}", disk.size_str());
println!(" Model: {}", disk.model());
println!();
print!("Type YES (uppercase) to continue with install: ");
io::stdout().flush()?;
let answer = read_line()?;
Ok(answer == "YES")
}

View File

@ -1,10 +1,8 @@
use std::path::{Path, PathBuf};
use std::fs;
use {Result,PathExt};
// Partition Type GUID of UEFI boot (ESP) partition
const ESP_GUID: &str = "c12a7328-f81f-11d2-ba4b-00a0c93ec93b";
use Result;
use util;
///
/// Represents a disk partition device on the system
@ -20,13 +18,15 @@ pub struct DiskPartition {
}
impl DiskPartition {
/// Return list of all UEFI ESP partitions on the system as a `Vec<DiskPartition>`
/// Return list of all vfat partitions on the system as a `Vec<DiskPartition>`
pub fn boot_partitions() -> Result<Vec<DiskPartition>> {
let pp = fs::read_to_string("/proc/partitions")?;
let mut v = Vec::new();
for line in Path::new("/proc/partitions").read_as_lines()?.iter().skip(2) {
for line in pp.lines().skip(2)
{
let part = DiskPartition::from_proc_line(&line)
.map_err(|e| format_err!("Failed to parse line '{}': {}", line, e))?;
if part.is_esp() {
if part.is_vfat()? {
v.push(part);
}
}
@ -48,52 +48,39 @@ impl DiskPartition {
v[0].parse::<u8>()?, // Major
v[1].parse::<u8>()?, // Minor
v[2].parse::<usize>()?, // number of blocks
v[3])) // device name
v[3],
)) // device name
}
// create a new `DiskPartion` from parsed components of line from /proc/partitions
fn from_line_components(major: u8, minor: u8, blocks: usize, name: &str) -> DiskPartition {
DiskPartition {
path: PathBuf::from("/dev").join(name),
major, minor, blocks,
major,
minor,
blocks,
}
}
// return `true` if partition has UEFI ESP partition type
fn is_esp(&self) -> bool {
match self.path.partition_type_guid() {
Ok(guid) => guid == ESP_GUID,
Err(err) => {
warn!("Error: {}", err);
false
},
}
// return `true` if partition is VFAT type
fn is_vfat(&self) -> Result<bool> {
let ok = self.partition_fstype()? == "vfat";
Ok(ok)
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn mount<P: AsRef<Path>>(&self, target: P) -> bool {
match self.path.mount(target) {
Err(e) => {
warn!("{}", e);
false
},
Ok(()) => true,
}
pub fn mount<P: AsRef<Path>>(&self, target: P) -> Result<()> {
util::exec_cmdline("/usr/bin/mount", format!("{} {}", self.path.display(), target.as_ref().display()))
}
pub fn umount(&self) -> bool {
match self.path.umount() {
Err(e) => {
warn!("{}", e);
false
},
Ok(()) => true,
}
}
pub fn umount(&self) -> Result<()> {
util::exec_cmdline("/usr/bin/umount", self.path().to_str().unwrap())
}
fn partition_fstype(&self) -> Result<String> {
util::exec_cmdline_with_output("/usr/bin/lsblk", format!("-dno FSTYPE {}", self.path().display()))
}
}

View File

@ -0,0 +1,445 @@
use std::cell::RefCell;
use std::fs::{self,File};
use std::io::Write;
use std::os::unix::fs as unixfs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::Instant;
use super::util;
use super::Result;
const BLKDEACTIVATE: &str = "/sbin/blkdeactivate";
const CRYPTSETUP: &str = "/sbin/cryptsetup";
const PARTED: &str = "/sbin/parted";
const EXTLINUX: &str = "/sbin/extlinux";
const PVCREATE: &str = "/sbin/pvcreate";
const VGCREATE: &str = "/sbin/vgcreate";
const LVCREATE: &str = "/sbin/lvcreate";
const VGCHANGE: &str = "/sbin/vgchange";
const MKFS_VFAT: &str = "/sbin/mkfs.vfat";
const MKFS_BTRFS: &str = "/bin/mkfs.btrfs";
const LSBLK: &str = "/bin/lsblk";
const BTRFS: &str = "/bin/btrfs";
const MOUNT: &str = "/bin/mount";
const UMOUNT: &str = "/bin/umount";
const CHOWN: &str = "/bin/chown";
const TAR: &str = "/bin/tar";
const XZ: &str = "/bin/xz";
const DD: &str = "/bin/dd";
const CITADEL_IMAGE: &str = "/usr/bin/citadel-image";
const LUKS_UUID: &str = "683a17fc-4457-42cc-a946-cde67195a101";
const EXTRA_IMAGE_NAME: &str = "citadel-extra.img";
const INSTALL_MOUNT: &str = "/run/installer/mnt";
const LUKS_PASSPHRASE_FILE: &str = "/run/installer/luks-passphrase";
const DEFAULT_ARTIFACT_DIRECTORY: &str = "/run/images";
const KERNEL_CMDLINE: &str = "add_efi_memmap intel_iommu=off cryptomgr.notests rcupdate.rcu_expedited=1 rcu_nocbs=0-64 tsc=reliable no_timer_check noreplace-smp i915.fastboot=1 citadel.nosignatures quiet splash";
pub struct Installer {
install_syslinux: bool,
target_device: String,
passphrase: String,
artifact_directory: String,
logfile: Option<RefCell<File>>,
}
impl Installer {
pub fn new() -> Installer {
Installer {
install_syslinux: true,
target_device: String::new(),
passphrase: String::new(),
artifact_directory: DEFAULT_ARTIFACT_DIRECTORY.to_string(),
logfile: None,
}
}
pub fn set_target(&mut self, target: &str) {
self.target_device = target.to_owned()
}
pub fn set_passphrase(&mut self, passphrase: &str) {
self.passphrase = passphrase.to_owned()
}
pub fn set_install_syslinux(&mut self, val: bool) {
self.install_syslinux = val;
}
pub fn verify(&self) -> Result<()> {
let tools = vec![
BLKDEACTIVATE,CRYPTSETUP,PARTED,EXTLINUX,PVCREATE,VGCREATE,LVCREATE,VGCHANGE,
MKFS_VFAT,MKFS_BTRFS,LSBLK,BTRFS,MOUNT,UMOUNT,CHOWN,TAR,XZ,CITADEL_IMAGE,
];
let modules_img = self.modules_imagename();
let artifacts = vec![
"bootx64.efi", "bzImage",
modules_img.as_str(), EXTRA_IMAGE_NAME,
];
if self.target_device.is_empty() {
bail!("No target device set in install configuration");
}
if self.passphrase.is_empty() {
bail!("No passphrase set in install configuration");
}
if !Path::new(&self.target_device).exists() {
bail!("Target device {} does not exist", self.target_device);
}
for tool in tools {
if !Path::new(tool).exists() {
bail!("Required installer utility program does not exist: {}", tool);
}
}
for a in artifacts {
if !self.artifact_path(a).exists() {
bail!("Required install artifact {} does not exist in {}", a, self.artifact_directory);
}
}
if !self.artifact_path("appimg-rootfs.tar").exists() && !self.artifact_path("appimg-rootfs.tar.xz").exists() {
bail!("Required component appimg-rootfs.tar(.xz) does not exist in {}",self.artifact_directory);
}
Ok(())
}
pub fn run(&self) -> Result<()> {
let start = Instant::now();
fs::create_dir_all(INSTALL_MOUNT)?;
self.partition_disk()?;
self.setup_luks()?;
self.setup_lvm()?;
self.setup_boot()?;
self.create_storage()?;
self.output("\n")?;
self.header("Installing rootfs partitions\n")?;
let args = format!("install-rootfs {}", self.artifact_path("citadel-rootfs.img").display());
self.cmd(CITADEL_IMAGE, &args)?;
self.cmd(CITADEL_IMAGE, &args)?;
self.cmd(LSBLK, format!("-o NAME,SIZE,TYPE,FSTYPE {}", &self.target_device))?;
self.cmd(VGCHANGE, "-an citadel")?;
self.cmd(CRYPTSETUP, "luksClose luks-install")?;
self.header(format!("Install completed successfully in {} seconds", start.elapsed().as_secs()))?;
Ok(())
}
pub fn live_setup(&self) -> Result<()> {
self.cmd(MOUNT, "-t tmpfs var-tmpfs /sysroot/var")?;
self.cmd(MOUNT, "-t tmpfs home-tmpfs /sysroot/home")?;
let _ = fs::read("/sys/class/zram-control/hot_add")?;
// Create an 8gb zram disk to use as /storage partition
fs::write("/sys/block/zram1/comp_algorithm", "lz4")?;
fs::write("/sys/block/zram1/disksize", "8G")?;
self.cmd(MKFS_BTRFS, "/dev/zram1")?;
self.cmd(MOUNT, "/dev/zram1 /sysroot/storage")?;
fs::create_dir_all("/sysroot/storage/realms")?;
self.cmd(MOUNT, "--bind /sysroot/storage/realms /sysroot/realms")?;
let cmdline = fs::read_to_string("/proc/cmdline")?;
if cmdline.contains("citadel.live") {
self.setup_storage(Path::new("/sysroot/storage"), false)?;
}
Ok(())
}
fn partition_disk(&self) -> Result<()> {
self.header("Partitioning target disk")?;
self.cmd(BLKDEACTIVATE, &self.target_device)?;
self.parted("mklabel gpt")?;
self.parted("mkpart boot fat32 1MiB 513MiB")?;
self.parted("set 1 boot on")?;
self.parted("mkpart data ext4 513MiB 100%")?;
self.parted("set 2 lvm on")?;
Ok(())
}
fn parted(&self, cmdline: &str) -> Result<()> {
let args = format!("-s {} {}", self.target_device, cmdline);
self.cmd(PARTED, args)
}
fn setup_luks(&self) -> Result<()> {
self.header("Setting up LUKS disk encryption")?;
fs::write(LUKS_PASSPHRASE_FILE, self.passphrase.as_bytes())?;
let luks_partition = self.target_partition(2);
let args = format!(
"-q --uuid={} luksFormat {} {}",
LUKS_UUID, luks_partition, LUKS_PASSPHRASE_FILE
);
self.cmd(CRYPTSETUP, args)?;
let args = format!(
"open --type luks --key-file {} {} luks-install",
LUKS_PASSPHRASE_FILE, luks_partition
);
self.cmd(CRYPTSETUP, args)?;
fs::remove_file(LUKS_PASSPHRASE_FILE)?;
Ok(())
}
fn setup_lvm(&self) -> Result<()> {
self.header("Setting up LVM volumes")?;
self.cmd(PVCREATE, "-ff --yes /dev/mapper/luks-install")?;
self.cmd(VGCREATE, "--yes citadel /dev/mapper/luks-install")?;
self.cmd(LVCREATE, "--yes --size 2g --name rootfsA citadel")?;
self.cmd(LVCREATE, "--yes --size 2g --name rootfsB citadel")?;
self.cmd(LVCREATE, "--yes --extents 100%VG --name storage citadel")?;
Ok(())
}
fn setup_boot(&self) -> Result<()> {
self.header("Setting up /boot partition")?;
let boot_partition = self.target_partition(1);
self.cmd(MKFS_VFAT, format!("-F 32 {}", boot_partition))?;
self.cmd(MOUNT, format!("{} {}", boot_partition, INSTALL_MOUNT))?;
fs::create_dir_all(format!("{}/loader/entries", INSTALL_MOUNT))?;
self.info("Writing /boot/loader/loader.conf")?;
fs::write(format!("{}/loader/loader.conf", INSTALL_MOUNT), self.loader_conf())?;
self.info("Writing /boot/entries/citadel.conf")?;
fs::write(format!("{}/loader/entries/citadel.conf", INSTALL_MOUNT), self.boot_conf())?;
self.copy_artifact("bzImage", INSTALL_MOUNT)?;
self.copy_artifact("bootx64.efi", format!("{}/EFI/BOOT", INSTALL_MOUNT))?;
if self.install_syslinux {
self.setup_syslinux()?;
}
self.cmd(UMOUNT, INSTALL_MOUNT)?;
if self.install_syslinux {
self.setup_syslinux_post_umount()?;
}
Ok(())
}
fn loader_conf(&self) -> Vec<u8> {
let mut v = Vec::new();
writeln!(&mut v, "default citadel").unwrap();
writeln!(&mut v, "timeout 5").unwrap();
v
}
fn boot_conf(&self) -> Vec<u8> {
let mut v = Vec::new();
writeln!(&mut v, "title Subgraph OS (Citadel)").unwrap();
writeln!(&mut v, "linux /bzImage").unwrap();
writeln!(&mut v, "options root=/dev/mapper/rootfs {}", KERNEL_CMDLINE).unwrap();
v
}
fn setup_syslinux(&self) -> Result<()> {
self.header("Installing syslinux")?;
let syslinux_src = self.artifact_path("syslinux");
if !syslinux_src.exists() {
bail!("No syslinux directory found in artifact directory, cannot install syslinux");
}
let dst = Path::new(INSTALL_MOUNT).join("syslinux");
fs::create_dir_all(&dst)?;
self.info("Copying syslinux files to /boot/syslinux")?;
for entry in fs::read_dir(&syslinux_src)? {
let entry = entry?;
fs::copy(entry.path(), dst.join(entry.file_name()))?;
}
self.info("Writing syslinux.cfg")?;
fs::write(dst.join("syslinux.cfg"), self.syslinux_conf())?;
self.cmd(EXTLINUX, format!("--install {}", dst.display()))?;
Ok(())
}
fn setup_syslinux_post_umount(&self) -> Result<()> {
let mbrbin = self.artifact_path("syslinux/gptmbr.bin");
if !mbrbin.exists() {
bail!("Could not find MBR image: {}", mbrbin.display());
}
let args = format!("bs=440 count=1 conv=notrunc if={} of={}", mbrbin.display(), self.target_device);
self.cmd(DD, args)?;
self.parted("set 1 legacy_boot on")?;
Ok(())
}
fn syslinux_conf(&self) -> Vec<u8> {
let mut v = Vec::new();
writeln!(&mut v, "UI menu.c32").unwrap();
writeln!(&mut v, "PROMPT 0").unwrap();
writeln!(&mut v, "").unwrap();
writeln!(&mut v, "MENU TITLE Boot Subgraph OS (Citadel)").unwrap();
writeln!(&mut v, "TIMEOUT 50").unwrap();
writeln!(&mut v, "DEFAULT subgraph").unwrap();
writeln!(&mut v, "").unwrap();
writeln!(&mut v, "LABEL subgraph").unwrap();
writeln!(&mut v, " MENU LABEL Subgraph OS").unwrap();
writeln!(&mut v, " LINUX ../bzImage").unwrap();
writeln!(&mut v, " APPEND root=/dev/mapper/rootfs {}", KERNEL_CMDLINE).unwrap();
v
}
fn create_storage(&self) -> Result<()> {
self.header("Setting up /storage partition")?;
self.cmd(MKFS_BTRFS, "/dev/mapper/citadel-storage")?;
self.cmd(MOUNT, format!("/dev/mapper/citadel-storage {}", INSTALL_MOUNT))?;
self.setup_storage(Path::new(INSTALL_MOUNT), true)?;
self.cmd(UMOUNT, INSTALL_MOUNT)?;
Ok(())
}
fn setup_storage(&self, base: &Path, copy_resources: bool) -> Result<()> {
if copy_resources {
self.setup_storage_resources(base)?;
}
self.setup_base_appimg(base)?;
self.setup_main_realm(base)?;
self.info("Creating /Shared realms directory")?;
fs::create_dir_all(base.join("realms/Shared"))?;
self.cmd(CHOWN, format!("1000:1000 {}/realms/Shared", base.display()))?;
Ok(())
}
fn setup_base_appimg(&self, base: &Path) -> Result<()> {
self.header("Unpacking appimg rootfs")?;
let appimg_dir = base.join("appimg");
fs::create_dir_all(&appimg_dir)?;
self.cmd(BTRFS, format!("subvolume create {}/base.appimg", appimg_dir.display()))?;
let xz_rootfs = self.artifact_path("appimg-rootfs.tar.xz");
if xz_rootfs.exists() {
self.cmd(XZ, format!("-d {}", xz_rootfs.display()))?;
}
let rootfs_bundle = self.artifact_path("appimg-rootfs.tar");
self.cmd(TAR, format!("-C {}/base.appimg -xf {}", appimg_dir.display(), rootfs_bundle.display()))?;
Ok(())
}
fn setup_main_realm(&self, base: &Path) -> Result<()> {
self.header("Creating main realm")?;
let realm = base.join("realms/realm-main");
let home = realm.join("home");
self.info("Creating home directory /realms/realm-main/home")?;
fs::create_dir_all(&home)?;
self.cmd(BTRFS, format!("subvolume snapshot {}/appimg/base.appimg {}/rootfs",
base.display(), realm.display()))?;
self.info("Copying .bashrc and .profile into home diectory")?;
fs::copy(realm.join("rootfs/home/user/.bashrc"), home.join(".bashrc"))?;
fs::copy(realm.join("rootfs/home/user/.profile"), home.join(".profile"))?;
self.cmd(CHOWN, format!("-R 1000:1000 {}", home.display()))?;
self.info("Creating default.realm symlink")?;
unixfs::symlink("realm-main", base.join("realms/default.realm"))?;
Ok(())
}
fn setup_storage_resources(&self, base: &Path) -> Result<()> {
let channel = util::read_rootfs_channel()?;
let resources = base.join("resources").join(channel);
fs::create_dir_all(&resources)?;
self.copy_artifact(EXTRA_IMAGE_NAME, &resources)?;
let modules = self.modules_imagename();
self.copy_artifact(&modules, &resources)?;
Ok(())
}
fn modules_imagename(&self) -> String {
let utsname = util::uname();
let v = utsname.release().split("-").collect::<Vec<_>>();
format!("citadel-modules-{}.img", v[0])
}
fn target_partition(&self, num: usize) -> String {
format!("{}{}", self.target_device, num)
}
fn artifact_path(&self, filename: &str) -> PathBuf {
Path::new(&self.artifact_directory).join(filename)
}
fn copy_artifact<P: AsRef<Path>>(&self, filename: &str, target: P) -> Result<()> {
self.info(format!("Copying {} to {}", filename, target.as_ref().display()))?;
let src = self.artifact_path(filename);
let target = target.as_ref();
if !target.exists() {
fs::create_dir_all(target)?;
}
let dst = target.join(filename);
fs::copy(src, dst)?;
Ok(())
}
fn header<S: AsRef<str>>(&self, s: S) -> Result<()> {
self.output(format!("\n[+] {}\n", s.as_ref()))
}
fn info<S: AsRef<str>>(&self, s: S) -> Result<()> {
self.output(format!(" [>] {}", s.as_ref()))
}
fn output<S: AsRef<str>>(&self, s: S) -> Result<()> {
println!("{}", s.as_ref());
if let Some(ref file) = self.logfile {
writeln!(file.borrow_mut(), "{}", s.as_ref())?;
}
Ok(())
}
fn cmd<S: AsRef<str>>(&self, cmd_path: &str, args: S) -> Result<()> {
self.output(format!(" # {} {}", cmd_path, args.as_ref()))?;
let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>();
let result = Command::new(cmd_path)
.args(args)
.output()?;
if !result.status.success() {
match result.status.code() {
Some(code) => bail!("command {} failed with exit code: {}", cmd_path, code),
None => bail!("command {} failed with no exit code", cmd_path),
}
}
for line in String::from_utf8_lossy(&result.stdout).lines() {
self.output(format!(" {}", line))?;
}
for line in String::from_utf8_lossy(&result.stderr).lines() {
self.output(format!("! {}", line))?;
}
Ok(())
}
}

150
citadel-install/src/main.rs Normal file
View File

@ -0,0 +1,150 @@
#[macro_use] extern crate failure;
extern crate libc;
extern crate rpassword;
mod installer;
mod cli;
mod util;
mod disks;
use std::result;
use std::path::Path;
use std::env;
use std::thread;
use std::time;
use std::fs;
use std::process::exit;
use failure::Error;
pub type Result<T> = result::Result<T,Error>;
fn main() {
let mut args = env::args();
args.next();
let result = match args.next() {
Some(ref s) if s == "live-setup" => live_setup(),
Some(ref s) if s == "copy-artifacts" => copy_artifacts(),
Some(ref s) => cli_install_to(s),
None => cli_install(),
};
if let Err(ref e) = result {
println!("Failed: {}", format_error(e));
exit(1);
}
}
pub fn format_error(err: &Error) -> String {
let mut output = err.to_string();
let mut prev = err.as_fail();
while let Some(next) = prev.cause() {
output.push_str(": ");
output.push_str(&next.to_string());
prev = next;
}
output
}
fn live_setup() -> Result<()> {
if !Path::new("/etc/initrd-release").exists() {
bail!("Not running in initramfs, cannot do live-setup");
}
let installer = installer::Installer::new();
installer.live_setup()
}
fn copy_artifacts() -> Result<()> {
for _ in 0..3 {
if try_copy_artifacts()? {
return Ok(())
}
// Try again after waiting for more devices to be discovered
println!("Failed to find partition with images, trying again in 2 seconds");
thread::sleep(time::Duration::from_secs(2));
}
Err(format_err!("Could not find partition containing resource images"))
}
fn try_copy_artifacts() -> Result<bool> {
let rootfs_image = Path::new("/boot/images/citadel-rootfs.img");
// Already mounted?
if rootfs_image.exists() {
deploy_artifacts()?;
return Ok(true);
}
for part in disks::DiskPartition::boot_partitions()? {
part.mount("/boot")?;
if rootfs_image.exists() {
deploy_artifacts()?;
part.umount()?;
return Ok(true);
}
part.umount()?;
}
Ok(false)
}
fn deploy_artifacts() -> Result<()> {
let run_images = Path::new("/run/images");
if !run_images.exists() {
fs::create_dir_all(run_images)?;
util::exec_cmdline("/bin/mount", "-t tmpfs -o size=4g images /run/images")?;
}
for entry in fs::read_dir("/boot/images")? {
let entry = entry?;
println!("Copying {:?} from /boot/images to /run/images", entry.file_name());
fs::copy(entry.path(), run_images.join(entry.file_name()))?;
}
println!("Copying bzImage to /run/images");
fs::copy("/boot/bzImage", "/run/images/bzImage")?;
println!("Copying bootx64.efi to /run/images");
fs::copy("/boot/EFI/BOOT/bootx64.efi", "/run/images/bootx64.efi")?;
deploy_syslinux_artifacts()?;
Ok(())
}
fn deploy_syslinux_artifacts() -> Result<()> {
let boot_syslinux = Path::new("/boot/syslinux");
if !boot_syslinux.exists() {
println!("Not copying syslinux components because /boot/syslinux does not exist");
return Ok(());
}
println!("Copying contents of /boot/syslinux to /run/images/syslinux");
let run_images_syslinux = Path::new("/run/images/syslinux");
fs::create_dir_all(run_images_syslinux)?;
for entry in fs::read_dir(boot_syslinux)? {
let entry = entry?;
if let Some(ext) = entry.path().extension() {
if ext == "c32" || ext == "bin" {
fs::copy(entry.path(), run_images_syslinux.join(entry.file_name()))?;
}
}
}
Ok(())
}
fn cli_install() -> Result<()> {
let ok = cli::run_cli_install()?;
if !ok {
println!("Install cancelled...");
}
Ok(())
}
fn cli_install_to(target: &str) -> Result<()> {
let ok = cli::run_cli_install_with(target)?;
if !ok {
println!("Install cancelled...");
}
Ok(())
}

157
citadel-install/src/util.rs Normal file
View File

@ -0,0 +1,157 @@
use std::mem;
use std::ffi::CStr;
use std::str::from_utf8_unchecked;
use std::path::{Path,PathBuf};
use std::process::{Command,ExitStatus,Stdio};
use std::fs;
use libc::{self, c_char};
use failure::ResultExt;
use super::Result;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct UtsName(libc::utsname);
#[allow(dead_code)]
impl UtsName {
pub fn sysname(&self) -> &str {
to_str(&(&self.0.sysname as *const c_char ) as *const *const c_char)
}
pub fn nodename(&self) -> &str {
to_str(&(&self.0.nodename as *const c_char ) as *const *const c_char)
}
pub fn release(&self) -> &str {
to_str(&(&self.0.release as *const c_char ) as *const *const c_char)
}
pub fn version(&self) -> &str {
to_str(&(&self.0.version as *const c_char ) as *const *const c_char)
}
pub fn machine(&self) -> &str {
to_str(&(&self.0.machine as *const c_char ) as *const *const c_char)
}
}
pub fn uname() -> UtsName {
unsafe {
let mut ret: UtsName = mem::uninitialized();
libc::uname(&mut ret.0);
ret
}
}
#[inline]
fn to_str<'a>(s: *const *const c_char) -> &'a str {
unsafe {
let res = CStr::from_ptr(*s).to_bytes();
from_utf8_unchecked(res)
}
}
#[derive(Debug, Clone)]
pub struct Disk {
path: PathBuf,
size: usize,
size_str: String,
model: String,
}
impl Disk {
pub fn probe_all() -> Result<Vec<Disk>> {
let mut v = Vec::new();
for entry in fs::read_dir("/sys/block")? {
let path = entry?.path();
if Disk::is_disk_device(&path) {
let disk = Disk::read_device(&path)?;
v.push(disk);
}
}
Ok(v)
}
fn is_disk_device(device: &Path) -> bool {
device.join("device/model").exists()
}
fn read_device(device: &Path) -> Result<Disk> {
let path = Path::new("/dev/").join(device.file_name().unwrap());
let size = fs::read_to_string(device.join("size"))?
.trim()
.parse::<usize>()?;
let size_str = format!("{}G", size >> 21);
let model = fs::read_to_string(device.join("device/model"))?
.trim()
.to_string();
Ok(Disk { path, size, size_str, model })
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn size_str(&self) -> &str {
&self.size_str
}
pub fn model(&self) -> &str {
&self.model
}
}
pub fn read_rootfs_channel() -> Result<String> {
let s = fs::read_to_string("/etc/citadel-channel")
.context("Failed to open /etc/citadel-channel")?;
match s.split_whitespace().next() {
Some(s) => Ok(s.to_owned()),
None => Err(format_err!("Failed to parse /etc/citadel-channel contents")),
}
}
pub fn exec_cmdline<S: AsRef<str>>(cmd_path: &str, args: S) -> Result<()> {
let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>();
let status = Command::new(cmd_path)
.args(args)
.stderr(Stdio::inherit())
.status()?;
if !status.success() {
match status.code() {
Some(code) => bail!("command {} failed with exit code: {}", cmd_path, code),
None => bail!("command {} failed with no exit code", cmd_path),
}
}
Ok(())
}
pub fn exec_cmdline_with_output<S: AsRef<str>>(cmd_path: &str, args: S) -> Result<String> {
let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>();
let res = Command::new(cmd_path)
.args(args)
.stderr(Stdio::inherit())
.output()
.context(format!("unable to execute {}", cmd_path))?;
check_cmd_status(cmd_path, &res.status)?;
Ok(String::from_utf8(res.stdout).unwrap().trim().to_owned())
}
fn check_cmd_status(cmd_path: &str, status: &ExitStatus) -> Result<()> {
if !status.success() {
match status.code() {
Some(code) => bail!("command {} failed with exit code: {}", cmd_path, code),
None => bail!("command {} failed with no exit code", cmd_path),
}
}
Ok(())
}

View File

@ -1,69 +0,0 @@
#[macro_use] extern crate libcitadel;
#[macro_use] extern crate failure;
#[macro_use] extern crate serde_derive;
extern crate clap;
extern crate toml;
use std::process::exit;
use clap::{App,Arg,SubCommand,ArgMatches};
use clap::AppSettings::*;
use failure::Error;
use build::UpdateBuilder;
use config::BuildConfig;
use libcitadel::{Result,set_verbose};
mod build;
mod config;
mod util;
fn main() {
let app = App::new("citadel-mkimage")
.about("Citadel update image builder")
.settings(&[ArgRequiredElseHelp,ColoredHelp, DisableHelpSubcommand, DisableVersion, DeriveDisplayOrder])
.subcommand(SubCommand::with_name("build")
.about("Build an update image specified by a configuration file")
.arg(Arg::with_name("build-file")
.required(true)
.help("Path to image build config file")));
set_verbose(true);
let matches = app.get_matches();
let result = match matches.subcommand() {
("build", Some(m)) => build_image(m),
_ => Ok(()),
};
if let Err(ref e) = result {
println!("Error: {}", format_error(e));
exit(1);
}
}
fn format_error(err: &Error) -> String {
let mut output = err.to_string();
let mut prev = err.as_fail();
while let Some(next) = prev.cause() {
output.push_str(": ");
output.push_str(&next.to_string());
prev = next;
}
output
}
fn build_image(arg_matches: &ArgMatches) -> Result<()> {
let build_file = arg_matches.value_of("build-file").unwrap();
let config = BuildConfig::load(build_file)?;
let mut builder = UpdateBuilder::new(config)?;
builder.build()?;
Ok(())
}

View File

@ -1,110 +0,0 @@
use std::process::{Command,Stdio};
use std::path::Path;
use std::collections::HashMap;
use failure::ResultExt;
use libcitadel::Result;
pub fn sha256<P: AsRef<Path>>(path: P) -> Result<String> {
let output = exec_command_with_output("/usr/bin/sha256sum", &[pathstr(path.as_ref())])
.context(format!("failed to calculate sha256 on {}", path.as_ref().display()))?;
let v: Vec<&str> = output.split_whitespace().collect();
Ok(v[0].trim().to_owned())
}
pub fn xz_compress<P: AsRef<Path>>(path: P) -> Result<()> {
exec_command("/usr/bin/xz", &["-T0", pathstr(path.as_ref())])
.context(format!("failed to compress {}", path.as_ref().display()))?;
Ok( ())
}
pub fn verity_initial_hashtree<P: AsRef<Path>, Q: AsRef<Path>>(path: P, hashfile: Q) -> Result<VerityOutput> {
let output = exec_command_with_output("/usr/sbin/veritysetup",
&["format",pathstr(path.as_ref()), pathstr(hashfile.as_ref())])
.context("veritysetup format command failed")?;
Ok(VerityOutput::parse(&output))
}
fn pathstr(path: &Path) -> &str {
path.to_str().unwrap()
}
fn exec_command(cmd_path: &str, args: &[&str]) -> Result<()> {
let status = Command::new(cmd_path)
.args(args)
.stderr(Stdio::inherit())
.status()
.context(format!("unable to execute {}", cmd_path))?;
if !status.success() {
match status.code() {
Some(code) => bail!("command {} failed with exit code: {}", cmd_path, code),
None => bail!("command {} failed with no exit code", cmd_path),
}
}
Ok(())
}
fn exec_command_with_output(cmd_path: &str, args: &[&str]) -> Result<String> {
let res = Command::new(cmd_path)
.args(args)
.stderr(Stdio::inherit())
.output()
.context(format!("unable to execute {}", cmd_path))?;
if !res.status.success() {
match res.status.code() {
Some(code) => bail!("command {} failed with exit code: {}", cmd_path, code),
None => bail!("command {} failed with no exit code", cmd_path),
}
}
Ok(String::from_utf8(res.stdout).unwrap().trim().to_owned())
}
/// The output from the `veritysetup format` command can be parsed as key/value
/// pairs. This class parses the output and stores it in a map for querying.
pub struct VerityOutput {
output: String,
map: HashMap<String,String>,
}
impl VerityOutput {
/// Parse the string `output` as standard output from the dm-verity
/// `veritysetup format` command.
fn parse(output: &str) -> VerityOutput {
let mut vo = VerityOutput {
output: output.to_owned(),
map: HashMap::new(),
};
for line in output.lines() {
vo.parse_line(line);
}
vo
}
fn parse_line(&mut self, line: &str) {
let v = line.split(':')
.map(|s| s.trim())
.collect::<Vec<_>>();
if v.len() == 2 {
self.map.insert(v[0].to_owned(), v[1].to_owned());
}
}
pub fn root_hash(&self) -> Option<&str> {
self.map.get("Root hash").map(|s| s.as_str())
}
pub fn salt(&self) -> Option<&str> {
self.map.get("Salt").map(|s| s.as_str())
}
pub fn output(&self) -> &str {
&self.output
}
}

294
citadel-mount/Cargo.lock generated
View File

@ -1,23 +1,24 @@
[[package]]
name = "arrayref"
version = "0.3.5"
name = "autocfg"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "backtrace"
version = "0.3.12"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)",
"autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace-sys 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.24"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
@ -29,25 +30,6 @@ name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "block-buffer"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "byte-tools"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cc"
version = "1.0.26"
@ -67,62 +49,12 @@ dependencies = [
"libcitadel 0.1.0",
]
[[package]]
name = "clear_on_drop"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "curve25519-dalek"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
"subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "digest"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ed25519-dalek"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"curve25519-dalek 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -133,37 +65,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "generic-array"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "1.2.0"
@ -178,17 +83,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "libcitadel"
version = "0.1.0"
dependencies = [
"ed25519-dalek 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ring 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -220,93 +124,19 @@ dependencies = [
]
[[package]]
name = "rand"
version = "0.5.5"
name = "ring"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_chacha"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand_hc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_pcg"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_xorshift"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-demangle"
version = "0.1.9"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -314,27 +144,6 @@ name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.82"
@ -347,28 +156,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sha2"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "subtle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.15.22"
version = "0.15.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
@ -383,7 +176,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -396,13 +189,13 @@ dependencies = [
]
[[package]]
name = "typenum"
version = "1.10.0"
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.1.0"
name = "untrusted"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -430,54 +223,29 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
"checksum backtrace 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eff3830839471718ef8522b9025b399bfb713e25bc220da721364efb660d7d"
"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0"
"checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727"
"checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5"
"checksum backtrace-sys 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3fcce89e5ad5c8949caa9434501f7b55415b3e7ad5270cb88c75a8d35e8f1279"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab"
"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
"checksum cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "389803e36973d242e7fecb092b2de44a3d35ac62524b3b9339e51d577d668e02"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum curve25519-dalek 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3eacf6ff1b911e3170a8c400b402e10c86dc3cb166bd69034ebbc2b785fea4c2"
"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90"
"checksum ed25519-dalek 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cd66d8a16ef71c23cf5eeb2140d8d3cd293457c6c7fd6804b593397a933fcf1e"
"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7"
"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
"checksum nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "921f61dc817b379d0834e45d5ec45beaacfae97082090a49c2cf30dcbc30206f"
"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09"
"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c"
"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
"checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a"
"checksum rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a"
"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372"
"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05"
"checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3"
"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395"
"checksum ring 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe642b9dd1ba0038d78c4a3999d1ee56178b4d415c1e1fbaba83b06dce012f0"
"checksum rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "01b90379b8664dd83460d59bdc5dd1fd3172b8913788db483ed1325171eab2f7"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "6fa52f19aee12441d5ad11c9a00459122bd8f98707cadf9778c540674f1935b6"
"checksum serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "96a7f9496ac65a2db5929afa087b54f8fc5008dcfbe48a8874ed20049b0d6154"
"checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0"
"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
"checksum syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)" = "ae8b29eb5210bc5cf63ed6149cbf9adfc82ac0be023d8735c176ee74a2db4da7"
"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

View File

@ -6,5 +6,5 @@ homepage = "http://github.com/subgraph/citadel"
[dependencies]
libcitadel = { path = "../libcitadel" }
libc = "0.2"
failure = "0.1.3"
libc = "0.2"

View File

@ -6,24 +6,24 @@ pub struct BootSelection {
}
impl BootSelection {
pub fn choose_install_partition(config: &Config) -> Result<Partition> {
let bs = BootSelection::load_partitions(config)?;
pub fn choose_install_partition() -> Result<Partition> {
let bs = BootSelection::load_partitions()?;
match bs._choose_install_partition() {
Some(p) => Ok(p.clone()),
None => bail!("no partition found for installation"),
}
}
pub fn choose_boot_partition(config: &Config) -> Result<Partition> {
let bs = BootSelection::load_partitions(config)?;
pub fn choose_boot_partition() -> Result<Partition> {
let bs = BootSelection::load_partitions()?;
match bs._choose_boot_partition() {
Some(p) => Ok(p.clone()),
None => bail!("no partition found to boot from"),
}
}
fn load_partitions(config: &Config) -> Result<BootSelection> {
let partitions = Partition::rootfs_partitions(config)
fn load_partitions() -> Result<BootSelection> {
let partitions = Partition::rootfs_partitions()
.map_err(|e| format_err!("Could not load rootfs partition info: {}", e))?;
Ok(BootSelection {

View File

@ -3,34 +3,37 @@
extern crate libc;
use std::env;
use std::process::exit;
use std::env;
use libcitadel::{Result,Config,CommandLine,set_verbose,format_error,ResourceImage};
use libcitadel::{Result,Config,CommandLine,set_verbose,ResourceImage};
mod boot_select;
mod rootfs;
mod uname;
pub use boot_select::BootSelection;
use rootfs::Rootfs;
/// mount command supports 3 subcommands
/// mount command supports 4 subcommands
///
/// citadel-mount rootfs
/// citadel-mount modules
/// citadel-mount extra
/// citadel-mount copy-artifacts
///
/// 'rootfs' creates the /dev/mapper/rootfs device which will be mounted as root filesystem
///
/// 'modules' mounts a resource bundle containing kernel modules
/// 'extra' mounts a resource bundle
/// 'extra' mounts a resource bundle containing extra files
///
/// 'copy-artifacts' searches for a boot partition containing an /images
/// directory and copies all image files to /run/images. Also, it
/// copies bzImage and EFI/BOOT/bootx64.efi
///
fn main() {
if CommandLine::verbose() {
set_verbose(true);
}
@ -52,8 +55,8 @@ fn main() {
_ => Err(format_err!("Bad or missing argument")),
};
if let Err(e) = result {
warn!("Failed: {}", e);
if let Err(ref e) = result {
warn!("Failed: {}", format_error(e));
exit(1);
}
}
@ -66,17 +69,14 @@ fn mount_rootfs(config: Config) -> Result<()> {
fn mount_modules(config: Config) -> Result<()> {
info!("citadel-mount modules");
let utsname = uname::uname();
let v = utsname.release().split("-").collect::<Vec<_>>();
let name = format!("citadel-modules-{}", v[0]);
let mut image = ResourceImage::find(&name)?;
let mut image = ResourceImage::find("modules")?;
image.mount(&config)?;
Ok(())
}
fn mount_extra(config: Config) -> Result<()> {
info!("citadel-mount extra");
let mut image = ResourceImage::find("citadel-extra")?;
let mut image = ResourceImage::find("extra")?;
image.mount(&config)?;
Ok(())
}

View File

@ -1,11 +1,10 @@
use std::process::Command;
use libcitadel::{BlockDev,Result,Partition,CommandLine,Config,ImageHeader,MetaInfo,PathExt};
use BootSelection;
use ResourceImage;
use libcitadel::{BlockDev,CommandLine,Config,ImageHeader,Partition,Result,verity};
use std::path::Path;
use std::process::Stdio;
use BootSelection;
use ResourceImage;
pub struct Rootfs {
config: Config,
@ -17,20 +16,12 @@ impl Rootfs {
}
pub fn setup(&self) -> Result<()> {
if let Ok(partition) = BootSelection::choose_boot_partition(&self.config) {
match self.setup_partition(partition) {
Ok(()) => return Ok(()),
Err(err) => {
warn!("Failed to set up selected boot partition: {}", err);
// fall through
}
}
}
if CommandLine::install_mode() || CommandLine::live_mode() {
self.setup_rootfs_resource()
} else {
let partition = BootSelection::choose_boot_partition()?;
self.setup_partition(partition)
}
fn allow_resource(&self) -> bool {
CommandLine::install_mode() || CommandLine::recovery_mode()
}
fn setup_partition(&self, partition: Partition) -> Result<()> {
@ -42,36 +33,38 @@ impl Rootfs {
}
fn setup_rootfs_resource(&self) -> Result<()> {
if !self.allow_resource() {
info!("Will not search for rootfs resource image because command line flags do not permit it");
return Ok(())
}
info!("Searching for rootfs resource image");
let img = ResourceImage::find_rootfs()?;
let hdr = ImageHeader::from_file(img.path())?;
let metainfo = hdr.verified_metainfo(&self.config)?;
if CommandLine::noverity() {
self.setup_resource_unverified(&img, &metainfo)
self.setup_resource_unverified(&img)
} else {
self.setup_resource_verified(&img, &hdr, &metainfo)
self.setup_resource_verified(&img)
}
}
fn setup_resource_unverified(&self, img: &ResourceImage, metainfo: &MetaInfo) -> Result<()> {
let loop_dev = img.path().setup_loop(
Some(ImageHeader::HEADER_SIZE),
Some(metainfo.nblocks() * 4096))?;
self.setup_linear_mapping(&loop_dev)
fn setup_resource_unverified(&self, img: &ResourceImage) -> Result<()> {
if img.is_compressed() {
img.decompress()?;
}
let loopdev = img.create_loopdev()?;
info!("Loop device created: {}", loopdev.display());
self.setup_linear_mapping(&loopdev)
}
fn setup_resource_verified(&self, img: &ResourceImage, hdr: &ImageHeader, metainfo: &MetaInfo) -> Result<()> {
if !hdr.has_flag(ImageHeader::FLAG_HASH_TREE) {
img.generate_verity_hashtree(&hdr, &metainfo)?;
fn maybe_check_signature(&self, hdr: &ImageHeader) -> Result<()> {
if !CommandLine::nosignatures() {
let signature = hdr.signature();
let metainfo = hdr.metainfo()?;
metainfo.verify(&self.config, &signature)?;
}
img.path().verity_setup(ImageHeader::HEADER_SIZE, metainfo.nblocks(), metainfo.verity_root(), "rootfs")
Ok(())
}
fn setup_resource_verified(&self, img: &ResourceImage) -> Result<()> {
let _ = img.setup_verity_device(&self.config)?;
Ok(())
}
fn setup_partition_unverified(&self, partition: &Partition) -> Result<()> {
@ -81,17 +74,14 @@ impl Rootfs {
fn setup_partition_verified(&self, partition: &Partition) -> Result<()> {
info!("Creating /dev/mapper/rootfs dm-verity device");
let nblocks = partition.metainfo().nblocks();
let roothash = partition.metainfo().verity_root();
partition.path().verity_setup(nblocks * 4096, nblocks, roothash, "rootfs")
self.maybe_check_signature(partition.header())?;
verity::setup_partition_device(partition)?;
Ok(())
}
fn setup_linear_mapping(&self, blockdev: &Path) -> Result<()> {
let dev = BlockDev::open_ro(blockdev)?;
let table = format!("0 {} linear {} 0",
dev.nsectors()?,
blockdev.pathstr());
let table = format!("0 {} linear {} 0", dev.nsectors()?, blockdev.display());
info!("/usr/sbin/dmsetup create rootfs --table '{}'", table);

View File

@ -1,48 +0,0 @@
use std::mem;
use libc::{self, c_char};
use std::ffi::CStr;
use std::str::from_utf8_unchecked;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct UtsName(libc::utsname);
#[allow(dead_code)]
impl UtsName {
pub fn sysname(&self) -> &str {
to_str(&(&self.0.sysname as *const c_char ) as *const *const c_char)
}
pub fn nodename(&self) -> &str {
to_str(&(&self.0.nodename as *const c_char ) as *const *const c_char)
}
pub fn release(&self) -> &str {
to_str(&(&self.0.release as *const c_char ) as *const *const c_char)
}
pub fn version(&self) -> &str {
to_str(&(&self.0.version as *const c_char ) as *const *const c_char)
}
pub fn machine(&self) -> &str {
to_str(&(&self.0.machine as *const c_char ) as *const *const c_char)
}
}
pub fn uname() -> UtsName {
unsafe {
let mut ret: UtsName = mem::uninitialized();
libc::uname(&mut ret.0);
ret
}
}
#[inline]
fn to_str<'a>(s: *const *const c_char) -> &'a str {
unsafe {
let res = CStr::from_ptr(*s).to_bytes();
from_utf8_unchecked(res)
}
}

294
libcitadel/Cargo.lock generated
View File

@ -1,23 +1,24 @@
[[package]]
name = "arrayref"
version = "0.3.5"
name = "autocfg"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "backtrace"
version = "0.3.12"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)",
"autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace-sys 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.24"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
@ -29,25 +30,6 @@ name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "block-buffer"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "byte-tools"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cc"
version = "1.0.26"
@ -58,62 +40,12 @@ name = "cfg-if"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clear_on_drop"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "curve25519-dalek"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
"subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "digest"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ed25519-dalek"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"curve25519-dalek 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -124,37 +56,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "generic-array"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "1.2.0"
@ -169,17 +74,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "libcitadel"
version = "0.1.0"
dependencies = [
"ed25519-dalek 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ring 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -211,93 +115,19 @@ dependencies = [
]
[[package]]
name = "rand"
version = "0.5.5"
name = "ring"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_chacha"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand_hc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_pcg"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_xorshift"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-demangle"
version = "0.1.9"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -305,27 +135,6 @@ name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.82"
@ -338,28 +147,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sha2"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
"fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "subtle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.15.22"
version = "0.15.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
@ -374,7 +167,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -387,13 +180,13 @@ dependencies = [
]
[[package]]
name = "typenum"
version = "1.10.0"
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.1.0"
name = "untrusted"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -421,54 +214,29 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
"checksum backtrace 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eff3830839471718ef8522b9025b399bfb713e25bc220da721364efb660d7d"
"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0"
"checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727"
"checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5"
"checksum backtrace-sys 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3fcce89e5ad5c8949caa9434501f7b55415b3e7ad5270cb88c75a8d35e8f1279"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab"
"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
"checksum cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "389803e36973d242e7fecb092b2de44a3d35ac62524b3b9339e51d577d668e02"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum curve25519-dalek 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3eacf6ff1b911e3170a8c400b402e10c86dc3cb166bd69034ebbc2b785fea4c2"
"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90"
"checksum ed25519-dalek 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cd66d8a16ef71c23cf5eeb2140d8d3cd293457c6c7fd6804b593397a933fcf1e"
"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7"
"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
"checksum nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "921f61dc817b379d0834e45d5ec45beaacfae97082090a49c2cf30dcbc30206f"
"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09"
"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c"
"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
"checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a"
"checksum rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a"
"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372"
"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05"
"checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3"
"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395"
"checksum ring 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe642b9dd1ba0038d78c4a3999d1ee56178b4d415c1e1fbaba83b06dce012f0"
"checksum rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "01b90379b8664dd83460d59bdc5dd1fd3172b8913788db483ed1325171eab2f7"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "6fa52f19aee12441d5ad11c9a00459122bd8f98707cadf9778c540674f1935b6"
"checksum serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "96a7f9496ac65a2db5929afa087b54f8fc5008dcfbe48a8874ed20049b0d6154"
"checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0"
"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
"checksum syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)" = "ae8b29eb5210bc5cf63ed6149cbf9adfc82ac0be023d8735c176ee74a2db4da7"
"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

View File

@ -6,10 +6,9 @@ authors = ["Bruce Leidl <bruce@subgraph.com>"]
[dependencies]
libc = "0.2"
nix = "0.12.0"
ring = "=0.13.2"
untrusted = "0.6.2"
failure = "0.1.3"
ed25519-dalek = "0.8.1"
sha2 = "0.7.0"
rand = "0.6"
toml = "0.4.10"
serde = "1.0.82"
serde_derive = "1.0.82"

View File

@ -1,7 +1,7 @@
use std::collections::HashMap;
use std::path::Path;
use std::fs;
use {Result,PathExt};
use Result;
lazy_static! {
static ref CMDLINE: CommandLine = match CommandLine::load() {
@ -41,11 +41,20 @@ impl CommandLine {
CommandLine::var_exists("citadel.noverity")
}
pub fn nosignatures() -> bool {
CommandLine::var_exists("citadel.nosignatures")
}
/// Return `true` if variable citadel.install is present on kernel command line.
pub fn install_mode() -> bool {
CommandLine::var_exists("citadel.install")
}
/// Return `true` if variable citadel.live is present on kernel command line.
pub fn live_mode() -> bool {
CommandLine::var_exists("citadel.live")
}
/// Return `true` if variable citadel.recovery is present on kernel command line.
pub fn recovery_mode() -> bool {
CommandLine::var_exists("citadel.recovery")
@ -61,7 +70,7 @@ impl CommandLine {
}
fn load() -> Result<CommandLine> {
let s = Path::new("/proc/cmdline").read_as_string()?;
let s = fs::read_to_string("/proc/cmdline")?;
let varmap = CommandLineParser::new(s).parse();
Ok(CommandLine{varmap})
}

View File

@ -1,12 +1,12 @@
use std::path::{Path,PathBuf};
use std::collections::HashMap;
use std::fs;
use ed25519_dalek::{Signature,PublicKey,Keypair};
use rustc_serialize::hex::FromHex;
use sha2::Sha512;
use toml;
use {Result,PathExt};
use Result;
use keys::{KeyPair,PublicKey,Signature};
const DEFAULT_CONFIG_PATH: &str = "/usr/share/citadel/citadel-image.conf";
@ -38,7 +38,7 @@ impl Config {
}
fn from_path(path: &Path) -> Result<Config> {
let s = path.read_as_string()?;
let s = fs::read_to_string(path)?;
let mut config = toml::from_str::<Config>(&s)?;
for (k,v) in config.channel.iter_mut() {
v.name = k.to_string();
@ -107,19 +107,19 @@ impl Channel {
pub fn sign(&self, data: &[u8]) -> Result<Signature> {
let keybytes = match self.keypair {
Some(ref hex) => hex.from_hex()?,
Some(ref hex) => hex,
None => bail!("No private signing key available for channel {}", self.name),
};
let privkey = Keypair::from_bytes(&keybytes)?;
let sig = privkey.sign::<Sha512>(data);
let privkey = KeyPair::from_hex(keybytes)?;
let sig = privkey.sign(data)?;
Ok(sig)
}
pub fn verify(&self, data: &[u8], sigbytes: &[u8]) -> Result<()> {
let keybytes = self.pubkey.from_hex()?;
let pubkey = PublicKey::from_bytes(&keybytes)?;
let sig = Signature::from_bytes(sigbytes)?;
pubkey.verify::<Sha512>(data, &sig)?;
pubkey.verify(data, sigbytes)?;
Ok(())
}

View File

@ -1,14 +1,14 @@
use std::cell::RefCell;
use std::path::Path;
use std::fs::File;
use std::io::{Write,Read};
use std::io::{Read, Write};
use std::path::Path;
use failure::ResultExt;
use toml;
use {Channel,Config,Result,BlockDev};
use blockdev::AlignedBuffer;
use {BlockDev, Channel, Config, Result};
/// Expected magic value in header
const MAGIC: &[u8] = b"SGOS";
@ -61,10 +61,17 @@ fn is_valid_status_code(code: u8) -> bool {
#[derive(Clone)]
pub struct ImageHeader(RefCell<Vec<u8>>);
const CODE_TO_LABEL: [&str; 7] = ["Invalid", "New", "Try Boot", "Good", "Failed Boot", "Bad Signature", "Bad Metainfo"];
const CODE_TO_LABEL: [&str; 7] = [
"Invalid",
"New",
"Try Boot",
"Good",
"Failed Boot",
"Bad Signature",
"Bad Metainfo",
];
impl ImageHeader {
pub const FLAG_PREFER_BOOT: u8 = 0x01; // Set to override usual strategy for choosing a partition to boot and force this one.
pub const FLAG_HASH_TREE: u8 = 0x02; // dm-verity hash tree data is appended to the image
pub const FLAG_DATA_COMPRESSED: u8 = 0x04; // The image data is compressed and needs to be uncompressed before use.
@ -80,7 +87,6 @@ impl ImageHeader {
/// Size of header block
pub const HEADER_SIZE: usize = 4096;
pub fn new() -> ImageHeader {
let v = vec![0u8; ImageHeader::HEADER_SIZE];
let header = ImageHeader(RefCell::new(v));
@ -89,12 +95,9 @@ impl ImageHeader {
}
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<ImageHeader> {
//let mut v = vec![0u8; ImageHeader::HEADER_SIZE];
// XXX check file size is at least HEADER_SIZE
let mut f = File::open(path.as_ref())?;
ImageHeader::from_reader(&mut f)
//f.read_exact(&mut v)?;
//Ok(ImageHeader(RefCell::new(v)))
}
pub fn from_reader<R: Read>(r: &mut R) -> Result<ImageHeader> {
@ -106,7 +109,12 @@ impl ImageHeader {
pub fn from_partition<P: AsRef<Path>>(path: P) -> Result<ImageHeader> {
let mut dev = BlockDev::open_ro(path.as_ref())?;
let nsectors = dev.nsectors()?;
ensure!(nsectors >= 8, "{} is a block device bit it's too short ({} sectors)", path.as_ref().display(), nsectors);
ensure!(
nsectors >= 8,
"{} is a block device bit it's too short ({} sectors)",
path.as_ref().display(),
nsectors
);
let mut buffer = AlignedBuffer::new(ImageHeader::HEADER_SIZE);
dev.read_sectors(nsectors - 8, buffer.as_mut())?;
let header = ImageHeader(RefCell::new(buffer.as_ref().into()));
@ -116,13 +124,18 @@ impl ImageHeader {
pub fn write_partition<P: AsRef<Path>>(&self, path: P) -> Result<()> {
let mut dev = BlockDev::open_rw(path.as_ref())?;
let nsectors = dev.nsectors()?;
ensure!(nsectors >= 8, "{} is a block device bit it's too short ({} sectors)", path.as_ref().display(), nsectors);
ensure!(
nsectors >= 8,
"{} is a block device bit it's too short ({} sectors)",
path.as_ref().display(),
nsectors
);
let buffer = AlignedBuffer::from_slice(&self.0.borrow());
dev.write_sectors(nsectors - 8, buffer.as_ref())?;
Ok(())
}
pub fn verified_metainfo(&self, config: &Config) -> Result<MetaInfo> {
pub fn metainfo(&self) -> Result<MetaInfo> {
let mlen = self.metainfo_len();
if mlen == 0 || mlen > MAX_METAINFO_LEN {
bail!("Invalid metainfo-len field: {}", mlen);
@ -130,7 +143,6 @@ impl ImageHeader {
let mbytes = self.metainfo_bytes();
let mut metainfo = MetaInfo::new(mbytes);
metainfo.parse_toml()?;
metainfo.verify(config, &self.signature())?;
Ok(metainfo)
}
@ -156,7 +168,9 @@ impl ImageHeader {
}
}
pub fn flags(&self) -> u8 { self.read_u8(5) }
pub fn flags(&self) -> u8 {
self.read_u8(5)
}
pub fn has_flag(&self, flag: u8) -> bool {
(self.flags() & flag) == flag
@ -207,7 +221,19 @@ impl ImageHeader {
let mlen = self.metainfo_len();
// XXX assert mlen is good
let sig = channel.sign(&self.0.borrow()[8..8 + mlen])?;
self.write_bytes(8+mlen, &sig.to_bytes());
self.write_bytes(8 + mlen, sig.to_bytes());
Ok(())
}
pub fn verify_signature(&self, config: &Config) -> Result<()> {
let metainfo = self.metainfo()?;
let channel = match config.channel(metainfo.channel()) {
Some(channel) => channel,
None => bail!("Cannot verify signature for channel '{}' because it does not exist in configuration file", metainfo.channel()),
};
channel
.verify(metainfo.bytes(), &self.signature())
.context("failed to verify header signature")?;
Ok(())
}
@ -253,18 +279,17 @@ impl ImageHeader {
}
}
#[derive(Clone)]
pub struct MetaInfo {
bytes: Vec<u8>,
is_parsed: bool,
is_valid: bool,
toml: Option<MetaInfoToml>,
}
#[derive(Deserialize, Serialize, Clone)]
struct MetaInfoToml {
#[serde(rename = "image-type")]
image_type: String,
channel: String,
version: u32,
#[serde(rename = "base-version")]
@ -284,20 +309,19 @@ impl MetaInfo {
MetaInfo {
bytes,
is_parsed: false,
is_valid: false,
toml: None,
}
}
pub fn is_valid(&self) -> bool {
self.is_valid
fn bytes(&self) -> &[u8] {
&self.bytes
}
pub fn parse_toml(&mut self) -> Result<()> {
if !self.is_parsed {
self.is_parsed = true;
let toml = toml::from_slice::<MetaInfoToml>(&self.bytes)
.context("parsing header metainfo")?;
let toml =
toml::from_slice::<MetaInfoToml>(&self.bytes).context("parsing header metainfo")?;
self.toml = Some(toml);
}
Ok(())
@ -308,17 +332,21 @@ impl MetaInfo {
Some(channel) => channel,
None => bail!("Channel '{}' not found in config file", self.channel()),
};
channel.verify(&self.bytes, signature)
channel
.verify(&self.bytes, signature)
.context("Bad metainfo signature in header")?;
Ok(())
}
fn toml(&self) -> &MetaInfoToml {
assert!(self.is_valid);
self.toml.as_ref().unwrap()
}
pub fn image_type(&self) -> &str {
self.toml().image_type.as_str()
}
pub fn channel(&self) -> &str {
self.toml().channel.as_str()
}

View File

@ -1,111 +1,85 @@
use Result;
use rand::rngs::OsRng;
use sha2::Sha512;
use ed25519_dalek::{self,PublicKey,Keypair,Signature};
use rustc_serialize::hex::{ToHex,FromHex};
use ring::rand;
use ring::signature::{self,Ed25519KeyPair,ED25519_PUBLIC_KEY_LEN,ED25519_PKCS8_V2_LEN};
use untrusted::Input;
use rustc_serialize::hex::{FromHex,ToHex};
pub const SIGNATURE_LENGTH: usize = ed25519_dalek::SIGNATURE_LENGTH;
///
/// Keys for signing or verifying signatures. Small convenience
/// wrapper around `ed25519_dalek`.
/// wrapper around `ring/ed25519`.
///
pub enum SigningKeys {
KEYPAIR(Keypair),
PUBLIC(PublicKey),
}
use self::SigningKeys::*;
impl SigningKeys {
/// Generate a new pair of signing/verifying keys using
/// the system random number generator. The resulting
/// `ed25519_dalek::KeyPair` can be extracted in an ascii
/// hex encoded format for storage in configuration files
/// with the `to_hex()` method.
pub fn generate() -> Result<SigningKeys> {
let mut rng = OsRng::new()?;
let pair = Keypair::generate::<Sha512,_>(&mut rng);
Ok(SigningKeys::KEYPAIR(pair))
}
/// Load a `Keypair` from ascii hex representation.
///
/// The `hex` string is read from a configuration file
/// and is used here to construct a `SigningKeys` instance
/// which can then be used for signing (or verifying).
pub fn from_keypair_hex(hex: String) -> Result<SigningKeys> {
let bytes = hex.from_hex()?;
let pair = Keypair::from_bytes(&bytes)?;
Ok(SigningKeys::KEYPAIR(pair))
}
/// Load only `PublicKey` from ascii hex representation
///
/// The string `hex` is read from a configuration file
/// and is used here to construct a `SigningKeys` instance
/// which can only be used for verifying signatures (not
/// for signing).
pub fn from_public_hex(hex: String) -> Result<SigningKeys> {
let bytes = hex.from_hex()?;
let public = PublicKey::from_bytes(&bytes)?;
Ok(SigningKeys::PUBLIC(public))
}
pub struct PublicKey([u8; ED25519_PUBLIC_KEY_LEN]);
pub struct KeyPair([u8; ED25519_PKCS8_V2_LEN]);
pub struct Signature(signature::Signature);
/// Return ascii hex representation of internal `Keypair`
/// or `PublicKey` depending on which variant `self` is.
///
/// Caller is expected to know which variant is being
/// converted.
pub fn to_hex(&self) -> String {
match *self {
KEYPAIR(ref pair) => pair.to_bytes().to_hex(),
PUBLIC(ref public) => public.to_bytes().to_hex(),
impl PublicKey {
pub fn from_bytes(bytes: &[u8]) -> Result<PublicKey> {
let mut key = [0u8; ED25519_PUBLIC_KEY_LEN];
key.copy_from_slice(bytes);
Ok(PublicKey(key))
}
}
/// Return ascii hex representation of the `PublicKey` associated
/// with this instance.
pub fn to_public_hex(&self) -> String {
match *self {
KEYPAIR(ref pair) => pair.public.to_bytes().to_hex(),
PUBLIC(ref public) => public.to_bytes().to_hex(),
}
}
/// Sign `data` with the private key associated with this instance
/// using `Sha512` as the hashing algorithm. Caller must ensure
/// that this instance is a `KEYPAIR` variant.
///
/// Returns signature of `data` encoded as a `SIGNATURE_LENGTH`
/// byte array (64 bytes).
///
pub fn sign(&self, data: &[u8]) -> [u8; SIGNATURE_LENGTH] {
let signature = match *self {
KEYPAIR(ref pair) => pair.sign::<Sha512>(data),
_ => panic!("Not a keypair, no signing key"),
};
signature.to_bytes()
}
/// Verify that `signature` is a valid signature for `data` using the
/// `PublicKey` associated with this instance. `signature` must be
/// a slice of `SIGNATURE_LENGTH` bytes.
///
/// Returns `Ok(())` if signature is valid.
///
pub fn verify(&self, data: &[u8], signature: &[u8]) -> Result<()> {
assert_eq!(signature.len(), SIGNATURE_LENGTH, "Signature bytes are not expected length");
let signature = Signature::from_bytes(signature)?;
self.pubkey().verify::<Sha512>(data, &signature)?;
let signature = Input::from(signature);
let data = Input::from(data);
let pubkey = Input::from(&self.0);
signature::verify(&signature::ED25519, pubkey, data, signature)?;
Ok(())
}
}
fn pubkey(&self) -> &PublicKey {
match *self {
KEYPAIR(ref keypair) => &keypair.public,
PUBLIC(ref public) => &public,
impl KeyPair {
/// Generate a new pair of signing/verifying keys using
/// the system random number generator. The resulting
/// `Ed25519KeyPair` can be extracted in an ascii
/// hex encoded pkcs#8 format for storage in configuration files
/// with the `to_hex()` method.
pub fn generate() -> Result<KeyPair> {
let rng = rand::SystemRandom::new();
let bytes = Ed25519KeyPair::generate_pkcs8(&rng)?;
KeyPair::from_bytes(&bytes)
}
pub fn from_hex(hex: &str) -> Result<KeyPair> {
KeyPair::from_bytes(&hex.from_hex()?)
}
fn from_bytes(bytes: &[u8]) -> Result<KeyPair> {
let mut pair = [0u8; ED25519_PKCS8_V2_LEN];
pair.copy_from_slice(bytes);
Ok(KeyPair(pair))
}
pub fn public_key_bytes(&self) -> Vec<u8> {
let pair = Ed25519KeyPair::from_pkcs8(Input::from(&self.0)).expect("failed to parse pkcs8 key");
pair.public_key_bytes().to_vec()
}
pub fn private_key_bytes(&self) -> Vec<u8> {
self.0.to_vec()
}
pub fn private_key_hex(&self) -> String {
self.0.to_hex()
}
pub fn public_key_hex(&self) -> String {
let pair = Ed25519KeyPair::from_pkcs8(Input::from(&self.0)).expect("failed to parse pkcs8 key");
pair.public_key_bytes().to_hex()
}
pub fn sign(&self, data: &[u8]) -> Result<Signature> {
let pair = Ed25519KeyPair::from_pkcs8(Input::from(&self.0))?;
let signature = pair.sign(data);
Ok(Signature(signature))
}
}
impl Signature {
pub fn to_bytes(&self) -> &[u8] {
self.0.as_ref()
}
}

View File

@ -23,9 +23,8 @@ macro_rules! notify {
extern crate libc;
extern crate serde;
extern crate toml;
extern crate ed25519_dalek;
extern crate sha2;
extern crate rand;
extern crate ring;
extern crate untrusted;
extern crate rustc_serialize;
use std::cell::RefCell;
@ -45,25 +44,38 @@ pub fn set_verbose(val: bool) {
VERBOSE.with(|f| { *f.borrow_mut() = val });
}
pub fn format_error(err: &Error) -> String {
let mut output = err.to_string();
let mut prev = err.as_fail();
while let Some(next) = prev.cause() {
output.push_str(": ");
output.push_str(&next.to_string());
prev = next;
}
output
}
mod blockdev;
mod config;
mod keys;
mod disks;
mod cmdline;
mod header;
mod partition;
mod resource;
mod path_ext;
pub mod util;
pub mod verity;
mod mount;
pub use config::Config;
pub use config::Channel;
pub use blockdev::BlockDev;
pub use keys::SigningKeys;
pub use cmdline::CommandLine;
pub use header::{ImageHeader,MetaInfo};
pub use path_ext::{PathExt,FileTypeResult,VerityOutput};
pub use partition::Partition;
pub use resource::ResourceImage;
pub use keys::KeyPair;
pub use mount::Mount;
pub type Result<T> = result::Result<T,Error>;

59
libcitadel/src/mount.rs Normal file
View File

@ -0,0 +1,59 @@
use std::path::{PathBuf,Path};
use std::fs;
use Result;
pub struct Mount {
source: String,
target: PathBuf,
fstype: String,
options: String,
}
impl Mount {
///
/// Returns `true` if `path` matches the source field (first field)
/// of any of the mount lines listed in /proc/mounts
///
pub fn is_path_mounted<P: AsRef<Path>>(path: P) -> Result<bool> {
let path_str = path.as_ref().to_string_lossy();
let mounts = Mount::all_mounts()?;
Ok(mounts.into_iter().any(|m| m.source == path_str))
}
pub fn all_mounts() -> Result<Vec<Mount>> {
let s = fs::read_to_string("/proc/mounts")?;
Ok(s.lines().flat_map(Mount::parse_mount_line).collect())
}
fn parse_mount_line(line: &str) -> Option<Mount> {
let parts = line.split_whitespace().collect::<Vec<_>>();
if parts.len() < 4 {
warn!("Failed to parse mount line: {}", line);
return None;
}
Some(Mount{
source: parts[0].to_string(),
target: PathBuf::from(parts[1]),
fstype: parts[2].to_string(),
options: parts[3].to_string(),
})
}
pub fn source(&self) -> &str {
&self.source
}
pub fn target(&self) -> &Path {
&self.target
}
pub fn fstype(&self) -> &str {
&self.fstype
}
pub fn options(&self) -> &str {
&self.options
}
}

View File

@ -1,6 +1,6 @@
use std::path::{Path,PathBuf};
use std::fs;
use {Config,Result,ImageHeader,MetaInfo,PathExt};
use {Config,CommandLine,Result,ImageHeader,MetaInfo,Mount};
#[derive(Clone)]
pub struct Partition {
@ -16,27 +16,27 @@ struct HeaderInfo {
}
impl Partition {
pub fn rootfs_partitions(config: &Config) -> Result<Vec<Partition>> {
pub fn rootfs_partitions() -> Result<Vec<Partition>> {
let mut v = Vec::new();
for path in rootfs_partition_paths()? {
let partition = Partition::load(&path, config)?;
let partition = Partition::load(&path)?;
v.push(partition);
}
Ok(v)
}
fn load(dev: &Path, config: &Config) -> Result<Partition> {
let is_mounted = is_path_mounted(dev)?;
let header = Partition::load_header(dev, config)?;
fn load(dev: &Path) -> Result<Partition> {
let is_mounted = is_in_use(dev)?;
let header = Partition::load_header(dev)?;
Ok(Partition::new(dev, header, is_mounted))
}
fn load_header(dev: &Path, config: &Config) -> Result<Option<HeaderInfo>> {
fn load_header(dev: &Path) -> Result<Option<HeaderInfo>> {
let header = ImageHeader::from_partition(dev)?;
if !header.is_magic_valid() {
return Ok(None);
}
let metainfo = match header.verified_metainfo(config) {
let metainfo = match header.metainfo() {
Ok(metainfo) => metainfo,
Err(e) => {
warn!("Reading partition {}: {}", dev.display(), e);
@ -116,11 +116,48 @@ impl Partition {
if self.header().status() == ImageHeader::STATUS_TRY_BOOT {
self.write_status(ImageHeader::STATUS_FAILED)?;
}
// XXX verify signature
if !CommandLine::nosignatures() {
if let Err(e) = self.header().verify_signature(config) {
warn!("Signature verification failed on partition: {}", e);
self.write_status(ImageHeader::STATUS_BAD_SIG)?;
}
}
Ok(())
}
}
fn is_in_use(path: &Path) -> Result<bool> {
if Mount::is_path_mounted(path)? {
return Ok(true);
}
let holders = count_block_holders(path)?;
Ok(holders > 0)
}
//
// Resolve /dev/mapper/citadel-rootfsX symlink to actual device name
// and then inspect directory /sys/block/${DEV}/holders and return
// the number of entries this directory contains. If this directory
// is not empty then device belongs to another device mapping.
//
fn count_block_holders(path: &Path) -> Result<usize> {
if !path.exists() {
bail!("Path to rootfs device does not exist: {}", path.display());
}
let resolved = fs::canonicalize(path)?;
let fname = match resolved.file_name() {
Some(s) => s,
None => bail!("path does not have filename?"),
};
let holders_dir =
Path::new("/sys/block")
.join(fname)
.join("holders");
let count = fs::read_dir(holders_dir)?.count();
Ok(count)
}
fn rootfs_partition_paths() -> Result<Vec<PathBuf>> {
let mut rootfs_paths = Vec::new();
for dent in fs::read_dir("/dev/mapper")? {
@ -145,19 +182,3 @@ fn path_filename(path: &Path) -> &str {
""
}
///
/// Returns `true` if `path` matches the source field (first field)
/// of any of the mount lines listed in /proc/mounts
///
pub fn is_path_mounted(path: &Path) -> Result<bool> {
let path_str = path.to_str().unwrap();
for line in Path::new("/proc/mounts").read_as_lines()? {
if let Some(s) = line.split_whitespace().next() {
if s == path_str {
return Ok(true);
}
}
}
Ok(false)
}

View File

@ -1,354 +0,0 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufReader,BufRead,Read};
use std::path::{Path,PathBuf};
use std::process::{Command,Stdio};
use failure::ResultExt;
use Result;
/// A collection of utility methods added to `Path` to perform various types of operations
/// on files and directories.
pub trait PathExt {
/// Run sha256sum command on file `self` and return output as a hex `String`
fn sha256(&self) -> Result<String>;
/// Write file `self` to partition device with dd command.
fn copy_to_partition<P: AsRef<Path>>(&self, partition: P) -> Result<()>;
/// Read entire file `self` and return contents as a `String`
fn read_as_string(&self) -> Result<String>;
/// Read entire file `self` and return contents as a `Vec` of individual lines.
fn read_as_lines(&self) -> Result<Vec<String>>;
/// Return `true` if path `self` is mounted.
fn is_mounted(&self) -> bool;
/// Compress file `self` with xz utility.
fn xz_compress(&self) -> Result<()>;
/// Uncompress file `self` with xz utility.
fn xz_uncompress(&self) -> Result<()>;
/// Run /usr/bin/file command on file `self` and return output as `FileTypeResult`
fn file_type(&self) -> Result<FileTypeResult>;
/// Mount path `self` to `target`
fn mount<P: AsRef<Path>>(&self, target: P) -> Result<()>;
/// Mount path `self` to `target` with additional argument `args` to mount command.
fn mount_with_args<P: AsRef<Path>>(&self, target: P, args: &str) -> Result<()>;
/// Bind mount path `self` to path `target`.
fn bind_mount<P: AsRef<Path>>(&self, target: P) -> Result<()>;
/// Set up loop device for file `self` with optional offset and size limit.
/// Returns `PathBuf` to associated loop device upon success.
fn setup_loop(&self, offset: Option<usize>, sizelimit: Option<usize>) -> Result<PathBuf>;
/// Unmount path `self`
fn umount(&self) -> Result<()>;
/// Return Partition Type GUID for a block device by running lsblk command
fn partition_type_guid(&self) -> Result<String>;
/// Generate dm-verity hashtree for a disk image and store in an external file
/// Parse output from command into VerityOutput structure and return it.
fn verity_initial_hashtree<P: AsRef<Path>>(&self, hashfile: P) -> Result<VerityOutput>;
/// Generate dm-verity hashtree with a given salt value and append it to the same image.
///
/// device
/// Parse output from command into VerityOutput structure and return it.
fn verity_regenerate_hashtree(&self, offset: usize, nblocks: usize, salt: &str) -> Result<VerityOutput>;
///
fn verity_setup(&self, offset: usize, nblocks: usize, roothash: &str, devname: &str) -> Result<()>;
/// Return path as a string without error checking
fn pathstr(&self) -> &str;
}
impl PathExt for Path {
fn sha256(&self) -> Result<String> {
let output = exec_command_with_output("/usr/bin/sha256sum", &[self.pathstr()])
.context(format!("failed to calculate sha256 on {}", self.display()))?;
let v: Vec<&str> = output.split_whitespace().collect();
Ok(v[0].trim().to_owned())
}
fn copy_to_partition<P: AsRef<Path>>(&self, partition: P) -> Result<()> {
let if_arg = format!("if={}", self.pathstr());
let of_arg = format!("of={}", partition.as_ref().pathstr());
exec_command("/usr/bin/dd", &[ if_arg.as_str(), of_arg.as_str(), "bs=4M" ])
.context(format!("failed to copy {} to {} with dd", self.display(), partition.as_ref().display()))?;
Ok(())
}
fn read_as_string(&self) -> Result<String> {
let mut f = File::open(&self)?;
let mut buffer = String::new();
f.read_to_string(&mut buffer)?;
Ok(buffer)
}
fn read_as_lines(&self) -> Result<Vec<String>> {
let mut v = Vec::new();
let f = File::open(&self)?;
let reader = BufReader::new(f);
for line in reader.lines() {
let line = line?;
v.push(line);
}
Ok(v)
}
fn is_mounted(&self) -> bool {
exec_command("/usr/bin/findmnt", &[self.pathstr()]).is_ok()
}
fn xz_compress(&self) -> Result<()> {
exec_command("/usr/bin/xz", &["-T0", self.pathstr()])
.context(format!("failed to compress {}", self.display()))?;
Ok(())
}
fn xz_uncompress(&self) -> Result<()> {
exec_command("/usr/bin/xz", &["-d", self.pathstr()])
.context(format!("failed to uncompress {}", self.display()))?;
Ok(())
}
fn file_type(&self) -> Result<FileTypeResult> {
let output = exec_command_with_output("/usr/bin/file", &["-b", self.pathstr()])
.context(format!("failed to run /usr/bin/file on {}", self.display()))?;
Ok(FileTypeResult(output))
}
fn mount<P: AsRef<Path>>(&self, target: P) -> Result<()> {
let target = target.as_ref().to_str().unwrap();
exec_command("/usr/bin/mount", &[self.pathstr(), target])
.context(format!("failed to mount {}", self.display()))?;
Ok(())
}
fn mount_with_args<P: AsRef<Path>>(&self, target: P, args: &str) -> Result<()> {
let target = target.as_ref().to_str().unwrap();
exec_command("/usr/bin/mount", &[args, self.pathstr(), target])
.context(format!("failed to mount {} with args [{}]", self.display(), args))?;
Ok(())
}
fn bind_mount<P: AsRef<Path>>(&self, target: P) -> Result<()> {
let target = target.as_ref().to_str().unwrap();
exec_command("/usr/bin/mount", &["--bind", self.pathstr(), target])
.context(format!("failed to bind mount {} to {}", self.display(), target))?;
Ok(())
}
fn setup_loop(&self, offset: Option<usize>, sizelimit: Option<usize>) -> Result<PathBuf> {
let offset_str: String;
let sizelimit_str: String;
let mut v = Vec::new();
if let Some(val) = offset {
v.push("--offset");
offset_str = val.to_string();
v.push(&offset_str);
}
if let Some(val) = sizelimit {
v.push("--sizelimit");
sizelimit_str = val.to_string();
v.push(&sizelimit_str);
}
v.push("-f");
v.push(self.pathstr());
let output = exec_command_with_output("/sbin/losetup", &v)
.context(format!("failed to run /sbin/losetup on {}", self.display()))?;
Ok(PathBuf::from(output))
}
fn umount(&self) -> Result<()> {
exec_command("/usr/bin/umount", &[self.pathstr()])
.context(format!("failed to umount {}", self.display()))?;
Ok(())
}
fn partition_type_guid(&self) -> Result<String> {
let output = exec_command_with_output("/usr/bin/lsblk", &["-dno", "PARTTYPE", self.pathstr()])
.context(format!("failed to run lsblk on {}", self.display()))?;
Ok(output)
}
fn verity_initial_hashtree<P: AsRef<Path>>(&self, hashfile: P) -> Result<VerityOutput> {
let output = exec_command_with_output("/usr/sbin/veritysetup",
&["format", self.pathstr(), hashfile.as_ref().pathstr()])
.context("veritysetup format command failed")?;
Ok(VerityOutput::parse(&output))
}
fn verity_regenerate_hashtree(&self, offset: usize, nblocks: usize, salt: &str) -> Result<VerityOutput> {
let arg_offset = format!("--hash-offset={}", offset);
let arg_blocks = format!("--data-blocks={}", nblocks);
let arg_salt = format!("--salt={}", salt);
let arg_path = self.pathstr();
let output = exec_command_with_output("/usr/sbin/veritysetup",
&[arg_offset.as_str(), arg_blocks.as_str(), arg_salt.as_str(),
"format", arg_path, arg_path])
.context("running veritysetup command failed")?;
Ok(VerityOutput::parse(&output))
}
fn verity_setup(&self, offset: usize, nblocks: usize, roothash: &str, devname: &str) -> Result<()> {
let arg_offset = format!("--hash-offset={}", offset);
let arg_blocks = format!("--data-blocks={}", nblocks);
let arg_path = self.pathstr();
exec_command("/usr/sbin/veritysetup",
&[arg_offset.as_str(), arg_blocks.as_str(), "create",
devname, arg_path, arg_path, roothash])
.context("running veritysetup failed")?;
Ok(())
}
fn pathstr(&self) -> &str {
self.to_str().unwrap()
}
}
/*
fn exec_command(cmd_path: &str, args: &[&str]) -> bool {
Command::new(cmd_path)
.args(args)
.stderr(Stdio::inherit())
.status()
.expect(&format!("unable to execute {}", cmd_path))
.success()
}
fn exec_command_with_output(cmd_path: &str, args: &[&str]) -> (bool, String) {
let res = Command::new(cmd_path)
.args(args)
.stderr(Stdio::inherit())
.output()
.expect(&format!("unable to execute {}", cmd_path));
if res.status.success() {
(true, String::from_utf8(res.stdout).unwrap().trim().to_owned())
} else {
(false, String::new())
}
}
*/
fn exec_command(cmd_path: &str, args: &[&str]) -> Result<()> {
let status = Command::new(cmd_path)
.args(args)
.stderr(Stdio::inherit())
.status()
.context(format!("unable to execute {}", cmd_path))?;
if !status.success() {
match status.code() {
Some(code) => bail!("command {} failed with exit code: {}", cmd_path, code),
None => bail!("command {} failed with no exit code", cmd_path),
}
}
Ok(())
}
fn exec_command_with_output(cmd_path: &str, args: &[&str]) -> Result<String> {
let res = Command::new(cmd_path)
.args(args)
.stderr(Stdio::inherit())
.output()
.context(format!("unable to execute {}", cmd_path))?;
if !res.status.success() {
match res.status.code() {
Some(code) => bail!("command {} failed with exit code: {}", cmd_path, code),
None => bail!("command {} failed with no exit code", cmd_path),
}
}
Ok(String::from_utf8(res.stdout).unwrap().trim().to_owned())
}
pub struct FileTypeResult(String);
impl FileTypeResult {
pub fn is_xz_compressed(&self) -> bool {
self.0.starts_with("XZ")
}
pub fn is_ext2_image(&self) -> bool {
self.0.starts_with("Linux rev 1.0 ext2 filesystem data")
}
pub fn output(&self) -> &str {
self.0.as_str()
}
}
/// The output from the `veritysetup format` command can be parsed as key/value
/// pairs. This class parses the output and stores it in a map for querying.
pub struct VerityOutput {
output: String,
map: HashMap<String,String>,
}
impl VerityOutput {
/// Parse the string `output` as standard output from the dm-verity
/// `veritysetup format` command.
fn parse(output: &str) -> VerityOutput {
let mut vo = VerityOutput {
output: output.to_owned(),
map: HashMap::new(),
};
for line in output.lines() {
vo.parse_line(line);
}
vo
}
fn parse_line(&mut self, line: &str) {
let v = line.split(':')
.map(|s| s.trim())
.collect::<Vec<_>>();
if v.len() == 2 {
self.map.insert(v[0].to_owned(), v[1].to_owned());
}
}
pub fn root_hash(&self) -> Option<&str> {
self.map.get("Root hash").map(|s| s.as_str())
}
pub fn salt(&self) -> Option<&str> {
self.map.get("Salt").map(|s| s.as_str())
}
pub fn output(&self) -> &str {
&self.output
}
}

View File

@ -1,20 +1,14 @@
use std::path::{Path,PathBuf};
use std::fs::{self, File};
use std::io::{self,Read};
use std::io::{self,Seek,SeekFrom};
use std::path::{Path, PathBuf};
use disks::DiskPartition;
use Result;
use CommandLine;
use PathExt;
use ImageHeader;
use Config;
use MetaInfo;
use {CommandLine,Config,ImageHeader,MetaInfo,Result,Partition,Mount,verity,util};
use failure::ResultExt;
const STORAGE_BASEDIR: &str = "/sysroot/storage/resources";
const BOOT_BASEDIR: &str = "/boot/images";
const RUN_DIRECTORY: &str = "/run/images";
/// Locates and mounts a resource image file.
///
/// Resource image files are files containing a disk image that can be
@ -23,69 +17,89 @@ const RUN_DIRECTORY: &str = "/run/images";
/// contains a list of bind mounts to perform from the mounted tree to
/// the system rootfs.
///
/// dm-verity will be set up for the mounted image unless the `citadel.noverity`
/// variable is set on the kernel command line.
/// Various kernel command line options control how the resource file is
/// searched for and how it is mounted.
///
/// Resource image files will first be searched for in the `/storage/resources/`
/// directory (with `/sysroot` prepended since these mounts are performed in initramfs).
/// If the storage device does not exist or kernel command line variables are set
/// indicating either an install mode or recovery mode boot then search of storage
/// directory is not performed.
/// citadel.noverity: Mount image without dm-verity. Also do not verify header signature.
/// citadel.nosignatures: Do not verify header signature.
///
/// If not located in `/storage/resources` the image file will be searched for on all
/// UEFI ESP partitions on the system. If found on a boot partition, it will be
/// copied to `/run/images` and uncompressed if necessary.
/// A requested image file will be searched for first in /run/images and if not found there the
/// usual location of /storage/resources is searched.
///
pub struct ResourceImage {
name: String,
path: PathBuf,
header: ImageHeader,
metainfo: MetaInfo,
}
impl ResourceImage {
/// Locate and return a resource image with `name`.
/// First the /storage/resources directory is searched, and if not found there,
/// each EFI boot partition will also be searched.
/// First the /run/images directory is searched, and if not found there,
/// the image will be searched for in /storage/resources/$channel
pub fn find(name: &str) -> Result<ResourceImage> {
let mut img = ResourceImage::new(name);
let search_storage = !(CommandLine::install_mode() || CommandLine::recovery_mode());
if search_storage && ResourceImage::ensure_storage_mounted() {
let path = PathBuf::from(format!("{}/{}.img", STORAGE_BASEDIR, name));
if path.exists() {
img.path.push(path);
info!("Image found at {}", img.path.display());
return Ok(img)
}
let filename = ResourceImage::image_filename(name);
let run_path = Path::new(RUN_DIRECTORY).join(&filename);
let channel = ResourceImage::read_rootfs_channel()?;
let storage_path = Path::new(STORAGE_BASEDIR).join(channel).join(&filename);
if run_path.exists() {
return ResourceImage::from_path(run_path);
}
if img.search_boot_partitions() {
Ok(img)
if !ResourceImage::ensure_storage_mounted()? {
bail!("Unable to mount /storage");
}
if storage_path.exists() {
ResourceImage::from_path(storage_path)
} else {
Err(format_err!("Failed to find resource image: {}", name))
}
}
/// Locate and return a rootfs resource image.
/// Only EFI boot partitions will be searched.
/// Locate a rootfs image in /run/images and return it
pub fn find_rootfs() -> Result<ResourceImage> {
let mut img = ResourceImage::new("citadel-rootfs");
if img.search_boot_partitions() {
info!("Found rootfs image at {}", img.path.display());
Ok(img)
let rootfs_path = Path::new(RUN_DIRECTORY).join(ResourceImage::image_filename("rootfs"));
if rootfs_path.exists() {
info!("Found rootfs image at {}", rootfs_path.display());
ResourceImage::from_path(rootfs_path)
} else {
Err(format_err!("Failed to find rootfs resource image"))
}
}
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<ResourceImage> {
let header = ImageHeader::from_file(path.as_ref())?;
if !header.is_magic_valid() {
bail!("Image file {} does not have a valid header", path.as_ref().display());
}
let metainfo = header.metainfo()?;
Ok(ResourceImage::new(path.as_ref(), header, metainfo))
}
pub fn is_valid_image(&self) -> bool {
self.header.is_magic_valid()
}
/// Return path to the resource image file.
pub fn path(&self) -> &Path {
&self.path
}
fn new(name: &str) -> ResourceImage {
pub fn header(&self) -> &ImageHeader {
&self.header
}
pub fn metainfo(&self) -> &MetaInfo {
&self.metainfo
}
fn new(path: &Path, header: ImageHeader, metainfo: MetaInfo) -> ResourceImage {
ResourceImage {
name: name.to_owned(),
path: PathBuf::new(),
path: path.to_owned(),
header, metainfo,
}
}
@ -99,172 +113,157 @@ impl ResourceImage {
self.process_manifest_file()
}
fn mount_verity(&self, config: &Config) -> Result<()> {
let hdr = ImageHeader::from_file(&self.path)?;
let metainfo = hdr.verified_metainfo(config)?;
info!("Setting up dm-verity device for image");
if !hdr.has_flag(ImageHeader::FLAG_HASH_TREE) {
self.generate_verity_hashtree(&hdr, &metainfo)?;
pub fn is_compressed(&self) -> bool {
self.header.has_flag(ImageHeader::FLAG_DATA_COMPRESSED)
}
let devname = format!("verity-{}", self.name);
pub fn has_verity_hashtree(&self) -> bool {
self.header.has_flag(ImageHeader::FLAG_HASH_TREE)
}
self.path.verity_setup(ImageHeader::HEADER_SIZE, metainfo.nblocks(), metainfo.verity_root(), &devname)?;
pub fn decompress(&self) -> Result<()> {
if !self.is_compressed() {
return Ok(())
}
self.decompress_and_generate_hashtree(true)
}
// Avoid copying the body twice in the common case that image is both
// compressed and needs hashtree generated.
fn decompress_and_generate_hashtree(&self, decompress_only: bool) -> Result<()> {
assert!(self.is_compressed());
let mut tmpfile = self.extract_body_to_tmpfile(Some("xz"))?;
util::xz_decompress(&tmpfile)?;
tmpfile.set_extension("");
self.header.clear_flag(ImageHeader::FLAG_DATA_COMPRESSED);
if !decompress_only && !self.has_verity_hashtree() {
verity::generate_image_hashtree(&tmpfile, &self.metainfo)?;
self.header.set_flag(ImageHeader::FLAG_HASH_TREE);
}
self.write_image_from_tmpfile(&tmpfile)
}
fn extract_body_to_tmpfile(&self, extension: Option<&str>) -> Result<PathBuf> {
let mut reader = File::open(&self.path)?;
reader.seek(SeekFrom::Start(4096))?;
fs::create_dir_all("/tmp/citadel-image-tmp")?;
let mut path = Path::new("/tmp/citadel-image-tmp").join(format!("{}-tmp", &self.metainfo.image_type()));
if let Some(ext) = extension {
path.set_extension(ext);
}
let mut out = File::create(&path)?;
io::copy(&mut reader, &mut out)?;
Ok(path)
}
pub fn write_to_partition(&self, partition: &Partition) -> Result<()> {
if self.metainfo.image_type() != "rootfs" {
bail!("Cannot write to partition, image type is not rootfs");
}
if !self.has_verity_hashtree() {
self.generate_verity_hashtree()?;
}
info!("writing rootfs image to {}", partition.path().display());
let args = format!("if={} of={} bs=4096 skip=1",
self.path.display(), partition.path().display());
util::exec_cmdline("/bin/dd", args)?;
self.header.set_status(ImageHeader::STATUS_NEW);
self.header.write_partition(partition.path())?;
Ok(())
}
fn write_image_from_tmpfile(&self, tmpfile: &Path) -> Result<()> {
let mut reader = File::open(&tmpfile)?;
let mut out = File::create(self.path())?;
self.header.write_header(&mut out)?;
io::copy(&mut reader, &mut out)?;
fs::remove_file(tmpfile)?;
Ok(())
}
fn mount_verity(&self, config: &Config) -> Result<()> {
let verity_dev = self.setup_verity_device(config)?;
info!("Mounting dm-verity device to {}", self.mount_path().display());
fs::create_dir_all(self.mount_path())?;
Path::new(&format!("/dev/mapper/{}", devname)).mount(self.mount_path())
util::mount(&verity_dev.to_string_lossy(), self.mount_path(), None)
}
pub fn generate_verity_hashtree(&self, hdr: &ImageHeader, metainfo: &MetaInfo) -> Result<()> {
info!("Generating dm-verity hash tree for image");
if !hdr.has_flag(ImageHeader::FLAG_HASH_TREE) {
let _ = self.path.verity_regenerate_hashtree(ImageHeader::HEADER_SIZE, metainfo.nblocks(), metainfo.verity_salt())?;
hdr.set_flag(ImageHeader::FLAG_HASH_TREE);
let w = fs::OpenOptions::new().write(true).open(&self.path)?;
hdr.write_header(w)?;
pub fn setup_verity_device(&self, config: &Config) -> Result<PathBuf> {
if !CommandLine::nosignatures() {
self.header.verify_signature(config)?;
}
Ok(())
info!("Setting up dm-verity device for image");
if !self.has_verity_hashtree() {
self.generate_verity_hashtree()?;
}
verity::setup_image_device(self.path())
}
pub fn generate_verity_hashtree(&self) -> Result<()> {
if self.has_verity_hashtree() {
return Ok(())
}
info!("Generating dm-verity hash tree for image {}", self.path.display());
if self.is_compressed() {
info!("Image is compressed, so need to decompress first");
self.decompress_and_generate_hashtree(false)
} else {
let tmpfile = self.extract_body_to_tmpfile(None)?;
verity::generate_image_hashtree(&tmpfile, &self.metainfo)?;
self.header.set_flag(ImageHeader::FLAG_HASH_TREE);
self.write_image_from_tmpfile(&tmpfile)
}
}
pub fn verify_verity(&self) -> Result<bool> {
if !self.has_verity_hashtree() {
self.generate_verity_hashtree()?;
}
verity::verify_image(self.path(), &self.metainfo)
}
pub fn verify_shasum(&self) -> Result<bool> {
unimplemented!();
}
// Mount the resource image but use a simple loop mount rather than setting up a dm-verity
// device for the image.
fn mount_noverity(&self) -> Result<()> {
info!("loop mounting image to {} (noverity)", self.mount_path().display());
fs::create_dir_all(self.mount_path())?;
Path::new(&self.path).mount_with_args(self.mount_path(), "-oloop,ro,offset=4096")
if self.is_compressed() {
self.decompress()?;
}
// Copy resource image from /boot partition to /run/images and uncompress
// with xz if it is a compressed file. Update `self.path` to refer to the
// copy rather than the source file.
fn copy_to_run(&mut self) -> bool {
if let Err(err) = fs::create_dir_all(RUN_DIRECTORY) {
warn!("Error creating {} directory: {}", RUN_DIRECTORY, err);
return false;
}
let mut new_path = PathBuf::from(RUN_DIRECTORY);
new_path.set_file_name(self.path.file_name().unwrap());
if let Err(err) = fs::copy(&self.path, &new_path) {
warn!("Error copying {} to {}: {}", self.path.display(), new_path.display(), err);
return false;
let mount_path = self.mount_path();
let loopdev = self.create_loopdev()?;
info!("Loop device created: {}", loopdev.display());
info!("Mounting to: {}", mount_path.display());
fs::create_dir_all(&mount_path)?;
util::mount(&loopdev.to_string_lossy(), mount_path, Some("-oro"))
}
if new_path.extension().unwrap() == "xz" {
if let Err(err) = new_path.xz_uncompress() {
warn!("Error uncompressing {}: {}", new_path.display(), err);
return false;
}
let stem = new_path.file_stem().unwrap().to_owned();
new_path.set_file_name(stem);
}
self.path.push(new_path);
if let Err(err) = self.maybe_decompress_image() {
warn!("Error decompressing image: {}", err);
return false;
}
true
}
fn maybe_decompress_image(&self) -> Result<()> {
let mut image = File::open(&self.path)?;
let hdr = ImageHeader::from_reader(&mut image)?;
if !hdr.has_flag(ImageHeader::FLAG_DATA_COMPRESSED) {
return Ok(())
}
info!("Decompressing internal image data");
let mut tempfile = self.write_compressed_tempfile(&mut image)?;
tempfile.xz_uncompress()?;
tempfile.set_extension("");
self.write_uncompressed_image(&hdr, &tempfile)?;
Ok(())
}
fn write_compressed_tempfile<R: Read>(&self, reader: &mut R) -> Result<PathBuf> {
let mut tmp_path = Path::new(RUN_DIRECTORY).join(format!("{}-tmp", self.name));
tmp_path.set_extension("xz");
let mut tmp_out = File::create(&tmp_path)?;
io::copy(reader, &mut tmp_out)?;
Ok(tmp_path)
}
fn write_uncompressed_image(&self, hdr: &ImageHeader, tempfile: &Path) -> Result<()> {
let mut image_out = File::create(&self.path)?;
hdr.clear_flag(ImageHeader::FLAG_DATA_COMPRESSED);
hdr.write_header(&mut image_out)?;
let mut tmp_in = File::open(&tempfile)?;
io::copy(&mut tmp_in, &mut image_out)?;
fs::remove_file(tempfile)?;
Ok(())
}
// Search for resource image file on any currently mounted /boot
// as well as on every UEFI ESP partition on the system.
//
// Return `true` if found
fn search_boot_partitions(&mut self) -> bool {
// Is /boot already mounted?
if Path::new("/boot").is_mounted() {
if self.search_current_boot_partition() && self.copy_to_run() {
info!("Image found on currently mounted boot partition and copied to {}", self.path.display());
return true;
}
let _ = Path::new("/boot").umount();
}
let partitions = match DiskPartition::boot_partitions() {
Ok(ps) => ps,
Err(e) => {
warn!("Error reading disk partition information: {}", e);
return false;
},
};
for part in partitions {
if part.mount("/boot") {
if self.search_current_boot_partition() && self.copy_to_run() {
part.umount();
info!("Image found on boot partition {} and copied to {}", part.path().display(), self.path.display());
return true;
}
part.umount();
}
}
false
}
// Search for resource file on currently mounted /boot partition
// with both .img and .img.xz extensions.
// Return `true` if found.
fn search_current_boot_partition(&mut self) -> bool {
let mut path = PathBuf::from(BOOT_BASEDIR);
for ext in ["img", "img.xz"].iter() {
path.set_file_name(format!("{}.{}", self.name, ext));
if path.exists() {
self.path.push(path);
return true;
}
}
false
pub fn create_loopdev(&self) -> Result<PathBuf> {
let args = format!("--offset 4096 -f --show {}", self.path.display());
let output = util::exec_cmdline_with_output("/sbin/losetup", args)?;
Ok(PathBuf::from(output))
}
// Return the path at which to mount this resource image.
fn mount_path(&self) -> PathBuf {
PathBuf::from(format!("{}/{}.mountpoint", RUN_DIRECTORY, self.name))
PathBuf::from(format!("{}/{}.mountpoint", RUN_DIRECTORY, self.metainfo.image_type()))
}
// Read and process a manifest file in the root directory of a mounted resource image.
@ -273,13 +272,14 @@ impl ResourceImage {
let manifest = self.mount_path().join("manifest");
if !manifest.exists() {
warn!("No manifest file found for resource image: {}", self.path.display());
} else {
for line in manifest.read_as_lines()? {
return Ok(())
}
let s = fs::read_to_string(manifest)?;
for line in s.lines() {
if let Err(e) = self.process_manifest_line(&line) {
warn!("Processing manifest file for resource image ({}): {}", self.path.display(), e);
}
}
}
Ok(())
}
@ -305,29 +305,49 @@ impl ResourceImage {
let to = Path::new("/sysroot").join(path_to);
info!("Bind mounting {} to {} from manifest", from.display(), to.display());
from.bind_mount(&to)
util::mount(&from.to_string_lossy(), to, Some("--bind"))
}
// If the /storage directory is not mounted, attempt to mount it.
// Return true if already mounted or if the attempt to mount it succeeds.
fn ensure_storage_mounted() -> bool {
if Path::new("/sysroot/storage").is_mounted() {
return true
fn ensure_storage_mounted() -> Result<bool> {
if Mount::is_path_mounted("/dev/mapper/citadel-storage")? {
return Ok(true);
}
let path = Path::new("/dev/mapper/citadel-storage");
if !path.exists() {
return false
return Ok(false);
}
info!("Mounting /sysroot/storage directory");
const MOUNT_ARGS: &str = "-odefaults,nossd,noatime,commit=120";
match path.mount_with_args("/sysroot/storage", MOUNT_ARGS) {
Err(e) => {
warn!("failed to mount /sysroot/storage: {}", e);
false
},
Ok(()) => true,
let res = util::mount(
"/dev/mapper/citadel-storage",
"/sysroot/storage",
Some("-odefaults,nossd,noatime,commit=120")
);
if let Err(err) = res {
warn!("failed to mount /sysroot/storage: {}", err);
return Ok(false);
}
Ok(true)
}
fn read_rootfs_channel() -> Result<String> {
let s = fs::read_to_string("/sysroot/etc/citadel-channel")
.context("Failed to open /sysroot/etc/citadel-channel")?;
match s.split_whitespace().next() {
Some(s) => Ok(s.to_owned()),
None => Err(format_err!("Failed to parse /sysroot/etc/citadel-channel contents")),
}
}
fn image_filename(image_type: &str) -> String {
if image_type == "modules" {
let utsname = util::uname();
let v = utsname.release().split("-").collect::<Vec<_>>();
format!("citadel-modules-{}.img", v[0])
} else {
format!("citadel-{}.img", image_type)
}
}
}

129
libcitadel/src/util.rs Normal file
View File

@ -0,0 +1,129 @@
use std::path::Path;
use std::process::{Command,ExitStatus,Stdio};
use std::mem;
use libc::{self, c_char};
use std::ffi::CStr;
use std::str::from_utf8_unchecked;
use failure::ResultExt;
use Result;
pub fn ensure_command_exists(cmd_path: &str) -> Result<()> {
if !Path::new(cmd_path).exists() {
bail!("Cannot execute '{}': command does not exist");
}
Ok(())
}
pub fn exec_cmdline<S: AsRef<str>>(cmd_path: &str, args: S) -> Result<()> {
ensure_command_exists(cmd_path)?;
let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>();
let status = Command::new(cmd_path)
.args(args)
.stderr(Stdio::inherit())
.status()?;
check_cmd_status(cmd_path, &status)
}
pub fn exec_cmdline_with_output<S: AsRef<str>>(cmd_path: &str, args: S) -> Result<String> {
ensure_command_exists(cmd_path)?;
let args: Vec<&str> = args.as_ref().split_whitespace().collect::<Vec<_>>();
let res = Command::new(cmd_path)
.args(args)
.stderr(Stdio::inherit())
.output()
.context(format!("unable to execute {}", cmd_path))?;
check_cmd_status(cmd_path, &res.status)?;
Ok(String::from_utf8(res.stdout).unwrap().trim().to_owned())
}
fn check_cmd_status(cmd_path: &str, status: &ExitStatus) -> Result<()> {
if !status.success() {
match status.code() {
Some(code) => bail!("command {} failed with exit code: {}", cmd_path, code),
None => bail!("command {} failed with no exit code", cmd_path),
}
}
Ok(())
}
pub fn sha256<P: AsRef<Path>>(path: P) -> Result<String> {
let output = exec_cmdline_with_output("/usr/bin/sha256sum", format!("{}", path.as_ref().display()))
.context(format!("failed to calculate sha256 on {}", path.as_ref().display()))?;
let v: Vec<&str> = output.split_whitespace().collect();
Ok(v[0].trim().to_owned())
}
pub fn xz_compress<P: AsRef<Path>>(path: P) -> Result<()> {
exec_cmdline("/usr/bin/xz", format!("-T0 {}", path.as_ref().display()))
.context(format!("failed to compress {}", path.as_ref().display()))?;
Ok(())
}
pub fn xz_decompress<P: AsRef<Path>>(path: P) -> Result<()> {
exec_cmdline("/usr/bin/xz", format!("-d {}", path.as_ref().display()))
.context(format!("failed to decompress {}", path.as_ref().display()))?;
Ok(())
}
pub fn mount<P: AsRef<Path>>(source: &str, target: P, options: Option<&str>) -> Result<()> {
let paths = format!("{} {}", source, target.as_ref().display());
let args = match options {
Some(s) => format!("{} {}", s, paths),
None => paths,
};
exec_cmdline("/usr/bin/mount", args)
}
pub fn umount<P: AsRef<Path>>(path: P) -> Result<()> {
let args = format!("{}", path.as_ref().display());
exec_cmdline("/usr/bin/umount", args)
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct UtsName(libc::utsname);
#[allow(dead_code)]
impl UtsName {
pub fn sysname(&self) -> &str {
to_str(&(&self.0.sysname as *const c_char ) as *const *const c_char)
}
pub fn nodename(&self) -> &str {
to_str(&(&self.0.nodename as *const c_char ) as *const *const c_char)
}
pub fn release(&self) -> &str {
to_str(&(&self.0.release as *const c_char ) as *const *const c_char)
}
pub fn version(&self) -> &str {
to_str(&(&self.0.version as *const c_char ) as *const *const c_char)
}
pub fn machine(&self) -> &str {
to_str(&(&self.0.machine as *const c_char ) as *const *const c_char)
}
}
pub fn uname() -> UtsName {
unsafe {
let mut ret: UtsName = mem::uninitialized();
libc::uname(&mut ret.0);
ret
}
}
#[inline]
fn to_str<'a>(s: *const *const c_char) -> &'a str {
unsafe {
let res = CStr::from_ptr(*s).to_bytes();
from_utf8_unchecked(res)
}
}

124
libcitadel/src/verity.rs Normal file
View File

@ -0,0 +1,124 @@
use std::path::{Path,PathBuf};
use std::collections::HashMap;
use std::process::{Command, Stdio};
use failure::ResultExt;
use {Result,ImageHeader,MetaInfo,Partition,util};
const VERITYSETUP: &str = "/sbin/veritysetup";
const LOSETUP: &str = "/sbin/losetup";
/// Generate dm-verity hashtree for a disk image and store in external file.
/// Parse output from veritysetup command and return as `VerityOutput`.
pub fn generate_initial_hashtree<P: AsRef<Path>, Q:AsRef<Path>>(source: P, hashtree: Q) -> Result<VerityOutput> {
let args = format!("format {} {}", source.as_ref().display(), hashtree.as_ref().display());
let output = util::exec_cmdline_with_output(VERITYSETUP, args)
.context("creating initial hashtree with veritysetup format failed")?;
Ok(VerityOutput::parse(&output))
}
pub fn generate_image_hashtree<P: AsRef<Path>>(image: P, metainfo: &MetaInfo) -> Result<VerityOutput> {
let args = format!("--hash-offset={} --data-blocks={} --salt={} format {} {}",
metainfo.nblocks() * 4096, metainfo.nblocks(), metainfo.verity_salt(),
image.as_ref().display(), image.as_ref().display());
let output = util::exec_cmdline_with_output(VERITYSETUP, args)
.context("Failed to generate hashtree with veritysetup")?;
// XXX check that root hash matches
Ok(VerityOutput::parse(&output))
}
pub fn verify_image<P: AsRef<Path>>(image: P, metainfo: &MetaInfo) -> Result<bool> {
let arg_offset = format!("--hash-offset={}", metainfo.nblocks() * 4096);
let loopdev = create_image_loop_device(image.as_ref())?;
let status = Command::new(VERITYSETUP)
.args(&[ arg_offset.as_str(), "verify", &loopdev, &loopdev, metainfo.verity_root()])
.stderr(Stdio::inherit())
.status()?;
util::exec_cmdline(LOSETUP, format!("-d {}", loopdev))
.context("Error removing loop device created for verification")?;
Ok(status.success())
}
pub fn setup_image_device<P: AsRef<Path>>(image: P) -> Result<PathBuf> {
let header = ImageHeader::from_file(image.as_ref())?;
let metainfo = header.metainfo()?;
let devname = if metainfo.image_type() == "rootfs" {
String::from("rootfs")
} else {
format!("verity-{}", metainfo.image_type())
};
let loopdev = create_image_loop_device(image.as_ref())?;
setup_device(&loopdev, &devname, metainfo.nblocks(), metainfo.verity_root())
}
pub fn setup_partition_device(partition: &Partition) -> Result<PathBuf> {
let metainfo = partition.header().metainfo()?;
let srcdev = partition.path().to_str().unwrap();
setup_device(srcdev, "rootfs", metainfo.nblocks(), metainfo.verity_root())
}
fn setup_device(srcdev: &str, devname: &str, nblocks: usize, roothash: &str) -> Result<PathBuf> {
let args = format!("--hash-offset={} --data-blocks={} create {} {} {} {}",
nblocks * 4096, nblocks, devname, srcdev, srcdev, roothash);
util::exec_cmdline(VERITYSETUP, args)
.context("Failed to set up verity device")?;
Ok(PathBuf::from(format!("/dev/mapper/{}", devname)))
}
fn create_image_loop_device(file: &Path) -> Result<String> {
let args = format!("--offset 4096 -f --show {}", file.display());
let output = util::exec_cmdline_with_output(LOSETUP, args)?;
Ok(output)
}
/// The output from the `veritysetup format` command can be parsed as key/value
/// pairs. This class parses the output and stores it in a map for querying.
pub struct VerityOutput {
output: String,
map: HashMap<String, String>,
}
impl VerityOutput {
/// Parse the string `output` as standard output from the dm-verity
/// `veritysetup format` command.
fn parse(output: &str) -> VerityOutput {
let mut vo = VerityOutput {
output: output.to_owned(),
map: HashMap::new(),
};
for line in output.lines() {
vo.parse_line(line);
}
vo
}
fn parse_line(&mut self, line: &str) {
let v = line.split(':').map(|s| s.trim()).collect::<Vec<_>>();
if v.len() == 2 {
self.map.insert(v[0].to_owned(), v[1].to_owned());
}
}
pub fn root_hash(&self) -> Option<&str> {
self.map.get("Root hash").map(|s| s.as_str())
}
pub fn salt(&self) -> Option<&str> {
self.map.get("Salt").map(|s| s.as_str())
}
pub fn output(&self) -> &str {
&self.output
}
}