diff --git a/Cargo.lock b/Cargo.lock index b31deeeb..a2084f42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,12 +83,12 @@ dependencies = [ [[package]] name = "actix-macros" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] @@ -270,9 +270,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56fc6cf8dc8c4158eed8649f9b8b0ea1518eb62b544fe9490d66fa0b349eafe9" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "ansi_term" @@ -334,9 +334,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "array-init" @@ -358,9 +358,9 @@ checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" [[package]] name = "assert_cmd" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d6b683edf8d1119fe420a94f8a7e389239666aa72e65495d91c00462510151" +checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" dependencies = [ "anstyle", "bstr", @@ -480,13 +480,13 @@ dependencies = [ "lazycell", "log", "peeking_take_while", - "prettyplease 0.2.10", + "prettyplease 0.2.12", "proc-macro2", "quote", "regex", "rustc-hash", "shlex 1.1.0", - "syn 2.0.25", + "syn 2.0.28", "which 4.4.0", ] @@ -731,18 +731,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.11" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" +checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.3.11" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" dependencies = [ "anstream", "anstyle", @@ -886,6 +886,12 @@ dependencies = [ "dbus", ] +[[package]] +name = "deranged" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8810e7e2cf385b1e9b50d68264908ec367ba642c96d02edfe61c39e88e2a3c01" + [[package]] name = "derive_more" version = "0.99.17" @@ -924,7 +930,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.28", ] [[package]] @@ -935,9 +941,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encode_unicode" @@ -971,18 +977,18 @@ checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.28", ] [[package]] name = "enumn" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9838a970f5de399d3070ae1739e131986b2f5dcc223c7423ca0927e3a878522" +checksum = "b893c4eb2dc092c811165f84dc7447fae16fb66521717968c34c509b39b1a5c5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.28", ] [[package]] @@ -1033,9 +1039,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", @@ -1236,7 +1242,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.28", ] [[package]] @@ -1589,9 +1595,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" @@ -2011,7 +2017,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.28", ] [[package]] @@ -2067,9 +2073,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "peeking_take_while" @@ -2113,9 +2119,9 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "portable-atomic" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d220334a184db82b31b83f5ff093e3315280fb2b6bbc032022b2304a509aab7a" +checksum = "f32154ba0af3a075eefa1eda8bb414ee928f62303a54ea85b8d6638ff1a6ee9e" [[package]] name = "positioned-io2" @@ -2174,12 +2180,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92139198957b410250d43fad93e630d956499a625c527eda65175c8680f83387" +checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ "proc-macro2", - "syn 2.0.25", + "syn 2.0.28", ] [[package]] @@ -2208,9 +2214,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.64" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -2284,9 +2290,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.29" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -2359,9 +2365,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" dependencies = [ "aho-corasick", "memchr", @@ -2472,15 +2478,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schannel" @@ -2493,9 +2499,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "seccomp-sys" @@ -2508,9 +2514,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -2521,9 +2527,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -2531,35 +2537,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.171" +version = "1.0.179" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "0a5bf42b8d227d4abf38a1ddb08602e229108a517cd4e5bb28f9c7eaafdce5c0" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.179" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "741e124f5485c7e60c03b043f79f320bff3527f4bbf12cf3831750dc46a0ec2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.28", ] [[package]] name = "serde_json" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -2723,9 +2729,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.25" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", @@ -2779,9 +2785,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +checksum = "ec96d2ffad078296368d46ff1cb309be1c23c513b4ab0e22a45de0185275ac96" dependencies = [ "filetime", "libc", @@ -2827,22 +2833,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.28", ] [[package]] @@ -2856,10 +2862,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" +checksum = "b79eabcd964882a646b3584543ccabeae7869e9ac32a46f6f22b7a5bd405308b" dependencies = [ + "deranged", "itoa", "serde", "time-core", @@ -2874,9 +2881,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" +checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" dependencies = [ "time-core", ] @@ -2962,9 +2969,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.12" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ "indexmap 2.0.0", "serde", @@ -3050,9 +3057,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -3094,12 +3101,12 @@ dependencies = [ [[package]] name = "usbsas-analyzer-server" -version = "0.1.3" +version = "0.1.4" dependencies = [ "actix-files", "actix-web", "clamav-rs", - "clap 4.3.11", + "clap 4.3.19", "env_logger 0.10.0", "futures", "log", @@ -3112,8 +3119,9 @@ dependencies = [ [[package]] name = "usbsas-cmdexec" -version = "0.1.2" +version = "0.1.3" dependencies = [ + "byteorder", "env_logger 0.10.0", "log", "thiserror", @@ -3134,7 +3142,7 @@ dependencies = [ [[package]] name = "usbsas-config" -version = "0.1.4" +version = "0.1.5" dependencies = [ "log", "serde", @@ -3177,9 +3185,8 @@ dependencies = [ [[package]] name = "usbsas-files2tar" -version = "0.1.2" +version = "0.1.3" dependencies = [ - "byteorder", "env_logger 0.10.0", "log", "tar", @@ -3288,9 +3295,10 @@ dependencies = [ [[package]] name = "usbsas-net" -version = "0.1.4" +version = "0.1.5" dependencies = [ "base64", + "byteorder", "env_logger 0.10.0", "libgssapi", "log", @@ -3307,7 +3315,7 @@ dependencies = [ [[package]] name = "usbsas-process" -version = "0.1.1" +version = "0.1.2" dependencies = [ "libc", "log", @@ -3319,7 +3327,7 @@ dependencies = [ [[package]] name = "usbsas-proto" -version = "0.1.3" +version = "0.1.4" dependencies = [ "prost", "prost-build", @@ -3366,13 +3374,13 @@ dependencies = [ [[package]] name = "usbsas-server" -version = "0.1.5" +version = "0.1.6" dependencies = [ "actix-files", "actix-web", "assert_cmd", "base64", - "clap 4.3.11", + "clap 4.3.19", "err-derive", "futures", "hmac", @@ -3385,7 +3393,6 @@ dependencies = [ "serde_json", "sha2", "systemstat", - "tempfile", "time", "toml", "uname", @@ -3414,10 +3421,10 @@ dependencies = [ [[package]] name = "usbsas-tools" -version = "0.1.4" +version = "0.1.5" dependencies = [ "bitvec", - "clap 4.3.11", + "clap 4.3.19", "env_logger 0.10.0", "fuse_mt", "indicatif", @@ -3455,28 +3462,30 @@ dependencies = [ [[package]] name = "usbsas-usbsas" -version = "0.1.6" +version = "0.2.0" dependencies = [ - "clap 4.3.11", + "clap 4.3.19", "log", "serde_json", "thiserror", "time", "uname", "usbsas-comm", + "usbsas-config", "usbsas-mass-storage", "usbsas-mock", "usbsas-process", "usbsas-proto", "usbsas-sandbox", "usbsas-utils", + "uuid", ] [[package]] name = "usbsas-utils" version = "0.1.2" dependencies = [ - "clap 4.3.11", + "clap 4.3.19", "env_logger 0.10.0", "log", "serde_json", @@ -3503,9 +3512,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ "getrandom", ] @@ -3573,7 +3582,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.28", "wasm-bindgen-shared", ] @@ -3607,7 +3616,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.28", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3846,9 +3855,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.9" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529" +checksum = "8bd122eb777186e60c3fdf765a58ac76e41c582f1f535fbf3314434c6b58f3f7" dependencies = [ "memchr", ] @@ -3913,18 +3922,18 @@ dependencies = [ [[package]] name = "zstd" -version = "0.12.3+zstd.1.5.2" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "6.0.5+zstd.1.5.4" +version = "6.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" dependencies = [ "libc", "zstd-sys", diff --git a/Cargo.toml b/Cargo.toml index 54483a11..c1095352 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,4 +62,4 @@ overflow-checks = true strip = true [workspace.metadata] -version = "0.1.6" +version = "0.2.0" diff --git a/client/kiosk/Makefile b/client/kiosk/Makefile index 64128651..66cac724 100644 --- a/client/kiosk/Makefile +++ b/client/kiosk/Makefile @@ -1,5 +1,5 @@ VER := $(shell grep 'Version' usbsas-kiosk/DEBIAN/control | cut -d' ' -f2) -NWJS_VER=0.75.0 +NWJS_VER=0.78.1 PKG=../../target/debian/usbsas-kiosk_$(VER)_amd64.deb diff --git a/client/kiosk/usbsas-kiosk/DEBIAN/control b/client/kiosk/usbsas-kiosk/DEBIAN/control index d1255d8d..71d33f7c 100644 --- a/client/kiosk/usbsas-kiosk/DEBIAN/control +++ b/client/kiosk/usbsas-kiosk/DEBIAN/control @@ -1,5 +1,5 @@ Package: usbsas-kiosk -Version: 0.1.5 +Version: 0.1.6 Maintainer: usbsas Architecture: amd64 Section: utility diff --git a/client/python/comm.py b/client/python/comm.py index ef5e329e..945e3d43 100644 --- a/client/python/comm.py +++ b/client/python/comm.py @@ -73,8 +73,8 @@ class CommUsbsas(Comm): "OpenPartition": proto_usbsas.RequestOpenPartition, "Partitions": proto_usbsas.RequestPartitions, "ReadDir": proto_usbsas.RequestReadDir, - "Report": proto_usbsas.RequestReport, "Wipe": proto_usbsas.RequestWipe, + "PostCopyCmd": proto_usbsas.RequestPostCopyCmd, } resp_types = { "AnalyzeDone": proto_usbsas.ResponseAnalyzeDone, @@ -97,7 +97,6 @@ class CommUsbsas(Comm): "Partitions": proto_usbsas.ResponsePartitions, "PostCopyCmd": proto_usbsas.ResponsePostCopyCmd, "ReadDir": proto_usbsas.ResponseReadDir, - "Report": proto_usbsas.ResponseReport, "Wipe": proto_usbsas.ResponseWipe, } response_cls = proto_usbsas.Response @@ -167,6 +166,3 @@ def imgdisk(self, busnum, devnum): )) return self.recv_resp() - def report(self): - self.send_req(proto_usbsas.RequestReport()) - return self.recv_resp() diff --git a/client/python/usbsas_transfer_example.py b/client/python/usbsas_transfer_example.py index 149f55de..2e9d1f57 100644 --- a/client/python/usbsas_transfer_example.py +++ b/client/python/usbsas_transfer_example.py @@ -23,9 +23,8 @@ from proto.usbsas import proto3_pb2 as proto_usbsas usbsas_bin = "/usr/libexec/usbsas-usbsas" +config_path = "../../config.example.toml" date = datetime.datetime.now() -out_tar = "/tmp/usbsas_tmp_%s.tar" % str(date).replace(' ', '_') -out_fs = "/tmp/usbsas_tmp_%s.fs" % str(date).replace(' ', '_') pid_usbsas = -1 if not os.path.exists(usbsas_bin): @@ -41,8 +40,6 @@ def start_usbsas(): (parent_to_child_r, parent_to_child_w) = os.pipe() os.set_inheritable(child_to_parent_w, True) os.set_inheritable(parent_to_child_r, True) - with open(out_tar, mode='w'): pass - with open(out_fs, mode='w'): pass pid_usbsas = os.fork() if pid_usbsas < 0: print("fork error") @@ -54,7 +51,7 @@ def start_usbsas(): os.environ["INPUT_PIPE_FD"] = str(parent_to_child_r) os.environ["OUTPUT_PIPE_FD"] = str(child_to_parent_w) os.environ["RUST_LOG"] = "error" - os.execv(usbsas_bin, [usbsas_bin, out_tar, out_fs, "--analyze"]) + os.execv(usbsas_bin, [usbsas_bin, "-c", config_path]) sys.exit(0) os.close(parent_to_child_r) os.close(child_to_parent_w) @@ -99,11 +96,10 @@ def copy_usb(comm, files, device): rep = comm.recv_resp() ok_or_exit(comm, rep, "error during copy") if isinstance(rep, proto_usbsas.ResponseCopyDone): - break + print("Transfer done") + print(json.dumps(json.loads(rep.report), indent=2)) + return print(rep) - print("Transfer done") - rep = comm.report() - print(json.dumps(json.loads(rep.report), indent=2)) def copy_net(comm, files, url): rep = comm.copy_files_net(selected=files, url=url) @@ -113,11 +109,10 @@ def copy_net(comm, files, url): rep = comm.recv_resp() ok_or_exit(comm, rep, "error during copy") if isinstance(rep, proto_usbsas.ResponseCopyDone): - break + print("Transfer done") + print(json.dumps(json.loads(rep.report), indent=2)) + return print(rep) - print("Transfer done") - rep = comm.report() - print(json.dumps(json.loads(rep.report), indent=2)) def confirm_copy(devices): print('Copy all files from \n\"{}\"\nto\n\"{}\"\n? [Y/n]'.format( @@ -140,8 +135,6 @@ def end(comm): comm.end() os.kill(pid_usbsas, signal.SIGTERM) os.waitpid(pid_usbsas, 0) - os.remove(out_tar) - os.remove(out_fs) sys.exit(0) def main(): diff --git a/client/web/static/i18n/en.json b/client/web/static/i18n/en.json index 564fc16c..6b4f9945 100644 --- a/client/web/static/i18n/en.json +++ b/client/web/static/i18n/en.json @@ -5,6 +5,7 @@ "confirm": "Confirm", "copy_cmd_start": "Running custom command", "copy_fromtar_tofs": "Creating filesystem", + "copy_fromtar_totar": "Creating clean archive", "copy_fs2dev_start": "Writing filesystem on output device", "copy_start": "Starting copy", "copy_upload_start": "Uploading archive", diff --git a/client/web/static/i18n/fr.json b/client/web/static/i18n/fr.json index 72c4857f..35c2fb22 100644 --- a/client/web/static/i18n/fr.json +++ b/client/web/static/i18n/fr.json @@ -5,6 +5,7 @@ "confirm": "Confirmer", "copy_cmd_start": "Exécution de la commande custom", "copy_fromtar_tofs": "Création du système de fichier", + "copy_fromtar_totar": "Création de l'archive", "copy_fs2dev_start": "Écriture du système de fichier sur la clé destination", "copy_start": "Préparation de la copie", "copy_upload_start": "Téléversement de l'archive", diff --git a/client/web/static/js/script.js b/client/web/static/js/script.js index 19814a3a..a32c2f2d 100644 --- a/client/web/static/js/script.js +++ b/client/web/static/js/script.js @@ -712,7 +712,7 @@ function do_copy() { tbody.appendChild(nothing_tr); document.querySelector("#cancel-button").classList.remove("d-none"); document.querySelector("#cancel-button").innerText = langDocument["return"]; - for (let filtered_path of json.filtered_path) { + for (let filtered_path of json.report.filtered_files) { // Display filtered elements has_error = true; let tr_err = document.createElement("tr"); @@ -735,28 +735,30 @@ function do_copy() { tr_err.appendChild(name_td_err); tbody.appendChild(tr_err); } - for (let dirty_path of json.dirty_path) { - // Display dirty elements - has_error = true; - let tr_err = document.createElement("tr"); - let status_td_err = document.createElement("td"); - status_td_err.innerHTML = " "; - let format_status_icon_err = document.createElement("i"); - format_status_icon_err.classList.add("fas"); - format_status_icon_err.classList.add("fa-times"); - format_status_icon_err.classList.add("text-danger"); - status_td_err.insertBefore(format_status_icon_err, status_td_err.firstChild); - tr_err.appendChild(status_td_err); - let name_td_err = document.createElement("td"); - let p_err = document.createElement("p"); - p_err.classList.add("text-danger"); - p_err.innerHTML = "" + langDocument["filterav"] + ""; - let span_err = document.createElement("span"); - span_err.innerText = dirty_path; - p_err.appendChild(span_err); - name_td_err.appendChild(p_err); - tr_err.appendChild(name_td_err); - tbody.appendChild(tr_err); + for (let path in json.report.analyzer_report.files) { + if (json.report.analyzer_report.files[path].status === "DIRTY") { + // Display dirty elements + has_error = true; + let tr_err = document.createElement("tr"); + let status_td_err = document.createElement("td"); + status_td_err.innerHTML = " "; + let format_status_icon_err = document.createElement("i"); + format_status_icon_err.classList.add("fas"); + format_status_icon_err.classList.add("fa-times"); + format_status_icon_err.classList.add("text-danger"); + status_td_err.insertBefore(format_status_icon_err, status_td_err.firstChild); + tr_err.appendChild(status_td_err); + let name_td_err = document.createElement("td"); + let p_err = document.createElement("p"); + p_err.classList.add("text-danger"); + p_err.innerHTML = "" + langDocument["filterav"] + ""; + let span_err = document.createElement("span"); + span_err.innerText = path; + p_err.appendChild(span_err); + name_td_err.appendChild(p_err); + tr_err.appendChild(name_td_err); + tbody.appendChild(tr_err); + } } set_state("WAIT_REMOVAL"); break; @@ -783,7 +785,7 @@ function do_copy() { case "final_report": elements[elements.length - 1].icon.classList.remove("spinner-border"); elements[elements.length - 1].icon.classList.add("fa-check"); - for (let error_path of json.error_path) { + for (let error_path of json.report.error_files) { // Display failed elements has_error = true; let tr_err = document.createElement("tr"); @@ -807,7 +809,7 @@ function do_copy() { tr_err.appendChild(name_td_err); tbody.appendChild(tr_err); } - for (let filtered_path of json.filtered_path) { + for (let filtered_path of json.report.filtered_files) { // Display failed elements has_error = true; let tr_err = document.createElement("tr"); @@ -831,29 +833,31 @@ function do_copy() { tr_err.appendChild(name_td_err); tbody.appendChild(tr_err); } - for (let dirty_path of json.dirty_path) { - // Display failed elements - has_error = true; - let tr_err = document.createElement("tr"); - let status_td_err = document.createElement("td"); - status_td_err.innerHTML = " "; - let format_status_icon_err = document.createElement("i"); - format_status_icon_err.classList.add("fas"); - format_status_icon_err.classList.add("fa-times"); - format_status_icon_err.classList.add("text-danger"); - status_td_err.insertBefore(format_status_icon_err, status_td_err.firstChild); - tr_err.appendChild(status_td_err); - - let name_td_err = document.createElement("td"); - let p_err = document.createElement("p"); - p_err.classList.add("text-danger"); - p_err.innerHTML = "" + langDocument["filterav"] + ""; - let span_err = document.createElement("span"); - span_err.innerText = dirty_path; - p_err.appendChild(span_err); - name_td_err.appendChild(p_err); - tr_err.appendChild(name_td_err); - tbody.appendChild(tr_err); + for (let path in json.report.analyzer_report.files) { + if (json.report.analyzer_report.files[path].status === "DIRTY") { + // Display dirty elements + has_error = true; + let tr_err = document.createElement("tr"); + let status_td_err = document.createElement("td"); + status_td_err.innerHTML = " "; + let format_status_icon_err = document.createElement("i"); + format_status_icon_err.classList.add("fas"); + format_status_icon_err.classList.add("fa-times"); + format_status_icon_err.classList.add("text-danger"); + status_td_err.insertBefore(format_status_icon_err, status_td_err.firstChild); + tr_err.appendChild(status_td_err); + + let name_td_err = document.createElement("td"); + let p_err = document.createElement("p"); + p_err.classList.add("text-danger"); + p_err.innerHTML = "" + langDocument["filterav"] + ""; + let span_err = document.createElement("span"); + span_err.innerText = path; + p_err.appendChild(span_err); + name_td_err.appendChild(p_err); + tr_err.appendChild(name_td_err); + tbody.appendChild(tr_err); + } } break; case "fatal_error": diff --git a/config.example.toml b/config.example.toml index 09884149..13cd8108 100644 --- a/config.example.toml +++ b/config.example.toml @@ -64,9 +64,14 @@ command_args = [ # Remote analyzer server. (Optional) # Like for network destination below, kerberos authentication can be enabled. # An analyzer report can optionally be written on the destination device. +# Analyzing files can be enabled/disabled based on destination (usb, net (upload) +# or cmd (command)). [analyzer] url = "http://127.0.0.1:8042/api/scanbundle" #krb_service_name = "HTTP@your.domain" +analyze_usb = true +analyze_net = true +analyze_cmd = true # Command to execute after a transfer. (Optional) diff --git a/usbsas-analyzer-server/Cargo.toml b/usbsas-analyzer-server/Cargo.toml index 6b71a60e..d31fde78 100644 --- a/usbsas-analyzer-server/Cargo.toml +++ b/usbsas-analyzer-server/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "usbsas-analyzer-server" description = "usbsas analyzer server (with clamav)" -version = "0.1.3" +version = "0.1.4" edition = "2021" license = "GPL-3.0" diff --git a/usbsas-analyzer-server/src/main.rs b/usbsas-analyzer-server/src/main.rs index 662228d7..fed4c7c7 100644 --- a/usbsas-analyzer-server/src/main.rs +++ b/usbsas-analyzer-server/src/main.rs @@ -145,7 +145,10 @@ impl AppState { &self, mut body: web::Payload, ) -> Result<(String, String), actix_web::Error> { + #[cfg(not(feature = "integration-tests"))] let bundle_id = uuid::Uuid::new_v4().simple().to_string(); + #[cfg(feature = "integration-tests")] + let bundle_id = "bundle_test".into(); let out_file_name = format!("{}/{}.tar", self.working_dir.lock().unwrap(), bundle_id); let mut out_file = fs::File::create(out_file_name.clone()).unwrap(); diff --git a/usbsas-cmdexec/Cargo.toml b/usbsas-cmdexec/Cargo.toml index 0bd0589c..d00a1eac 100644 --- a/usbsas-cmdexec/Cargo.toml +++ b/usbsas-cmdexec/Cargo.toml @@ -1,10 +1,11 @@ [package] name = "usbsas-cmdexec" -version = "0.1.2" +version = "0.1.3" edition = "2021" license = "GPL-3.0" [dependencies] +byteorder = "1.4" env_logger = "0.10" log = "0.4" thiserror = "1.0" diff --git a/usbsas-cmdexec/src/lib.rs b/usbsas-cmdexec/src/lib.rs index 2e072319..9a7ff9c0 100644 --- a/usbsas-cmdexec/src/lib.rs +++ b/usbsas-cmdexec/src/lib.rs @@ -3,6 +3,7 @@ //! This process will execute the target command specified in the configuration //! file with the output of the transfer as argument. +use byteorder::ReadBytesExt; use log::{error, info, trace}; use std::process::{Command, Stdio}; use thiserror::Error; @@ -81,8 +82,24 @@ struct RunningState { struct WaitEndState {} impl InitState { - fn run(self, _comm: &mut Comm) -> Result { + fn run(mut self, comm: &mut Comm) -> Result { let config = conf_parse(&conf_read(&self.config_path)?)?; + + match comm.read_u8()? { + // Nothing to do, exit + 0 => return Ok(State::WaitEnd(WaitEndState {})), + // Use provided tar path + 1 => (), + // Files of this transfer were analyzed, use clean tar path + 2 => self.out_tar = format!("{}_clean.tar", self.out_tar.trim_end_matches(".tar")), + _ => { + error!("bad unlock value"); + return Ok(State::WaitEnd(WaitEndState {})); + } + } + + log::trace!("unlocked, using archive {}", self.out_tar); + Ok(State::Running(RunningState { out_tar: self.out_tar, out_fs: self.out_fs, diff --git a/usbsas-config/Cargo.toml b/usbsas-config/Cargo.toml index 92a97a05..cceac871 100644 --- a/usbsas-config/Cargo.toml +++ b/usbsas-config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "usbsas-config" -version = "0.1.4" +version = "0.1.5" edition = "2021" license = "GPL-3.0" diff --git a/usbsas-config/src/lib.rs b/usbsas-config/src/lib.rs index 734b39da..4f831708 100644 --- a/usbsas-config/src/lib.rs +++ b/usbsas-config/src/lib.rs @@ -44,6 +44,9 @@ pub struct PathFilter { pub struct Analyzer { pub url: String, pub krb_service_name: Option, + pub analyze_usb: bool, + pub analyze_net: bool, + pub analyze_cmd: bool, } #[derive(Debug, Deserialize)] diff --git a/usbsas-files2tar/Cargo.toml b/usbsas-files2tar/Cargo.toml index 01b9be9e..e08dfad4 100644 --- a/usbsas-files2tar/Cargo.toml +++ b/usbsas-files2tar/Cargo.toml @@ -1,11 +1,10 @@ [package] name = "usbsas-files2tar" -version = "0.1.2" +version = "0.1.3" edition = "2021" license = "GPL-3.0" [dependencies] -byteorder = "1.4" env_logger = "0.10" log = "0.4" tar = "0.4" diff --git a/usbsas-files2tar/src/main.rs b/usbsas-files2tar/src/main.rs index bf5c011f..d94cd30a 100644 --- a/usbsas-files2tar/src/main.rs +++ b/usbsas-files2tar/src/main.rs @@ -1,4 +1,3 @@ -use byteorder::ReadBytesExt; use usbsas_utils::{self, clap::UsbsasClap}; fn main() -> usbsas_files2tar::Result<()> { @@ -9,14 +8,8 @@ fn main() -> usbsas_files2tar::Result<()> { let tar_path = matches.get_one::("tar_path").unwrap().to_owned(); log::info!("start ({}): {}", std::process::id(), tar_path); - let mut comm = usbsas_comm::Comm::from_env()?; - match comm.read_u8()? { - // 0: unlock to start writing files in a tar - 0 => usbsas_files2tar::Files2Tar::new(comm, tar_path)?.main_loop()?, - // 1: unlock to exit value - 1 => usbsas_files2tar::Files2Tar::new_end(comm)?.main_loop()?, - _ => return Err(usbsas_files2tar::Error::Error("Bad unlock value".into())), - } - log::debug!("exit"); - Ok(()) + + usbsas_files2tar::Files2Tar::new(usbsas_comm::Comm::from_env()?, tar_path)? + .main_loop() + .map(|_| log::debug!("exit")) } diff --git a/usbsas-net/Cargo.toml b/usbsas-net/Cargo.toml index 2397abf2..94b22bbe 100644 --- a/usbsas-net/Cargo.toml +++ b/usbsas-net/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "usbsas-net" -version = "0.1.4" +version = "0.1.5" edition = "2021" license = "GPL-3.0" [dependencies] base64 = { version = "0.21", optional = true } +byteorder = "1.4" env_logger = "0.10" libgssapi = { version = "0.6", optional = true } log = "0.4" diff --git a/usbsas-net/src/downloader.rs b/usbsas-net/src/downloader.rs index 9eb08145..d30b6cec 100644 --- a/usbsas-net/src/downloader.rs +++ b/usbsas-net/src/downloader.rs @@ -1,7 +1,7 @@ use crate::{Error, HttpClient, Result}; use log::{error, trace}; use std::{ - fs::File, + fs::{File, OpenOptions}, io::{self, Write}, }; use usbsas_comm::{protoresponse, Comm}; @@ -91,7 +91,10 @@ impl InitState { Some(&[&self.tarpath]), )?; - let file = File::create(&self.tarpath)?; + let file = OpenOptions::new() + .write(true) + .read(false) + .open(&self.tarpath)?; let config_str = conf_read(&self.config_path)?; let config = conf_parse(&config_str)?; let net_conf = config.source_network.ok_or(Error::NoConf)?; diff --git a/usbsas-net/src/uploader.rs b/usbsas-net/src/uploader.rs index 054c0778..e2dd41fa 100644 --- a/usbsas-net/src/uploader.rs +++ b/usbsas-net/src/uploader.rs @@ -1,4 +1,5 @@ use crate::{Error, HttpClient, Result}; +use byteorder::ReadBytesExt; use log::{error, trace}; use reqwest::blocking::Body; use std::{ @@ -72,10 +73,12 @@ struct RunningState { struct WaitEndState {} impl InitState { - fn run(self, _comm: &mut Comm) -> Result { + fn run(mut self, comm: &mut Comm) -> Result { + let cleantarpath = format!("{}_clean.tar", self.tarpath.trim_end_matches(".tar")); usbsas_sandbox::landlock( Some(&[ &self.tarpath, + &cleantarpath, "/etc", "/lib", "/usr/lib/", @@ -84,6 +87,19 @@ impl InitState { None, )?; + match comm.read_u8()? { + // Nothing to do, exit + 0 => return Ok(State::WaitEnd(WaitEndState {})), + // Use provided tar path + 1 => (), + // Files of this transfer were analyzed, use clean tar path + 2 => self.tarpath = cleantarpath, + _ => { + error!("bad unlock value"); + return Ok(State::WaitEnd(WaitEndState {})); + } + } + let file = File::open(self.tarpath)?; Ok(State::Running(RunningState { file: Some(file) })) diff --git a/usbsas-process/Cargo.toml b/usbsas-process/Cargo.toml index 95039543..50beeefd 100644 --- a/usbsas-process/Cargo.toml +++ b/usbsas-process/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "usbsas-process" -version = "0.1.1" +version = "0.1.2" edition = "2021" license = "GPL-3.0" diff --git a/usbsas-process/src/lib.rs b/usbsas-process/src/lib.rs index 3d8d835f..d910b190 100644 --- a/usbsas-process/src/lib.rs +++ b/usbsas-process/src/lib.rs @@ -5,7 +5,11 @@ use nix::{ fcntl::{FcntlArg, FdFlag}, unistd, }; -use std::{io, os::unix::io::RawFd, path, process}; +use std::{ + io::{self, Write}, + os::unix::io::RawFd, + path, process, +}; use thiserror::Error; use usbsas_comm::Comm; use usbsas_utils::{INPUT_PIPE_FD_VAR, OUTPUT_PIPE_FD_VAR, USBSAS_BIN_PATH}; @@ -121,6 +125,15 @@ impl UsbsasChild { pub fn wait(&mut self) -> Result { Ok(self.child.wait()?) } + + pub fn unlock_with(&mut self, buf: &[u8]) -> Result<()> { + if !self.locked { + return Err(Error::Error("not locked".into())); + } + self.comm.write_all(buf)?; + self.locked = false; + Ok(()) + } } fn fcntl(fd: RawFd, arg: FcntlArg) -> io::Result { diff --git a/usbsas-proto/Cargo.toml b/usbsas-proto/Cargo.toml index 214183b5..d215778c 100644 --- a/usbsas-proto/Cargo.toml +++ b/usbsas-proto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "usbsas-proto" -version = "0.1.3" +version = "0.1.4" edition = "2021" license = "GPL-3.0" diff --git a/usbsas-proto/proto/usbsas.proto3 b/usbsas-proto/proto/usbsas.proto3 index 1373f37d..3c238f96 100644 --- a/usbsas-proto/proto/usbsas.proto3 +++ b/usbsas-proto/proto/usbsas.proto3 @@ -74,7 +74,6 @@ message RequestCopyStart { SrcNet src_net = 6; }; repeated string selected = 4; - bool write_report = 7; }; message RequestWipe { @@ -88,9 +87,6 @@ message RequestImgDisk { common.Device device = 1; }; -message RequestReport { -}; - message Request { oneof msg { RequestEnd End = 1; @@ -105,7 +101,6 @@ message Request { RequestWipe Wipe = 10; RequestPostCopyCmd PostCopyCmd = 11; RequestImgDisk ImgDisk = 12; - RequestReport Report = 13; } }; @@ -153,9 +148,7 @@ message ResponseCopyStart { }; message ResponseCopyDone { - repeated string error_path = 1; - repeated string filtered_path = 2; - repeated string dirty_path = 3; + bytes report = 1; }; message ResponseCopyStatus { @@ -186,8 +179,7 @@ message ResponseNotEnoughSpace { }; message ResponseNothingToCopy { - repeated string rejected_filter = 1; - repeated string rejected_dirty = 2; + bytes report = 1; }; message ResponseWipe { @@ -199,10 +191,6 @@ message ResponseImgDisk { message ResponsePostCopyCmd { }; -message ResponseReport { - bytes report = 1; -}; - message Response { oneof msg { ResponseEnd End = 1; @@ -227,6 +215,5 @@ message Response { ResponseImgDisk ImgDisk = 20; ResponseCopyDone CopyDone = 21; ResponseNothingToCopy NothingToCopy = 22; - ResponseReport Report = 23; } }; diff --git a/usbsas-server/Cargo.toml b/usbsas-server/Cargo.toml index bda086f4..594f4c5d 100644 --- a/usbsas-server/Cargo.toml +++ b/usbsas-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "usbsas-server" -version = "0.1.5" +version = "0.1.6" edition = "2021" license = "GPL-3.0" description = "usbsas web server" @@ -21,7 +21,6 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sha2 = "0.10" systemstat = "0.2" -tempfile = "3.7" time = "0.3" toml = "0.7" uname = "0.1" diff --git a/usbsas-server/src/appstate.rs b/usbsas-server/src/appstate.rs index bbe96ed9..65edc2a5 100644 --- a/usbsas-server/src/appstate.rs +++ b/usbsas-server/src/appstate.rs @@ -1,5 +1,4 @@ use crate::error::{AuthentError, ServiceError}; -use crate::tmpfiles::TmpFiles; use actix_web::web; use base64::{engine as b64eng, Engine as _}; use futures::task::{Context, Poll, Waker}; @@ -11,7 +10,7 @@ use sha2::{Digest, Sha256}; use std::{ fs, io::Write, - path, + path::{self, Path}, pin::Pin, sync::{ atomic::{AtomicBool, Ordering}, @@ -22,7 +21,7 @@ use usbsas_comm::{protorequest, Comm}; use usbsas_config::{conf_parse, conf_read, Config}; use usbsas_process::UsbsasChildSpawner; use usbsas_proto as proto; -use usbsas_proto::common::OutFileType; +use usbsas_proto::common::{OutFileType, OutFsType}; protorequest!( CommUsbsas, @@ -37,7 +36,6 @@ protorequest!( getattr = GetAttr[RequestGetAttr, ResponseGetAttr], wipe = Wipe[RequestWipe, ResponseWipe], imgdisk = ImgDisk[RequestImgDisk, ResponseImgDisk], - report = Report[RequestReport, ResponseReport], end = End[RequestEnd, ResponseEnd] ); @@ -61,7 +59,7 @@ pub(crate) struct TargetDevice { is_dst: bool, } -enum CopyDestination { +enum Destination { Usb { busnum: u32, devnum: u32, @@ -73,7 +71,7 @@ enum CopyDestination { Cmd, } -enum CopySource { +enum Source { Usb { opendev: proto::usbsas::RequestOpenDevice, }, @@ -119,7 +117,7 @@ pub struct ReadDir { } #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] -pub struct USBDeviceDesc { +pub struct USBDesc { vendorid: u32, productid: u32, manufacturer: String, @@ -143,7 +141,7 @@ pub struct CmdDesc { #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum Desc { - Usb(USBDeviceDesc), + Usb(USBDesc), Net(NetDesc), Cmd(CmdDesc), } @@ -189,7 +187,7 @@ impl From<&TargetDevice> for DeviceDesc { } } Device::Usb(ref usb) => { - let net_json = USBDeviceDesc { + let net_json = USBDesc { vendorid: usb.vendorid, productid: usb.productid, manufacturer: usb.manufacturer.to_owned(), @@ -292,9 +290,7 @@ struct ReportDeviceSize<'a> { #[derive(Deserialize, Serialize, Debug)] pub struct ReportCopy<'a> { status: &'a str, - pub error_path: Vec, - pub filtered_path: Vec, - pub dirty_path: Vec, + pub report: serde_json::Value, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -338,14 +334,17 @@ impl ReqAuthentication for Vec { } } +// 1536 == tar with only a "/data" entry (512b) + 1024b zeroes (created by +// files2tar when it starts) +const USBSAS_EMPTY_TAR: u64 = 1536; + /// Actix data struct pub(crate) struct AppState { config: Mutex, pub config_path: Mutex, comm: Mutex>, - out_dev: Mutex>, + dest: Mutex>, hmac: Mutex>, - tmpfiles: Mutex, pub status: Arc>, pub session_id: Arc>, } @@ -354,7 +353,10 @@ impl AppState { pub(crate) fn new(config_path: String) -> Result { let config = conf_parse(&conf_read(&config_path)?)?; - let tmpfiles = TmpFiles::new(config.out_directory.clone())?; + #[cfg(feature = "integration-tests")] + let session_id = "00000000000000000000000000000000".to_string(); + #[cfg(not(feature = "integration-tests"))] + let session_id = uuid::Uuid::new_v4().simple().to_string(); // Create reports directory if it doesn't exists if let Some(report_config) = &config.report { @@ -368,22 +370,13 @@ impl AppState { }; }; - #[cfg(feature = "integration-tests")] - let session_id = "0".to_string(); - #[cfg(not(feature = "integration-tests"))] - let session_id = uuid::Uuid::new_v4().simple().to_string(); - - debug!("Out tar file name: {:?}", tmpfiles.out_tar); - debug!("Out fs file name: {:?}", tmpfiles.out_fs); - - let comm = AppState::start_usbsas(&config, &config_path, &tmpfiles, &session_id)?; + let comm = AppState::start_usbsas(&config_path, &session_id)?; Ok(AppState { config: Mutex::new(config), config_path: Mutex::new(config_path), - tmpfiles: Mutex::new(tmpfiles), comm: Mutex::new(comm), - out_dev: Mutex::new(None), + dest: Mutex::new(None), hmac: Mutex::new(Hmac::new_from_slice( &rand::thread_rng().gen::<[u8; 0x10]>(), )?), @@ -393,21 +386,12 @@ impl AppState { } fn start_usbsas( - config: &Config, config_path: &str, - tmpfiles: &TmpFiles, session_id: &str, ) -> Result, ServiceError> { debug!("starting usbsas"); - let mut usbsas_cmd = UsbsasChildSpawner::new("usbsas-usbsas") - .arg(&tmpfiles.out_tar) - .arg(&tmpfiles.out_fs) - .args(&["-c", config_path]); - - if config.analyzer.is_some() { - usbsas_cmd = usbsas_cmd.arg("--analyze"); - } + let usbsas_cmd = UsbsasChildSpawner::new("usbsas-usbsas").args(&["-c", config_path]); std::env::set_var("USBSAS_SESSION_ID", session_id); @@ -421,19 +405,45 @@ impl AppState { let _ = comm.end(proto::usbsas::RequestEnd {})?; nix::sys::wait::wait()?; - self.tmpfiles.lock()?.reset()?; - #[cfg(not(feature = "integration-tests"))] let new_session_id = uuid::Uuid::new_v4().simple().to_string(); #[cfg(feature = "integration-tests")] let new_session_id = "0".to_string(); + std::env::set_var("USBSAS_SESSION_ID", &new_session_id); + + // Delete out files if empty + let tar_path = format!( + "{}/usbsas_{}.tar", + self.config.lock()?.out_directory.trim_end_matches('/'), + self.session_id.read()? + ); + let clean_tar_path = format!( + "{}/usbsas_{}_clean.tar", + self.config.lock()?.out_directory.trim_end_matches('/'), + self.session_id.read()? + ); + let fs_path = format!( + "{}/usbsas_{}.img", + self.config.lock()?.out_directory.trim_end_matches('/'), + self.session_id.read()? + ); + if let Ok(metadata) = fs::metadata(&fs_path) { + if metadata.len() == 0 { + let _ = fs::remove_file(Path::new(&fs_path)).ok(); + } + }; + if let Ok(metadata) = fs::metadata(&tar_path) { + if metadata.len() == USBSAS_EMPTY_TAR { + let _ = fs::remove_file(Path::new(&tar_path)).ok(); + } + }; + if let Ok(metadata) = fs::metadata(&clean_tar_path) { + if metadata.len() == USBSAS_EMPTY_TAR { + let _ = fs::remove_file(Path::new(&clean_tar_path)).ok(); + } + }; - let new_comm = AppState::start_usbsas( - &*self.config.lock()?, - &self.config_path.lock()?, - &*self.tmpfiles.lock()?, - &new_session_id, - )?; + let new_comm = AppState::start_usbsas(&self.config_path.lock()?, &new_session_id)?; *self.session_id.write()? = new_session_id; @@ -522,51 +532,51 @@ impl AppState { let devices = self.list_all_devices()?; let mut in_dev = None; - let mut out_dev = None; + let mut dest = None; for dev in devices { let fingerprint = dev.device.fingerprint(); if fingerprint_in == fingerprint { debug!("in_dev set"); match &dev.device { Device::Usb(ref usb) => { - in_dev = Some(CopySource::Usb { + in_dev = Some(Source::Usb { opendev: proto::usbsas::RequestOpenDevice { device: Some(usb.to_owned()), }, }); } Device::Net(_) => { - in_dev = Some(CopySource::Net); + in_dev = Some(Source::Net); } Device::Cmd(_) => in_dev = None, } } if fingerprint_out == fingerprint { - debug!("out_dev set"); + debug!("dest set"); match &dev.device { Device::Usb(ref usb) => { - out_dev = Some(CopyDestination::Usb { + dest = Some(Destination::Usb { busnum: usb.busnum, devnum: usb.devnum, }); } Device::Net(ref net) => { - out_dev = Some(CopyDestination::Net { + dest = Some(Destination::Net { url: net.url.clone(), krb_service_name: net.krb_service_name.clone(), }) } - Device::Cmd(_) => out_dev = Some(CopyDestination::Cmd), + Device::Cmd(_) => dest = Some(Destination::Cmd), } } } - let in_dev = match (in_dev, &out_dev) { - (Some(CopySource::Net), Some(_)) => { - *self.out_dev.lock()? = out_dev; + let in_dev = match (in_dev, &dest) { + (Some(Source::Net), Some(_)) => { + *self.dest.lock()? = dest; return Ok(()); } - (Some(CopySource::Usb { opendev }), Some(_)) => opendev, + (Some(Source::Usb { opendev }), Some(_)) => opendev, (_, _) => { error!("Cannot find in or out dev"); return Err(ServiceError::Error("Cannot find in or out dev".to_string())); @@ -577,7 +587,7 @@ impl AppState { .lock()? .opendev(in_dev) .map_err(|err| ServiceError::Error(format!("couldn't open input device: {err}")))?; - *self.out_dev.lock()? = out_dev; + *self.dest.lock()? = dest; Ok(()) } @@ -663,7 +673,10 @@ impl AppState { ) -> Result<(), ServiceError> { use proto::usbsas::response::Msg; let mut src_is_net = false; - + let dest_lock = self.dest.lock()?; + let dest = dest_lock + .as_ref() + .ok_or(ServiceError::InternalServerError)?; let source = match download_pin { Some(pin) => { let pin = pin @@ -697,35 +710,52 @@ impl AppState { let mut comm = self.comm.lock()?; resp_stream.report_progress("copy_start", progress)?; - let out_dev = self.out_dev.lock()?; - let destination = match out_dev.as_ref().ok_or(ServiceError::InternalServerError)? { - CopyDestination::Usb { busnum, devnum } => { + let (analyze_usb, analyze_net, analyze_cmd) = + if let Some(conf) = &self.config.lock()?.analyzer { + (conf.analyze_usb, conf.analyze_net, conf.analyze_cmd) + } else { + (false, false, false) + }; + + let (destination, analyze) = match dest { + Destination::Usb { busnum, devnum } => { debug!("do copy usb {} {} ({})", busnum, devnum, fsfmt); let fstype = match fsfmt.as_str() { - "ntfs" => proto::common::OutFsType::Ntfs, - "exfat" => proto::common::OutFsType::Exfat, - "fat32" => proto::common::OutFsType::Fat, + "ntfs" => OutFsType::Ntfs, + "exfat" => OutFsType::Exfat, + "fat32" => OutFsType::Fat, _ => return Err(ServiceError::InternalServerError), }; - proto::usbsas::request_copy_start::Destination::Usb(proto::usbsas::DestUsb { - busnum: *busnum, - devnum: *devnum, - fstype: fstype.into(), - }) + ( + proto::usbsas::request_copy_start::Destination::Usb(proto::usbsas::DestUsb { + busnum: *busnum, + devnum: *devnum, + fstype: fstype.into(), + }), + analyze_usb, + ) } - CopyDestination::Net { + Destination::Net { url, krb_service_name, } => { debug!("do copy net"); - proto::usbsas::request_copy_start::Destination::Net(proto::common::DestNet { - url: url.to_owned(), - krb_service_name: krb_service_name.clone().unwrap_or_else(|| String::from("")), - }) + ( + proto::usbsas::request_copy_start::Destination::Net(proto::common::DestNet { + url: url.to_owned(), + krb_service_name: krb_service_name + .clone() + .unwrap_or_else(|| String::from("")), + }), + analyze_net, + ) } - CopyDestination::Cmd { .. } => { + Destination::Cmd { .. } => { debug!("do copy cmd"); - proto::usbsas::request_copy_start::Destination::Cmd(proto::usbsas::DestCmd {}) + ( + proto::usbsas::request_copy_start::Destination::Cmd(proto::usbsas::DestCmd {}), + analyze_cmd, + ) } }; @@ -734,19 +764,12 @@ impl AppState { progress += 1.0; resp_stream.report_progress("copy_usb_filter", progress)?; - let write_report = if let Some(report_conf) = &self.config.lock()?.report { - report_conf.write_dest - } else { - false - }; - comm.send(proto::usbsas::Request { msg: Some(proto::usbsas::request::Msg::CopyStart( proto::usbsas::RequestCopyStart { destination: Some(destination), selected, source, - write_report, }, )), })?; @@ -781,9 +804,7 @@ impl AppState { Msg::NothingToCopy(msg) => { resp_stream.add_message(ReportCopy { status: "nothing_to_copy", - filtered_path: msg.rejected_filter, - dirty_path: msg.rejected_dirty, - error_path: vec![], + report: serde_json::from_slice(&msg.report)?, })?; resp_stream.done()?; return Ok(()); @@ -802,83 +823,84 @@ impl AppState { } progress = current_progress + 30.0; - if self.config.lock()?.analyzer.is_some() && !src_is_net { - if let Some(CopyDestination::Usb { .. }) = *out_dev { - resp_stream.report_progress("analyzing", progress)?; - current_progress = progress; - loop { - resp = comm.recv()?; - match resp.msg.ok_or(ServiceError::InternalServerError)? { - Msg::AnalyzeStatus(msg) => { - progress = current_progress - + (msg.current_size as f32 / msg.total_size as f32 * 5.0); - resp_stream.report_progress("analyze_update", progress)?; - } - Msg::AnalyzeDone(_) => break, - Msg::Error(err) => { - resp_stream.report_error(&err.err)?; - return Err(ServiceError::InternalServerError); - } - _ => { - error!("Unexpected resp"); - resp_stream.report_error("Unexpected response from usbsas")?; - return Err(ServiceError::InternalServerError); - } + if analyze && !src_is_net { + resp_stream.report_progress("analyzing", progress)?; + current_progress = progress; + loop { + resp = comm.recv()?; + match resp.msg.ok_or(ServiceError::InternalServerError)? { + Msg::AnalyzeStatus(msg) => { + progress = current_progress + + (msg.current_size as f32 / msg.total_size as f32 * 5.0); + resp_stream.report_progress("analyze_update", progress)?; + } + Msg::AnalyzeDone(_) => break, + Msg::Error(err) => { + resp_stream.report_error(&err.err)?; + return Err(ServiceError::InternalServerError); + } + _ => { + error!("Unexpected resp"); + resp_stream.report_error("Unexpected response from usbsas")?; + return Err(ServiceError::InternalServerError); } } - progress = current_progress + 5.0; - }; + } + progress = current_progress + 5.0; }; size_read = 0; current_progress = progress; - match out_dev.as_ref().ok_or(ServiceError::InternalServerError)? { - CopyDestination::Usb { .. } => { - resp_stream.report_progress("copy_fromtar_tofs", progress)?; - // create fs - loop { - resp = comm.recv()?; - match resp.msg.ok_or(ServiceError::InternalServerError)? { - Msg::CopyStatus(msg) => { - size_read += msg.current_size; - progress = - current_progress + (size_read as f32 / total_size as f32 * 30.0); - resp_stream.report_progress("copy_fromtar_update", progress)?; - } - Msg::CopyStatusDone(_) => break, - Msg::NothingToCopy(msg) => { - resp_stream.add_message(ReportCopy { - status: "nothing_to_copy", - filtered_path: msg.rejected_filter, - dirty_path: msg.rejected_dirty, - error_path: vec![], - })?; - resp_stream.done()?; - return Ok(()); - } - Msg::Error(err) => { - error!("{}", err.err); - resp_stream.report_error(&err.err)?; - return Err(ServiceError::InternalServerError); - } - _ => { - resp_stream.report_error("Unexpected response from usbsas")?; - return Err(ServiceError::InternalServerError); - } + if analyze || matches!(dest, &Destination::Usb { .. }) { + match dest { + Destination::Usb { .. } => { + resp_stream.report_progress("copy_fromtar_tofs", progress)?; + } + Destination::Net { .. } | Destination::Cmd { .. } => { + resp_stream.report_progress("copy_fromtar_totar", progress)?; + } + } + // create fs or clean tar + loop { + resp = comm.recv()?; + match resp.msg.ok_or(ServiceError::InternalServerError)? { + Msg::CopyStatus(msg) => { + size_read += msg.current_size; + progress = current_progress + (size_read as f32 / total_size as f32 * 30.0); + resp_stream.report_progress("copy_fromtar_update", progress)?; + } + Msg::CopyStatusDone(_) => break, + Msg::NothingToCopy(msg) => { + resp_stream.add_message(ReportCopy { + status: "nothing_to_copy", + report: serde_json::from_slice(&msg.report)?, + })?; + resp_stream.done()?; + return Ok(()); + } + Msg::Error(err) => { + error!("{}", err.err); + resp_stream.report_error(&err.err)?; + return Err(ServiceError::InternalServerError); + } + _ => { + resp_stream.report_error("Unexpected response from usbsas")?; + return Err(ServiceError::InternalServerError); } } - progress = current_progress + 30.0; + } + } + + progress = current_progress + 30.0; + match dest { + Destination::Usb { .. } => { resp_stream.report_progress("copy_fs2dev_start", progress)? } - CopyDestination::Net { .. } => { - progress = current_progress + 30.0; + Destination::Net { .. } => { resp_stream.report_progress("copy_upload_start", progress)? } - CopyDestination::Cmd { .. } => { - progress = current_progress + 30.0; - resp_stream.report_progress("copy_cmd_start", progress)? - } + Destination::Cmd { .. } => resp_stream.report_progress("copy_cmd_start", progress)?, } progress += 1.0; @@ -899,15 +921,10 @@ impl AppState { // wait for response copy to break continue; } - Msg::CopyDone(info) => { + Msg::CopyDone(msg) => { progress = current_progress + 30.0; resp_stream.report_progress("terminate", progress)?; - break ReportCopy { - status: "final_report", - error_path: info.error_path, - filtered_path: info.filtered_path, - dirty_path: info.dirty_path, - }; + break msg.report; } Msg::Error(err) => { error!("{}", err.err); @@ -925,7 +942,6 @@ impl AppState { if let Some(report_conf) = &self.config.lock()?.report { if let Some(report_dir) = &report_conf.write_local { // save report on local disk - let transfer_report = comm.report(proto::usbsas::RequestReport {})?.report; let datetime = time::OffsetDateTime::now_utc(); let report_file_name = format!( "usbsas_transfer_{:04}{:02}{:02}{:02}{:02}{:02}_{}.json", @@ -939,22 +955,25 @@ impl AppState { ); let mut report_file = fs::File::create(path::Path::new(&report_dir).join(report_file_name))?; - report_file.write_all(&transfer_report)?; + report_file.write_all(&final_report)?; } } // post copy cmd if let Some(usbsas_config::PostCopy { .. }) = self.config.lock()?.post_copy { - let outfiletype = match out_dev.as_ref().ok_or(ServiceError::InternalServerError)? { - CopyDestination::Usb { .. } => OutFileType::Fs, - CopyDestination::Net { .. } | CopyDestination::Cmd { .. } => OutFileType::Tar, + let outfiletype = match dest { + Destination::Usb { .. } => OutFileType::Fs, + Destination::Net { .. } | Destination::Cmd { .. } => OutFileType::Tar, }; comm.postcopycmd(proto::usbsas::RequestPostCopyCmd { outfiletype: outfiletype.into(), })?; }; - resp_stream.add_message(final_report)?; + resp_stream.add_message(ReportCopy { + status: "final_report", + report: serde_json::from_slice(&final_report)?, + })?; resp_stream.done()?; Ok(()) } @@ -972,9 +991,9 @@ impl AppState { resp_stream.report_progress("wipe_start", 0.0)?; let fstype = match fsfmt.as_str() { - "ntfs" => proto::common::OutFsType::Ntfs, - "exfat" => proto::common::OutFsType::Exfat, - "fat32" => proto::common::OutFsType::Fat, + "ntfs" => OutFsType::Ntfs, + "exfat" => OutFsType::Exfat, + "fat32" => OutFsType::Fat, _ => return Err(ServiceError::InternalServerError), }; @@ -1057,7 +1076,11 @@ impl AppState { // Keep out fs let datetime = time::OffsetDateTime::now_utc(); fs::rename( - self.tmpfiles.lock()?.out_fs.clone(), + format!( + "{}/usbsas_{}.img", + self.config.lock()?.out_directory.trim_end_matches('/'), + self.session_id.read()? + ), format!( "{}/imgdisk_{:04}{:02}{:02}{:02}{:02}{:02}_{}_{}_{}.bin", self.config.lock()?.out_directory, diff --git a/usbsas-server/src/error.rs b/usbsas-server/src/error.rs index 7f633bc1..7b979ed7 100644 --- a/usbsas-server/src/error.rs +++ b/usbsas-server/src/error.rs @@ -93,13 +93,6 @@ impl From for ServiceError { } } -impl From for ServiceError { - fn from(error: tempfile::PersistError) -> ServiceError { - error!("{}", error); - ServiceError::InternalServerError - } -} - impl From for ServiceError { fn from(_error: usbsas_process::Error) -> ServiceError { dbg!(_error); diff --git a/usbsas-server/src/lib.rs b/usbsas-server/src/lib.rs index 0ab8dfb3..799c5edb 100644 --- a/usbsas-server/src/lib.rs +++ b/usbsas-server/src/lib.rs @@ -6,4 +6,3 @@ pub mod appstate; pub(crate) mod error; pub mod server; pub(crate) mod srv_infos; -pub(crate) mod tmpfiles; diff --git a/usbsas-server/src/tmpfiles.rs b/usbsas-server/src/tmpfiles.rs deleted file mode 100644 index 3223a432..00000000 --- a/usbsas-server/src/tmpfiles.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::error::ServiceError; -use std::{fs, path::Path}; - -pub(crate) struct TmpFiles { - pub(crate) out_tar: String, - pub(crate) out_fs: String, - pub(crate) out_directory: String, -} - -impl TmpFiles { - pub(crate) fn new(out_directory: String) -> Result { - let (out_tar, out_fs) = TmpFiles::create_files(&out_directory)?; - Ok(TmpFiles { - out_tar, - out_fs, - out_directory, - }) - } - - fn create_files(out_directory: &str) -> Result<(String, String), ServiceError> { - let (_, out_tar) = tempfile::Builder::new() - .prefix("usbsas_out_") - .suffix(".tar") - .rand_bytes(6) - .tempfile_in(out_directory)? - .keep()?; - let out_tar = out_tar.as_path().display().to_string(); - - let (_, out_fs) = tempfile::Builder::new() - .prefix("usbsas_fs_") - .suffix(".bin") - .rand_bytes(6) - .tempfile_in(out_directory)? - .keep()?; - let out_fs = out_fs.as_path().display().to_string(); - Ok((out_tar, out_fs)) - } - - fn delete_if_empty(&self) { - // XXX TODO add a config to always remove or not ? - if let Ok(metadata) = fs::metadata(&self.out_fs) { - if metadata.len() == 0 { - let _ = fs::remove_file(Path::new(&self.out_fs)).ok(); - } - }; - - if let Ok(metadata) = fs::metadata(&self.out_tar) { - if metadata.len() == 0 { - let _ = fs::remove_file(Path::new(&self.out_tar)).ok(); - } - }; - } - - pub(crate) fn reset(&mut self) -> Result<(), ServiceError> { - self.delete_if_empty(); - let (new_out_tar, new_out_fs) = TmpFiles::create_files(&self.out_directory)?; - self.out_tar = new_out_tar; - self.out_fs = new_out_fs; - Ok(()) - } -} - -impl Drop for TmpFiles { - fn drop(&mut self) { - self.delete_if_empty() - } -} diff --git a/usbsas-server/test_data/config_test.toml b/usbsas-server/test_data/config_test.toml index 8e1d5ac8..29000749 100644 --- a/usbsas-server/test_data/config_test.toml +++ b/usbsas-server/test_data/config_test.toml @@ -13,6 +13,9 @@ url = "http://127.0.0.1:8042/api/downloadbundle" [analyzer] url = "http://127.0.0.1:8042/api/scanbundle" referer = "http://127.0.0.1" +analyze_usb = true +analyze_net = false +analyze_cmd = false [report] write_dest = true diff --git a/usbsas-server/tests/integration_test.rs b/usbsas-server/tests/integration_test.rs index 04a5d200..1604f357 100644 --- a/usbsas-server/tests/integration_test.rs +++ b/usbsas-server/tests/integration_test.rs @@ -9,6 +9,7 @@ use { std::{ collections::HashMap, env, fs, io, + path::Path, process::{Child, Command, Stdio}, thread::sleep, time::Duration, @@ -333,13 +334,25 @@ impl IntegrationTester { } let status: StatusJson = serde_json::from_str(line).expect("quiche"); if status.status == *"final_report" { - let report: appstate::ReportCopy = serde_json::from_str(line).expect("plop"); - assert_eq!(report.dirty_path, dirty_path, "dirty path mismatch"); - assert_eq!(report.error_path, error_path, "error_path mismatch"); - assert_eq!( - report.filtered_path, filtered_path, - "filtered_path mismatch" - ); + let response: appstate::ReportCopy = serde_json::from_str(line).expect("plop"); + assert!(response.report["file_names"].as_array().is_some()); + if let Some(resp_filtered) = response.report["filtered_files"].as_array() { + assert_eq!(resp_filtered, filtered_path, "filtered_path mismatch"); + } + + if let Some(resp_error) = response.report["error_files"].as_array() { + assert_eq!(resp_error, error_path, "error_path mismatch"); + } + + if let Some(analyzed_files) = response.report["analyzer_report"].as_object() { + let mut resp_dirty: Vec = Vec::new(); + for (file, status) in analyzed_files["files"].as_object().unwrap() { + if status["status"] == "DIRTY" { + resp_dirty.push(format!("/{}", file)); + } + } + assert_eq!(resp_dirty, dirty_path, "dirty path mismatch"); + } } } @@ -353,6 +366,8 @@ impl IntegrationTester { sha1sum.split_whitespace().next().unwrap().to_string(), expected_sha1sum ); + } else if let appstate::DevType::Net = output_type { + assert!(Path::new(&format!("{}/bundle_test.tar", self.working_dir)).exists()); } } diff --git a/usbsas-tools/Cargo.toml b/usbsas-tools/Cargo.toml index 71078469..1e989172 100644 --- a/usbsas-tools/Cargo.toml +++ b/usbsas-tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "usbsas-tools" -version = "0.1.4" +version = "0.1.5" edition = "2021" license = "GPL-3.0" description = "usbsas tools" diff --git a/usbsas-tools/src/fswriter.rs b/usbsas-tools/src/fswriter.rs index 6d6bd617..92943b4c 100644 --- a/usbsas-tools/src/fswriter.rs +++ b/usbsas-tools/src/fswriter.rs @@ -58,10 +58,7 @@ impl FsWriter { )?; // unlock fs2dev with busnum / devnum - fs2dev - .comm - .write_all(&(((u64::from(devnum)) << 32) | (u64::from(busnum))).to_ne_bytes())?; - fs2dev.locked = false; + fs2dev.unlock_with(&(((u64::from(devnum)) << 32) | (u64::from(busnum))).to_ne_bytes())?; log::info!( "Writing fs '{}' on device BUS {} DEV {}", diff --git a/usbsas-tools/src/imager.rs b/usbsas-tools/src/imager.rs index 6b04bcd7..389d64fc 100644 --- a/usbsas-tools/src/imager.rs +++ b/usbsas-tools/src/imager.rs @@ -94,8 +94,7 @@ impl Imager { fn image_device(&mut self) -> Result<()> { // Unlock dev2scsi let buf = (u64::from(self.devnum)) << 32 | u64::from(self.busnum); - self.dev2scsi.comm.write_all(&buf.to_le_bytes())?; - self.dev2scsi.locked = false; + self.dev2scsi.unlock_with(&buf.to_le_bytes())?; let rep: proto::scsi::Response = self.dev2scsi.comm.recv()?; let (dev_size, block_size) = diff --git a/usbsas-usbsas/Cargo.toml b/usbsas-usbsas/Cargo.toml index b8206e4a..5fc5ba48 100644 --- a/usbsas-usbsas/Cargo.toml +++ b/usbsas-usbsas/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "usbsas-usbsas" -version = "0.1.6" +version = "0.2.0" edition = "2021" license = "GPL-3.0" @@ -12,12 +12,14 @@ thiserror = "1.0" time = "0.3" uname = "0.1" usbsas-comm = { path = "../usbsas-comm" } +usbsas-config = { path = "../usbsas-config" } usbsas-mass-storage = { path = "../usbsas-mass-storage" } usbsas-mock = { path = "../usbsas-mock", optional = true } usbsas-process = { path = "../usbsas-process" } usbsas-proto = { path = "../usbsas-proto" } usbsas-sandbox = { path = "../usbsas-sandbox" } usbsas-utils = { path = "../usbsas-utils" } +uuid = { version = "1.4", features = ["v4"] } [features] integration-tests = [] diff --git a/usbsas-usbsas/src/main.rs b/usbsas-usbsas/src/main.rs index 1093e9be..d38b235f 100644 --- a/usbsas-usbsas/src/main.rs +++ b/usbsas-usbsas/src/main.rs @@ -10,10 +10,12 @@ use std::sync::{Arc, RwLock}; use std::{ collections::{HashSet, VecDeque}, convert::TryFrom, - io::Write, + env, + fs::File, }; use thiserror::Error; use usbsas_comm::{protorequest, protoresponse, Comm}; +use usbsas_config::{conf_parse, conf_read}; use usbsas_mass_storage::UsbDevice; use usbsas_process::{UsbsasChild, UsbsasChildSpawner}; use usbsas_proto as proto; @@ -76,7 +78,6 @@ protoresponse!( nothingtocopy = NothingToCopy[ResponseNothingToCopy], wipe = Wipe[ResponseWipe], imgdisk = ImgDisk[ResponseImgDisk], - report = Report[ResponseReport], postcopycmd = PostCopyCmd[ResponsePostCopyCmd] ); @@ -183,8 +184,10 @@ enum State { DevOpened(DevOpenedState), PartitionOpened(PartitionOpenedState), CopyFiles(CopyFilesState), + Analyze(AnalyzeState), DownloadTar(DownloadTarState), - WriteFiles(WriteFilesState), + WriteCleanTar(WriteCleanTarState), + WriteFs(WriteFsState), UploadOrCmd(UploadOrCmdState), TransferDone(TransferDoneState), Wipe(WipeState), @@ -200,8 +203,10 @@ impl State { State::DevOpened(s) => s.run(comm, children), State::PartitionOpened(s) => s.run(comm, children), State::CopyFiles(s) => s.run(comm, children), + State::Analyze(s) => s.run(comm, children), State::DownloadTar(s) => s.run(comm, children), - State::WriteFiles(s) => s.run(comm, children), + State::WriteCleanTar(s) => s.run(comm, children), + State::WriteFs(s) => s.run(comm, children), State::UploadOrCmd(s) => s.run(comm, children), State::TransferDone(s) => s.run(comm, children), State::Wipe(s) => s.run(comm, children), @@ -212,7 +217,9 @@ impl State { } } -struct InitState {} +struct InitState { + config: Config, +} impl InitState { fn run( @@ -228,7 +235,13 @@ impl InitState { Msg::Devices(_) => self.devices(comm, children), Msg::OpenDevice(req) => { match self.open_device(comm, children, req.device.ok_or(Error::BadRequest)?) { - Ok(device) => return Ok(State::DevOpened(DevOpenedState { device, id })), + Ok(device) => { + return Ok(State::DevOpened(DevOpenedState { + device, + id, + config: self.config, + })) + } Err(err) => Err(err), } } @@ -241,6 +254,7 @@ impl InitState { id: id_str.clone(), destination: req.destination.ok_or(Error::BadRequest)?, bundle_path: src.pin.to_string(), + config: self.config, })) } _ => { @@ -334,6 +348,7 @@ impl InitState { struct DevOpenedState { device: UsbDevice, id: Option, + config: Config, } impl DevOpenedState { @@ -352,6 +367,7 @@ impl DevOpenedState { return Ok(State::PartitionOpened(PartitionOpenedState { device: self.device, id: self.id, + config: self.config, })) } Err(err) => { @@ -410,6 +426,7 @@ impl DevOpenedState { struct PartitionOpenedState { device: UsbDevice, id: Option, + config: Config, } impl PartitionOpenedState { @@ -432,7 +449,7 @@ impl PartitionOpenedState { id, selected: req.selected, destination: req.destination.ok_or(Error::BadRequest)?, - write_report: req.write_report, + config: self.config, })); } else { error!("empty id"); @@ -499,7 +516,7 @@ struct CopyFilesState { device: UsbDevice, id: String, selected: Vec, - write_report: bool, + config: Config, } impl CopyFilesState { @@ -530,15 +547,26 @@ impl CopyFilesState { let all_files_filtered = self.filter_files(children, all_files, &mut filtered)?; let all_directories_filtered = self.filter_files(children, all_directories, &mut filtered)?; + let mut all_entries_filtered = vec![]; all_entries_filtered.append(&mut all_directories_filtered.clone()); all_entries_filtered.append(&mut all_files_filtered.clone()); - // Abort if no files passed name filtering - if all_entries_filtered.is_empty() { + report["file_names"] = all_files_filtered.clone().into(); + report["filtered_files"] = filtered.clone().into(); + report["user"] = serde_json::Value::String(self.id.clone()); + report["source"] = json!({ + "vendorid": self.device.vendorid, + "productid": self.device.productid, + "manufacturer": self.device.manufacturer, + "serial": self.device.serial, + "description": self.device.description + }); + + // Abort if no files passed name filtering and no report requested + if all_entries_filtered.is_empty() && !self.config.write_report_dest { comm.nothingtocopy(proto::usbsas::ResponseNothingToCopy { - rejected_filter: filtered, - rejected_dirty: vec![], + report: serde_json::to_vec(&report)?, })?; warn!("Aborting copy, no files survived filter"); return Ok(State::WaitEnd(WaitEndState {})); @@ -550,22 +578,8 @@ impl CopyFilesState { Err(err) => return Err(err), }; - children.files2tar.comm.write_all(&[0_u8])?; - children.files2tar.locked = false; - comm.copystart(proto::usbsas::ResponseCopyStart { total_files_size })?; - report["file_names"] = all_files_filtered.clone().into(); - report["filtered_files"] = filtered.clone().into(); - report["user"] = serde_json::Value::String(self.id.clone()); - report["source"] = json!({ - "vendorid": self.device.vendorid, - "productid": self.device.productid, - "manufacturer": self.device.manufacturer, - "serial": self.device.serial, - "description": self.device.description - }); - self.tar_src_files( comm, children, @@ -575,33 +589,48 @@ impl CopyFilesState { &report, )?; - match self.destination { - Destination::Usb(usb) => { - children.tar2files.comm.write_all(&[1_u8])?; - children.tar2files.locked = false; - Ok(State::WriteFiles(WriteFilesState { - directories: all_directories_filtered, - dirty: Vec::new(), - errors, - files: all_files_filtered, - filtered, - id: self.id, - usb, - analyze: true, - write_report: self.write_report, - report, - })) - } - Destination::Net(_) | Destination::Cmd(_) => { - children.tar2files.comm.write_all(&[0_u8])?; - children.tar2files.locked = false; - Ok(State::UploadOrCmd(UploadOrCmdState { - errors, - filtered, - id: self.id, - destination: self.destination, - report, - })) + let analyze = match self.destination { + Destination::Usb(_) => self.config.analyze_usb, + Destination::Net(_) => self.config.analyze_net, + Destination::Cmd(_) => self.config.analyze_cmd, + }; + + if analyze { + Ok(State::Analyze(AnalyzeState { + directories: all_directories_filtered, + files: all_files_filtered, + errors, + id: self.id, + destination: self.destination, + report, + config: self.config, + })) + } else { + match self.destination { + Destination::Usb(usb) => { + children.uploader.unlock_with(&[0_u8])?; + children.cmdexec.unlock_with(&[0_u8])?; + children.tar2files.unlock_with(&[1_u8])?; + Ok(State::WriteFs(WriteFsState { + directories: all_directories_filtered, + errors, + files: all_files_filtered, + usb, + report, + config: self.config, + })) + } + Destination::Net(_) | Destination::Cmd(_) => { + report["error_files"] = errors.into(); + children.uploader.unlock_with(&[1_u8])?; + children.cmdexec.unlock_with(&[1_u8])?; + children.tar2files.unlock_with(&[0_u8])?; + Ok(State::UploadOrCmd(UploadOrCmdState { + id: self.id, + destination: self.destination, + report, + })) + } } } } @@ -710,7 +739,7 @@ impl CopyFilesState { } fn tar_src_files( - &mut self, + &self, comm: &mut Comm, children: &mut Children, entries_filtered: &[String], @@ -736,7 +765,7 @@ impl CopyFilesState { } fn file_to_tar( - &mut self, + &self, comm: &mut Comm, children: &mut Children, path: &str, @@ -817,6 +846,7 @@ struct DownloadTarState { destination: Destination, id: String, bundle_path: String, + config: Config, } impl DownloadTarState { @@ -847,12 +877,9 @@ impl DownloadTarState { Err(err) => return Err(err), }; - children.files2tar.comm.write_all(&[1_u8])?; - children.files2tar.locked = false; comm.copystart(proto::usbsas::ResponseCopyStart { total_files_size })?; self.download_tar(comm, children, &remote_path)?; - children.tar2files.comm.write_all(&[1_u8])?; - children.tar2files.locked = false; + children.tar2files.unlock_with(&[1_u8])?; self.tar_to_files_list( children, &mut errors, @@ -865,25 +892,23 @@ impl DownloadTarState { report["source"] = "network".into(); report["file_names"] = all_files.clone().into(); + self.config.analyze_usb = false; + self.config.analyze_net = false; + self.config.analyze_cmd = false; + self.config.write_report_dest = false; match self.destination { - Destination::Usb(usb) => Ok(State::WriteFiles(WriteFilesState { + Destination::Usb(usb) => Ok(State::WriteFs(WriteFsState { directories: all_directories, - dirty: Vec::new(), errors, files: all_files, - filtered: Vec::new(), - id: self.id, usb, - analyze: false, - write_report: false, report, + config: self.config, })), Destination::Net(_) | Destination::Cmd(_) => { - children.tar2files.comm.write_all(&[0_u8])?; - children.tar2files.locked = false; + report["error_files"] = errors.into(); + children.tar2files.unlock_with(&[0_u8])?; Ok(State::UploadOrCmd(UploadOrCmdState { - errors, - filtered: Vec::new(), id: self.id, destination: self.destination, report, @@ -1014,41 +1039,266 @@ impl DownloadTarState { } } -struct WriteFilesState { +struct AnalyzeState { directories: Vec, - dirty: Vec, errors: Vec, files: Vec, - filtered: Vec, id: String, - usb: proto::usbsas::DestUsb, - analyze: bool, - write_report: bool, + destination: Destination, report: serde_json::Value, + config: Config, } -impl WriteFilesState { +impl AnalyzeState { fn run( mut self, comm: &mut Comm, children: &mut Children, ) -> Result { - let analyze_report = if self.analyze { - self.analyze_files(comm, children)? - } else { - None - }; + let mut dirty: Vec = Vec::new(); + let analyze_report = self.analyze_files(comm, children, &mut dirty)?; + self.report["analyzer_report"] = analyze_report; + + children.tar2files.unlock_with(&[1])?; // Abort if no files survived antivirus and no report requested - if self.files.is_empty() && !self.write_report { + if self.files.is_empty() && !self.config.write_report_dest { comm.nothingtocopy(proto::usbsas::ResponseNothingToCopy { - rejected_filter: self.filtered, - rejected_dirty: self.dirty, + report: serde_json::to_vec(&self.report)?, })?; warn!("Aborting copy, no files survived filter and antivirus"); return Ok(State::WaitEnd(WaitEndState {})); } + children.cmdexec.unlock_with(&[2])?; + children.uploader.unlock_with(&[2])?; + + match self.destination { + Destination::Usb(usb) => Ok(State::WriteFs(WriteFsState { + directories: self.directories, + errors: self.errors, + files: self.files, + usb, + report: self.report, + config: self.config, + })), + Destination::Net(_) | Destination::Cmd(_) => { + Ok(State::WriteCleanTar(WriteCleanTarState { + directories: self.directories, + errors: self.errors, + files: self.files, + id: self.id, + destination: self.destination, + report: self.report, + })) + } + } + } + + fn analyze_files( + &mut self, + comm: &mut Comm, + children: &mut Children, + dirty: &mut Vec, + ) -> Result { + trace!("analyzing files"); + use proto::analyzer::response::Msg; + children.analyzer.comm.send(proto::analyzer::Request { + msg: Some(proto::analyzer::request::Msg::Analyze( + proto::analyzer::RequestAnalyze { + id: self.id.to_string(), + }, + )), + })?; + + loop { + let rep: proto::analyzer::Response = children.analyzer.comm.recv()?; + match rep.msg.ok_or(Error::BadRequest)? { + Msg::Analyze(res) => { + let report_json: serde_json::Value = serde_json::from_str(&res.report)?; + log::trace!("analyzer report: {:?}", report_json); + let files_status = report_json["files"].as_object().ok_or(Error::Error( + "Couldn't get files from analyzer report".into(), + ))?; + + match &report_json["version"].as_u64() { + Some(2) => self.files.retain(|x| { + if let Some(status) = files_status.get(x.trim_start_matches('/')) { + match status["status"].as_str() { + Some("CLEAN") => true, + Some("DIRTY") => { + dirty.push(x.to_string()); + false + } + _ => { + self.errors.push(x.to_string()); + false + } + } + } else { + false + } + }), + _ => self.files.retain(|x| { + if let Some(status) = files_status + .get(&format!("{TAR_DATA_DIR}/{}", x.trim_start_matches('/'))) + { + match status.as_str() { + Some("CLEAN") => true, + Some("DIRTY") => { + dirty.push(x.to_string()); + false + } + _ => { + self.errors.push(x.to_string()); + false + } + } + } else { + false + } + }), + } + + comm.analyzedone(proto::usbsas::ResponseAnalyzeDone {})?; + return Ok(report_json); + } + Msg::UploadStatus(status) => { + comm.analyzestatus(proto::usbsas::ResponseAnalyzeStatus { + current_size: status.current_size, + total_size: status.total_size, + })?; + continue; + } + Msg::Error(err) => { + error!("{}", err.err); + return Err(Error::Analyze(err.err)); + } + _ => return Err(Error::Analyze("Unexpected response".into())), + } + } + } +} + +struct WriteCleanTarState { + directories: Vec, + errors: Vec, + files: Vec, + id: String, + destination: Destination, + report: serde_json::Value, +} + +impl WriteCleanTarState { + fn run( + mut self, + comm: &mut Comm, + children: &mut Children, + ) -> Result { + trace!("write clean tar"); + + for path in self.directories.iter().chain(self.files.iter()) { + if let Err(err) = self.file_to_clean_tar(comm, children, path) { + error!("Couldn't copy file {}: {}", &path, err); + self.errors.push(path.clone()); + }; + } + + self.report["error_files"] = self.errors.clone().into(); + + children + .files2cleantar + .comm + .close(proto::writetar::RequestClose { + infos: serde_json::to_vec(&self.report)?, + })?; + + comm.copystatusdone(proto::usbsas::ResponseCopyStatusDone {})?; + + Ok(State::UploadOrCmd(UploadOrCmdState { + id: self.id, + destination: self.destination, + report: self.report, + })) + } + + fn file_to_clean_tar( + &self, + comm: &mut Comm, + children: &mut Children, + path: &str, + ) -> Result<()> { + let mut attrs = children + .tar2files + .comm + .getattr(proto::files::RequestGetAttr { path: path.into() })?; + + children + .files2cleantar + .comm + .newfile(proto::writetar::RequestNewFile { + path: path.to_string(), + size: attrs.size, + ftype: attrs.ftype, + timestamp: attrs.timestamp, + })?; + + let mut offset: u64 = 0; + while attrs.size > 0 { + let size_todo = if attrs.size < READ_FILE_MAX_SIZE { + attrs.size + } else { + READ_FILE_MAX_SIZE + }; + let rep = children + .tar2files + .comm + .readfile(proto::files::RequestReadFile { + path: path.to_string(), + offset, + size: size_todo, + })?; + children + .files2cleantar + .comm + .writefile(proto::writetar::RequestWriteFile { + path: path.to_string(), + offset, + data: rep.data, + })?; + offset += size_todo; + attrs.size -= size_todo; + comm.copystatus(proto::usbsas::ResponseCopyStatus { + current_size: size_todo, + })?; + } + + children + .files2cleantar + .comm + .endfile(proto::writetar::RequestEndFile { + path: path.to_string(), + })?; + + Ok(()) + } +} + +struct WriteFsState { + directories: Vec, + errors: Vec, + files: Vec, + usb: proto::usbsas::DestUsb, + report: serde_json::Value, + config: Config, +} + +impl WriteFsState { + fn run( + mut self, + comm: &mut Comm, + children: &mut Children, + ) -> Result { self.init_fs(children)?; trace!("copy usb"); @@ -1104,11 +1354,7 @@ impl WriteFilesState { self.report["error_files"] = self.errors.clone().into(); - if let Some(report) = analyze_report { - self.report["analyzer_report"] = report; - } - - if self.write_report { + if self.config.write_report_dest { if let Err(err) = self.write_report_file(children) { error!("Couldn't write report on destination fs"); comm.error(proto::usbsas::ResponseError { @@ -1128,9 +1374,7 @@ impl WriteFilesState { match self.write_fs(comm, children) { Ok(()) => { comm.copydone(proto::usbsas::ResponseCopyDone { - error_path: self.errors, - filtered_path: self.filtered, - dirty_path: self.dirty, + report: serde_json::to_vec(&self.report)?, })?; info!("transfer done"); } @@ -1142,9 +1386,7 @@ impl WriteFilesState { } } - Ok(State::TransferDone(TransferDoneState { - report: self.report, - })) + Ok(State::TransferDone(TransferDoneState {})) } fn init_fs(&mut self, children: &mut Children) -> Result<()> { @@ -1164,96 +1406,6 @@ impl WriteFilesState { Ok(()) } - fn analyze_files( - &mut self, - comm: &mut Comm, - children: &mut Children, - ) -> Result> { - trace!("analyzing files"); - use proto::analyzer::response::Msg; - if let Some(ref mut analyzer) = children.analyzer { - analyzer.comm.send(proto::analyzer::Request { - msg: Some(proto::analyzer::request::Msg::Analyze( - proto::analyzer::RequestAnalyze { - id: self.id.to_string(), - }, - )), - })?; - - loop { - let rep: proto::analyzer::Response = analyzer.comm.recv()?; - match rep.msg.ok_or(Error::BadRequest)? { - Msg::Analyze(res) => { - let report_json: serde_json::Value = serde_json::from_str(&res.report)?; - log::trace!("analyzer report: {:?}", report_json); - let files_status = report_json["files"].as_object().ok_or(Error::Error( - "Couldn't get files from analyzer report".into(), - ))?; - - match &report_json["version"].as_u64() { - Some(2) => self.files.retain(|x| { - if let Some(status) = files_status.get(x.trim_start_matches('/')) { - match status["status"].as_str() { - Some("CLEAN") => true, - Some("DIRTY") => { - self.dirty.push(x.to_string()); - false - } - _ => { - self.errors.push(x.to_string()); - false - } - } - } else { - false - } - }), - _ => self.files.retain(|x| { - if let Some(status) = files_status - .get(&format!("{TAR_DATA_DIR}/{}", x.trim_start_matches('/'))) - { - match status.as_str() { - Some("CLEAN") => true, - Some("DIRTY") => { - self.dirty.push(x.to_string()); - false - } - _ => { - self.errors.push(x.to_string()); - false - } - } - } else { - false - } - }), - } - - comm.analyzedone(proto::usbsas::ResponseAnalyzeDone {})?; - if self.write_report { - return Ok(Some(report_json)); - } else { - return Ok(None); - } - } - Msg::UploadStatus(status) => { - comm.analyzestatus(proto::usbsas::ResponseAnalyzeStatus { - current_size: status.current_size, - total_size: status.total_size, - })?; - continue; - } - Msg::Error(err) => { - error!("{}", err.err); - return Err(Error::Analyze(err.err)); - } - _ => return Err(Error::Analyze("Unexpected response".into())), - } - } - } - Ok(None) - } - fn write_file( &self, comm: &mut Comm, @@ -1379,8 +1531,6 @@ impl WriteFilesState { struct UploadOrCmdState { destination: Destination, - errors: Vec, - filtered: Vec, id: String, report: serde_json::Value, } @@ -1391,7 +1541,6 @@ impl UploadOrCmdState { comm: &mut Comm, children: &mut Children, ) -> Result { - self.report["error_files"] = self.errors.clone().into(); match &self.destination { Destination::Usb(_) => unreachable!("already handled"), Destination::Net(dest_net) => self.upload_files(comm, children, dest_net.clone())?, @@ -1403,20 +1552,15 @@ impl UploadOrCmdState { } // Unlock fs2dev so it can exit - children.fs2dev.comm.write_all(&(0_u64).to_ne_bytes())?; - children.fs2dev.locked = false; + children.fs2dev.unlock_with(&(0_u64).to_ne_bytes())?; comm.finalcopystatusdone(proto::usbsas::ResponseFinalCopyStatusDone {})?; comm.copydone(proto::usbsas::ResponseCopyDone { - error_path: self.errors, - filtered_path: self.filtered, - dirty_path: Vec::new(), + report: serde_json::to_vec(&self.report)?, })?; info!("net transfer done"); - Ok(State::TransferDone(TransferDoneState { - report: self.report, - })) + Ok(State::TransferDone(TransferDoneState {})) } fn upload_files( @@ -1486,9 +1630,7 @@ impl WipeState { // Unlock fs2dev children .fs2dev - .comm - .write_all(&((self.devnum << 32) | self.busnum).to_ne_bytes())?; - children.fs2dev.locked = false; + .unlock_with(&((self.devnum << 32) | self.busnum).to_ne_bytes())?; if !self.quick { trace!("secure wipe"); @@ -1618,9 +1760,7 @@ impl ImgDiskState { } } -struct TransferDoneState { - report: serde_json::Value, -} +struct TransferDoneState {} impl TransferDoneState { fn run( @@ -1630,11 +1770,6 @@ impl TransferDoneState { ) -> Result { let req: proto::usbsas::Request = comm.recv()?; match req.msg.ok_or(Error::BadRequest)? { - Msg::Report(_) => { - comm.report(proto::usbsas::ResponseReport { - report: serde_json::to_vec(&self.report)?, - })?; - } Msg::End(_) => { children.end_wait_all(comm)?; return Ok(State::End); @@ -1721,18 +1856,19 @@ fn init_report() -> Result { time.year(), time.month() as u8, time.day(), time.hour(),time.minute(),time.second()), "hostname": hostname, - "transfer_id": std::env::var("USBSAS_SESSION_ID").unwrap_or("0".to_string()), + "transfer_id": env::var("USBSAS_SESSION_ID").unwrap_or("0".to_string()), }); Ok(report) } struct Children { - analyzer: Option>, + analyzer: UsbsasChild, identificator: UsbsasChild, cmdexec: UsbsasChild, downloader: UsbsasChild, files2fs: UsbsasChild, files2tar: UsbsasChild, + files2cleantar: UsbsasChild, filter: UsbsasChild, fs2dev: UsbsasChild, scsi2files: UsbsasChild, @@ -1796,10 +1932,9 @@ impl Children { match destination { Destination::Usb(ref usb) => { // Unlock fs2dev to get dev_size - self.fs2dev.comm.write_all( + self.fs2dev.unlock_with( &(((u64::from(usb.devnum)) << 32) | (u64::from(usb.busnum))).to_ne_bytes(), )?; - self.fs2dev.locked = false; let dev_size = self .fs2dev .comm @@ -1827,10 +1962,8 @@ impl Children { fn end_all(&mut self) -> Result<()> { trace!("req end"); - if let Some(ref mut analyzer) = self.analyzer { - if let Err(err) = analyzer.comm.end(proto::analyzer::RequestEnd {}) { - error!("Couldn't end analyzer: {}", err); - }; + if let Err(err) = self.analyzer.comm.end(proto::analyzer::RequestEnd {}) { + error!("Couldn't end analyzer: {}", err); }; if let Err(err) = self .identificator @@ -1839,6 +1972,7 @@ impl Children { { error!("Couldn't end identificator: {}", err); }; + self.cmdexec.unlock_with(&[0]).ok(); if let Err(err) = self.cmdexec.comm.end(proto::cmdexec::RequestEnd {}) { error!("Couldn't end cmdexec: {}", err); }; @@ -1848,30 +1982,27 @@ impl Children { if let Err(err) = self.files2fs.comm.end(proto::writefs::RequestEnd {}) { error!("Couldn't end files2fs: {}", err); }; - if self.files2tar.locked { - self.files2tar.comm.write_all(&[1_u8]).ok(); - } if let Err(err) = self.files2tar.comm.end(proto::writetar::RequestEnd {}) { error!("Couldn't end files2tar: {}", err); }; + if let Err(err) = self.files2cleantar.comm.end(proto::writetar::RequestEnd {}) { + error!("Couldn't end files2cleantar: {}", err); + }; if let Err(err) = self.filter.comm.end(proto::filter::RequestEnd {}) { error!("Couldn't end filter: {}", err); }; - if self.fs2dev.locked { - self.fs2dev.comm.write_all(&(0_u64).to_ne_bytes()).ok(); - } + self.fs2dev.unlock_with(&(0_u64).to_ne_bytes()).ok(); if let Err(err) = self.fs2dev.comm.end(proto::fs2dev::RequestEnd {}) { error!("Couldn't end fs2dev: {}", err); }; if let Err(err) = self.scsi2files.comm.end(proto::files::RequestEnd {}) { error!("Couldn't end scsi2files: {}", err); }; - if self.tar2files.locked { - self.tar2files.comm.write_all(&[0_u8]).ok(); - } + self.tar2files.unlock_with(&[0]).ok(); if let Err(err) = self.tar2files.comm.end(proto::files::RequestEnd {}) { error!("Couldn't end tar2files: {}", err); }; + self.uploader.unlock_with(&[0]).ok(); if let Err(err) = self.uploader.comm.end(proto::uploader::RequestEnd {}) { error!("Couldn't end uploader: {}", err); }; @@ -1883,11 +2014,9 @@ impl Children { fn wait_all(&mut self) -> Result<()> { debug!("waiting children"); - if let Some(ref mut analyzer) = self.analyzer { - trace!("waiting analyzer"); - if let Err(err) = analyzer.wait() { - error!("Waiting analyzer failed: {}", err); - }; + trace!("waiting analyzer"); + if let Err(err) = self.analyzer.wait() { + error!("Waiting analyzer failed: {}", err); }; trace!("waiting identificator"); if let Err(err) = self.identificator.wait() { @@ -1909,6 +2038,9 @@ impl Children { if let Err(err) = self.files2tar.wait() { error!("Waiting files2tar failed: {}", err); }; + if let Err(err) = self.files2cleantar.wait() { + error!("Waiting files2tar failed: {}", err); + }; trace!("waiting filter"); if let Err(err) = self.filter.wait() { error!("Waiting filter failed: {}", err); @@ -1954,10 +2086,9 @@ pub struct Usbsas { impl Usbsas { fn new( comm: Comm, + config: Config, config_path: &str, - out_tar: &str, - out_fs: &str, - analyze: bool, + out_files: OutFiles, ) -> Result { trace!("init"); let mut pipes_read = vec![]; @@ -1972,15 +2103,16 @@ impl Usbsas { pipes_write.push(identificator.comm.output_fd()); let cmdexec = UsbsasChildSpawner::new("usbsas-cmdexec") - .arg(out_tar) - .arg(out_fs) + .arg(&out_files.tar_path) + .arg(&out_files.fs_path) .args(&["-c", config_path]) + .wait_on_startup() .spawn::()?; pipes_read.push(cmdexec.comm.input_fd()); pipes_write.push(cmdexec.comm.output_fd()); let downloader = UsbsasChildSpawner::new("usbsas-downloader") - .arg(out_tar) + .arg(&out_files.tar_path) .args(&["-c", config_path]) .spawn::()?; pipes_read.push(downloader.comm.input_fd()); @@ -1998,14 +2130,22 @@ impl Usbsas { pipes_write.push(scsi2files.comm.output_fd()); let files2tar = UsbsasChildSpawner::new("usbsas-files2tar") - .arg(out_tar) - .wait_on_startup() + .arg(&out_files.tar_path) .spawn::()?; pipes_read.push(files2tar.comm.input_fd()); pipes_write.push(files2tar.comm.output_fd()); + let files2cleantar = UsbsasChildSpawner::new("usbsas-files2tar") + .arg(&format!( + "{}_clean.tar", + &out_files.tar_path.trim_end_matches(".tar") + )) + .spawn::()?; + pipes_read.push(files2cleantar.comm.input_fd()); + pipes_write.push(files2cleantar.comm.output_fd()); + let files2fs = UsbsasChildSpawner::new("usbsas-files2fs") - .arg(out_fs) + .arg(&out_files.fs_path) .spawn::()?; pipes_read.push(files2fs.comm.input_fd()); pipes_write.push(files2fs.comm.output_fd()); @@ -2017,37 +2157,32 @@ impl Usbsas { pipes_write.push(filter.comm.output_fd()); let fs2dev = UsbsasChildSpawner::new("usbsas-fs2dev") - .arg(out_fs) + .arg(&out_files.fs_path) .wait_on_startup() .spawn::()?; pipes_read.push(fs2dev.comm.input_fd()); pipes_write.push(fs2dev.comm.output_fd()); let tar2files = UsbsasChildSpawner::new("usbsas-tar2files") - .arg(out_tar) + .arg(&out_files.tar_path) .wait_on_startup() .spawn::()?; pipes_read.push(tar2files.comm.input_fd()); pipes_write.push(tar2files.comm.output_fd()); let uploader = UsbsasChildSpawner::new("usbsas-uploader") - .arg(out_tar) + .arg(&out_files.tar_path) + .wait_on_startup() .spawn::()?; pipes_read.push(uploader.comm.input_fd()); pipes_write.push(uploader.comm.output_fd()); - let analyzer = if analyze { - let analyzer = UsbsasChildSpawner::new("usbsas-analyzer") - .arg(out_tar) - .args(&["-c", config_path]) - .spawn::()?; - pipes_read.push(analyzer.comm.input_fd()); - pipes_write.push(analyzer.comm.output_fd()); - - Some(analyzer) - } else { - None - }; + let analyzer = UsbsasChildSpawner::new("usbsas-analyzer") + .arg(&out_files.tar_path) + .args(&["-c", config_path]) + .spawn::()?; + pipes_read.push(analyzer.comm.input_fd()); + pipes_write.push(analyzer.comm.output_fd()); trace!("enter seccomp"); usbsas_sandbox::usbsas::seccomp(pipes_read, pipes_write)?; @@ -2059,6 +2194,7 @@ impl Usbsas { downloader, files2fs, files2tar, + files2cleantar, filter, fs2dev, scsi2files, @@ -2070,7 +2206,7 @@ impl Usbsas { Ok(Usbsas { comm, children, - state: State::Init(InitState {}), + state: State::Init(InitState { config }), }) } @@ -2093,32 +2229,83 @@ impl Usbsas { } } +struct Config { + analyze_usb: bool, + analyze_net: bool, + analyze_cmd: bool, + write_report_dest: bool, +} + +struct OutFiles { + pub tar_path: String, + pub clean_tar_path: String, + pub fs_path: String, +} + fn main() -> Result<()> { + let session_id = match env::var("USBSAS_SESSION_ID") { + Ok(id) => id.to_string(), + Err(_) => { + let id = uuid::Uuid::new_v4().simple().to_string(); + env::set_var("USBSAS_SESSION_ID", &id); + id + } + }; usbsas_utils::log::init_logger(); + let matches = usbsas_utils::clap::new_usbsas_cmd("usbsas-usbsas") .add_config_arg() - .add_tar_path_arg() - .add_fs_path_arg() - .arg( - clap::Arg::new("analyze") - .short('a') - .long("analyze") - .help("Analyze files with antivirus server") - .action(clap::ArgAction::SetTrue), - ) .get_matches(); - let config = matches.get_one::("config").unwrap(); - let tar_path = matches.get_one::("tar_path").unwrap(); - let fs_path = matches.get_one::("fs_path").unwrap(); - - info!("init ({}): {} {}", std::process::id(), tar_path, fs_path); - Usbsas::new( - Comm::from_env()?, - config, - tar_path, - fs_path, - matches.get_flag("analyze"), - )? - .main_loop() - .map(|_| log::debug!("exit")) + let config_path = matches.get_one::("config").unwrap(); + + let config = conf_parse(&conf_read(config_path)?)?; + + let mut conf = Config { + analyze_usb: false, + analyze_net: false, + analyze_cmd: false, + write_report_dest: false, + }; + if let Some(analyzer_conf) = config.analyzer { + conf.analyze_usb = analyzer_conf.analyze_usb; + conf.analyze_net = analyzer_conf.analyze_net; + conf.analyze_cmd = analyzer_conf.analyze_cmd; + } + if let Some(report_conf) = &config.report { + conf.write_report_dest = report_conf.write_dest; + }; + + let out_files = OutFiles { + tar_path: format!( + "{}/usbsas_{}.tar", + &config.out_directory.trim_end_matches('/'), + session_id, + ), + clean_tar_path: format!( + "{}/usbsas_{}_clean.tar", + &config.out_directory.trim_end_matches('/'), + session_id, + ), + fs_path: format!( + "{}/usbsas_{}.img", + &config.out_directory.trim_end_matches('/'), + session_id, + ), + }; + + info!( + "init ({}): {} {} {}", + std::process::id(), + &out_files.tar_path, + &out_files.clean_tar_path, + &out_files.fs_path + ); + + let _ = File::create(&out_files.tar_path)?; + let _ = File::create(&out_files.clean_tar_path)?; + let _ = File::create(&out_files.fs_path)?; + + Usbsas::new(Comm::from_env()?, conf, config_path, out_files)? + .main_loop() + .map(|_| log::debug!("exit")) }