From df5841605f5a6318e2e302d490877e79a80007cb Mon Sep 17 00:00:00 2001
From: Daniel McCarney <daniel@binaryparadox.net>
Date: Tue, 25 Jun 2024 16:42:01 -0400
Subject: [PATCH] conf: implement VerifyCAPath/VerifyCAFile commands

---
 rustls-libssl/src/conf.rs    |  60 ++++++++++++++-
 rustls-libssl/tests/config.c | 144 ++++++++++++++++++++++++++---------
 2 files changed, 166 insertions(+), 38 deletions(-)

diff --git a/rustls-libssl/src/conf.rs b/rustls-libssl/src/conf.rs
index f86c433..99cbbb7 100644
--- a/rustls-libssl/src/conf.rs
+++ b/rustls-libssl/src/conf.rs
@@ -220,6 +220,48 @@ impl SslConfigCtx {
         }
     }
 
+    fn verify_ca_path(&mut self, path: Option<&str>) -> Result<ActionResult, Error> {
+        let path = match path {
+            Some(path) => path,
+            None => return Ok(ActionResult::ValueRequired),
+        };
+
+        match &self.state {
+            State::Validating => Ok(ActionResult::Applied),
+            State::ApplyingToCtx(ctx) => {
+                ctx.get_mut().default_cert_dir = Some(path.into());
+                Ok(ActionResult::Applied)
+            }
+            State::ApplyingToSsl(_) => {
+                // NYI: would require setting a constructed `RootCertStore` on the `Ssl` instance.
+                Err(Error::not_supported(
+                    "VerifyCAPath with SSL structure not supported",
+                ))
+            }
+        }
+    }
+
+    fn verify_ca_file(&mut self, path: Option<&str>) -> Result<ActionResult, Error> {
+        let path = match path {
+            Some(path) => path,
+            None => return Ok(ActionResult::ValueRequired),
+        };
+
+        match &self.state {
+            State::Validating => Ok(ActionResult::Applied),
+            State::ApplyingToCtx(ctx) => {
+                ctx.get_mut().default_cert_file = Some(path.into());
+                Ok(ActionResult::Applied)
+            }
+            State::ApplyingToSsl(_) => {
+                // NYI: would require setting a constructed `RootCertStore` on the `Ssl` instance.
+                Err(Error::not_supported(
+                    "VerifyCAFile with SSL structure not supported",
+                ))
+            }
+        }
+    }
+
     fn parse_protocol_version(proto: Option<&str>) -> Option<u16> {
         Some(match proto {
             Some("None") => 0,
@@ -274,8 +316,8 @@ pub(super) enum ValueType {
     String = 0x1,
     /// The option value is a filename.
     File = 0x2,
-    // The option value is a directory name.
-    //Dir = 0x3,
+    /// The option value is a directory name.
+    Dir = 0x3,
     // The option value is not used.
     //None = 0x4,
 }
@@ -460,4 +502,18 @@ const SUPPORTED_COMMANDS: &[Command] = &[
         value_type: ValueType::File,
         action: SslConfigCtx::private_key,
     },
+    Command {
+        name_file: Some("VerifyCAPath"),
+        name_cmdline: Some("verifyCApath"),
+        flags: Flags(Flags::CERTIFICATE),
+        value_type: ValueType::Dir,
+        action: SslConfigCtx::verify_ca_path,
+    },
+    Command {
+        name_file: Some("VerifyCAFile"),
+        name_cmdline: Some("verifyCAfile"),
+        flags: Flags(Flags::CERTIFICATE),
+        value_type: ValueType::File,
+        action: SslConfigCtx::verify_ca_file,
+    },
 ];
diff --git a/rustls-libssl/tests/config.c b/rustls-libssl/tests/config.c
index 637d64a..bddbd0d 100644
--- a/rustls-libssl/tests/config.c
+++ b/rustls-libssl/tests/config.c
@@ -14,20 +14,38 @@ static const int conf_flags[] = {SSL_CONF_FLAG_SERVER, SSL_CONF_FLAG_CLIENT,
 
 #define NUM_FLAGS (sizeof(conf_flags) / sizeof(conf_flags[0]))
 
-static const char *supported_cmds[] = {
-    "-min_protocol", CUSTOM_PREFIX "min_protocol",
-    "MinProtocol",   CUSTOM_PREFIX "MinProtocol",
-
-    "-max_protocol", CUSTOM_PREFIX "max_protocol",
-    "MaxProtocol",   CUSTOM_PREFIX "MaxProtocol",
-
-    "VerifyMode",    CUSTOM_PREFIX "VerifyMode",
-
-    "-cert",         CUSTOM_PREFIX "cert",
-    "Certificate",   CUSTOM_PREFIX "Certificate",
-
-    "-key",          CUSTOM_PREFIX "key",
-    "PrivateKey",    CUSTOM_PREFIX "PrivateKey"};
+static const char *supported_cmds[] = {"-min_protocol",
+                                       CUSTOM_PREFIX "min_protocol",
+                                       "MinProtocol",
+                                       CUSTOM_PREFIX "MinProtocol",
+
+                                       "-max_protocol",
+                                       CUSTOM_PREFIX "max_protocol",
+                                       "MaxProtocol",
+                                       CUSTOM_PREFIX "MaxProtocol",
+
+                                       "VerifyMode",
+                                       CUSTOM_PREFIX "VerifyMode",
+
+                                       "-cert",
+                                       CUSTOM_PREFIX "cert",
+                                       "Certificate",
+                                       CUSTOM_PREFIX "Certificate",
+
+                                       "-key",
+                                       CUSTOM_PREFIX "key",
+                                       "PrivateKey",
+                                       CUSTOM_PREFIX "PrivateKey"
+
+                                                     "-verifyCApath",
+                                       CUSTOM_PREFIX "verifyCApath",
+                                       "VerifyCAPath",
+                                       CUSTOM_PREFIX "VerifyCAPath",
+
+                                       "-verifyCAfile",
+                                       CUSTOM_PREFIX "verifyCAfile",
+                                       "VerifyCAFile",
+                                       CUSTOM_PREFIX "VerifyCAFile"};
 
 #define NUM_SUPPORTED_CMDS (sizeof(supported_cmds) / sizeof(supported_cmds[0]))
 
@@ -283,32 +301,86 @@ void test_certificate_and_private_key(void) {
   SSL_CTX_free(ctx);
 }
 
-int main(void) {
-  printf("Supported commands:\n");
-  printf("no base flags, default prefix:\n");
-  test_supported_cmd_value_types(0, "");
-  printf("no base flags, custom prefix:\n");
-  test_supported_cmd_value_types(0, CUSTOM_PREFIX);
+void set_verify_ca(SSL_CONF_CTX *cctx) {
+  // Note: we don't test invalid values here - our implementation diverges
+  //       slightly due to later processing of the cert file/dir.
+  printf("\t\tcmd VerifyCAPath NULL returns %d\n",
+         SSL_CONF_cmd(cctx, "VerifyCAPath", NULL));
+  printf("\t\tcmd VerifyCAPath 'test-ca/rsa' returns %d\n",
+         SSL_CONF_cmd(cctx, "VerifyCAPath", "test-ca/rsa"));
+
+  printf("\t\tcmd VerifyCAFile NULL returns %d\n",
+         SSL_CONF_cmd(cctx, "VerifyCAFile", NULL));
+  printf("\t\tcmd VerifyCAFile 'test-ca/rsa/ca.cert' returns %d\n",
+         SSL_CONF_cmd(cctx, "VerifyCAFile", "test-ca/rsa/ca.cert"));
+}
 
-  printf("CMDLINE base flags, default prefix:\n");
-  test_supported_cmd_value_types(SSL_CONF_FLAG_CMDLINE, "");
-  printf("CMDLINE base flags,custom prefix:\n");
-  test_supported_cmd_value_types(SSL_CONF_FLAG_CMDLINE, CUSTOM_PREFIX);
+void test_verify_ca_path_file(void) {
+  SSL_CONF_CTX *cctx = SSL_CONF_CTX_new();
+  assert(cctx != NULL);
 
-  printf("FILE base flags, default prefix:\n");
-  test_supported_cmd_value_types(SSL_CONF_FLAG_FILE, "");
-  printf("FILE base flags, custom prefix:\n");
-  test_supported_cmd_value_types(SSL_CONF_FLAG_FILE, CUSTOM_PREFIX);
+  SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE);
 
-  printf("Fictional commands:\n");
-  test_fictional_cmds();
+  printf("\tPre-ctx (not certificate flag):\n");
+  set_verify_ca(cctx);
 
-  printf("Min/Max version:\n");
-  test_min_max_versions();
+  printf("\tPre-ctx (certificate flag):\n");
+  SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);
+  set_verify_ca(cctx);
+  SSL_CONF_CTX_clear_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);
 
-  printf("VerifyMode:\n");
-  test_verify_mode();
+  SSL_CTX *ctx = SSL_CTX_new(TLS_method());
+  assert(ctx != NULL);
+  SSL_CONF_CTX_set_ssl_ctx(cctx, ctx);
 
-  printf("Certificate/PrivateKey:\n");
-  test_certificate_and_private_key();
+  printf("\tWith ctx (not certificate flag):\n");
+  set_verify_ca(cctx);
+
+  printf("\tWith ctx (certificate flag):\n");
+  SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);
+  set_verify_ca(cctx);
+  SSL_CONF_CTX_clear_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);
+
+  // Note: we do not test with `SSL_CONF_set_ssl()` here - we lack
+  //       support for the `Certificate` command updating an `SSL`
+  //       struct at this time.
+
+  assert(SSL_CONF_CTX_finish(cctx));
+  SSL_CONF_CTX_free(cctx);
+  SSL_CTX_free(ctx);
+}
+
+int main(void) {
+  /*
+printf("Supported commands:\n");
+printf("no base flags, default prefix:\n");
+test_supported_cmd_value_types(0, "");
+printf("no base flags, custom prefix:\n");
+test_supported_cmd_value_types(0, CUSTOM_PREFIX);
+
+printf("CMDLINE base flags, default prefix:\n");
+test_supported_cmd_value_types(SSL_CONF_FLAG_CMDLINE, "");
+printf("CMDLINE base flags,custom prefix:\n");
+test_supported_cmd_value_types(SSL_CONF_FLAG_CMDLINE, CUSTOM_PREFIX);
+
+printf("FILE base flags, default prefix:\n");
+test_supported_cmd_value_types(SSL_CONF_FLAG_FILE, "");
+printf("FILE base flags, custom prefix:\n");
+test_supported_cmd_value_types(SSL_CONF_FLAG_FILE, CUSTOM_PREFIX);
+
+printf("Fictional commands:\n");
+test_fictional_cmds();
+
+printf("Min/Max version:\n");
+test_min_max_versions();
+
+printf("VerifyMode:\n");
+test_verify_mode();
+
+printf("Certificate/PrivateKey:\n");
+test_certificate_and_private_key();
+   */
+
+  printf("VerifyCAPath/VerifyCAFile:\n");
+  test_verify_ca_path_file();
 }