diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 9ac825d41..3fbb587d8 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -229,6 +229,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" @@ -559,6 +569,12 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e57e3272f0190c3f1584272d613719ba5fc7df7f4942fe542e63d949cf3a649b" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "core-foundation" version = "0.9.4" @@ -719,6 +735,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "deranged" version = "0.3.11" @@ -1924,6 +1950,22 @@ 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", + "sha1", + "sha2", + "storage", + "time", + "x509-certificate", +] + [[package]] name = "nasl-builtin-cryptographic" version = "0.1.0" @@ -2047,6 +2089,7 @@ name = "nasl-builtin-std" version = "0.1.0" dependencies = [ "models", + "nasl-builtin-cert", "nasl-builtin-cryptographic", "nasl-builtin-description", "nasl-builtin-host", @@ -2432,6 +2475,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 = "percent-encoding" version = "2.3.1" @@ -3309,6 +3362,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" @@ -3370,6 +3432,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -4368,6 +4440,25 @@ 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 = "xxhash-rust" version = "0.8.11" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index d46b6f8a1..6ee007f1a 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..0e7c21a39 --- /dev/null +++ b/rust/nasl-builtin-cert/Cargo.toml @@ -0,0 +1,21 @@ +[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" +time = { version = "0", features = ["parsing"] } +sha1 = "0" +sha2 = "0" + +[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..e49526a99 --- /dev/null +++ b/rust/nasl-builtin-cert/src/lib.rs @@ -0,0 +1,281 @@ +// 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::{Context, Register}; +use nasl_function_proc_macro::nasl_function; +use nasl_syntax::NaslValue; +use x509_certificate::X509Certificate; + +#[derive(Default)] +struct Handles { + certs: Vec>, + closed_fd: Vec, +} + +#[derive(Default)] +pub struct NaslCerts(RwLock); + +type NaslCertFunction = fn(&NaslCerts, &Register, &Context) -> Result; + +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(), + "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(), + _ => "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(), + } +} + +impl NaslCerts { + #[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(), + )) + } + + #[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(()) + } + + #[nasl_function] + fn cert_query(self, fd: usize, query: &str) -> Result { + 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 subject = cert.subject_name(); + Ok(NaslValue::String( + subject.user_friendly_str().unwrap_or_default(), + )) + } + "issuer" => { + let issuer = cert.issuer_name(); + Ok(NaslValue::String( + issuer.user_friendly_str().unwrap_or_default(), + )) + } + "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" => { + todo!(); + } + "hostnames" => { + let mut ret = vec![]; + if let Some(cn) = cert.subject_common_name() { + ret.push(NaslValue::String(cn)); + } + + 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" => { + todo!(); + } + "exponent" => { + todo!(); + } + "key-size" => { + todo!(); + } + _ => Err(FunctionErrorKind::WrongArgument( + "The given query is not valid.".to_string(), + )), + } + } else { + Err(FunctionErrorKind::WrongArgument( + "The given file descriptor is not valid.".to_string(), + )) + } + } + + /// Returns found function for key or None when not found + fn lookup(key: &str) -> Option { + match key { + "cert_open" => Some(Self::cert_open), + "cert_close" => Some(Self::cert_close), + "cert_query" => Some(Self::cert_query), + _ => None, + } + } +} + +impl nasl_builtin_utils::NaslFunctionExecuter for NaslCerts { + fn nasl_fn_cache_clear(&self) -> Option { + let mut handle = self.0.write().unwrap(); + if handle.certs.is_empty() { + return None; + } + let result = handle.certs.len(); + handle.certs.clear(); + handle.certs.shrink_to_fit(); + handle.closed_fd.clear(); + handle.closed_fd.shrink_to_fit(); + Some(result) + } + + fn nasl_fn_execute( + &self, + name: &str, + register: &Register, + context: &Context, + ) -> Option { + NaslCerts::lookup(name).map(|x| x(self, register, context)) + } + + fn nasl_fn_defined(&self, name: &str) -> bool { + NaslCerts::lookup(name).is_some() + } +} diff --git a/rust/nasl-builtin-std/Cargo.toml b/rust/nasl-builtin-std/Cargo.toml index 56944d8a1..fe41d679f 100644 --- a/rust/nasl-builtin-std/Cargo.toml +++ b/rust/nasl-builtin-std/Cargo.toml @@ -6,27 +6,28 @@ 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-knowledge-base = {path = "../nasl-builtin-knowledge-base"} -nasl-builtin-cryptographic = {path = "../nasl-builtin-cryptographic"} -nasl-builtin-string = {path = "../nasl-builtin-string"} -nasl-builtin-host = {path = "../nasl-builtin-host"} +nasl-builtin-utils = { path = "../nasl-builtin-utils" } +nasl-builtin-knowledge-base = { path = "../nasl-builtin-knowledge-base" } +nasl-builtin-cryptographic = { path = "../nasl-builtin-cryptographic" } +nasl-builtin-string = { path = "../nasl-builtin-string" } +nasl-builtin-host = { path = "../nasl-builtin-host" } nasl-builtin-http = { version = "0.1.0", path = "../nasl-builtin-http" } -nasl-builtin-description = {path = "../nasl-builtin-description"} -nasl-builtin-misc = {path = "../nasl-builtin-misc"} -nasl-function-proc-macro = {path = "../nasl-function-proc-macro"} -storage = {path = "../storage"} +nasl-builtin-description = { path = "../nasl-builtin-description" } +nasl-builtin-misc = { path = "../nasl-builtin-misc" } +nasl-builtin-cert = { path = "../nasl-builtin-cert" } +nasl-function-proc-macro = { path = "../nasl-function-proc-macro" } +storage = { path = "../storage" } models = { path = "../models" } -nasl-syntax = {path = "../nasl-syntax"} +nasl-syntax = { path = "../nasl-syntax" } # depend on c libraries and are considered unstable for now -nasl-builtin-raw-ip = {path = "../nasl-builtin-raw-ip", optional = true} +nasl-builtin-raw-ip = { path = "../nasl-builtin-raw-ip", optional = true } # has license issues on debian:stable and is therefore disabled -nasl-builtin-ssh = {path = "../nasl-builtin-ssh", optional = true} +nasl-builtin-ssh = { path = "../nasl-builtin-ssh", optional = true } [dev-dependencies] -nasl-interpreter = {path = "../nasl-interpreter"} +nasl-interpreter = { path = "../nasl-interpreter" } [features] nasl-c-lib = ["nasl-builtin-cryptographic/nasl-c-lib"] diff --git a/rust/nasl-builtin-std/src/lib.rs b/rust/nasl-builtin-std/src/lib.rs index 00323291f..3a0b1b9fb 100644 --- a/rust/nasl-builtin-std/src/lib.rs +++ b/rust/nasl-builtin-std/src/lib.rs @@ -78,7 +78,8 @@ pub fn nasl_std_functions() -> nasl_builtin_utils::NaslFunctionRegister { .push_register(nasl_builtin_host::Host) .push_register(nasl_builtin_http::NaslHttp::default()) .push_register(nasl_builtin_cryptographic::Cryptographic) - .push_register(nasl_builtin_description::Description); + .push_register(nasl_builtin_description::Description) + .push_register(nasl_builtin_cert::NaslCerts::default()); builder = add_ssh(builder); builder = add_raw_ip(builder);