From 99052ef09acd556426a7139c014ca84915074d30 Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Sun, 15 Dec 2024 22:40:06 -0500 Subject: [PATCH] Code review fixes Fixup sigtool manpage. Other stuff. --- CMakeOptions.cmake | 2 +- common/optparser.c | 1 + docs/man/sigtool.1.in | 112 ++++++++++++++++++++++++++------- libclamav/clamav.h | 2 + libclamav/cvd.c | 10 ++- libclamav/cvd.h | 2 - libclamav_rust/src/codesign.rs | 38 ++++++----- libclamav_rust/src/cvd.rs | 4 +- sigtool/sigtool.c | 9 +++ 9 files changed, 134 insertions(+), 46 deletions(-) diff --git a/CMakeOptions.cmake b/CMakeOptions.cmake index e30340f5d6..82adb98e87 100644 --- a/CMakeOptions.cmake +++ b/CMakeOptions.cmake @@ -18,7 +18,7 @@ else() "share/clamav" CACHE STRING "Database directory.") set(CERTS_DIRECTORY - "etc/certs" CACHE STRING + "${APP_CONFIG_DIRECTORY}/certs" CACHE STRING "ClamAV CA certificates directory.") endif() diff --git a/common/optparser.c b/common/optparser.c index fe379b4da5..050ddce26e 100644 --- a/common/optparser.c +++ b/common/optparser.c @@ -170,6 +170,7 @@ const struct clam_option __clam_options[] = { {NULL, "verify", 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_SIGTOOL, "", ""}, {NULL, "key", 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_SIGTOOL, "", ""}, {NULL, "cert", 0, CLOPT_TYPE_STRING, NULL, -1, NULL, FLAG_MULTIPLE, OPT_SIGTOOL, "", ""}, + {NULL, "append", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_SIGTOOL, "", ""}, {NULL, "max-bad-sigs", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, 3000, NULL, 0, OPT_SIGTOOL, "Maximum number of mismatched signatures when building a CVD. Zero disables this limit.", "3000"}, {NULL, "flevel", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, CL_FLEVEL, NULL, 0, OPT_SIGTOOL, "Feature level to put in the CVD", ""}, {NULL, "cvd-version", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, 0, NULL, 0, OPT_SIGTOOL, "Version number of the CVD to build", ""}, diff --git a/docs/man/sigtool.1.in b/docs/man/sigtool.1.in index 990349393d..71b32c94a3 100644 --- a/docs/man/sigtool.1.in +++ b/docs/man/sigtool.1.in @@ -1,16 +1,19 @@ .TH "sigtool" "1" "February 12, 2007" "ClamAV @VERSION@" "Clam AntiVirus" + .SH "NAME" .LP sigtool \- signature and database management tool + .SH "SYNOPSIS" .LP sigtool [options] + .SH "DESCRIPTION" .LP sigtool can be used to generate MD5 checksums, convert data into hexadecimal format, list virus signatures and build/unpack/test/verify CVD databases and update scripts. -.SH "OPTIONS" -.LP +.SH "COMMON OPTIONS" +.LP .TP \fB\-h, \-\-help\fR Output help information and exit. @@ -19,13 +22,40 @@ Output help information and exit. Print version number and exit. .TP \fB\-\-quiet\fR -Be quiet \- output only error messages. +Be quiet, output only error messages. +.TP +\fB\-\-debug\fR +Enable debug messages .TP \fB\-\-stdout\fR Write all messages to stdout. .TP -\fB\-\-hex\-dump\fR -Read data from stdin and write hex string to stdout. +\fB\-\-tempdir=DIRECTORY\fR +Create temporary files in DIRECTORY. Directory must be writable for the user running sigtool. +.TP +\fB\-\-leave\-temps\fR +Do not remove temporary files. +.TP +\fB\-\-datadir=DIR\fR +Use DIR as the default database directory for all operations. + +.SH "COMMANDS FOR WORKING WITH SIGNATURES" +.LP +.TP +\fB\-l[FILE], \-\-list\-sigs[=FILE]\fR +List all signature names from the local database directory (default) or from FILE. +.TP +\fB\-fREGEX, \-\-find\-sigs=REGEX\fR +Find and display signatures from the local database directory which match the given REGEX. The whole signature body (name, hex string, etc.) is checked. +.TP +\fB\-\-decode\-sigs=REGEX\fR +Decode signatures read from the standard input (eg. piped from \-\-find\-sigs) +.TP +\fB\-\-test\-sigs=DATABASE TARGET_FILE\fR +Test all signatures from DATABASE against TARGET_FILE. This option will only give valid results if the target file is the final one (after unpacking, normalization, etc.) for which the signatures were created. + +.SH "COMMANDS TO GENERATE SIGNATURES" +.LP .TP \fB\-\-md5 [FILES]\fR Generate MD5 checksum from stdin or MD5 sigs for FILES. @@ -37,13 +67,28 @@ Generate SHA1 checksum from stdin or SHA1 sigs for FILES. Generate SHA256 checksum from stdin or SHA256 sigs for FILES. .TP \fB\-\-mdb [FILES]\fR -Generate .mdb signatures for FILES. +Generate .mdb (PE section hash) signatures for FILES. +.TP +\fB\-\-imp [FILES]\fR +Generate .imp (PE import address table hash) signatures for FILES. +.TP +\fB\-\-fuzzy\-img [FILES]\fR +Generate image fuzzy hash for each file. + +.SH "COMMANDS TO NORMALIZE FILES" +.LP .TP \fB\-\-html\-normalise=FILE\fR Create normalised HTML files comment.html, nocomment.html, and script.html in current working directory. .TP +\fB\-\-ascii\-normalise=FILE\fR +Create normalised text file from ascii source. +.TP \fB\-\-utf16\-decode=FILE\fR Decode UTF16 encoded data. + +.SH "COMMANDS FOR FILE ANALYSIS" +.LP .TP \fB\-\-vba=FILE\fR Extract VBA/Word6 macros from given MS Office document. @@ -51,9 +96,19 @@ Extract VBA/Word6 macros from given MS Office document. \fB\-\-vba\-hex=FILE\fR Extract Word6 macros from given MS Office document and display the corresponding hex values. .TP +\fB\-\-print\-certs=FILE\fR +Print Authenticode details from a PE file. +.TP +\fB\-\-hex\-dump\fR +Read data from stdin and write hex string to stdout. + +.SH "COMMANDS FOR WORKING WITH CVDS" +.LP +.TP \fB\-i, \-\-info\fR Print a CVD information and verify MD5 and a digital signature. .TP +.TP \fB\-\-build=FILE, \-b FILE\fR Build a CVD file. \-s, \-\-server is required for signed virus databases(.cvd), or, \-\-unsigned for unsigned(.cud). .TP @@ -73,20 +128,25 @@ NOTE: If a CVD is found in the \-\-datadir its version+1 is used and this value \fB\-\-no\-cdiff\fR Don't create a .cdiff file when building a new database file. .TP +\fB\-\-hybrid\fR +Create a hybrid (standard and bytecode) database file. +.TP \fB\-\-unsigned\fR Create a database file without digital signatures (.cud). .TP -\fB\-\-server\fR +\fB\-\-server=ADDR\fR ClamAV Signing Service address (for virus database maintainers only). .TP -\fB\-\-datadir=DIR\fR -Use DIR as the default database directory for all operations. .TP \fB\-\-unpack=FILE, \-u FILE\fR Unpack FILE (CVD) to a current directory. .TP +.TP \fB\-\-unpack\-current\fR Unpack a local CVD file (main or daily) to current directory. + +.SH "COMMANDS FOR WORKING WITH CDIFF PATCH FILES" +.LP .TP \fB\-\-diff=OLD NEW, \-d OLD NEW\fR Create a diff file for OLD and NEW CVDs/INCDIRs. @@ -99,27 +159,33 @@ Execute update script FILE in current directory. .TP \fB\-\-verify\-cdiff=FILE, \-r FILE\fR Verify DIFF against CVD/INCDIR. + +.SH "COMMANDS FOR CREATING AND VERIFYING DETACHED DIGITAL SIGNATURES" +.LP .TP -\fB\-l[FILE], \-\-list\-sigs[=FILE]\fR -List all signature names from the local database directory (default) or from FILE. +\fB\-\-sign\fR +Sign a file. The resulting .sign file name will be in the form: dbname\-version.cvd.sign +or FILE.sign for non\-CVD targets. It will be created next to the target file. +If a .sign file already exists, then the new signature will be appended to file. .TP -\fB\-fREGEX, \-\-find\-sigs=REGEX\fR -Find and display signatures from the local database directory which match the given REGEX. The whole signature body (name, hex string, etc.) is checked. +\fB\-\-key=FILE\fR +Specify a signing key. .TP -\fB\-\-decode\-sigs=REGEX\fR -Decode signatures read from the standard input (eg. piped from \-\-find\-sigs) +\fB\-\-cert=FILE\fR +Specify a signing cert. May be used more than once to add intermediate and root certificates. .TP -\fB\-\-test\-sigs=DATABASE TARGET_FILE\fR -Test all signatures from DATABASE against TARGET_FILE. This option will only give valid results if the target file is the final one (after unpacking, normalization, etc.) for which the signatures were created. +\fB\-\-append\fR +Use to add a signature line to an existing .sign file. Otherwise an existing .sign file will be overwritten. .TP -\fB\-\-print\-certs=FILE\fR -Print Authenticode details from a PE file. .TP -\fB\-\-tempdir=DIRECTORY\fR -Create temporary files in DIRECTORY. Directory must be writable for the user running sigtool. +\fB\-\-verify\fR +Find and verify a detached digital signature for the given file. +The digital signature file name must be in the form: dbname\-version.cvd.sign or FILE.sign for non-CVD targets. +It must be found next to the target file. .TP -\fB\-\-leave\-temps\fR -Do not remove temporary files. +\fB\-\-cvdcertsdir=DIR\fR +Specify a directory containing the root CA cert needed to verify the signature. +If not provided, then sigtool will look in the default certs directory. .SH "ENVIRONMENT VARIABLES" .LP diff --git a/libclamav/clamav.h b/libclamav/clamav.h index 1eb2b4eb10..e93d87a6e0 100644 --- a/libclamav/clamav.h +++ b/libclamav/clamav.h @@ -1135,6 +1135,8 @@ extern struct cl_cvd *cl_cvdparse(const char *head); /** * @brief Verify a CVD file by loading and unloading it. * + * This function is deprecated. Use cl_cvdverify_ex() instead. + * * @param file Filepath of CVD file. * @return cl_error_t CL_SUCCESS if success, else a CL_E* error code. */ diff --git a/libclamav/cvd.c b/libclamav/cvd.c index 51d0cb9d1b..8c37c3a2ef 100644 --- a/libclamav/cvd.c +++ b/libclamav/cvd.c @@ -396,7 +396,7 @@ cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory) { struct cl_engine *engine; cl_error_t ret; - cvd_type dbtype = CVD_TYPE_CVD; + cvd_type dbtype = CVD_TYPE_UNKNOWN; if (!(engine = cl_engine_new())) { cli_errmsg("cl_cvdverify: Can't create new engine\n"); @@ -413,10 +413,16 @@ cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory) } } - if (!!cli_strbcasestr(file, ".cld")) { + if (!!cli_strbcasestr(file, ".cvd")) { + dbtype = CVD_TYPE_CVD; + } else if (!!cli_strbcasestr(file, ".cld")) { dbtype = CVD_TYPE_CLD; } else if (!!cli_strbcasestr(file, ".cud")) { dbtype = CVD_TYPE_CUD; + } else { + cli_errmsg("cl_cvdverify: File is not a CVD, CLD, or CUD: %s\n", file); + ret = CL_ECVD; + goto done; } ret = cli_cvdload(engine, NULL, CL_DB_STDOPT | CL_DB_PUA, dbtype, file, 1); diff --git a/libclamav/cvd.h b/libclamav/cvd.h index d7f109cd1b..ff5c06c15d 100644 --- a/libclamav/cvd.h +++ b/libclamav/cvd.h @@ -52,8 +52,6 @@ typedef enum cvd_type { CVD_TYPE_CUD, } cvd_type; -typedef void *cvd_t; - cl_error_t cli_cvdload(struct cl_engine *engine, unsigned int *signo, unsigned int options, cvd_type dbtype, const char *filename, unsigned int chkonly); #endif diff --git a/libclamav_rust/src/codesign.rs b/libclamav_rust/src/codesign.rs index 3710553d7e..8031b8d6a2 100644 --- a/libclamav_rust/src/codesign.rs +++ b/libclamav_rust/src/codesign.rs @@ -87,6 +87,7 @@ pub unsafe extern "C" fn codesign_sign_file( signing_cert_path_str: *const c_char, intermediate_cert_paths_str: *const *const c_char, intermediate_cert_paths_len: usize, + append: bool, err: *mut *mut FFIError, ) -> bool { let target_file_path_str = validate_str_param!(target_file_path_str); @@ -177,6 +178,7 @@ pub unsafe extern "C" fn codesign_sign_file( &signing_cert_path, &intermediate_cert_paths, &signing_key_path, + append, ) { Ok(()) => { debug!("File signed successfully"); @@ -196,6 +198,7 @@ pub fn sign_file

( signing_cert_path: &Path, intermediate_cert_paths: &[P], signing_key_path: &Path, + append: bool, ) -> Result<(), Error> where P: AsRef, @@ -210,23 +213,26 @@ where let mut sig_bytes: SigBytes = SigBytes::new(); signature.append_sigbytes(&mut sig_bytes)?; - // If the signature file already exists, open it and append the signature to it. - let mut writer = if signature_file_path.exists() { - let mut writer = std::io::BufWriter::new( - std::fs::OpenOptions::new() - .append(true) - .open(signature_file_path)?, - ); - // Seek to the end of the file - writer.seek(std::io::SeekFrom::End(0))?; - // Write a newline before the signature - writer.write_all(b"\n")?; + let mut writer = { + let mut options = std::fs::OpenOptions::new(); + options.write(true).create(true); - writer - } else { - // Create the signature file if it doesn't exist. - let mut writer = std::io::BufWriter::new(std::fs::File::create(signature_file_path)?); - writer.write_all(b"#clamsign-1.0\n")?; + if signature_file_path.exists() { + if append { + options.append(true); + } else { + options.truncate(true); + } + } + + let mut writer = std::io::BufWriter::new(options.open(&signature_file_path)?); + + if !signature_file_path.exists() || !append { + writer.write_all(b"#clamsign-1.0\n")?; + } else if append { + writer.seek(std::io::SeekFrom::End(0))?; + writer.write_all(b"\n")?; + } writer }; diff --git a/libclamav_rust/src/cvd.rs b/libclamav_rust/src/cvd.rs index 2192c2d4c3..31e6375ca3 100644 --- a/libclamav_rust/src/cvd.rs +++ b/libclamav_rust/src/cvd.rs @@ -469,7 +469,7 @@ impl CVD { match self.verify_rsa_dsig() { Ok(()) => { debug!("CVD verified successfully with Talos MD5-based RSA digital signature"); - Ok("Talos".to_string()) + Ok("Cisco Talos MD5-Based RSA Digital Signature".to_string()) } Err(e) => { warn!( @@ -575,7 +575,7 @@ pub unsafe extern "C" fn cvd_unpack( match cvd.unpack_to(&destination_path) { Ok(()) => { - println!("CVD unpacked successfully"); + debug!("CVD unpacked successfully"); true } Err(e) => { diff --git a/sigtool/sigtool.c b/sigtool/sigtool.c index 059b7e5f03..a5ab1c640a 100644 --- a/sigtool/sigtool.c +++ b/sigtool/sigtool.c @@ -975,6 +975,8 @@ static int sign(const struct optstruct *opts) bool sign_result = false; FFIError *sign_file_error = NULL; + bool append = false; + target = optget(opts, "sign")->strarg; if (NULL == target) { mprintf(LOGG_ERROR, "sign: No target file specified.\n"); @@ -1033,6 +1035,10 @@ static int sign(const struct optstruct *opts) opt = opt->nextarg; } + if (optget(opts, "append")->enabled) { + append = true; + } + sign_result = codesign_sign_file( target, sign_file_name, @@ -1040,6 +1046,7 @@ static int sign(const struct optstruct *opts) signing_cert, (const char **)intermediate_certs, intermediate_certs_count, + append, &sign_file_error); if (!sign_result) { @@ -4057,6 +4064,8 @@ static void help(void) mprintf(LOGG_INFO, " --cert /path/to/private.key Specify a signing cert.\n"); mprintf(LOGG_INFO, " May be used more than once to add\n"); mprintf(LOGG_INFO, " intermediate and root certificates.\n"); + mprintf(LOGG_INFO, " --append Use to add a signature line to an existing .sign file.\n"); + mprintf(LOGG_INFO, " Otherwise an existing .sign file will be overwritten.\n"); mprintf(LOGG_INFO, "\n"); mprintf(LOGG_INFO, " --verify FILE Find and verify a detached digital\n"); mprintf(LOGG_INFO, " signature for the given file.\n");