rust: bssl-tls: Add controls for CAs

Bug: 479599893
Signed-off-by: Xiangfei Ding <xfding@google.com>
Change-Id: I2b58066e499d157b7dce1560b2f161f76a6a6964
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/98791
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/rust/bssl-tls/src/connection/credentials.rs b/rust/bssl-tls/src/connection/credentials.rs
index 73c5d2b..cc2b0d9 100644
--- a/rust/bssl-tls/src/connection/credentials.rs
+++ b/rust/bssl-tls/src/connection/credentials.rs
@@ -31,6 +31,7 @@
 
 use super::{
     Client,
+    Server,
     TlsConnection,
     TlsConnectionBuilder,
     lifecycle::{
@@ -46,6 +47,7 @@
     credentials::{
         CertificateType,
         CertificateVerificationMode,
+        DistinguishedName,
         PrivateKeyDelegate,
         SignatureAlgorithm,
         TlsCredential,
@@ -495,3 +497,25 @@
         Some(crate::credentials::marshal_evp_into_spki(pkey))
     }
 }
+
+/// # Certificate authorities - Server
+///
+/// TLS can send a list of supported certificate authorities to guide the peer in certificate
+/// selection.
+impl<M> TlsConnectionInHandshake<'_, Server, M> {
+    /// This setting advertises the list of certificate authorities names in the
+    /// `certificate_authorities` extension to send the client.
+    pub fn set_ca_names(
+        &mut self,
+        names: impl IntoIterator<Item = DistinguishedName>,
+    ) -> &mut Self {
+        unsafe {
+            // Safety: this call only transfers the ownership of the stack.
+            bssl_sys::SSL_set0_CA_names(
+                self.ptr(),
+                DistinguishedName::into_crypto_buffer_stack(names),
+            )
+        }
+        self
+    }
+}
diff --git a/rust/bssl-tls/src/context/credentials.rs b/rust/bssl-tls/src/context/credentials.rs
index 8159b8a..f9f951b 100644
--- a/rust/bssl-tls/src/context/credentials.rs
+++ b/rust/bssl-tls/src/context/credentials.rs
@@ -31,6 +31,7 @@
     credentials::{
         CertificateType,
         CertificateVerificationMode,
+        DistinguishedName,
         PrivateKeyDelegate,
         SignatureAlgorithm,
         TlsCredential,
@@ -333,3 +334,25 @@
         Ok(self)
     }
 }
+
+/// # Certificate authorities - Server
+///
+/// TLS can send a list of supported certificate authorities to guide the peer in certificate
+/// selection.
+impl<M> TlsContextBuilder<M> {
+    /// This setting advertises the list of certificate authorities names in the
+    /// `certificate_authorities` extension to send the client.
+    pub fn set_ca_names(
+        &mut self,
+        names: impl IntoIterator<Item = DistinguishedName>,
+    ) -> &mut Self {
+        unsafe {
+            // Safety: this call only transfers the ownership of the stack.
+            bssl_sys::SSL_CTX_set0_client_CAs(
+                self.ptr(),
+                DistinguishedName::into_crypto_buffer_stack(names),
+            )
+        }
+        self
+    }
+}
diff --git a/rust/bssl-tls/src/credentials.rs b/rust/bssl-tls/src/credentials.rs
index 7fe822b..2fa6181 100644
--- a/rust/bssl-tls/src/credentials.rs
+++ b/rust/bssl-tls/src/credentials.rs
@@ -65,6 +65,7 @@
     ffi::{
         Alloc,
         Bio,
+        Stack,
         sanitize_slice,
         slice_into_ffi_raw_parts, //
     },
@@ -1122,5 +1123,28 @@
     buffer.as_ref().to_vec()
 }
 
+crypto_buffer_wrapper! {
+    /// A name `DistinguishedName` encoded into DER per [RFC 5280].
+    /// Typically this is used to enclose a certificate authority name.
+    ///
+    /// [RFC 5280]: <https://datatracker.ietf.org/doc/html/rfc5280#appendix-A.1>
+    pub struct DistinguishedName
+}
+
+impl DistinguishedName {
+    pub(crate) fn into_crypto_buffer_stack(
+        names: impl IntoIterator<Item = Self>,
+    ) -> *mut bssl_sys::stack_st_CRYPTO_BUFFER {
+        let mut sk = Stack::new();
+        for name in names {
+            unsafe {
+                // Safety: `name` is owned at the moment.
+                sk.push(name.into_raw());
+            }
+        }
+        sk.into_raw()
+    }
+}
+
 #[cfg(test)]
 mod tests;
diff --git a/rust/bssl-tls/src/ffi.rs b/rust/bssl-tls/src/ffi.rs
index 2cd03f1..c462fd9 100644
--- a/rust/bssl-tls/src/ffi.rs
+++ b/rust/bssl-tls/src/ffi.rs
@@ -14,7 +14,10 @@
 
 use core::{
     marker::PhantomData,
-    mem::MaybeUninit,
+    mem::{
+        MaybeUninit,
+        forget, //
+    },
     ptr::{
         NonNull,
         null,
@@ -271,3 +274,106 @@
         self.filled()
     }
 }
+
+pub(crate) unsafe trait BsslStack: Sized {
+    type Element: StackElement;
+
+    fn new() -> *mut Self;
+
+    /// Safety: `this` handle must be a live `stack_st_*` handle.
+    unsafe fn size(this: *const Self) -> usize;
+
+    /// Safety: both `this` and `elem` cannot be aliased.
+    unsafe fn push(this: *mut Self, elem: *mut Self::Element);
+
+    const POP_FREE: unsafe extern "C" fn(
+        *mut Self,
+        Option<unsafe extern "C" fn(*mut Self::Element)>,
+    );
+}
+
+unsafe impl BsslStack for bssl_sys::stack_st_CRYPTO_BUFFER {
+    type Element = bssl_sys::CRYPTO_BUFFER;
+
+    fn new() -> *mut Self {
+        let st = unsafe {
+            // Safety: this call only allocates memory
+            bssl_sys::sk_CRYPTO_BUFFER_new_null()
+        };
+        if st.is_null() {
+            panic!("allocation failed")
+        }
+        st
+    }
+
+    unsafe fn size(this: *const Self) -> usize {
+        unsafe {
+            // Safety: `this` is still live and valid.
+            bssl_sys::sk_CRYPTO_BUFFER_num(this)
+        }
+    }
+
+    unsafe fn push(this: *mut Self, elem: *mut bssl_sys::CRYPTO_BUFFER) {
+        let rc = unsafe {
+            // Safety: `this` and `elem` are exclusively owned and valid.
+            bssl_sys::sk_CRYPTO_BUFFER_push(this, elem)
+        };
+        if rc == 0 {
+            panic!("allocation failed")
+        }
+    }
+
+    const POP_FREE: unsafe extern "C" fn(
+        *mut Self,
+        Option<unsafe extern "C" fn(*mut Self::Element)>,
+    ) = bssl_sys::sk_CRYPTO_BUFFER_pop_free;
+}
+
+pub(crate) unsafe trait StackElement: Sized {
+    type Stack: BsslStack<Element = Self>;
+
+    const FREE: unsafe extern "C" fn(*mut Self);
+}
+
+unsafe impl StackElement for bssl_sys::CRYPTO_BUFFER {
+    type Stack = bssl_sys::stack_st_CRYPTO_BUFFER;
+
+    const FREE: unsafe extern "C" fn(*mut Self) = bssl_sys::CRYPTO_BUFFER_free;
+}
+
+pub(crate) struct Stack<T: StackElement> {
+    inner: *mut T::Stack,
+    _p: PhantomData<T>,
+}
+
+impl<T: StackElement> Drop for Stack<T> {
+    fn drop(&mut self) {
+        unsafe {
+            // Safety: we still own the stack at this moment
+            T::Stack::POP_FREE(self.inner, Some(T::FREE))
+        }
+    }
+}
+
+impl<T: StackElement> Stack<T> {
+    pub fn new() -> Self {
+        Self {
+            inner: T::Stack::new(),
+            _p: PhantomData,
+        }
+    }
+
+    // Safety: `elem` must not alias because its ownership will be transferred.
+    pub unsafe fn push(&mut self, elem: *mut T) {
+        unsafe {
+            // Safety: `this` is owned by the caller.
+            T::Stack::push(self.inner, elem);
+        }
+    }
+
+    pub fn into_raw(self) -> *mut T::Stack {
+        let ptr = self.inner;
+        forget(self);
+        ptr
+    }
+}