1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 use core::{fmt, mem, ptr};
15 use std::ffi::CString;
16 use std::path::Path;
17 
18 use libc::{c_int, c_long, c_uint, c_void};
19 
20 use super::filetype::SslFiletype;
21 use super::method::SslMethod;
22 use super::version::SslVersion;
23 use crate::c_openssl::ffi::ssl::{
24     SSL_CTX_free, SSL_CTX_get_cert_store, SSL_CTX_set_default_verify_paths, SSL_CTX_set_verify,
25 };
26 use crate::c_openssl::x509::{X509Store, X509StoreRef};
27 use crate::util::c_openssl::error::ErrorStack;
28 use crate::util::c_openssl::ffi::ssl::{
29     SSL_CTX_ctrl, SSL_CTX_load_verify_locations, SSL_CTX_new, SSL_CTX_set_alpn_protos,
30     SSL_CTX_set_cert_store, SSL_CTX_set_cert_verify_callback, SSL_CTX_set_cipher_list,
31     SSL_CTX_set_ciphersuites, SSL_CTX_up_ref, SSL_CTX_use_certificate_chain_file,
32     SSL_CTX_use_certificate_file, SSL_CTX,
33 };
34 use crate::util::c_openssl::foreign::{Foreign, ForeignRef};
35 use crate::util::c_openssl::{cert_verify, check_ptr, check_ret, ssl_init};
36 use crate::util::config::tls::DefaultCertVerifier;
37 
38 const SSL_CTRL_SET_MIN_PROTO_VERSION: c_int = 123;
39 const SSL_CTRL_SET_MAX_PROTO_VERSION: c_int = 124;
40 const SSL_CTRL_SET_SIGALGS_LIST: c_int = 98;
41 
42 foreign_type!(
43     type CStruct = SSL_CTX;
44     fn drop = SSL_CTX_free;
45     pub(crate) struct SslContext;
46     pub(crate) struct SslContextRef;
47 );
48 
49 impl SslContext {
builder(method: SslMethod) -> Result<SslContextBuilder, ErrorStack>50     pub(crate) fn builder(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
51         SslContextBuilder::new(method)
52     }
53 }
54 
55 // TODO: add useful info here.
56 impl fmt::Debug for SslContext {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result57     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
58         write!(fmt, "SslContext")
59     }
60 }
61 
62 impl Clone for SslContext {
clone(&self) -> Self63     fn clone(&self) -> Self {
64         (**self).to_owned()
65     }
66 }
67 
68 impl ToOwned for SslContextRef {
69     type Owned = SslContext;
70 
to_owned(&self) -> Self::Owned71     fn to_owned(&self) -> Self::Owned {
72         unsafe {
73             SSL_CTX_up_ref(self.as_ptr());
74             SslContext::from_ptr(self.as_ptr())
75         }
76     }
77 }
78 
79 pub(crate) const SSL_VERIFY_NONE: c_int = 0;
80 pub(crate) const SSL_VERIFY_PEER: c_int = 1;
81 
82 /// A builder for `SslContext`.
83 pub(crate) struct SslContextBuilder(SslContext);
84 
85 impl SslContextBuilder {
new(method: SslMethod) -> Result<Self, ErrorStack>86     pub(crate) fn new(method: SslMethod) -> Result<Self, ErrorStack> {
87         ssl_init();
88 
89         let ptr = check_ptr(unsafe { SSL_CTX_new(method.as_ptr()) })?;
90         check_ret(unsafe { SSL_CTX_set_default_verify_paths(ptr) })?;
91 
92         let mut builder = Self::from_ptr(ptr);
93         builder.set_verify(SSL_VERIFY_PEER);
94         builder.set_cipher_list(
95             "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK:!SHA1:!CBC",
96         )?;
97         builder.set_sigalgs_list()?;
98 
99         Ok(builder)
100     }
101 
102     /// Creates a `SslContextBuilder` from a `SSL_CTX`.
from_ptr(ptr: *mut SSL_CTX) -> Self103     pub(crate) fn from_ptr(ptr: *mut SSL_CTX) -> Self {
104         SslContextBuilder(SslContext(ptr))
105     }
106 
107     /// Creates a `*mut SSL_CTX` from a `SSL_CTX`.
as_ptr_mut(&mut self) -> *mut SSL_CTX108     pub(crate) fn as_ptr_mut(&mut self) -> *mut SSL_CTX {
109         self.0 .0
110     }
111 
112     /// Builds a `SslContext`.
build(self) -> SslContext113     pub(crate) fn build(self) -> SslContext {
114         self.0
115     }
116 
set_min_proto_version(&mut self, version: SslVersion) -> Result<(), ErrorStack>117     pub(crate) fn set_min_proto_version(&mut self, version: SslVersion) -> Result<(), ErrorStack> {
118         let ptr = self.as_ptr_mut();
119 
120         check_ret(unsafe {
121             SSL_CTX_ctrl(
122                 ptr,
123                 SSL_CTRL_SET_MIN_PROTO_VERSION,
124                 version.0 as c_long,
125                 ptr::null_mut(),
126             )
127         } as c_int)
128         .map(|_| ())
129     }
130 
set_max_proto_version(&mut self, version: SslVersion) -> Result<(), ErrorStack>131     pub(crate) fn set_max_proto_version(&mut self, version: SslVersion) -> Result<(), ErrorStack> {
132         let ptr = self.as_ptr_mut();
133 
134         check_ret(unsafe {
135             SSL_CTX_ctrl(
136                 ptr,
137                 SSL_CTRL_SET_MAX_PROTO_VERSION,
138                 version.0 as c_long,
139                 ptr::null_mut(),
140             )
141         } as c_int)
142         .map(|_| ())
143     }
144 
145     /// Loads trusted root certificates from a file.\
146     /// Uses to Set default locations for trusted CA certificates.
147     ///
148     /// The file should contain a sequence of PEM-formatted CA certificates.
set_ca_file<P>(&mut self, file: P) -> Result<(), ErrorStack> where P: AsRef<Path>,149     pub(crate) fn set_ca_file<P>(&mut self, file: P) -> Result<(), ErrorStack>
150     where
151         P: AsRef<Path>,
152     {
153         let file = Self::get_c_file(file)?;
154         let ptr = self.as_ptr_mut();
155         check_ret(unsafe {
156             SSL_CTX_load_verify_locations(ptr, file.as_ptr() as *const _, ptr::null())
157         })
158         .map(|_| ())
159     }
160 
161     /// Sets the list of supported ciphers for protocols before `TLSv1.3`.
set_cipher_list(&mut self, list: &str) -> Result<(), ErrorStack>162     pub(crate) fn set_cipher_list(&mut self, list: &str) -> Result<(), ErrorStack> {
163         let list = match CString::new(list) {
164             Ok(cstr) => cstr,
165             Err(_) => return Err(ErrorStack::get()),
166         };
167         let ptr = self.as_ptr_mut();
168 
169         check_ret(unsafe { SSL_CTX_set_cipher_list(ptr, list.as_ptr() as *const _) }).map(|_| ())
170     }
171 
172     /// Sets the list of supported ciphers for the `TLSv1.3` protocol.
set_cipher_suites(&mut self, list: &str) -> Result<(), ErrorStack>173     pub(crate) fn set_cipher_suites(&mut self, list: &str) -> Result<(), ErrorStack> {
174         let list = match CString::new(list) {
175             Ok(cstr) => cstr,
176             Err(_) => return Err(ErrorStack::get()),
177         };
178         let ptr = self.as_ptr_mut();
179 
180         check_ret(unsafe { SSL_CTX_set_ciphersuites(ptr, list.as_ptr() as *const _) }).map(|_| ())
181     }
182 
183     /// Loads a leaf certificate from a file.
184     ///
185     /// Only a single certificate will be loaded - use `add_extra_chain_cert` to
186     /// add the remainder of the certificate chain, or
187     /// `set_certificate_chain_file` to load the entire chain from a
188     /// single file.
set_certificate_file<P>( &mut self, file: P, file_type: SslFiletype, ) -> Result<(), ErrorStack> where P: AsRef<Path>,189     pub(crate) fn set_certificate_file<P>(
190         &mut self,
191         file: P,
192         file_type: SslFiletype,
193     ) -> Result<(), ErrorStack>
194     where
195         P: AsRef<Path>,
196     {
197         let file = Self::get_c_file(file)?;
198         let ptr = self.as_ptr_mut();
199         check_ret(unsafe {
200             SSL_CTX_use_certificate_file(ptr, file.as_ptr() as *const _, file_type.as_raw())
201         })
202         .map(|_| ())
203     }
204 
205     /// Loads a certificate chain from file into ctx.
206     /// The certificates must be in PEM format and must be sorted starting with
207     /// the subject's certificate (actual client or server certificate),
208     /// followed by intermediate CA certificates if applicable, and ending
209     /// at the highest level (root) CA.
set_certificate_chain_file<P>(&mut self, file: P) -> Result<(), ErrorStack> where P: AsRef<Path>,210     pub(crate) fn set_certificate_chain_file<P>(&mut self, file: P) -> Result<(), ErrorStack>
211     where
212         P: AsRef<Path>,
213     {
214         let file = Self::get_c_file(file)?;
215         let ptr = self.as_ptr_mut();
216         check_ret(unsafe { SSL_CTX_use_certificate_chain_file(ptr, file.as_ptr() as *const _) })
217             .map(|_| ())
218     }
219 
get_c_file<P>(file: P) -> Result<CString, ErrorStack> where P: AsRef<Path>,220     pub(crate) fn get_c_file<P>(file: P) -> Result<CString, ErrorStack>
221     where
222         P: AsRef<Path>,
223     {
224         let path = match file.as_ref().as_os_str().to_str() {
225             Some(path) => path,
226             None => return Err(ErrorStack::get()),
227         };
228         match CString::new(path) {
229             Ok(path) => Ok(path),
230             Err(_) => Err(ErrorStack::get()),
231         }
232     }
233 
234     /// Sets the protocols to sent to the server for Application Layer Protocol
235     /// Negotiation (ALPN).
set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack>236     pub(crate) fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> {
237         assert!(protocols.len() <= c_uint::max_value() as usize);
238 
239         let ptr = self.as_ptr_mut();
240         match unsafe { SSL_CTX_set_alpn_protos(ptr, protocols.as_ptr(), protocols.len() as c_uint) }
241         {
242             0 => Ok(()),
243             _ => Err(ErrorStack::get()),
244         }
245     }
246 
set_verify(&mut self, mode: c_int)247     pub(crate) fn set_verify(&mut self, mode: c_int) {
248         let ptr = self.as_ptr_mut();
249         unsafe { SSL_CTX_set_verify(ptr, mode, None) };
250     }
251 
set_cert_verify_callback(&mut self, verifier: *const DefaultCertVerifier)252     pub(crate) fn set_cert_verify_callback(&mut self, verifier: *const DefaultCertVerifier) {
253         let ptr = self.as_ptr_mut();
254         unsafe {
255             SSL_CTX_set_cert_verify_callback(ptr, cert_verify, verifier as *mut c_void);
256         }
257     }
258 
set_cert_store(&mut self, cert_store: X509Store)259     pub(crate) fn set_cert_store(&mut self, cert_store: X509Store) {
260         let ptr = self.as_ptr_mut();
261         unsafe {
262             SSL_CTX_set_cert_store(ptr, cert_store.as_ptr());
263             mem::forget(cert_store);
264         }
265     }
266 
cert_store_mut(&mut self) -> &mut X509StoreRef267     pub(crate) fn cert_store_mut(&mut self) -> &mut X509StoreRef {
268         let ptr = self.as_ptr_mut();
269         unsafe { X509StoreRef::from_ptr_mut(SSL_CTX_get_cert_store(ptr)) }
270     }
271 
set_sigalgs_list(&mut self) -> Result<(), ErrorStack>272     pub(crate) fn set_sigalgs_list(&mut self) -> Result<(), ErrorStack> {
273         // Allowed signature algorithms:
274         // ecdsa_secp256r1_sha256 (0x0403)
275         // ecdsa_secp384r1_sha384 (0x0503)
276         // ecdsa_secp521r1_sha512 (0x0603)
277         // ed25519 (0x0807)
278         // ed448 (0x0808)
279         // rsa_pss_pss_sha256 (0x0809)
280         // rsa_pss_pss_sha384 (0x080a)
281         // rsa_pss_pss_sha512 (0x080b)
282         // rsa_pss_rsae_sha256 (0x0804)
283         // rsa_pss_rsae_sha384 (0x0805)
284         // rsa_pss_rsae_sha512 (0x0806)
285         // rsa_pkcs1_sha256 (0x0401)
286         // rsa_pkcs1_sha384 (0x0501)
287         // rsa_pkcs1_sha512 (0x0601)
288         // SHA256 DSA (0x0402)
289         // SHA384 DSA (0x0502)
290         // SHA512 DSA (0x0602)
291         const SUPPORT_SIGNATURE_ALGORITHMS: &str = "\
292         ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:ed25519:\
293         ed448:rsa_pss_pss_sha256:rsa_pss_pss_sha384:\
294         rsa_pss_pss_sha512:rsa_pss_rsae_sha256:rsa_pss_rsae_sha384:\
295         rsa_pss_rsae_sha512:rsa_pkcs1_sha256:rsa_pkcs1_sha384:rsa_pkcs1_sha512:DSA+SHA256:DSA+SHA384:DSA+SHA512";
296         let list = match CString::new(SUPPORT_SIGNATURE_ALGORITHMS) {
297             Ok(cstr) => cstr,
298             Err(_) => return Err(ErrorStack::get()),
299         };
300 
301         let ptr = self.as_ptr_mut();
302 
303         check_ret(unsafe {
304             SSL_CTX_ctrl(
305                 ptr,
306                 SSL_CTRL_SET_SIGALGS_LIST,
307                 0,
308                 list.as_ptr() as *const c_void as *mut c_void,
309             )
310         } as c_int)
311         .map(|_| ())
312     }
313 }
314