From 06ef85ad68ae29ebe0cc054b301c1dc554028e71 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 24 Jan 2024 12:03:16 -0800 Subject: [PATCH] Add parser for ALZ archives --- Cargo.lock | 323 +++++----- libclamav/dconf.c | 1 + libclamav/dconf.h | 1 + libclamav/filetypes.c | 1 + libclamav/filetypes.h | 1 + libclamav/filetypes_int.h | 1 + libclamav/scanners.c | 5 + libclamav_rust/Cargo.toml | 3 + libclamav_rust/cbindgen.toml | 1 + libclamav_rust/src/alz.rs | 552 ++++++++++++++++++ libclamav_rust/src/evidence.rs | 15 +- libclamav_rust/src/fuzzy_hash.rs | 9 +- libclamav_rust/src/lib.rs | 1 + libclamav_rust/src/scanners.rs | 49 ++ unit_tests/clamscan/alz_test.py | 121 ++++ .../input/other_scanfiles/alz/bzip2.alz | Bin 0 -> 435 bytes .../input/other_scanfiles/alz/bzip2.bin.alz | Bin 0 -> 2909 bytes .../input/other_scanfiles/alz/deflate.alz | Bin 0 -> 274 bytes .../other_scanfiles/alz/uncompressed.alz | Bin 0 -> 240 bytes .../other_scanfiles/alz/uncompressed.bin.alz | Bin 0 -> 16907 bytes unit_tests/input/other_sigs/alz.hdb | 13 + 21 files changed, 904 insertions(+), 193 deletions(-) create mode 100644 libclamav_rust/src/alz.rs create mode 100644 unit_tests/clamscan/alz_test.py create mode 100644 unit_tests/input/other_scanfiles/alz/bzip2.alz create mode 100644 unit_tests/input/other_scanfiles/alz/bzip2.bin.alz create mode 100644 unit_tests/input/other_scanfiles/alz/deflate.alz create mode 100644 unit_tests/input/other_scanfiles/alz/uncompressed.alz create mode 100644 unit_tests/input/other_scanfiles/alz/uncompressed.bin.alz create mode 100644 unit_tests/input/other_sigs/alz.hdb diff --git a/Cargo.lock b/Cargo.lock index 6bbe5b8852..d7d9707a6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,11 +8,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -34,9 +40,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "base64" @@ -63,7 +69,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.52", + "syn 2.0.58", "which", ] @@ -81,9 +87,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block-buffer" @@ -96,15 +102,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.3" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.14.3" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" [[package]] name = "byteorder" @@ -114,9 +120,19 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "bzip2-rs" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beeb59e7e4c811ab37cc73680c798c7a5da77fc9989c62b09138e31ee740f735" +dependencies = [ + "crc32fast", + "tinyvec", +] [[package]] name = "cbindgen" @@ -138,9 +154,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.88" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" +checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" [[package]] name = "cexpr" @@ -159,16 +175,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.4", + "windows-targets", ] [[package]] @@ -177,12 +193,15 @@ version = "0.0.1" dependencies = [ "base64", "bindgen", + "byteorder", + "bzip2-rs", "cbindgen", "delharc", "flate2", "hex", "hex-literal", "image", + "inflate", "libc", "log", "num-traits", @@ -284,7 +303,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f420b1ede12094758715adaee221d93cb2af86dc499cef368ef81a23fea96029" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "chrono", "memchr", ] @@ -301,15 +320,15 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -332,7 +351,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -353,9 +372,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "fdeflate" @@ -413,9 +432,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "half" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", @@ -451,7 +470,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -505,6 +524,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inflate" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" +dependencies = [ + "adler32", +] + [[package]] name = "itertools" version = "0.10.5" @@ -516,9 +544,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jpeg-decoder" @@ -531,9 +559,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -564,12 +592,12 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-targets", ] [[package]] @@ -596,9 +624,9 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "minimal-lexical" @@ -702,12 +730,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ "proc-macro2", - "syn 2.0.52", + "syn 2.0.58", ] [[package]] @@ -721,9 +749,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -739,18 +767,18 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -768,9 +796,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -780,9 +808,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -791,9 +819,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustc-hash" @@ -827,15 +855,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -867,14 +895,14 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ "itoa", "ryu", @@ -917,9 +945,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "spin" @@ -949,9 +977,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -967,27 +995,27 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", ] [[package]] @@ -1001,6 +1029,12 @@ dependencies = [ "weezl", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" + [[package]] name = "toml" version = "0.5.11" @@ -1040,9 +1074,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "uuid" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" [[package]] name = "version_check" @@ -1052,9 +1086,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1062,24 +1096,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1087,22 +1121,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "weezl" @@ -1124,9 +1158,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "windows-core" @@ -1134,16 +1168,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.4", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -1152,122 +1177,72 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] -name = "windows_i686_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "zune-inflate" diff --git a/libclamav/dconf.c b/libclamav/dconf.c index 3bb5617739..b3037e9392 100644 --- a/libclamav/dconf.c +++ b/libclamav/dconf.c @@ -108,6 +108,7 @@ static struct dconf_module modules[] = { {"ARCHIVE", "EGG", ARCH_CONF_EGG, 1}, {"ARCHIVE", "UDF", ARCH_CONF_UDF, 1}, {"ARCHIVE", "LHA", ARCH_CONF_LHA_LZH, 1}, + {"ARCHIVE", "ALZ", ARCH_CONF_ALZ, 1}, {"DOCUMENT", "HTML", DOC_CONF_HTML, 1}, {"DOCUMENT", "RTF", DOC_CONF_RTF, 1}, diff --git a/libclamav/dconf.h b/libclamav/dconf.h index e5df428e49..39bbc553b6 100644 --- a/libclamav/dconf.h +++ b/libclamav/dconf.h @@ -98,6 +98,7 @@ struct cli_dconf { #define ARCH_CONF_EGG 0x4000000 #define ARCH_CONF_UDF 0x8000000 #define ARCH_CONF_LHA_LZH 0x10000000 +#define ARCH_CONF_ALZ 0x20000000 /* Document flags */ #define DOC_CONF_HTML 0x1 diff --git a/libclamav/filetypes.c b/libclamav/filetypes.c index 284120a0e8..88a2b18a32 100644 --- a/libclamav/filetypes.c +++ b/libclamav/filetypes.c @@ -138,6 +138,7 @@ static const struct ftmap_s { { "CL_TYPE_EGG", CL_TYPE_EGG }, { "CL_TYPE_EGGSFX", CL_TYPE_EGGSFX }, { "CL_TYPE_UDF", CL_TYPE_UDF }, + { "CL_TYPE_ALZ", CL_TYPE_ALZ }, { "CL_TYPE_ONENOTE", CL_TYPE_ONENOTE }, { "CL_TYPE_PYTHON_COMPILED", CL_TYPE_PYTHON_COMPILED }, { "CL_TYPE_LHA_LZH", CL_TYPE_LHA_LZH }, diff --git a/libclamav/filetypes.h b/libclamav/filetypes.h index 637b7487a3..8a7a1e0dd2 100644 --- a/libclamav/filetypes.h +++ b/libclamav/filetypes.h @@ -126,6 +126,7 @@ typedef enum cli_file { CL_TYPE_MHTML, CL_TYPE_LNK, CL_TYPE_UDF, + CL_TYPE_ALZ, CL_TYPE_OTHER, /* on-the-fly, used for target 14 (OTHER) */ CL_TYPE_IGNORED /* please don't add anything below */ } cli_file_t; diff --git a/libclamav/filetypes_int.h b/libclamav/filetypes_int.h index 832b6c3942..256f40c5c5 100644 --- a/libclamav/filetypes_int.h +++ b/libclamav/filetypes_int.h @@ -301,5 +301,6 @@ static const char *ftypes_int[] = { "1:2:2d6c68(30|31|32|33|34|35|36|37|64|78)2d:LHA or LZH archive:CL_TYPE_ANY:CL_TYPE_LHA_LZH:210", "1:2:2d6c7a(73|34|35)2d:LHA archive using .LZS extension:CL_TYPE_ANY:CL_TYPE_LHA_LZH:210", "1:2:2d706d302d:LHA archive using PMarc (.PMA) extension:CL_TYPE_ANY:CL_TYPE_LHA_LZH:210", + "0:0:414c5a01:ALZ:CL_TYPE_ANY:CL_TYPE_ALZ:210", NULL}; #endif diff --git a/libclamav/scanners.c b/libclamav/scanners.c index 832f0dc670..0831c56ef0 100644 --- a/libclamav/scanners.c +++ b/libclamav/scanners.c @@ -4550,6 +4550,11 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type) if (SCAN_PARSE_ONENOTE && (DCONF_ARCH & DOC_CONF_ONENOTE)) ret = scan_onenote(ctx); break; + case CL_TYPE_ALZ: + if (SCAN_PARSE_ARCHIVE && (DCONF_ARCH & ARCH_CONF_ALZ)) { + ret = cli_scanalz(ctx); + } + break; case CL_TYPE_LHA_LZH: if (SCAN_PARSE_ARCHIVE && (DCONF_ARCH & ARCH_CONF_LHA_LZH)) diff --git a/libclamav_rust/Cargo.toml b/libclamav_rust/Cargo.toml index 2d9f27e3e1..a579248b30 100644 --- a/libclamav_rust/Cargo.toml +++ b/libclamav_rust/Cargo.toml @@ -22,6 +22,9 @@ unicode-segmentation = "1.10" bindgen = "0.65" onenote_parser = { git = "https://github.com/Cisco-Talos/onenote.rs.git", branch = "CLAM-2329-new-from-slice" } hex-literal = "0.4" +inflate = "0.4" +bzip2-rs = "0.1" +byteorder = "1.5" delharc = "0.5" [lib] diff --git a/libclamav_rust/cbindgen.toml b/libclamav_rust/cbindgen.toml index 28e146e358..ef642ef7c5 100644 --- a/libclamav_rust/cbindgen.toml +++ b/libclamav_rust/cbindgen.toml @@ -37,6 +37,7 @@ include = [ "evidence::evidence_add_indicator", "evidence::IndicatorType", "scanners::scan_onenote", + "scanners::cli_scanalz", ] # prefix = "CAPI_" diff --git a/libclamav_rust/src/alz.rs b/libclamav_rust/src/alz.rs new file mode 100644 index 0000000000..41d0b292c7 --- /dev/null +++ b/libclamav_rust/src/alz.rs @@ -0,0 +1,552 @@ +/* + * ALZ archive extraction. + * + * Copyright (C) 2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * Authors: Andy Ragusa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +/* +#![warn( + clippy::all, + clippy::restriction, + clippy::pedantic, + clippy::nursery, + clippy::cargo, +)] +*/ + +use std::io::{Cursor, Read}; + +use byteorder::{LittleEndian, ReadBytesExt}; +use bzip2_rs::DecoderReader; +use inflate::InflateStream; +use log::debug; + +/// File header +const ALZ_FILE_HEADER: u32 = 0x015a_4c41; +/// Local file header +const ALZ_LOCAL_FILE_HEADER: u32 = 0x015a_4c42; +/// Central directory header +const ALZ_CENTRAL_DIRECTORY_HEADER: u32 = 0x015a_4c43; +/// End of Central directory header +const ALZ_END_OF_CENTRAL_DIRECTORY_HEADER: u32 = 0x025a_4c43; + +/// Error enumerates all possible errors returned by this library. +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Error parsing ALZ archive: {0}")] + Parse(&'static str), + + #[error("Unrecognized sig: '{0}'")] + UnrecognizedSig(String), + + #[error("Unsupported ALZ feature: {0}")] + UnsupportedFeature(&'static str), + + #[error("Failed to extract file")] + Extract, + + #[error("Failed to read field: {0}")] + Read(&'static str), +} + +struct AlzLocalFileHeaderHead { + file_name_length: u16, + + file_attribute: u8, + + file_time_date: u32, + + file_descriptor: u8, + + unknown: u8, +} + +const ALZ_ENCR_HEADER_LEN: u32 = 12; + +struct AlzLocalFileHeader { + head: AlzLocalFileHeaderHead, + + compression_method: u8, + unknown: u8, + file_crc: u32, + + /* Can be smaller sizes, depending on file_descriptor/0x10 .*/ + compressed_size: u64, + uncompressed_size: u64, + + file_name: String, + + enc_chk: [u8; ALZ_ENCR_HEADER_LEN as usize], + + start_of_compressed_data: u64, +} + +#[allow(dead_code)] +enum AlzFileAttribute { + Readonly = 0x1, + Hidden = 0x2, + Directory = 0x10, + File = 0x20, +} + +impl AlzLocalFileHeader { + const fn is_encrypted(&self) -> bool { + 0 != (self.head.file_descriptor & 0x1) + } + + const fn is_data_descriptor(&self) -> bool { + 0 != (self.head.file_descriptor & 0x8) + } + + const fn is_directory(&self) -> bool { + 0 != ((AlzFileAttribute::Directory as u8) & self.head.file_attribute) + } + + const fn _is_file(&self) -> bool { + 0 != ((AlzFileAttribute::File as u8) & self.head.file_attribute) + } + + const fn _is_readonly(&self) -> bool { + 0 != ((AlzFileAttribute::Readonly as u8) & self.head.file_attribute) + } + + const fn _is_hidden(&self) -> bool { + 0 != ((AlzFileAttribute::Hidden as u8) & self.head.file_attribute) + } + + fn _dump(&self) { + println!( + "self.start_of_compressed_data = {}", + self.start_of_compressed_data + ); + + println!( + "self.head.file_name_length = {:x}", + self.head.file_name_length + ); + println!( + "self.head.file_attribute = {:02x}", + self.head.file_attribute + ); + println!("self.head.file_time_date = {:x}", self.head.file_time_date); + println!( + "self.head.file_descriptor = {:x}", + self.head.file_descriptor + ); + println!("self.head.unknown = {:x}", self.head.unknown); + + println!("self.compression_method = {:x}", self.compression_method); + println!("self.unknown = {:x}", self.unknown); + println!("self.file_crc = {:x}", self.file_crc); + println!("self.compressed_size = {:x}", self.compressed_size); + println!("self.uncompressed_size = {:x}", self.uncompressed_size); + + println!("self.file_name = {}", self.file_name); + + print!("self.enc_chk = "); + for i in 0..ALZ_ENCR_HEADER_LEN { + if 0 != i { + print!(" "); + } + print!("{}", self.enc_chk[i as usize]); + } + println!(); + + println!("is_encrypted = {}", self.is_encrypted()); + println!("is_data_descriptor = {}", self.is_data_descriptor()); + + println!(); + } + + pub const fn new() -> Self { + Self { + head: AlzLocalFileHeaderHead { + file_name_length: 0, + file_attribute: 0, + file_time_date: 0, + file_descriptor: 0, + unknown: 0, + }, + + compression_method: 0, + unknown: 0, + file_crc: 0, + compressed_size: 0, + uncompressed_size: 0, + file_name: String::new(), + enc_chk: [0; ALZ_ENCR_HEADER_LEN as usize], + start_of_compressed_data: 0, + } + } + + pub fn parse(&mut self, cursor: &mut std::io::Cursor<&Vec>) -> Result<(), Error> { + self.head.file_name_length = cursor + .read_u16::() + .map_err(|_| Error::Read("file_name_length"))?; + self.head.file_attribute = cursor + .read_u8() + .map_err(|_| Error::Read("file_attribute"))?; + self.head.file_time_date = cursor + .read_u32::() + .map_err(|_| Error::Read("file_time_date"))?; + self.head.file_descriptor = cursor + .read_u8() + .map_err(|_| Error::Read("file_descriptor"))?; + self.head.unknown = cursor.read_u8().map_err(|_| Error::Read("unknown u8"))?; + + if 0 == self.head.file_name_length { + return Err(Error::Parse("File Name Length is zero")); + } + + let byte_len = self.head.file_descriptor / 0x10; + if byte_len > 0 { + self.compression_method = cursor + .read_u8() + .map_err(|_| Error::Read("compression_method"))?; + self.unknown = cursor.read_u8().map_err(|_| Error::Read("unknown u8"))?; + self.file_crc = cursor + .read_u32::() + .map_err(|_| Error::Read("file_crc"))?; + + match byte_len { + 1 => { + self.compressed_size = u64::from( + cursor + .read_u8() + .map_err(|_| Error::Read("compressed_size"))?, + ); + self.uncompressed_size = u64::from( + cursor + .read_u8() + .map_err(|_| Error::Read("uncompressed_size"))?, + ); + } + 2 => { + self.compressed_size = u64::from( + cursor + .read_u16::() + .map_err(|_| Error::Read("compressed_size"))?, + ); + self.uncompressed_size = u64::from( + cursor + .read_u16::() + .map_err(|_| Error::Read("uncompressed_size"))?, + ); + } + 4 => { + self.compressed_size = u64::from( + cursor + .read_u32::() + .map_err(|_| Error::Read("compressed_size"))?, + ); + self.uncompressed_size = u64::from( + cursor + .read_u32::() + .map_err(|_| Error::Read("uncompressed_size"))?, + ); + } + 8 => { + self.compressed_size = cursor + .read_u64::() + .map_err(|_| Error::Read("compressed_size"))?; + self.uncompressed_size = cursor + .read_u64::() + .map_err(|_| Error::Read("uncompressed_size"))?; + } + _ => return Err(Error::Parse("Unsupported File Descriptor")), + } + } + + #[allow(clippy::cast_possible_truncation)] + let idx0: usize = cursor.position() as usize; + let idx1: usize = idx0 + self.head.file_name_length as usize; + + if idx1 > cursor.get_ref().len() { + return Err(Error::Parse("Invalid file name length")); + } + + let filename = &cursor.get_ref().as_slice()[idx0..idx1]; + cursor.set_position(idx1 as u64); + + self.file_name = String::from_utf8_lossy(filename).into_owned(); + + if self.is_encrypted() { + cursor + .read_exact(&mut self.enc_chk) + .map_err(|_| Error::Read("encrypted buffer"))?; + } + + self.start_of_compressed_data = cursor.position(); + cursor.set_position(self.start_of_compressed_data + self.compressed_size); + + if self.start_of_compressed_data + self.compressed_size > cursor.get_ref().len() as u64 { + return Err(Error::Parse("Invalid compressed data length")); + } + + Ok(()) + } + + pub fn is_supported(&self) -> Result<(), Error> { + if self.is_encrypted() { + return Err(Error::UnsupportedFeature("Encryption Unsupported")); + } + + if self.is_data_descriptor() { + return Err(Error::UnsupportedFeature("Data Descriptors are Unsupported")); + } + + Ok(()) + } + + /* + * This has no header/checksum validation. + */ + fn extract_file_deflate( + &mut self, + cursor: &std::io::Cursor<&Vec>, + files: &mut Vec, + ) -> Result<(), Error> { + #[allow(clippy::cast_possible_truncation)] + let start: usize = self.start_of_compressed_data as usize; + #[allow(clippy::cast_possible_truncation)] + let end: usize = start + self.compressed_size as usize; + if end >= cursor.get_ref().len() { + return Err(Error::Extract); + } + let data: &[u8] = &cursor.get_ref().as_slice()[start..end]; + + let mut inflater = InflateStream::new(); + let mut out: Vec = Vec::::new(); + let mut n: usize = 0; + + while n < data.len() { + let res = inflater.update(&data[n..]); + if let Ok((num_bytes_read, result)) = res { + n += num_bytes_read; + out.extend(result.iter().copied()); + } else { + return Err(Error::Extract); + } + } + + self.write_file(&out, files); + + Ok(()) + } + + fn write_file(&mut self, buffer: &[u8], files: &mut Vec) { + let extracted_file: ExtractedFile = ExtractedFile { + name: Some(self.file_name.to_string()), + data: buffer.to_vec(), + }; + + files.push(extracted_file); + } + + fn extract_file_nocomp( + &mut self, + cursor: &mut std::io::Cursor<&Vec>, + files: &mut Vec, + ) -> Result<(), Error> { + #[allow(clippy::cast_possible_truncation)] + let idx0: usize = self.start_of_compressed_data as usize; + + let mut len = self.compressed_size; + if self.compressed_size != self.uncompressed_size { + debug!("Uncompressed file has different lengths for compressed vs uncompressed, using the shorter"); + if self.compressed_size > self.uncompressed_size { + len = self.uncompressed_size; + } + } + + #[allow(clippy::cast_possible_truncation)] + let idx1: usize = idx0 + len as usize; + if idx1 > cursor.get_ref().len() { + debug!("Invalid data length"); + return Err(Error::Extract); + } + + let contents = &cursor.get_ref().as_slice()[idx0..idx1]; + cursor.set_position(idx1 as u64); + + self.write_file(contents, files); + Ok(()) + } + + fn extract_file_bzip2( + &mut self, + cursor: &std::io::Cursor<&Vec>, + files: &mut Vec, + ) -> Result<(), Error> { + #[allow(clippy::cast_possible_truncation)] + let idx0: usize = self.start_of_compressed_data as usize; + #[allow(clippy::cast_possible_truncation)] + let idx1: usize = idx0 + self.compressed_size as usize; + + let contents = &cursor.get_ref().as_slice()[idx0..idx1]; + + /* + * Create vector of the needed capacity. + */ + let mut out: Vec = Vec::new(); + for _i in 0..self.uncompressed_size { + out.push(0); + } + + let mut decompressor = DecoderReader::new(contents); + let ret = decompressor.read_exact(&mut out); + if ret.is_err() { + debug!("Unable to decompress bz2 data"); + return Err(Error::Extract); + } + + self.write_file(&out, files); + Ok(()) + } + + fn extract_file( + &mut self, + cursor: &mut std::io::Cursor<&Vec>, + files: &mut Vec, + ) -> Result<(), Error> { + const ALZ_COMP_NOCOMP: u8 = 0; + const ALZ_COMP_BZIP2: u8 = 1; + const ALZ_COMP_DEFLATE: u8 = 2; + + match self.compression_method { + ALZ_COMP_NOCOMP => self.extract_file_nocomp(cursor, files), + ALZ_COMP_BZIP2 => self.extract_file_bzip2(cursor, files), + ALZ_COMP_DEFLATE => self.extract_file_deflate(cursor, files), + _ => Err(Error::Extract), + } + } +} + +/*TODO: Merge this with the onenote extracted_file struct, and use the same one everywhere.*/ +pub struct ExtractedFile { + pub name: Option, + pub data: Vec, +} + +#[derive(Default)] +pub struct Alz { + pub embedded_files: Vec, +} + +impl<'aa> Alz { + /* Check for the ALZ file header. */ + #[allow(clippy::unused_self)] + fn is_alz(&self, cursor: &mut std::io::Cursor<&Vec>) -> bool { + cursor + .read_u32::() + .map_or(false, |n| ALZ_FILE_HEADER == n) + } + + fn parse_local_fileheader(&mut self, cursor: &mut std::io::Cursor<&Vec>) -> bool { + let mut local_fileheader = AlzLocalFileHeader::new(); + + if let Err(err) = local_fileheader.parse(cursor) { + debug!("{err}"); + return false; + } + + if let Err(err) = local_fileheader.is_supported() { + debug!("{err}"); + return false; + } + + if !local_fileheader.is_directory() { + /* The is_file flag doesn't appear to always be set, so we'll just assume it's a file if + * it's not marked as a directory.*/ + let res2 = local_fileheader.extract_file(cursor, &mut self.embedded_files); + if res2.is_err() { + return false; + } + } + + true + } + + #[allow(clippy::unused_self)] + fn parse_central_directoryheader(&self, cursor: &mut std::io::Cursor<&Vec>) -> bool { + /* + * This is ignored in unalz (UnAlz.cpp ReadCentralDirectoryStructure). + * + * It actually reads 12 bytes, and I think it happens to work because EOF is hit on the next + * read, which it does not consider an error. + */ + let ret = cursor.read_u64::(); + ret.is_ok() + } + + #[must_use] + pub const fn new() -> Self { + Self { + embedded_files: Vec::new(), + } + } + + /// # Errors + /// Will return `Error::Parse` if file headers are not correct or are inconsistent. + pub fn from_bytes(bytes: &'aa [u8]) -> Result { + let binding = bytes.to_vec(); + let mut cursor = Cursor::new(&binding); + + let mut alz: Self = Self::new(); + + if !alz.is_alz(&mut cursor) { + return Err(Error::Parse("No ALZ file header")); + } + + //What these bytes are supposed to be in unspecified, but they need to be there. + let ret = cursor.read_u32::(); + if ret.is_err() { + return Err(Error::Parse("Error reading uint32 from file")); + } + + loop { + let Ok(sig) = cursor.read_u32::() else { + break; + }; + + match sig { + ALZ_LOCAL_FILE_HEADER => { + if alz.parse_local_fileheader(&mut cursor) { + continue; + } + } + ALZ_CENTRAL_DIRECTORY_HEADER => { + if alz.parse_central_directoryheader(&mut cursor) { + continue; + } + } + ALZ_END_OF_CENTRAL_DIRECTORY_HEADER => { + break; + /*This is the end, nothing really to do here.*/ + } + _ => { + #[allow(clippy::uninlined_format_args)] + return Err(Error::UnrecognizedSig(format!("{:x}", sig))); + } + } + } + + Ok(alz) + } +} diff --git a/libclamav_rust/src/evidence.rs b/libclamav_rust/src/evidence.rs index 4cde71033e..e91bc7a991 100644 --- a/libclamav_rust/src/evidence.rs +++ b/libclamav_rust/src/evidence.rs @@ -244,17 +244,11 @@ impl Evidence { match indicator_type { IndicatorType::Strong => { - self.strong - .entry(name.to_string()) - .or_default() - .push(meta); + self.strong.entry(name.to_string()).or_default().push(meta); } IndicatorType::PotentiallyUnwanted => { - self.pua - .entry(name.to_string()) - .or_default() - .push(meta); + self.pua.entry(name.to_string()).or_default().push(meta); } #[cfg(feature = "not_ready")] @@ -262,10 +256,7 @@ impl Evidence { // match the archive/extraction level at which each was found. // This will be required for alerting signatures to depend on weak-indicators for embedded content. IndicatorType::Weak => { - self.weak - .entry(name.to_string()) - .or_default() - .push(meta); + self.weak.entry(name.to_string()).or_default().push(meta); } } diff --git a/libclamav_rust/src/fuzzy_hash.rs b/libclamav_rust/src/fuzzy_hash.rs index 9c260a0ffd..6d848370ae 100644 --- a/libclamav_rust/src/fuzzy_hash.rs +++ b/libclamav_rust/src/fuzzy_hash.rs @@ -26,8 +26,7 @@ use std::{ ffi::CStr, mem::ManuallyDrop, os::raw::c_char, - panic, - slice, + panic, slice, }; use image::{imageops::FilterType::Lanczos3, DynamicImage, ImageBuffer, Luma, Pixel, Rgb}; @@ -308,10 +307,7 @@ impl FuzzyHashMap { // If the hash key does not exist in the hashmap, insert an empty vec. // Then add the current meta struct to the entry. - self.hashmap - .entry(fuzzy_hash) - .or_default() - .push(meta); + self.hashmap.entry(fuzzy_hash).or_default().push(meta); Ok(()) } @@ -415,7 +411,6 @@ impl FuzzyHashMap { /// param: hash_out is an output variable /// param: hash_out_len indicates the size of the hash_out buffer pub fn fuzzy_hash_calculate_image(buffer: &[u8]) -> Result, Error> { - // Load image and attempt to catch panics in case the decoders encounter unexpected issues let result = panic::catch_unwind(|| -> Result { let image = image::load_from_memory(buffer).map_err(Error::ImageLoad)?; diff --git a/libclamav_rust/src/lib.rs b/libclamav_rust/src/lib.rs index 3a19b84b50..3ff2d0e0c3 100644 --- a/libclamav_rust/src/lib.rs +++ b/libclamav_rust/src/lib.rs @@ -34,3 +34,4 @@ pub mod logging; pub mod onenote; pub mod scanners; pub mod util; +pub mod alz; diff --git a/libclamav_rust/src/scanners.rs b/libclamav_rust/src/scanners.rs index 554205c9e5..f02ebe3975 100644 --- a/libclamav_rust/src/scanners.rs +++ b/libclamav_rust/src/scanners.rs @@ -33,6 +33,7 @@ use libc::c_void; use log::{debug, error, warn}; use crate::{ + alz::Alz, ctx, onenote::OneNote, sys::{ @@ -295,3 +296,51 @@ pub unsafe extern "C" fn scan_lha_lzh(ctx: *mut cli_ctx) -> cl_error_t { cl_error_t_CL_SUCCESS } + +/// Scan an Alz file for attachments +/// +/// # Safety +/// +/// Must be a valid ctx pointer. +#[no_mangle] +pub unsafe extern "C" fn cli_scanalz(ctx: *mut cli_ctx) -> cl_error_t { + let fmap = match ctx::current_fmap(ctx) { + Ok(fmap) => fmap, + Err(e) => { + warn!("Error getting FMap from ctx: {e}"); + return cl_error_t_CL_ERROR; + } + }; + + let file_bytes = match fmap.need_off(0, fmap.len()) { + Ok(bytes) => bytes, + Err(err) => { + error!( + "Failed to get file bytes for fmap of size {}: {err}", + fmap.len() + ); + return cl_error_t_CL_ERROR; + } + }; + + let alz = match Alz::from_bytes(file_bytes) { + Ok(x) => x, + Err(err) => { + error!("Failed to parse Alz file: {}", err.to_string()); + return cl_error_t_CL_ERROR; + } + }; + + for i in 0..alz.embedded_files.len() { + let ret = magic_scan( + ctx, + &alz.embedded_files[i].data, + alz.embedded_files[i].name.clone(), + ); + if ret != cl_error_t_CL_SUCCESS { + return ret; + } + } + + cl_error_t_CL_SUCCESS +} diff --git a/unit_tests/clamscan/alz_test.py b/unit_tests/clamscan/alz_test.py new file mode 100644 index 0000000000..78b294b339 --- /dev/null +++ b/unit_tests/clamscan/alz_test.py @@ -0,0 +1,121 @@ +# Copyright (C) 2020-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + +""" +Run clamscan tests. +""" + +import sys + +sys.path.append('../unit_tests') +import testcase + + +class TC(testcase.TestCase): + @classmethod + def setUpClass(cls): + super(TC, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TC, cls).tearDownClass() + + def setUp(self): + super(TC, self).setUp() + + def tearDown(self): + super(TC, self).tearDown() + self.verify_valgrind_log() + + def test_deflate(self): + self.step_name('Test alz files compressed with deflate (gzip)') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'deflate.alz' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'ALZ_TEST_FILE.UNOFFICIAL FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_bzip2(self): + self.step_name('Test alz files compressed with bzip2') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'bzip2.alz' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'ALZ_TEST_FILE.UNOFFICIAL FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_bzip2_with_binary(self): + self.step_name('Test alz files compressed with bzip2') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'bzip2.bin.alz' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'ALZ_TEST_FILE_EXECUTABLE.UNOFFICIAL FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_uncompressed(self): + self.step_name('Test alz files with no compression') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'uncompressed.alz' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'ALZ_TEST_FILE.UNOFFICIAL FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_uncompressed_with_binary(self): + self.step_name('Test alz files with no compression with binary data') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'uncompressed.bin.alz' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'ALZ_TEST_FILE_EXECUTABLE.UNOFFICIAL FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + + + diff --git a/unit_tests/input/other_scanfiles/alz/bzip2.alz b/unit_tests/input/other_scanfiles/alz/bzip2.alz new file mode 100644 index 0000000000000000000000000000000000000000..c3449e43fb8eaae0a3be5a93442a934dc4339c58 GIT binary patch literal 435 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 literal 0 HcmV?d00001 diff --git a/unit_tests/input/other_scanfiles/alz/deflate.alz b/unit_tests/input/other_scanfiles/alz/deflate.alz new file mode 100644 index 0000000000000000000000000000000000000000..1114dab3d74015c2b9817e000f082f11d7d6ce6f GIT binary patch literal 274 zcmZ?tiDKkpU|?_p(+ZoKogx$%m>AaGY`-AHAi!X#Uy@o}qE}K;60GH^s~6~T`iy6& zK}gVpr%W&{TcKL&7OPJOX)(g8B?wu|4ycxMUGA$uT8y!32}9Ph8>;1ASh@sAiwRaO nfp9Gx3<_(YS}sg7*WhR1Wq|oa8{rX|q7~9Ib5a!yKZEqm z1@ZEl=)+VhA(WaZp{boCufoF1YlKa$5xUyDdiJusyvEqn8l$U?_uQ0svsLHR}KX literal 0 HcmV?d00001 diff --git a/unit_tests/input/other_scanfiles/alz/uncompressed.bin.alz b/unit_tests/input/other_scanfiles/alz/uncompressed.bin.alz new file mode 100644 index 0000000000000000000000000000000000000000..6cf38a4c9eddc191de10a365c41411a02bbdce35 GIT binary patch literal 16907 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 literal 0 HcmV?d00001 diff --git a/unit_tests/input/other_sigs/alz.hdb b/unit_tests/input/other_sigs/alz.hdb new file mode 100644 index 0000000000..c8cfb7e943 --- /dev/null +++ b/unit_tests/input/other_sigs/alz.hdb @@ -0,0 +1,13 @@ +03d4c70e6aa3832fa51959137b6a3fc55d8b9f55:16:ALZ_TEST_FILE +05cf0585be97d8f544f034c7e46cf98778925c66:13:ALZ_TEST_FILE +12f41f69d25d0ba9b73da429e0d69b27c95522db:16:ALZ_TEST_FILE +24578375a0454c0657bac54084b50fdda1efaa21:11:ALZ_TEST_FILE +26c0e077ad49260d416dbf449569efbc7ce02448:16:ALZ_TEST_FILE +33ab5639bfd8e7b95eb1d8d0b87781d4ffea4d5d:12:ALZ_TEST_FILE +67b33eebc1e4537d839bc6b04affd6b06074c746:13:ALZ_TEST_FILE +6847c9c6e9218691910a0d7e36ac544149e3ce7d:13:ALZ_TEST_FILE +9645df16bc733a92129563ad2e5f1f6a9ed483c9:16:ALZ_TEST_FILE +9ac5483905f6c4b72c314c901ceb5eca2fee95c3:13:ALZ_TEST_FILE +cb9431a94ca1d5c64d9a1e467c543905f592f351:13:ALZ_TEST_FILE +ce5cec9fef4940d0d1fe2bc5004b14d7f8fc290c:77:ALZ_TEST_FILE +edf6cd48d7b44a6cc0a96a6139cfe020865f8c4c:16712:ALZ_TEST_FILE_EXECUTABLE