diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 19d6a9911..2c201289c 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -155,6 +155,45 @@ dependencies = [ "term 1.0.0", ] +[[package]] +name = "asn1-rs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "async-trait" version = "0.1.83" @@ -243,6 +282,16 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bcder" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627747a6774aab38beb35990d88309481378558875a41da1a4b2e373c906ef0" +dependencies = [ + "bytes", + "smallvec", +] + [[package]] name = "bcrypt-pbkdf" version = "0.10.0" @@ -831,6 +880,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + [[package]] name = "deranged" version = "0.3.11" @@ -1546,7 +1609,7 @@ dependencies = [ "hyper 1.5.1", "hyper-util", "log", - "rustls 0.23.18", + "rustls 0.23.19", "rustls-native-certs", "rustls-pki-types", "tokio", @@ -1957,9 +2020,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.165" +version = "0.2.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb4d3d38eab6c5239a362fa8bae48c03baf980a6e7079f063942d563ef3533e" +checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36" [[package]] name = "libgcrypt-sys" @@ -2294,6 +2357,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "oid-registry" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.20.2" @@ -2499,6 +2571,16 @@ dependencies = [ "windows-sys 0.36.1", ] +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -3013,9 +3095,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" dependencies = [ "const-oid", "digest", @@ -3157,6 +3239,15 @@ dependencies = [ "semver", ] +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustix" version = "0.38.41" @@ -3184,9 +3275,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.18" +version = "0.23.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "aws-lc-rs", "log", @@ -3353,7 +3444,7 @@ dependencies = [ "rsa", "russh", "russh-keys", - "rustls 0.23.18", + "rustls 0.23.19", "rustls-pemfile 1.0.4", "rustls-pemfile 2.2.0", "sequoia-ipc", @@ -3375,6 +3466,8 @@ dependencies = [ "urlencoding", "uuid", "walkdir", + "x509-certificate", + "x509-parser", ] [[package]] @@ -3947,6 +4040,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", + "itoa", "num-conv", "powerfmt", "serde", @@ -4044,7 +4138,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.18", + "rustls 0.23.19", "rustls-pki-types", "tokio", ] @@ -4127,9 +4221,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -4692,6 +4786,42 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "x509-certificate" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66534846dec7a11d7c50a74b7cdb208b9a581cad890b7866430d438455847c85" +dependencies = [ + "bcder", + "bytes", + "chrono", + "der", + "hex", + "pem", + "ring", + "signature", + "spki", + "thiserror", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + [[package]] name = "xxhash-rust" version = "0.8.12" @@ -4769,6 +4899,20 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] [[package]] name = "zerovec" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 636b17c7e..c215b4e75 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -60,7 +60,7 @@ rustls-pemfile = "2.1.2" rustls-pemfile-old = { version = "1.0.2", package = "rustls-pemfile" } sequoia-ipc = "0.30.1" sequoia-openpgp = { version = "1.16.1", default-features = false, features = [ - "crypto-openssl", + "crypto-openssl", ] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.96" @@ -78,6 +78,8 @@ tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } urlencoding = "2.1.2" uuid = { version = "1", features = ["v4", "fast-rng", "serde"] } walkdir = "2" +x509-certificate = "0.23.1" +x509-parser = "0.16.0" rayon = { version = "1.8.0", optional = true } pcap = { version = "1.0.0", optional = true } @@ -87,8 +89,8 @@ pnet_macros = { version = "0.33.0", optional = true } pnet_macros_support = { version = "0.33.0", optional = true } libssh-rs = { version = "~0.2", features = [ - "vendored-openssl", - "vendored", + "vendored-openssl", + "vendored", ], optional = true } nasl-function-proc-macro = { path = "crates/nasl-function-proc-macro" } @@ -111,18 +113,18 @@ dep-graph-parallel = ["rayon", "crossbeam-channel"] openvas_serde_support = [] serde_support = [] default = [ - "dep-graph-parallel", - "openvas_serde_support", - "enforce-no-trailing-arguments", - "serde_support", + "dep-graph-parallel", + "openvas_serde_support", + "enforce-no-trailing-arguments", + "serde_support", ] nasl-builtin-raw-ip = [ - "pcap", - "pnet_base", - "pnet", - "pnet_macros", - "pnet_macros_support", + "pcap", + "pnet_base", + "pnet", + "pnet_macros", + "pnet_macros_support", ] nasl-builtin-libssh = ["libssh-rs"] experimental = ["nasl-builtin-raw-ip", "nasl-builtin-libssh", "nasl-c-lib"] diff --git a/rust/src/nasl/builtin/cert/mod.rs b/rust/src/nasl/builtin/cert/mod.rs new file mode 100644 index 000000000..eee4cda56 --- /dev/null +++ b/rust/src/nasl/builtin/cert/mod.rs @@ -0,0 +1,452 @@ +// SPDX-FileCopyrightText: 2024 Greenbone AG +// +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::collections::HashMap; +use std::sync::RwLock; + +use nasl_function_proc_macro::nasl_function; +use x509_certificate::X509Certificate; +use x509_parser::prelude::GeneralName; + +use crate::{ + function_set, + nasl::{FunctionErrorKind, NaslValue}, +}; + +use super::string::encode_hex; + +fn sign_alg_oid_to_name(oid: &str) -> &str { + match oid { + "1.2.840.10040.4.1" => "id-dsa", + "1.2.840.10046.2.1" => "dhpublicnumber", + "2.16.840.1.101.2.1.1.22" => "id-keyExchangeAlgorithm", + "1.2.840.10045.1.1" => "prime-field", + "1.2.840.10045.2.1" => "id-ecPublicKey", + "1.2.840.10045.4.1" => "ecdsa-with-SHA1", + "1.2.840.10045.4.3.1" => "ecdsa-with-SHA224", + "1.2.840.10045.4.3.2" => "ecdsa-with-SHA256", + "1.2.840.10045.4.3.3" => "ecdsa-with-SHA384", + "1.2.840.10045.4.3.4" => "ecdsa-with-SHA512", + "1.3.132.1.12" => "id-ecDH", + "1.2.840.10045.2.13" => "id-ecMQV", + "1.2.840.113549.1.1.10" => "id-RSASSA-PSS", + "1.2.840.113549.1.1.11" => "sha256WithRSAEncryption", + "1.2.840.113549.1.1.12" => "sha384WithRSAEncryption", + "1.2.840.113549.1.1.13" => "sha512WithRSAEncryption", + "1.2.840.113549.1.1.14" => "sha224WithRSAEncryption", + "1.2.840.113549.1.1.8" => "id-mgf1", + "1.2.840.113549.2.2" => "md2", + "1.2.840.113549.2.4" => "md4", + "1.2.840.113549.2.5" => "md5", + "1.2.840.113549.1.1.1" => "rsaEncryption", + "1.2.840.113549.1.1.2" => "md2WithRSAEncryption", + "1.2.840.113549.1.1.3" => "md4WithRSAEncryption", + "1.2.840.113549.1.1.4" => "md5WithRSAEncryption", + "1.2.840.113549.1.1.6" => "rsaOAEPEncryptionSET", + "1.2.840.10045.3.1.1" => "secp192r1", + "1.3.132.0.1" => "sect163k1", + "1.3.132.0.15" => "sect163r2", + "1.3.132.0.33" => "secp224r1", + "1.3.132.0.26" => "sect233k1", + "1.3.132.0.27" => "sect233r1", + "1.2.840.10045.3.1.7" => "secp256r1", + "1.3.132.0.16" => "sect283k1", + "1.3.132.0.17" => "sect283r1", + "1.3.132.0.34" => "secp384r1", + "1.3.132.0.36" => "sect409k1", + "1.3.132.0.37" => "sect409r1", + "1.3.132.0.35" => "sect521r1", + "1.3.132.0.38" => "sect571k1", + "1.3.132.0.39" => "sect571r1", + "2.16.840.1.101.3.4.3.1" => "id-dsa-with-sha224", + "2.16.840.1.101.3.4.3.2" => "id-dsa-with-sha256", + "2.16.840.1.101.3.4.2.1" => "sha256", + "2.16.840.1.101.3.4.2.2" => "sha384", + "2.16.840.1.101.3.4.2.3" => "sha512", + "2.16.840.1.101.3.4.2.4" => "sha224", + _ => "unknown", + } +} + +fn pub_key_alg_oid_to_name(name: &str) -> &str { + match name { + "1.2.840.113549.1.1.1" => "RSA", + "2.5.8.1.1" => "RSA (X.509)", + "1.2.840.113549.1.1.4" => "RSA (MD5)", + "1.2.840.113549.1.1.5" => "RSA (SHA1)", + "1.2.840.10040.4.1" => "DSA", + "1.2.643.2.2.19" => "GOST R 34.10-2001", + "1.2.643.2.2.20" => "GOST R 34.10-94", + "1.2.840.10045.2.1" => "EC", + _ => "unknown", + } +} + +fn subject_oid_to_name(oid: &str) -> &str { + match oid { + "2.5.4.6" => "C", + "2.5.4.8" => "ST", + "2.5.4.7" => "L", + "2.5.4.10" => "O", + "2.5.4.3" => "CN", + "2.5.4.11" => "OU", + "2.5.4.12" => "T", + "2.5.4.42" => "GN", + "2.5.4.43" => "I", + "2.5.4.4" => "SN", + _ => oid, + } +} + +pub enum CertCommands { + Serial, + Issuer, + Subject, + NotBefore, + NotAfter, + All, + Hostnames, + FprSha256, + FprSha1, + Image, + SignatureAlgorithmName, + PublicKeyAlgorithmName, + Modulus, + Exponent, + KeySize, +} + +impl TryFrom<&str> for CertCommands { + type Error = FunctionErrorKind; + + fn try_from(value: &str) -> Result { + match value { + "serial" => Ok(Self::Serial), + "issuer" => Ok(Self::Issuer), + "subject" => Ok(Self::Subject), + "not-before" => Ok(Self::NotBefore), + "not-after" => Ok(Self::NotAfter), + "all" => Ok(Self::All), + "hostnames" => Ok(Self::Hostnames), + "fpr-sha-256" => Ok(Self::FprSha256), + "fpr-sha-1" => Ok(Self::FprSha1), + "image" => Ok(Self::Image), + "algorithm-name" => Ok(Self::SignatureAlgorithmName), + "signature-algorithm-name" => Ok(Self::SignatureAlgorithmName), + "public-key-algorithm-name" => Ok(Self::PublicKeyAlgorithmName), + "modulus" => Ok(Self::Modulus), + "exponent" => Ok(Self::Exponent), + "key-size" => Ok(Self::KeySize), + _ => Err(FunctionErrorKind::WrongArgument( + "The given query is not valid.".to_string(), + )), + } + } +} + +/// This structure holds a list of certificates. The entries of the list are +/// Optional to allow for the removal of certificates. The closed list holds +/// the indexes of the removed certificates. +#[derive(Default)] +struct Handles { + certs: HashMap, + next: usize, +} + +#[derive(Default)] +pub struct NaslCerts(RwLock); + +impl NaslCerts { + fn insert(&self, cert: X509Certificate) -> usize { + let mut handle = self.0.write().unwrap(); + let index = handle.next; + handle.certs.insert(index, cert); + handle.next += 1; + handle.next - 1 + } + + /// Create a certificate object. + /// + /// Takes a string/data as unnamed argument and returns an identifier + /// used with the other cert functions. The data is usually the BER + /// encoded certificate but the function will also try a PEM encoding + /// on failure to parse BER encoded one. + /// + /// On success the function returns a cert identifier that can be used + /// for further operations. + #[nasl_function] + fn cert_open(&self, cert: &[u8]) -> Result { + if let Ok(cert) = X509Certificate::from_der(cert) { + return Ok(self.insert(cert)); + } + if let Ok(cert) = X509Certificate::from_pem(cert) { + return Ok(self.insert(cert)); + } + if let Ok(cert) = X509Certificate::from_ber(cert) { + return Ok(self.insert(cert)); + } + + Err(FunctionErrorKind::WrongArgument( + "The given string is not a valid DER, BER or PEM encoded X.509 certificate." + .to_string(), + )) + } + + /// Release a certificate object. + /// + /// Takes a cert identifier as returned by cert_open and releases the + /// associated resources. + #[nasl_function] + fn cert_close(&self, cert_handle: usize) { + let mut handle = self.0.write().unwrap(); + handle.certs.remove(&cert_handle); + } + + fn subject(cert: &X509Certificate, idx: usize) -> Option { + // The error originates from the io::Write trait. Internally a Vec is used, which + // implementation of that trait is infallible. Therefore we can unwrap here. + let der = cert.encode_der().unwrap(); + let (_, cert) = x509_parser::parse_x509_certificate(&der).unwrap(); + + if idx == 0 { + Some(cert.subject.to_string()) + } else { + cert.subject_alternative_name() + .ok() + .flatten() + .and_then(|san| san.value.general_names.get(idx - 1)) + .map(|san| Some(san.to_string())) + .unwrap_or(None) + } + } + + fn issuer(cert: &X509Certificate, idx: usize) -> Option { + let subject = cert.issuer_name(); + subject.get(idx).map(|entry| { + entry + .iter() + .filter_map(|val| { + val.value.to_string().ok().map(|value| { + format!("{}={}", subject_oid_to_name(&val.typ.to_string()), value) + }) + }) + .collect::>() + .join(", ") + }) + } + + fn hostnames(cert: &X509Certificate) -> Vec { + let mut ret = vec![]; + if let Some(cn) = cert.subject_common_name() { + ret.push(cn); + } + + let der = cert.encode_der().unwrap(); + let (_, cert) = x509_parser::parse_x509_certificate(&der).unwrap(); + + if let Ok(Some(san)) = cert.subject_alternative_name() { + for name in san.value.general_names.iter() { + if let GeneralName::DNSName(dns) = name { + ret.push(dns.to_string()); + } + } + } + + ret + } + + fn key_size(cert: &X509Certificate) -> Option { + let algorithm = cert.key_algorithm()?; + match algorithm { + x509_certificate::KeyAlgorithm::Rsa => { + if let Ok(data) = cert.rsa_public_key_data() { + return Some(((data.modulus.into_bytes().len() - 1) * 8) as i64); + } + } + _ => { + if let Ok(data) = cert.rsa_public_key_data() { + return Some((data.public_exponent.into_bytes().len() * 8) as i64); + } + } + } + None + } + + /// Query a certificate object. + /// + /// Takes a cert identifier as first unnamed argument and a command + /// string as second argument. That command is used to select specific + /// information from the certificate. For certain commands the named + /// argument @a idx is used as well. Depending on this command the + /// return value may be a number, a string, or an array of strings. + /// Supported commands are: + /// + /// - serial The serial number of the certificate as a hex string. + /// + /// - issuer Returns the issuer. The returned value is a string in + /// rfc-2253 format. + + /// - subject Returns the subject. The returned value is a string in + /// rfc-2253 format. To query the subjectAltName the + /// named parameters @a idx with values starting at 1 can + /// be used. In this case the format is either an rfc2253 + /// string as used above, an rfc2822 mailbox name + /// indicated by the first character being a left angle + /// bracket or an S-expression in advanced format for all + /// other types of subjectAltnames which is indicated by + /// an opening parentheses. + /// + /// - not-before The notBefore time as UTC value in ISO time format + /// (e.g. "20120930T143521"). + /// + /// - not-after The notAfter time as UTC value in ISO time format + /// (e.g. "20280929T143520"). + /// + /// - all Return all available information in a human readable + /// format. Not yet implemented. + /// + /// - hostnames Return an array with all hostnames listed in the + /// certificates, i.e. the CN part of the subject and all dns-name + /// type subjectAltNames. + /// + /// - fpr-sha-256 The SHA-256 fingerprint of the certificate. The + /// fingerprint is, as usual, computed over the entire + /// DER encode certificate. + /// + /// - fpr-sha-1 The SHA-1 fingerprint of the certificate. The + /// fingerprint is, as usual, computed over the entire + /// DER encode certificate. + /// + /// - image Return the entire certificate as binary data. + /// + /// - algorithm-name Same as signature-algorithm-name. TODO: Remove it and + /// leave only signature-algorithm-name. + /// + /// - signature-algorithm-name Return the algorithm name used to sign the + /// certificate. Get the OID of the digest + /// algorithm and translated to a name from a + /// list from Wireshark. + /// See epan/dissectors/packet-pkcs1.c + /// + /// - public-key-algorithm-name Return the algorithm name of the public key. + /// + /// - modulus Return the RSA public key's modulus found in the + /// structure of the given cert. + /// + /// - exponent Return the RSA public key's exponent found in + /// the structure of the given cert. + /// + /// - key-size Return the size to hold the parameters size in bits. + /// For RSA the bits returned is the modulus. + /// For DSA the bits returned are of the public exponent. + /// + /// + /// The following arguments are required: + /// - pos(0): Object id of the certificate. + /// + /// - pos(1): A string with the command to select what to return; see above. + /// + /// The following arguments are optional: + /// - idx Used by certain commands to select the n-th value of a set + /// of values. If not given 0 is assumed. + /// + /// A NASL type depending on the used command. + #[nasl_function(named(idx))] + fn cert_query( + &self, + cert_handle: usize, + query: &str, + idx: Option, + ) -> Result { + let idx = idx.unwrap_or(0); + let handle = self.0.read().unwrap(); + + let cert = handle.certs.get(&cert_handle).ok_or_else(|| { + FunctionErrorKind::WrongArgument("The given file descriptor is not valid.".to_string()) + })?; + let result = match CertCommands::try_from(query)? { + CertCommands::Serial => { + let serial = cert.serial_number_asn1().clone().into_bytes(); + NaslValue::String(encode_hex(&serial)) + } + CertCommands::Subject => Self::subject(cert, idx) + .map(NaslValue::String) + .unwrap_or(NaslValue::Null), + CertCommands::Issuer => Self::issuer(cert, idx) + .map(NaslValue::String) + .unwrap_or(NaslValue::Null), + CertCommands::NotBefore => { + let not_before = cert.validity_not_before().format("%Y%m%dT%H%M%S"); + NaslValue::String(not_before.to_string()) + } + CertCommands::NotAfter => { + let not_after = cert.validity_not_after().format("%Y%m%dT%H%M%S"); + NaslValue::String(not_after.to_string()) + } + CertCommands::FprSha256 => cert + .sha256_fingerprint() + .map(|fpr| NaslValue::String(encode_hex(fpr.as_ref()))) + .map_err(|_| { + FunctionErrorKind::Diagnostic( + "Unable to calculate SHA256 fingerprint".to_string(), + None, + ) + })?, + CertCommands::FprSha1 => cert + .sha1_fingerprint() + .map(|fpr| NaslValue::String(encode_hex(fpr.as_ref()))) + .map_err(|_| { + FunctionErrorKind::Diagnostic( + "Unable to calculate SHA1 fingerprint".to_string(), + None, + ) + })?, + CertCommands::All => { + return Err(FunctionErrorKind::Diagnostic( + "Query parameter 'all' is not implemented yet".to_string(), + None, + )) + } + CertCommands::Hostnames => NaslValue::Array( + Self::hostnames(cert) + .into_iter() + .map(NaslValue::String) + .collect::>(), + ), + CertCommands::Image => NaslValue::Data(cert.encode_der().unwrap_or_default()), + CertCommands::SignatureAlgorithmName => { + let signature_algorithm_oid = cert.signature_algorithm_oid().to_string(); + let signature_algorithm = sign_alg_oid_to_name(&signature_algorithm_oid); + NaslValue::String(signature_algorithm.to_string()) + } + CertCommands::PublicKeyAlgorithmName => { + let key_algorithm_oid = cert.key_algorithm_oid().to_string(); + let public_key_algorithm = pub_key_alg_oid_to_name(&key_algorithm_oid); + NaslValue::String(public_key_algorithm.to_string()) + } + CertCommands::Modulus => cert + .rsa_public_key_data() + .map(|data| NaslValue::Data(data.modulus.into_bytes().to_vec())) + .unwrap_or(NaslValue::Null), + CertCommands::Exponent => cert + .rsa_public_key_data() + .map(|data| NaslValue::Data(data.public_exponent.into_bytes().to_vec())) + .unwrap_or(NaslValue::Null), + CertCommands::KeySize => Self::key_size(cert) + .map(NaslValue::Number) + .unwrap_or(NaslValue::Null), + }; + Ok(result) + } +} + +function_set! { + NaslCerts, + sync_stateful, + ( + (NaslCerts::cert_open, "cert_open"), + (NaslCerts::cert_close, "cert_close"), + (NaslCerts::cert_query, "cert_query"), + ) +} diff --git a/rust/src/nasl/builtin/mod.rs b/rust/src/nasl/builtin/mod.rs index 238d385c0..9f7be4f39 100644 --- a/rust/src/nasl/builtin/mod.rs +++ b/rust/src/nasl/builtin/mod.rs @@ -5,6 +5,7 @@ #![doc = include_str!("README.md")] mod array; +mod cert; mod cryptographic; mod description; mod host; @@ -52,7 +53,8 @@ pub fn nasl_std_functions() -> Executor { .add_set(description::Description) .add_set(isotime::NaslIsotime) .add_set(cryptographic::rc4::CipherHandlers::default()) - .add_set(ssh::Ssh::default()); + .add_set(ssh::Ssh::default()) + .add_set(cert::NaslCerts::default()); #[cfg(feature = "nasl-builtin-raw-ip")] executor.add_set(raw_ip::RawIp); diff --git a/rust/typos.toml b/rust/typos.toml index 25e73c048..411162901 100644 --- a/rust/typos.toml +++ b/rust/typos.toml @@ -5,6 +5,12 @@ des_ede_cbc_encrypt = "des_ede_cbc_encrypt" [default.extend-words] hd = "hd" guid = "guid" +GOST = "GOST" +fpr = "fpr" [files] -extend-exclude = ["data/osp/response_*.xml", "crates/smoketest/configs/client_sample.cert", "*.notus"] +extend-exclude = [ + "data/osp/response_*.xml", + "crates/smoketest/configs/client_sample.cert", + "*.notus", +]