Compare commits

...

2 Commits

Author SHA1 Message Date
Karolin Varner
ed5d7b4fa4 WIP support for the Hermit microkernel.
A whole bunch of close-to-the-metal crates are yielding compiler errors
upon compilation with --target x86_64-unknown-hermit. MIO is not.

Using hermit might necessitate relying on threads for IO instead of
using MIO.

https://github.com/hermit-os/kernel/issues/1043
2024-01-27 23:17:10 +01:00
Karolin Varner
97a2a30678 feat: First version of broker based WireGuard PSK interface
This allows us to run with minimal priviledges in the Rosenpass process itself
2024-01-27 21:48:36 +01:00
23 changed files with 1391 additions and 187 deletions

455
Cargo.lock generated
View File

@@ -190,7 +190,7 @@ dependencies = [
"regex",
"rustc-hash",
"shlex",
"syn",
"syn 2.0.39",
"which",
]
@@ -392,7 +392,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
"syn 2.0.39",
]
[[package]]
@@ -434,6 +434,15 @@ dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "criterion"
version = "0.4.0"
@@ -519,6 +528,41 @@ dependencies = [
"typenum",
]
[[package]]
name = "darling"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 1.0.109",
]
[[package]]
name = "darling_macro"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a"
dependencies = [
"darling_core",
"quote",
"syn 1.0.109",
]
[[package]]
name = "derive_arbitrary"
version = "1.3.2"
@@ -527,7 +571,38 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.39",
]
[[package]]
name = "derive_builder"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "derive_builder_macro"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73"
dependencies = [
"derive_builder_core",
"syn 1.0.109",
]
[[package]]
@@ -582,6 +657,43 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "filetime"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"windows-sys 0.52.0",
]
[[package]]
name = "flate2"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -661,6 +773,17 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f54046de71e77899abc5fee9a9ada4b6299e0829cf26cf47cdfe2163be3d33a"
dependencies = [
"flate2",
"tar",
"ureq",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
@@ -691,6 +814,22 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "1.9.3"
@@ -840,17 +979,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "memsec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa0916b001582d253822171bd23f4a0229d32b9507fae236f5da8cad515ba7c"
dependencies = [
"getrandom",
"libc",
"windows-sys 0.45.0",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@@ -878,6 +1006,31 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "neli"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1805440578ced23f85145d00825c0a831e43c587132a90e100552172543ae30"
dependencies = [
"byteorder",
"libc",
"log",
"neli-proc-macros",
]
[[package]]
name = "neli-proc-macros"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c168194d373b1e134786274020dae7fc5513d565ea2ebb9bc9ff17ffb69106d4"
dependencies = [
"either",
"proc-macro2",
"quote",
"serde",
"syn 1.0.109",
]
[[package]]
name = "nom"
version = "7.1.3"
@@ -954,6 +1107,12 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "plotters"
version = "0.3.5"
@@ -1006,7 +1165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d"
dependencies = [
"proc-macro2",
"syn",
"syn 2.0.39",
]
[[package]]
@@ -1086,6 +1245,15 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "regex"
version = "1.10.2"
@@ -1115,6 +1283,20 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "ring"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
dependencies = [
"cc",
"getrandom",
"libc",
"spin",
"untrusted",
"windows-sys 0.48.0",
]
[[package]]
name = "rosenpass"
version = "0.2.1"
@@ -1123,6 +1305,7 @@ dependencies = [
"clap 4.4.10",
"criterion",
"env_logger",
"hermit",
"log",
"memoffset",
"mio",
@@ -1135,6 +1318,7 @@ dependencies = [
"rosenpass-secret-memory",
"rosenpass-to",
"rosenpass-util",
"rosenpass-wireguard-broker",
"serde",
"stacker",
"static_assertions",
@@ -1167,7 +1351,6 @@ dependencies = [
name = "rosenpass-constant-time"
version = "0.1.0"
dependencies = [
"memsec",
"rosenpass-to",
]
@@ -1211,7 +1394,6 @@ dependencies = [
"allocator-api2-tests",
"anyhow",
"log",
"memsec",
"rand",
"rosenpass-to",
"rosenpass-util",
@@ -1235,6 +1417,23 @@ dependencies = [
"typenum",
]
[[package]]
name = "rosenpass-wireguard-broker"
version = "0.1.0"
dependencies = [
"anyhow",
"clap 4.4.10",
"env_logger",
"log",
"mio",
"paste",
"rosenpass-lenses",
"rosenpass-to",
"rosenpass-util",
"thiserror",
"wireguard-uapi",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
@@ -1258,9 +1457,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.26"
version = "0.38.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a"
checksum = "bfeae074e687625746172d639330f1de242a178bf3189b51e35a7a21573513ac"
dependencies = [
"bitflags 2.4.1",
"errno",
@@ -1269,6 +1468,28 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "rustls"
version = "0.21.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
dependencies = [
"log",
"ring",
"rustls-webpki",
"sct",
]
[[package]]
name = "rustls-webpki"
version = "0.101.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "ryu"
version = "1.0.15"
@@ -1290,6 +1511,16 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sct"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "semver"
version = "1.0.21"
@@ -1313,7 +1544,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.39",
]
[[package]]
@@ -1388,6 +1619,17 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.39"
@@ -1399,6 +1641,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tar"
version = "0.4.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb"
dependencies = [
"filetime",
"libc",
"xattr",
]
[[package]]
name = "termcolor"
version = "1.4.0"
@@ -1437,7 +1690,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.39",
]
[[package]]
@@ -1450,6 +1703,21 @@ dependencies = [
"serde_json",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
version = "0.7.8"
@@ -1490,12 +1758,27 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-bidi"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "universal-hash"
version = "0.5.1"
@@ -1506,6 +1789,39 @@ dependencies = [
"subtle",
]
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97"
dependencies = [
"base64",
"flate2",
"log",
"once_cell",
"rustls",
"rustls-webpki",
"url",
"webpki-roots",
]
[[package]]
name = "url"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "utf8parse"
version = "0.2.1"
@@ -1555,7 +1871,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
"syn 2.0.39",
"wasm-bindgen-shared",
]
@@ -1577,7 +1893,7 @@ checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.39",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -1598,6 +1914,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki-roots"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10"
[[package]]
name = "which"
version = "4.4.2"
@@ -1641,15 +1963,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
@@ -1668,21 +1981,6 @@ dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
@@ -1713,12 +2011,6 @@ dependencies = [
"windows_x86_64_msvc 0.52.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
@@ -1731,12 +2023,6 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
@@ -1749,12 +2035,6 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
@@ -1767,12 +2047,6 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
@@ -1785,12 +2059,6 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
@@ -1803,12 +2071,6 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
@@ -1821,12 +2083,6 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
@@ -1848,6 +2104,27 @@ dependencies = [
"memchr",
]
[[package]]
name = "wireguard-uapi"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ba4e9811befc20af3b6efb15924a7238ee5e8e8706a196576462a00b9f1af1"
dependencies = [
"derive_builder",
"libc",
"neli",
"thiserror",
]
[[package]]
name = "xattr"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbc6ab6ec1907d1a901cdbcd2bd4cb9e7d64ce5c9739cbb97d3c391acd8c7fae"
dependencies = [
"libc",
]
[[package]]
name = "zeroize"
version = "1.7.0"

View File

@@ -12,10 +12,12 @@ members = [
"fuzz",
"secret-memory",
"lenses",
"wireguard-broker",
]
default-members = [
"rosenpass"
"rosenpass",
"wireguard-broker"
]
[workspace.metadata.release]
@@ -32,6 +34,7 @@ rosenpass-to = { path = "to" }
rosenpass-secret-memory = { path = "secret-memory" }
rosenpass-oqs = { path = "oqs" }
rosenpass-lenses = { path = "lenses" }
rosenpass-wireguard-broker = { path = "wireguard-broker" }
criterion = "0.4.0"
test_bin = "0.4.0"
libfuzzer-sys = "0.4"
@@ -47,15 +50,15 @@ toml = "0.7.8"
static_assertions = "1.1.0"
allocator-api2 = "0.2.14"
allocator-api2-tests = "0.2.14"
memsec = "0.6.3"
rand = "0.8.5"
wireguard-uapi = "3.0.0"
typenum = "1.17.0"
log = { version = "0.4.20" }
clap = { version = "4.4.10", features = ["derive"] }
serde = { version = "1.0.193", features = ["derive"] }
arbitrary = { version = "1.3.2", features = ["derive"] }
anyhow = { version = "1.0.75", features = ["backtrace", "std"] }
mio = { version = "0.8.9", features = ["net", "os-poll"] }
mio = { version = "0.8.9", features = ["net"] }
oqs-sys = { version = "0.8", default-features = false, features = ['classic_mceliece', 'kyber'] }
blake2 = "0.10.6"
chacha20poly1305 = { version = "0.10.1", default-features = false, features = [ "std", "heapless" ] }

View File

@@ -13,4 +13,3 @@ readme = "readme.md"
[dependencies]
rosenpass-to = { workspace = true }
memsec = { workspace = true }

View File

@@ -29,14 +29,13 @@ pub fn xor(src: &[u8]) -> impl To<[u8], ()> + '_ {
#[inline]
pub fn memcmp(a: &[u8], b: &[u8]) -> bool {
a.len() == b.len()
&& unsafe { memsec::memeq(a.as_ptr() as *const u8, b.as_ptr() as *const u8, a.len()) }
a == b
}
#[inline]
pub fn compare(a: &[u8], b: &[u8]) -> i32 {
assert!(a.len() == b.len());
unsafe { memsec::memcmp(a.as_ptr(), b.as_ptr(), a.len()) }
a.cmp(b) as i32
}
/// Interpret the given slice as a little-endian unsigned integer

View File

@@ -1,10 +1,10 @@
[package]
name = "rosenpass"
description = "Build post-quantum-secure VPNs with WireGuard!"
version = "0.2.1"
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Build post-quantum-secure VPNs with WireGuard!"
homepage = "https://rosenpass.eu/"
repository = "https://github.com/rosenpass/rosenpass"
readme = "readme.md"
@@ -21,6 +21,7 @@ rosenpass-cipher-traits = { workspace = true }
rosenpass-to = { workspace = true }
rosenpass-secret-memory = { workspace = true }
rosenpass-lenses = { workspace = true }
rosenpass-wireguard-broker = { workspace = true }
anyhow = { workspace = true }
static_assertions = { workspace = true }
memoffset = { workspace = true }
@@ -34,6 +35,9 @@ clap = { workspace = true }
mio = { workspace = true }
rand = { workspace = true }
[target.'cfg(target_os = "hermit")'.dependencies]
hermit = { version = "0.8", features = ["pci", "pci-ids", "acpi", "fsgsbase", "tcp", "rtl8139"]}
[build-dependencies]
anyhow = { workspace = true }

View File

@@ -1,38 +1,25 @@
use anyhow::bail;
use anyhow::Result;
use log::{debug, error, info, warn};
use mio::Interest;
use mio::Token;
use rosenpass_util::file::fopen_w;
use std::cell::Cell;
use std::io::Write;
use std::io::ErrorKind;
use std::net::Ipv4Addr;
use std::net::Ipv6Addr;
use std::net::SocketAddr;
use std::net::SocketAddrV4;
use std::net::SocketAddrV6;
use std::net::ToSocketAddrs;
use std::cell::{Cell, RefCell};
use std::io::{ErrorKind, Write};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs, TcpStream};
use std::path::PathBuf;
use std::process::Command;
use std::process::Stdio;
use std::slice;
use std::thread;
use std::time::Duration;
use crate::{
config::Verbosity,
protocol::{CryptoServer, MsgBuf, PeerPtr, SPk, SSk, SymKey, Timing},
};
use rosenpass_util::attempt;
use anyhow::{bail, Result};
use log::{error, info, warn};
use mio::{Interest, Token};
use rosenpass_secret_memory::Public;
use rosenpass_util::b64::{b64_writer, fmt_b64};
use rosenpass_util::{attempt, file::fopen_w};
use rosenpass_wireguard_broker::api::mio_client::MioBrokerClient as PskBroker;
use rosenpass_wireguard_broker::WireGuardBroker;
use crate::config::Verbosity;
use crate::protocol::{CryptoServer, MsgBuf, PeerPtr, SPk, SSk, SymKey, Timing};
const IPV4_ANY_ADDR: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
const IPV6_ANY_ADDR: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
fn ipv4_any_binding() -> SocketAddr {
// addr, port
SocketAddr::V4(SocketAddrV4::new(IPV4_ANY_ADDR, 0))
@@ -43,6 +30,19 @@ fn ipv6_any_binding() -> SocketAddr {
SocketAddr::V6(SocketAddrV6::new(IPV6_ANY_ADDR, 0, 0, 0))
}
#[derive(Default)]
struct MioTokenDispenser {
counter: usize,
}
impl MioTokenDispenser {
fn get_token(&mut self) -> Token {
let r = self.counter;
self.counter += 1;
Token(r)
}
}
#[derive(Default, Debug)]
pub struct AppPeer {
pub outfile: Option<PathBuf>,
@@ -59,14 +59,24 @@ impl AppPeer {
}
}
#[derive(Default, Debug)]
#[derive(Debug)]
pub struct WireguardOut {
// impl KeyOutput
pub dev: String,
pub pk: String,
pub pk: Public<32>,
pub extra_params: Vec<String>,
}
impl Default for WireguardOut {
fn default() -> Self {
Self {
dev: Default::default(),
pk: Public::zero(),
extra_params: Default::default(),
}
}
}
/// Holds the state of the application, namely the external IO
///
/// Responsible for file IO, network IO
@@ -77,6 +87,7 @@ pub struct AppServer {
pub sockets: Vec<mio::net::UdpSocket>,
pub events: mio::Events,
pub mio_poll: mio::Poll,
pub psk_broker: RefCell<PskBroker>,
pub peers: Vec<AppPeer>,
pub verbosity: Verbosity,
pub all_sockets_drained: bool,
@@ -341,11 +352,24 @@ impl AppServer {
sk: SSk,
pk: SPk,
addrs: Vec<SocketAddr>,
psk_broker_socket: TcpStream,
verbosity: Verbosity,
) -> anyhow::Result<Self> {
// setup mio
let mio_poll = mio::Poll::new()?;
let events = mio::Events::with_capacity(8);
let mut dispenser = MioTokenDispenser::default();
// Create the Wireguard broker connection
let psk_broker = {
let mut sock = mio::net::TcpStream::from_std(psk_broker_socket);
mio_poll.registry().register(
&mut sock,
dispenser.get_token(),
Interest::READABLE | Interest::WRITABLE,
)?;
PskBroker::new(sock)
};
// bind each SocketAddr to a socket
let maybe_sockets: Result<Vec<_>, _> =
@@ -430,6 +454,7 @@ impl AppServer {
Ok(Self {
crypt: CryptoServer::new(sk, pk),
peers: Vec::new(),
psk_broker: RefCell::new(psk_broker),
verbosity,
sockets,
events,
@@ -624,31 +649,9 @@ impl AppServer {
}
if let Some(owg) = ap.outwg.as_ref() {
let mut child = Command::new("wg")
.arg("set")
.arg(&owg.dev)
.arg("peer")
.arg(&owg.pk)
.arg("preshared-key")
.arg("/dev/stdin")
.stdin(Stdio::piped())
.args(&owg.extra_params)
.spawn()?;
b64_writer(child.stdin.take().unwrap()).write_all(key.secret())?;
thread::spawn(move || {
let status = child.wait();
if let Ok(status) = status {
if status.success() {
debug!("successfully passed psk to wg")
} else {
error!("could not pass psk to wg {:?}", status)
}
} else {
error!("wait failed: {:?}", status)
}
});
self.psk_broker
.borrow_mut()
.set_psk(&owg.dev, owg.pk.value, *key.secret())?;
}
Ok(())
@@ -706,9 +709,16 @@ impl AppServer {
// only poll if we drained all sockets before
if self.all_sockets_drained {
self.mio_poll.poll(&mut self.events, Some(timeout))?;
self.mio_poll
.poll(&mut self.events, Some(timeout))
.or_else(|e| match e.kind() {
ErrorKind::Interrupted | ErrorKind::WouldBlock => Ok(()),
_ => Err(e),
})?;
}
self.psk_broker.get_mut().poll()?;
let mut would_block_count = 0;
for (sock_no, socket) in self.sockets.iter_mut().enumerate() {
match socket.recv_from(buf) {

View File

@@ -1,10 +1,16 @@
use anyhow::{bail, ensure};
use std::io::{BufReader, Read};
use std::net::TcpStream;
use std::path::PathBuf;
use anyhow::{bail, ensure, Context};
use clap::Parser;
use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::kem::StaticKem;
use rosenpass_secret_memory::file::StoreSecret;
use rosenpass_secret_memory::Public;
use rosenpass_util::b64::b64_reader;
use rosenpass_util::file::{LoadValue, LoadValueB64};
use std::path::PathBuf;
use crate::app_server;
use crate::app_server::AppServer;
@@ -62,6 +68,7 @@ pub enum Cli {
config_file: PathBuf,
/// Forcefully overwrite existing config file
/// - [ ] Janepie
#[clap(short, long)]
force: bool,
},
@@ -258,11 +265,14 @@ impl Cli {
let sk = SSk::load(&config.secret_key)?;
let pk = SPk::load(&config.public_key)?;
// Spawn the psk broker and use socketpair(2) to connect with them
let psk_broker_socket = TcpStream::connect("127.0.0.1:8001")?;
// start an application server
let mut srv = std::boxed::Box::<AppServer>::new(AppServer::new(
sk,
pk,
config.listen,
psk_broker_socket,
config.verbosity,
)?);
@@ -272,11 +282,24 @@ impl Cli {
cfg_peer.pre_shared_key.map(SymKey::load_b64).transpose()?,
SPk::load(&cfg_peer.public_key)?,
cfg_peer.key_out,
cfg_peer.wg.map(|cfg| app_server::WireguardOut {
dev: cfg.device,
pk: cfg.peer,
extra_params: cfg.extra_params,
}),
cfg_peer
.wg
.map(|cfg| -> anyhow::Result<_> {
let b64pk = &cfg.peer;
let mut pk = Public::zero();
b64_reader(BufReader::new(b64pk.as_bytes()))
.read_exact(&mut pk.value)
.with_context(|| {
format!("Could not decode base64 public key: '{b64pk}'")
})?;
Ok(app_server::WireguardOut {
pk,
dev: cfg.device,
extra_params: cfg.extra_params,
})
})
.transpose()?,
cfg_peer.endpoint.clone(),
)?;
}

View File

@@ -2,6 +2,9 @@ use log::error;
use rosenpass::cli::Cli;
use std::process::exit;
#[cfg(target_os = "hermit")]
use hermit as _;
/// Catches errors, prints them through the logger, then exits
pub fn main() {
// default to displaying warning and error log messages only

2
rust-toolchain.toml Normal file
View File

@@ -0,0 +1,2 @@
[toolchain]
channel = "1.74.1"

View File

@@ -15,7 +15,6 @@ rosenpass-to = { workspace = true }
rosenpass-util = { workspace = true }
zeroize = { workspace = true }
rand = { workspace = true }
memsec = { workspace = true }
allocator-api2 = { workspace = true }
log = { workspace = true }

View File

@@ -1,7 +1,7 @@
use std::fmt;
use std::ptr::NonNull;
use allocator_api2::alloc::{AllocError, Allocator, Layout};
use allocator_api2::alloc::{AllocError, Allocator, Layout, Global};
#[derive(Copy, Clone, Default)]
struct MemsecAllocatorContents;
@@ -9,7 +9,7 @@ struct MemsecAllocatorContents;
/// Memory allocation using using the memsec crate
#[derive(Copy, Clone, Default)]
pub struct MemsecAllocator {
_dummy_private_data: MemsecAllocatorContents,
global: Global
}
/// A box backed by the memsec allocator
@@ -29,40 +29,18 @@ pub fn memsec_vec<T>() -> MemsecVec<T> {
impl MemsecAllocator {
pub fn new() -> Self {
Self {
_dummy_private_data: MemsecAllocatorContents,
global: Global
}
}
}
unsafe impl Allocator for MemsecAllocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
// Call memsec allocator
let mem: Option<NonNull<[u8]>> = unsafe { memsec::malloc_sized(layout.size()) };
// Unwrap the option
let Some(mem) = mem else {
log::error!("Allocation {layout:?} was requested but memsec returned a null pointer");
return Err(AllocError);
};
// Ensure the right alignment is used
let off = (mem.as_ptr() as *const u8).align_offset(layout.align());
if off != 0 {
log::error!("Allocation {layout:?} was requested but memsec returned allocation \
with offset {off} from the requested alignment. Memsec always allocates values \
at the end of a memory page for security reasons, custom alignments are not supported. \
You could try allocating an oversized value.");
unsafe { memsec::free(mem) };
return Err(AllocError);
};
Ok(mem)
self.global.allocate(layout)
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, _layout: Layout) {
unsafe {
memsec::free(ptr);
}
unsafe { self.global.deallocate(ptr, _layout) }
}
}

0
util/src/fd.rs Normal file
View File

View File

@@ -1,6 +1,7 @@
#![recursion_limit = "256"]
pub mod b64;
pub mod fd;
pub mod file;
pub mod functional;
pub mod mem;

View File

@@ -0,0 +1,41 @@
[package]
name = "rosenpass-wireguard-broker"
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Rosenpass internal broker that runs as root and supplies exchanged keys to the kernel."
homepage = "https://rosenpass.eu/"
repository = "https://github.com/rosenpass/rosenpass"
readme = "readme.md"
[dependencies]
thiserror = { workspace = true }
rosenpass-lenses = { workspace = true }
paste = { workspace = true } # TODO: Using lenses should not necessitate importing paste
# Privileged only
wireguard-uapi = { workspace = true }
# Socket handler only
rosenpass-to = { workspace = true }
anyhow = { workspace = true }
clap = { workspace = true }
env_logger = { workspace = true }
log = { workspace = true }
# Mio broker client
mio = { workspace = true }
rosenpass-util = { workspace = true }
[[bin]]
name = "rosenpass-wireguard-broker-privileged"
path = "src/bin/priviledged.rs"
test = false
doc = false
[[bin]]
name = "rosenpass-wireguard-broker-socket-handler"
test = false
path = "src/bin/socket_handler.rs"
doc = false

View File

@@ -0,0 +1,5 @@
# Rosenpass internal broker supplying WireGuard with keys.
This crate contains a small application purpose-built to supply WireGuard in the linux kernel with pre-shared keys.
This is an internal library; not guarantee is made about its API at this point in time.

View File

@@ -0,0 +1,152 @@
use std::{borrow::BorrowMut, marker::PhantomData};
use rosenpass_lenses::LenseView;
use crate::{
api::msgs::{self, EnvelopeExt, SetPskRequestExt, SetPskResponseExt},
WireGuardBroker,
};
#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
pub enum BrokerClientPollResponseError<RecvError> {
#[error(transparent)]
IoError(RecvError),
#[error("Invalid message.")]
InvalidMessage,
}
impl<RecvError> From<msgs::InvalidMessageTypeError> for BrokerClientPollResponseError<RecvError> {
fn from(value: msgs::InvalidMessageTypeError) -> Self {
let msgs::InvalidMessageTypeError = value; // Assert that this is a unit type
BrokerClientPollResponseError::<RecvError>::InvalidMessage
}
}
fn io_pollerr<RecvError>(e: RecvError) -> BrokerClientPollResponseError<RecvError> {
BrokerClientPollResponseError::<RecvError>::IoError(e)
}
fn invalid_msg_pollerr<RecvError>() -> BrokerClientPollResponseError<RecvError> {
BrokerClientPollResponseError::<RecvError>::InvalidMessage
}
#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
pub enum BrokerClientSetPskError<SendError> {
#[error(transparent)]
IoError(SendError),
#[error("Interface name out of bounds")]
IfaceOutOfBounds,
}
pub trait BrokerClientIo {
type SendError;
type RecvError;
fn send_msg(&mut self, buf: &[u8]) -> Result<(), Self::SendError>;
fn recv_msg(&mut self) -> Result<Option<&[u8]>, Self::RecvError>;
}
#[derive(Debug)]
pub struct BrokerClient<'a, Io, IoRef>
where
Io: BrokerClientIo,
IoRef: 'a + BorrowMut<Io>,
{
io: IoRef,
_phantom_io: PhantomData<&'a mut Io>,
}
impl<'a, Io, IoRef> BrokerClient<'a, Io, IoRef>
where
Io: BrokerClientIo,
IoRef: 'a + BorrowMut<Io>,
{
pub fn new(io: IoRef) -> Self {
Self {
io,
_phantom_io: PhantomData,
}
}
pub fn io(&self) -> &IoRef {
&self.io
}
pub fn io_mut(&mut self) -> &mut IoRef {
&mut self.io
}
pub fn poll_response(
&mut self,
) -> Result<Option<msgs::SetPskResult>, BrokerClientPollResponseError<Io::RecvError>> {
let res: &[u8] = match self.io.borrow_mut().recv_msg().map_err(io_pollerr)? {
Some(r) => r,
None => return Ok(None),
};
let typ = res.get(0).ok_or(invalid_msg_pollerr())?;
let typ = msgs::MsgType::try_from(*typ)?;
let msgs::MsgType::SetPsk = typ; // Assert type
let res: msgs::Envelope<_, msgs::SetPskResponse<&[u8]>> = res
.envelope_truncating()
.map_err(|_| invalid_msg_pollerr())?;
let res: msgs::SetPskResponse<&[u8]> = res
.payload()
.set_psk_response()
.map_err(|_| invalid_msg_pollerr())?;
let res: msgs::SetPskResponseReturnCode = res.return_code()[0]
.try_into()
.map_err(|_| invalid_msg_pollerr())?;
let res: msgs::SetPskResult = res.into();
Ok(Some(res))
}
}
impl<'a, Io, IoRef> WireGuardBroker for BrokerClient<'a, Io, IoRef>
where
Io: BrokerClientIo,
IoRef: 'a + BorrowMut<Io>,
{
type Error = BrokerClientSetPskError<Io::SendError>;
fn set_psk(
&mut self,
iface: &str,
peer_id: [u8; 32],
psk: [u8; 32],
) -> Result<(), Self::Error> {
use BrokerClientSetPskError::*;
const BUF_SIZE: usize = <msgs::Envelope<(), msgs::SetPskRequest<()>> as LenseView>::LEN;
// Allocate message
let mut req = [0u8; BUF_SIZE];
// Construct message view
let mut req: msgs::Envelope<_, msgs::SetPskRequest<&mut [u8]>> =
(&mut req as &mut [u8]).envelope_truncating().unwrap();
// Populate envelope
req.msg_type_mut()
.copy_from_slice(&[msgs::MsgType::SetPsk as u8]);
{
// Derived payload
let mut req: msgs::SetPskRequest<&mut [u8]> =
req.payload_mut().set_psk_request().unwrap();
// Populate payload
req.peer_id_mut().copy_from_slice(&peer_id);
req.psk_mut().copy_from_slice(&psk);
req.set_iface(iface).ok_or(IfaceOutOfBounds)?;
}
// Send message
self.io
.borrow_mut()
.send_msg(req.all_bytes())
.map_err(IoError)?;
Ok(())
}
}

View File

@@ -0,0 +1,204 @@
use std::collections::VecDeque;
use std::io::{ErrorKind, Read, Write};
use anyhow::{bail, ensure};
use crate::WireGuardBroker;
use super::client::{
BrokerClient, BrokerClientIo, BrokerClientPollResponseError, BrokerClientSetPskError,
};
use super::msgs;
#[derive(Debug)]
pub struct MioBrokerClient {
inner: BrokerClient<'static, MioBrokerClientIo, MioBrokerClientIo>,
}
#[derive(Debug)]
struct MioBrokerClientIo {
socket: mio::net::TcpStream,
send_buf: VecDeque<u8>,
receiving_size: bool,
recv_buf: Vec<u8>,
recv_off: usize,
}
impl MioBrokerClient {
pub fn new(socket: mio::net::TcpStream) -> Self {
let io = MioBrokerClientIo {
socket,
send_buf: VecDeque::new(),
receiving_size: false,
recv_buf: Vec::new(),
recv_off: 0,
};
let inner = BrokerClient::new(io);
Self { inner }
}
pub fn poll(&mut self) -> anyhow::Result<Option<msgs::SetPskResult>> {
self.inner.io_mut().flush()?;
// This sucks
match self.inner.poll_response() {
Ok(res) => {
return Ok(res);
}
Err(BrokerClientPollResponseError::IoError(e)) => {
return Err(e);
}
Err(BrokerClientPollResponseError::InvalidMessage) => {
bail!("Invalid message");
}
};
}
}
impl WireGuardBroker for MioBrokerClient {
type Error = anyhow::Error;
fn set_psk(&mut self, iface: &str, peer_id: [u8; 32], psk: [u8; 32]) -> anyhow::Result<()> {
use BrokerClientSetPskError::*;
let e = self.inner.set_psk(iface, peer_id, psk);
match e {
Ok(()) => Ok(()),
Err(IoError(e)) => Err(e),
Err(IfaceOutOfBounds) => bail!("Interface name size is out of bounds."),
}
}
}
impl BrokerClientIo for MioBrokerClientIo {
type SendError = anyhow::Error;
type RecvError = anyhow::Error;
fn send_msg(&mut self, buf: &[u8]) -> Result<(), Self::SendError> {
self.flush()?;
self.send_or_buffer(&(buf.len() as u64).to_le_bytes())?;
self.send_or_buffer(&buf)?;
self.flush()?;
Ok(())
}
fn recv_msg(&mut self) -> Result<Option<&[u8]>, Self::RecvError> {
// Stale message in receive buffer. Reset!
if self.recv_off == self.recv_buf.len() {
self.receiving_size = true;
self.recv_off = 0;
self.recv_buf.resize(8, 0);
}
// Try filling the receive buffer
self.recv_off += raw_recv(&self.socket, &mut self.recv_buf[self.recv_off..])?;
if self.recv_off < self.recv_buf.len() {
return Ok(None);
}
// Received size, now start receiving
if self.receiving_size {
// Received the size
// Parse the received length
let len: &[u8; 8] = self.recv_buf[..].try_into().unwrap();
let len: usize = u64::from_le_bytes(*len) as usize;
ensure!(
len <= msgs::RESPONSE_MSG_BUFFER_SIZE,
"Oversized buffer ({len}) in psk buffer response."
);
// Prepare the message buffer for receiving an actual message of the given size
self.receiving_size = false;
self.recv_off = 0;
self.recv_buf.resize(len, 0);
// Try to receive the message
return self.recv_msg();
}
// Received an actual message
return Ok(Some(&self.recv_buf[..]));
}
}
impl MioBrokerClientIo {
fn flush(&mut self) -> anyhow::Result<()> {
let (fst, snd) = self.send_buf.as_slices();
let (written, res) = match raw_send(&self.socket, fst) {
Ok(w1) if w1 >= fst.len() => match raw_send(&self.socket, snd) {
Ok(w2) => (w1 + w2, Ok(())),
Err(e) => (w1, Err(e)),
},
Ok(w1) => (w1, Ok(())),
Err(e) => (0, Err(e)),
};
self.send_buf.drain(..written);
(&self.socket).try_io(|| (&self.socket).flush())?;
res
}
fn send_or_buffer(&mut self, buf: &[u8]) -> anyhow::Result<()> {
let mut off = 0;
if self.send_buf.is_empty() {
off += raw_send(&self.socket, buf)?;
}
self.send_buf.extend((&buf[off..]).iter());
Ok(())
}
}
fn raw_send(mut socket: &mio::net::TcpStream, data: &[u8]) -> anyhow::Result<usize> {
let mut off = 0;
socket.try_io(|| {
loop {
if off == data.len() {
return Ok(());
}
match socket.write(&data[off..]) {
Ok(n) => {
off += n;
}
Err(e) if e.kind() == ErrorKind::Interrupted => {
// pass retry
}
Err(e) if off > 0 || e.kind() == ErrorKind::WouldBlock => return Ok(()),
Err(e) => return Err(e),
}
}
})?;
return Ok(off);
}
fn raw_recv(mut socket: &mio::net::TcpStream, out: &mut [u8]) -> anyhow::Result<usize> {
let mut off = 0;
socket.try_io(|| {
loop {
if off == out.len() {
return Ok(());
}
match socket.read(&mut out[off..]) {
Ok(n) => {
off += n;
}
Err(e) if e.kind() == ErrorKind::Interrupted => {
// pass retry
}
Err(e) if off > 0 || e.kind() == ErrorKind::WouldBlock => return Ok(()),
Err(e) => return Err(e),
}
}
})?;
return Ok(off);
}

View File

@@ -0,0 +1,4 @@
pub mod client;
pub mod mio_client;
pub mod msgs;
pub mod server;

View File

@@ -0,0 +1,140 @@
use std::result::Result;
use std::str::{from_utf8, Utf8Error};
use rosenpass_lenses::{lense, LenseView};
pub const REQUEST_MSG_BUFFER_SIZE: usize = <Envelope<(), SetPskRequest<()>> as LenseView>::LEN;
pub const RESPONSE_MSG_BUFFER_SIZE: usize = <Envelope<(), SetPskResponse<()>> as LenseView>::LEN;
lense! { Envelope<M> :=
/// [MsgType] of this message
msg_type: 1,
/// Reserved for future use
reserved: 3,
/// The actual Paylod
payload: M::LEN
}
lense! { SetPskRequest :=
peer_id: 32,
psk: 32,
iface_size: 1, // TODO: We should have variable length strings in lenses
iface_buf: 255
}
impl SetPskRequest<&[u8]> {
pub fn iface_bin(&self) -> &[u8] {
let len = self.iface_size()[0] as usize;
&self.iface_buf()[..len]
}
pub fn iface(&self) -> Result<&str, Utf8Error> {
from_utf8(self.iface_bin())
}
}
impl SetPskRequest<&mut [u8]> {
pub fn set_iface_bin(&mut self, iface: &[u8]) -> Option<()> {
(iface.len() < 256).then_some(())?; // Assert iface.len() < 256
self.iface_size_mut()[0] = iface.len() as u8;
self.iface_buf_mut().fill(0);
(&mut self.iface_buf_mut()[..iface.len()]).copy_from_slice(iface);
Some(())
}
pub fn set_iface(&mut self, iface: &str) -> Option<()> {
self.set_iface_bin(iface.as_bytes())
}
}
lense! { SetPskResponse :=
return_code: 1
}
#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
pub enum SetPskError {
#[error("The wireguard pre-shared-key assignment broker experienced an internal error.")]
InternalError,
#[error("The indicated wireguard interface does not exist")]
NoSuchInterface,
#[error("The indicated peer does not exist on the wireguard interface")]
NoSuchPeer,
}
pub type SetPskResult = Result<(), SetPskError>;
#[repr(u8)]
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub enum SetPskResponseReturnCode {
Success = 0x00,
InternalError = 0x01,
NoSuchInterface = 0x02,
NoSuchPeer = 0x03,
}
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct InvalidSetPskResponseError;
impl TryFrom<u8> for SetPskResponseReturnCode {
type Error = InvalidSetPskResponseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
use SetPskResponseReturnCode::*;
match value {
0x00 => Ok(Success),
0x01 => Ok(InternalError),
0x02 => Ok(NoSuchInterface),
0x03 => Ok(NoSuchPeer),
_ => Err(InvalidSetPskResponseError),
}
}
}
impl From<SetPskResponseReturnCode> for SetPskResult {
fn from(value: SetPskResponseReturnCode) -> Self {
use SetPskError as E;
use SetPskResponseReturnCode as C;
match value {
C::Success => Ok(()),
C::InternalError => Err(E::InternalError),
C::NoSuchInterface => Err(E::NoSuchInterface),
C::NoSuchPeer => Err(E::NoSuchPeer),
}
}
}
impl From<SetPskResult> for SetPskResponseReturnCode {
fn from(value: SetPskResult) -> Self {
use SetPskError as E;
use SetPskResponseReturnCode as C;
match value {
Ok(()) => C::Success,
Err(E::InternalError) => C::InternalError,
Err(E::NoSuchInterface) => C::NoSuchInterface,
Err(E::NoSuchPeer) => C::NoSuchPeer,
}
}
}
#[repr(u8)]
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub enum MsgType {
SetPsk = 0x01,
}
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct InvalidMessageTypeError;
impl TryFrom<u8> for MsgType {
type Error = InvalidMessageTypeError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x01 => Ok(MsgType::SetPsk),
_ => Err(InvalidMessageTypeError),
}
}
}

View File

@@ -0,0 +1,99 @@
use std::borrow::BorrowMut;
use std::marker::PhantomData;
use std::result::Result;
use rosenpass_lenses::LenseError;
use crate::api::msgs::{self, EnvelopeExt, SetPskRequestExt, SetPskResponseExt};
use crate::WireGuardBroker;
#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
pub enum BrokerServerError {
#[error("No such request type: {}", .0)]
NoSuchRequestType(u8),
#[error("Invalid message received.")]
InvalidMessage,
}
impl From<LenseError> for BrokerServerError {
fn from(value: LenseError) -> Self {
use BrokerServerError as Be;
use LenseError as Le;
match value {
Le::BufferSizeMismatch => Be::InvalidMessage,
}
}
}
impl From<msgs::InvalidMessageTypeError> for BrokerServerError {
fn from(value: msgs::InvalidMessageTypeError) -> Self {
let msgs::InvalidMessageTypeError = value; // Assert that this is a unit type
BrokerServerError::InvalidMessage
}
}
pub struct BrokerServer<'a, Err, Inner, Ref>
where
msgs::SetPskError: From<Err>,
Inner: WireGuardBroker<Error = Err>,
Ref: BorrowMut<Inner> + 'a,
{
inner: Ref,
_phantom: PhantomData<&'a mut Inner>,
}
impl<'a, Err, Inner, Ref> BrokerServer<'a, Err, Inner, Ref>
where
msgs::SetPskError: From<Err>,
Inner: WireGuardBroker<Error = Err>,
Ref: 'a + BorrowMut<Inner>,
{
pub fn new(inner: Ref) -> Self {
Self {
inner,
_phantom: PhantomData,
}
}
pub fn handle_message(
&mut self,
req: &[u8],
res: &mut [u8; msgs::RESPONSE_MSG_BUFFER_SIZE],
) -> Result<usize, BrokerServerError> {
use BrokerServerError::*;
let typ = req.get(0).ok_or(InvalidMessage)?;
let typ = msgs::MsgType::try_from(*typ)?;
let msgs::MsgType::SetPsk = typ; // Assert type
let req: msgs::Envelope<_, msgs::SetPskRequest<&[u8]>> = req.envelope_truncating()?;
let mut res: msgs::Envelope<_, msgs::SetPskResponse<&mut [u8]>> =
(res as &mut [u8]).envelope_truncating()?;
(&mut res).msg_type_mut()[0] = msgs::MsgType::SetPsk as u8;
self.handle_set_psk(
req.payload().set_psk_request()?,
res.payload_mut().set_psk_response()?,
)?;
Ok(res.all_bytes().len())
}
fn handle_set_psk(
&mut self,
req: msgs::SetPskRequest<&[u8]>,
mut res: msgs::SetPskResponse<&mut [u8]>,
) -> Result<(), BrokerServerError> {
// Using unwrap here since lenses can not return fixed-size arrays
// TODO: Slices should give access to fixed size arrays
let r: Result<(), Err> = self.inner.borrow_mut().set_psk(
req.iface()
.map_err(|_e| BrokerServerError::InvalidMessage)?,
req.peer_id().try_into().unwrap(),
req.psk().try_into().unwrap(),
);
let r: msgs::SetPskResult = r.map_err(|e| e.into());
let r: msgs::SetPskResponseReturnCode = r.into();
res.return_code_mut()[0] = r as u8;
Ok(())
}
}

View File

@@ -0,0 +1,56 @@
use std::io::{stdin, stdout, Read, Write};
use std::result::Result;
use rosenpass_wireguard_broker::api::msgs;
use rosenpass_wireguard_broker::api::server::BrokerServer;
use rosenpass_wireguard_broker::netlink as wg;
#[derive(thiserror::Error, Debug)]
pub enum BrokerAppError {
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
WgConnectError(#[from] wg::ConnectError),
#[error(transparent)]
WgSetPskError(#[from] wg::SetPskError),
#[error("Oversized message {}; something about the request is fatally wrong", .0)]
OversizedMessage(u64),
}
fn main() -> Result<(), BrokerAppError> {
let mut broker = BrokerServer::new(wg::NetlinkWireGuardBroker::new()?);
let mut stdin = stdin().lock();
let mut stdout = stdout().lock();
loop {
// Read the message length
let mut len = [0u8; 8];
stdin.read_exact(&mut len)?;
// Parse the message length
let len = u64::from_le_bytes(len);
if (len as usize) > msgs::REQUEST_MSG_BUFFER_SIZE {
return Err(BrokerAppError::OversizedMessage(len));
}
// Read the message itself
let mut req_buf = [0u8; msgs::REQUEST_MSG_BUFFER_SIZE];
let req_buf = &mut req_buf[..(len as usize)];
stdin.read_exact(req_buf)?;
// Process the message
let mut res_buf = [0u8; msgs::RESPONSE_MSG_BUFFER_SIZE];
let res = match broker.handle_message(req_buf, &mut res_buf) {
Ok(len) => &res_buf[..len],
Err(e) => {
eprintln!("Error processing message for wireguard PSK broker: {e:?}");
continue;
}
};
// Write the response
stdout.write_all(&(res.len() as u64).to_le_bytes())?;
stdout.write_all(&res)?;
stdout.flush()?;
}
}

View File

@@ -0,0 +1,191 @@
use std::process::Stdio;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{UnixListener, UnixStream};
use tokio::process::Command;
use tokio::sync::{mpsc, oneshot};
use tokio::task;
use anyhow::{bail, ensure, Result};
use clap::{ArgGroup, Parser};
use rosenpass_util::fd::claim_fd;
use rosenpass_wireguard_broker::api::msgs;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
#[clap(group(
ArgGroup::new("socket")
.required(true)
.args(&["listen_path", "listen_fd", "stream_fd"]),
))]
struct Args {
/// Where in the file-system to create the unix socket this broker will be listening for
/// connections on
#[arg(long)]
listen_path: Option<String>,
/// When this broker is called from another process, the other process can open and bind the
/// unix socket to use themselves, passing it to this process. In Rust this can be achieved
/// using the [command-fds](https://docs.rs/command-fds/latest/command_fds/) crate.
#[arg(long)]
listen_fd: Option<i32>,
/// When this broker is called from another process, the other process can connect the unix socket
/// themselves, for instance using the `socketpair(2)` system call.
#[arg(long)]
stream_fd: Option<i32>,
/// The underlying broker, accepting commands through stdin and sending results through stdout.
#[arg(
last = true,
allow_hyphen_values = true,
default_value = "rosenpass-wireguard-broker-privileged"
)]
command: Vec<String>,
}
struct BrokerRequest {
reply_to: oneshot::Sender<BrokerResponse>,
request: Vec<u8>,
}
struct BrokerResponse {
response: Vec<u8>,
}
#[tokio::main]
async fn main() -> Result<()> {
env_logger::init();
let args = Args::parse();
let (proc_tx, proc_rx) = mpsc::channel(100);
// Start the inner broker handler
task::spawn(async move {
if let Err(e) = direct_broker_process(proc_rx, args.command).await {
log::error!("Error in broker command handler: {e}");
panic!("Can not proceed without underlying broker process");
}
});
// Listen for incoming requests
if let Some(path) = args.listen_path {
let sock = UnixListener::bind(path)?;
listen_for_clients(proc_tx, sock).await
} else if let Some(fd) = args.listen_fd {
let sock = std::os::unix::net::UnixListener::from(claim_fd(fd)?);
sock.set_nonblocking(true)?;
listen_for_clients(proc_tx, UnixListener::from_std(sock)?).await
} else if let Some(fd) = args.stream_fd {
let stream = std::os::unix::net::UnixStream::from(claim_fd(fd)?);
stream.set_nonblocking(true)?;
on_accept(proc_tx, UnixStream::from_std(stream)?).await
} else {
unreachable!();
}
}
async fn direct_broker_process(
mut queue: mpsc::Receiver<BrokerRequest>,
cmd: Vec<String>,
) -> Result<()> {
let proc = Command::new(&cmd[0])
.args(&cmd[1..])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
let mut stdin = proc.stdin.unwrap();
let mut stdout = proc.stdout.unwrap();
loop {
let BrokerRequest { reply_to, request } = queue.recv().await.unwrap();
stdin
.write_all(&(request.len() as u64).to_le_bytes())
.await?;
stdin.write_all(&request[..]).await?;
// Read the response length
let mut len = [0u8; 8];
stdout.read_exact(&mut len).await?;
// Parse the response length
let len = u64::from_le_bytes(len) as usize;
ensure!(
len <= msgs::RESPONSE_MSG_BUFFER_SIZE,
"Oversized buffer ({len}) in broker stdout."
);
// Read the message itself
let mut res_buf = request; // Avoid allocating memory if we don't have to
res_buf.resize(len as usize, 0);
stdout.read_exact(&mut res_buf[..len]).await?;
// Return to the unix socket connection worker
reply_to
.send(BrokerResponse { response: res_buf })
.or_else(|_| bail!("Unable to send respnse to unix socket worker."))?;
}
}
async fn listen_for_clients(queue: mpsc::Sender<BrokerRequest>, sock: UnixListener) -> Result<()> {
loop {
let (stream, _addr) = sock.accept().await?;
let queue = queue.clone();
task::spawn(async move {
if let Err(e) = on_accept(queue, stream).await {
log::error!("Error during connection processing: {e}");
}
});
}
// NOTE: If loop can ever terminate we need to join the spawned tasks
}
async fn on_accept(queue: mpsc::Sender<BrokerRequest>, mut stream: UnixStream) -> Result<()> {
let mut req_buf = Vec::new();
loop {
stream.readable().await?;
// Read the message length
let mut len = [0u8; 8];
stream.read_exact(&mut len).await?;
// Parse the message length
let len = u64::from_le_bytes(len) as usize;
ensure!(
len <= msgs::REQUEST_MSG_BUFFER_SIZE,
"Oversized buffer ({len}) in unix socket input."
);
// Read the message itself
req_buf.resize(len as usize, 0);
stream.read_exact(&mut req_buf[..len]).await?;
// Handle the message
let (reply_tx, reply_rx) = oneshot::channel();
queue
.send(BrokerRequest {
reply_to: reply_tx,
request: req_buf,
})
.await?;
// Wait for the reply
let BrokerResponse { response } = reply_rx.await.unwrap();
// Write reply back to unix socket
stream
.write_all(&(response.len() as u64).to_le_bytes())
.await?;
stream.write_all(&response[..]).await?;
stream.flush().await?;
// Reuse the same memory for the next message
req_buf = response;
}
}

View File

@@ -0,0 +1,14 @@
use std::result::Result;
pub trait WireGuardBroker {
type Error;
fn set_psk(
&mut self,
interface: &str,
peer_id: [u8; 32],
psk: [u8; 32],
) -> Result<(), Self::Error>;
}
pub mod api;