From 7c2330ec3b6bc9e265a206aa096008481ff2704f Mon Sep 17 00:00:00 2001 From: Kraemii Date: Mon, 19 Aug 2024 07:46:57 +0200 Subject: [PATCH] Add: NASL bultin functions for certificate handling Added functions: cert_open, cert_query, cert_close --- rust/Cargo.lock | 212 +++++++++++++-- rust/Cargo.toml | 1 + rust/nasl-builtin-cert/Cargo.toml | 20 ++ rust/nasl-builtin-cert/README.md | 6 + rust/nasl-builtin-cert/src/lib.rs | 422 ++++++++++++++++++++++++++++++ rust/nasl-builtin-std/Cargo.toml | 1 + rust/nasl-builtin-std/src/lib.rs | 3 +- 7 files changed, 647 insertions(+), 18 deletions(-) create mode 100644 rust/nasl-builtin-cert/Cargo.toml create mode 100644 rust/nasl-builtin-cert/README.md create mode 100644 rust/nasl-builtin-cert/src/lib.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index ad26e6432..daa64b588 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -152,6 +152,45 @@ dependencies = [ "term", ] +[[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.77", + "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.77", +] + [[package]] name = "async-trait" version = "0.1.82" @@ -235,6 +274,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 = "bindgen" version = "0.69.4" @@ -390,9 +439,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.15" +version = "1.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" dependencies = [ "jobserver", "libc", @@ -501,9 +550,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" dependencies = [ "clap_builder", "clap_derive", @@ -511,9 +560,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" dependencies = [ "anstream", "anstyle", @@ -727,6 +776,12 @@ dependencies = [ "cipher", ] +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + [[package]] name = "dbl" version = "0.3.2" @@ -758,6 +813,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" @@ -1389,9 +1458,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", @@ -2042,6 +2111,21 @@ dependencies = [ "uuid", ] +[[package]] +name = "nasl-builtin-cert" +version = "0.1.0" +dependencies = [ + "nasl-builtin-string", + "nasl-builtin-utils", + "nasl-function-proc-macro", + "nasl-interpreter", + "nasl-syntax", + "storage", + "time", + "x509-certificate", + "x509-parser", +] + [[package]] name = "nasl-builtin-cryptographic" version = "0.1.0" @@ -2195,6 +2279,7 @@ name = "nasl-builtin-std" version = "0.1.0" dependencies = [ "models", + "nasl-builtin-cert", "nasl-builtin-cryptographic", "nasl-builtin-description", "nasl-builtin-host", @@ -2339,12 +2424,31 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -2373,6 +2477,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.19.0" @@ -2476,7 +2589,7 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "hyper 1.4.1", - "hyper-rustls 0.27.2", + "hyper-rustls 0.27.3", "hyper-util", "infisto", "lazy_static", @@ -2588,6 +2701,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" @@ -3105,11 +3228,20 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[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.35" +version = "0.38.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" +checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" dependencies = [ "bitflags 2.6.0", "errno 0.3.9", @@ -3161,9 +3293,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" dependencies = [ "openssl-probe", "rustls-pemfile 2.1.3", @@ -3412,9 +3544,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -3515,6 +3647,15 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -3764,6 +3905,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", + "itoa", "num-conv", "powerfmt", "serde", @@ -3894,9 +4036,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", @@ -4594,6 +4736,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" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 9c3fea5fd..a032abacc 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,7 @@ [workspace] resolver = "2" members = [ + "nasl-builtin-cert", "nasl-builtin-knowledge-base", "nasl-builtin-raw-ip", "nasl-builtin-cryptographic", diff --git a/rust/nasl-builtin-cert/Cargo.toml b/rust/nasl-builtin-cert/Cargo.toml new file mode 100644 index 000000000..db1bed8f8 --- /dev/null +++ b/rust/nasl-builtin-cert/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "nasl-builtin-cert" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nasl-builtin-utils = { path = "../nasl-builtin-utils" } +nasl-builtin-string = { path = "../nasl-builtin-string" } +nasl-function-proc-macro = { path = "../nasl-function-proc-macro" } +nasl-syntax = { path = "../nasl-syntax" } +storage = { path = "../storage" } + +x509-certificate = "0" +x509-parser = "0" +time = { version = "0", features = ["parsing"] } + +[dev-dependencies] +nasl-interpreter = { path = "../nasl-interpreter" } diff --git a/rust/nasl-builtin-cert/README.md b/rust/nasl-builtin-cert/README.md new file mode 100644 index 000000000..82117fdce --- /dev/null +++ b/rust/nasl-builtin-cert/README.md @@ -0,0 +1,6 @@ +## Implements +- cert_close +- cert_open +- cert_query + +## Missing diff --git a/rust/nasl-builtin-cert/src/lib.rs b/rust/nasl-builtin-cert/src/lib.rs new file mode 100644 index 000000000..e5119f0ed --- /dev/null +++ b/rust/nasl-builtin-cert/src/lib.rs @@ -0,0 +1,422 @@ +// SPDX-FileCopyrightText: 2024 Greenbone AG +// +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::sync::RwLock; + +use nasl_builtin_string::encode_hex; +use nasl_builtin_utils::error::FunctionErrorKind; +use nasl_builtin_utils::function_set; +use nasl_function_proc_macro::nasl_function; +use nasl_syntax::NaslValue; +use x509_certificate::X509Certificate; +use x509_parser::prelude::GeneralName; + +#[derive(Default)] +struct Handles { + certs: Vec>, + closed_fd: Vec, +} + +#[derive(Default)] +pub struct NaslCerts(RwLock); + +fn sign_alg_oid_to_name(oid: &str) -> String { + match oid { + "1.2.840.10040.4.1" => "id-dsa".to_string(), + "1.2.840.10046.2.1" => "dhpublicnumber".to_string(), + "2.16.840.1.101.2.1.1.22" => "id-keyExchangeAlgorithm".to_string(), + "1.2.840.10045.1.1" => "prime-field".to_string(), + "1.2.840.10045.2.1" => "id-ecPublicKey".to_string(), + "1.2.840.10045.4.1" => "ecdsa-with-SHA1".to_string(), + "1.2.840.10045.4.3.1" => "ecdsa-with-SHA224".to_string(), + "1.2.840.10045.4.3.2" => "ecdsa-with-SHA256".to_string(), + "1.2.840.10045.4.3.3" => "ecdsa-with-SHA384".to_string(), + "1.2.840.10045.4.3.4" => "ecdsa-with-SHA512".to_string(), + "1.3.132.1.12" => "id-ecDH".to_string(), + "1.2.840.10045.2.13" => "id-ecMQV".to_string(), + "1.2.840.113549.1.1.10" => "id-RSASSA-PSS".to_string(), + "1.2.840.113549.1.1.11" => "sha256WithRSAEncryption".to_string(), + "1.2.840.113549.1.1.12" => "sha384WithRSAEncryption".to_string(), + "1.2.840.113549.1.1.13" => "sha512WithRSAEncryption".to_string(), + "1.2.840.113549.1.1.14" => "sha224WithRSAEncryption".to_string(), + "1.2.840.113549.1.1.8" => "id-mgf1".to_string(), + "1.2.840.113549.2.2" => "md2".to_string(), + "1.2.840.113549.2.4" => "md4".to_string(), + "1.2.840.113549.2.5" => "md5".to_string(), + "1.2.840.113549.1.1.1" => "rsaEncryption".to_string(), + "1.2.840.113549.1.1.2" => "md2WithRSAEncryption".to_string(), + "1.2.840.113549.1.1.3" => "md4WithRSAEncryption".to_string(), + "1.2.840.113549.1.1.4" => "md5WithRSAEncryption".to_string(), + "1.2.840.113549.1.1.6" => "rsaOAEPEncryptionSET".to_string(), + "1.2.840.10045.3.1.1" => "secp192r1".to_string(), + "1.3.132.0.1" => "sect163k1".to_string(), + "1.3.132.0.15" => "sect163r2".to_string(), + "1.3.132.0.33" => "secp224r1".to_string(), + "1.3.132.0.26" => "sect233k1".to_string(), + "1.3.132.0.27" => "sect233r1".to_string(), + "1.2.840.10045.3.1.7" => "secp256r1".to_string(), + "1.3.132.0.16" => "sect283k1".to_string(), + "1.3.132.0.17" => "sect283r1".to_string(), + "1.3.132.0.34" => "secp384r1".to_string(), + "1.3.132.0.36" => "sect409k1".to_string(), + "1.3.132.0.37" => "sect409r1".to_string(), + "1.3.132.0.35" => "sect521r1".to_string(), + "1.3.132.0.38" => "sect571k1".to_string(), + "1.3.132.0.39" => "sect571r1".to_string(), + "2.16.840.1.101.3.4.3.1" => "id-dsa-with-sha224".to_string(), + "2.16.840.1.101.3.4.3.2" => "id-dsa-with-sha256".to_string(), + "2.16.840.1.101.3.4.2.1" => "sha256".to_string(), + "2.16.840.1.101.3.4.2.2" => "sha384".to_string(), + "2.16.840.1.101.3.4.2.3" => "sha512".to_string(), + "2.16.840.1.101.3.4.2.4" => "sha224".to_string(), + _ => "unknown".to_string(), + } +} + +fn pub_key_alg_oid_to_name(name: &str) -> String { + match name { + "1.2.840.113549.1.1.1" => "RSA".to_string(), + "2.5.8.1.1" => "RSA (X.509)".to_string(), + "1.2.840.113549.1.1.4" => "RSA (MD5)".to_string(), + "1.2.840.113549.1.1.5" => "RSA (SHA1)".to_string(), + "1.2.840.10040.4.1" => "DSA".to_string(), + "1.2.643.2.2.19" => "GOST R 34.10-2001".to_string(), + "1.2.643.2.2.20" => "GOST R 34.10-94".to_string(), + "1.2.840.10045.2.1" => "EC".to_string(), + _ => "unknown".to_string(), + } +} + +fn subject_oid_to_name(oid: &str) -> String { + match oid { + "2.5.4.6" => "C".to_string(), + "2.5.4.8" => "ST".to_string(), + "2.5.4.7" => "L".to_string(), + "2.5.4.10" => "O".to_string(), + "2.5.4.3" => "CN".to_string(), + "2.5.4.11" => "OU".to_string(), + "2.5.4.12" => "T".to_string(), + "2.5.4.42" => "GN".to_string(), + "2.5.4.43" => "I".to_string(), + "2.5.4.4" => "SN".to_string(), + _ => oid.to_string(), + } +} + +impl NaslCerts { + /// 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) = x509_certificate::X509Certificate::from_der(cert) { + let mut handle = self.0.write().unwrap(); + if let Some(fd) = handle.closed_fd.pop() { + handle.certs[fd] = Some(cert); + return Ok(fd); + } + handle.certs.push(Some(cert)); + return Ok(handle.certs.len() - 1); + } + + Err(FunctionErrorKind::WrongArgument( + "The given string is not a valid DER 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, fd: usize) -> Result<(), FunctionErrorKind> { + let mut handle = self.0.write().unwrap(); + match handle.certs.get(fd) { + Some(Some(_)) => { + handle.certs[fd] = None; + handle.closed_fd.push(fd); + } + Some(None) => { + return Err(FunctionErrorKind::WrongArgument( + "The given file descriptor is already closed.".to_string(), + )); + } + None => { + return Err(FunctionErrorKind::WrongArgument( + "The given file descriptor is not valid.".to_string(), + )); + } + }; + Ok(()) + } + + /// 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, + fd: usize, + query: &str, + idx: Option, + ) -> Result { + let idx = idx.unwrap_or(0); + let handle = self.0.read().unwrap(); + if fd >= handle.certs.len() { + return Err(FunctionErrorKind::WrongArgument( + "The given file descriptor is not valid.".to_string(), + )); + } + if let Some(cert) = &handle.certs[fd] { + match query { + "serial" => { + let serial = cert.serial_number_asn1().clone().into_bytes(); + Ok(NaslValue::String(encode_hex(&serial))) + } + "subject" => { + let der = cert.encode_der().unwrap(); + let (_, cert) = x509_parser::parse_x509_certificate(&der).unwrap(); + + if idx == 0 { + Ok(NaslValue::String(cert.subject.to_string())) + } else { + match cert.subject_alternative_name() { + Ok(Some(san)) => { + if let Some(san) = san.value.general_names.get(idx - 1) { + return Ok(NaslValue::String(san.to_string())); + } + Ok(NaslValue::Null) + } + _ => Ok(NaslValue::Null), + } + } + } + "issuer" => { + let subject = cert.issuer_name(); + if let Some(entry) = subject.get(idx) { + let mut fields = vec![]; + for val in entry.iter() { + if let Ok(value) = val.value.to_string() { + fields.push(format!( + "{}={}", + subject_oid_to_name(&val.typ.to_string()), + value + )); + } + } + return Ok(NaslValue::String(fields.join(","))); + } + Ok(NaslValue::String("".to_string())) + } + "not-before" => { + let not_before = cert.validity_not_before().format("%Y%m%dT%H%M%S"); + Ok(NaslValue::String(not_before.to_string())) + } + "not-after" => { + let not_after = cert.validity_not_after().format("%Y%m%dT%H%M%S"); + Ok(NaslValue::String(not_after.to_string())) + } + "fpr-sha-256" => { + let mut result = Vec::new(); + match cert.sha256_fingerprint() { + Ok(fpr) => result.extend_from_slice(fpr.as_ref()), + Err(_) => { + return Err(FunctionErrorKind::Diagnostic( + "Unable to calculate SHA256 fingerprint".to_string(), + None, + )) + } + }; + Ok(NaslValue::String(encode_hex(&result))) + } + "fpr-sha-1" => { + let mut result = Vec::new(); + match cert.sha1_fingerprint() { + Ok(fpr) => result.extend_from_slice(fpr.as_ref()), + Err(_) => { + return Err(FunctionErrorKind::Diagnostic( + "Unable to calculate SHA256 fingerprint".to_string(), + None, + )) + } + }; + Ok(NaslValue::String(encode_hex(&result))) + } + "all" => Err(FunctionErrorKind::Diagnostic( + "Query parameter 'all' is not implemented yet".to_string(), + None, + )), + "hostnames" => { + let mut ret = vec![]; + if let Some(cn) = cert.subject_common_name() { + ret.push(NaslValue::String(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(NaslValue::String(dns.to_string())); + } + } + } + + Ok(NaslValue::Array(ret)) + } + "image" => Ok(NaslValue::Data(cert.encode_der().unwrap_or_default())), + "algorithm-name" | "signature-algorithm-name" => { + let signature_algorithm = + sign_alg_oid_to_name(&cert.signature_algorithm_oid().to_string()); + Ok(NaslValue::String(signature_algorithm)) + } + "public-key-algorithm-name" => { + let public_key_algorithm = + pub_key_alg_oid_to_name(&cert.key_algorithm_oid().to_string()); + Ok(NaslValue::String(public_key_algorithm)) + } + "modulus" => { + if let Ok(data) = cert.rsa_public_key_data() { + Ok(NaslValue::Data(data.modulus.into_bytes().to_vec())) + } else { + Ok(NaslValue::Null) + } + } + "exponent" => { + if let Ok(data) = cert.rsa_public_key_data() { + Ok(NaslValue::Data(data.public_exponent.into_bytes().to_vec())) + } else { + Ok(NaslValue::Null) + } + } + "key-size" => { + if let Some(algorithm) = cert.key_algorithm() { + match algorithm { + x509_certificate::KeyAlgorithm::Rsa => { + if let Ok(data) = cert.rsa_public_key_data() { + Ok(NaslValue::Number( + ((data.modulus.into_bytes().len() - 1) * 8) as i64, + )) + } else { + Ok(NaslValue::Null) + } + } + _ => { + if let Ok(data) = cert.rsa_public_key_data() { + Ok(NaslValue::Number( + (data.public_exponent.into_bytes().len() * 8) as i64, + )) + } else { + Ok(NaslValue::Null) + } + } + } + } else { + Ok(NaslValue::Null) + } + } + _ => Err(FunctionErrorKind::WrongArgument( + "The given query is not valid.".to_string(), + )), + } + } else { + Err(FunctionErrorKind::WrongArgument( + "The given file descriptor is not valid.".to_string(), + )) + } + } +} + +function_set! { + NaslCerts, + sync_stateful, + ( + (NaslCerts::cert_open, "cert_open"), + (NaslCerts::cert_close, "cert_close"), + (NaslCerts::cert_query, "cert_query"), + ) +} diff --git a/rust/nasl-builtin-std/Cargo.toml b/rust/nasl-builtin-std/Cargo.toml index 79fd7ade0..839ae4a19 100644 --- a/rust/nasl-builtin-std/Cargo.toml +++ b/rust/nasl-builtin-std/Cargo.toml @@ -16,6 +16,7 @@ nasl-builtin-description = { path = "../nasl-builtin-description" } nasl-builtin-network = { path = "../nasl-builtin-network" } nasl-builtin-misc = { path = "../nasl-builtin-misc" } nasl-builtin-regex = { path = "../nasl-builtin-regex" } +nasl-builtin-cert = { path = "../nasl-builtin-cert" } nasl-function-proc-macro = { path = "../nasl-function-proc-macro" } storage = { path = "../storage" } models = { path = "../models" } diff --git a/rust/nasl-builtin-std/src/lib.rs b/rust/nasl-builtin-std/src/lib.rs index a0e81f9d3..be26d3188 100644 --- a/rust/nasl-builtin-std/src/lib.rs +++ b/rust/nasl-builtin-std/src/lib.rs @@ -30,7 +30,8 @@ pub fn nasl_std_functions() -> Executor { .add_set(nasl_builtin_network::network::Network) .add_set(nasl_builtin_regex::RegularExpressions) .add_set(nasl_builtin_cryptographic::Cryptographic) - .add_set(nasl_builtin_description::Description); + .add_set(nasl_builtin_description::Description) + .add_set(nasl_builtin_cert::NaslCerts::default()); #[cfg(feature = "nasl-builtin-ssh")] executor.add_set(nasl_builtin_ssh::Ssh::default());