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_cipher_list, SSL_CTX_set_ciphersuites, SSL_CTX_up_ref, 31 SSL_CTX_use_certificate_chain_file, SSL_CTX_use_certificate_file, SSL_CTX, 32 }; 33 use crate::util::c_openssl::foreign::{Foreign, ForeignRef}; 34 use crate::util::c_openssl::x509::{X509Ref, X509}; 35 use crate::util::c_openssl::{check_ptr, check_ret, ssl_init}; 36 37 const SSL_CTRL_EXTRA_CHAIN_CERT: c_int = 14; 38 39 const SSL_CTRL_SET_MIN_PROTO_VERSION: c_int = 123; 40 const SSL_CTRL_SET_MAX_PROTO_VERSION: c_int = 124; 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", 96 )?; 97 98 Ok(builder) 99 } 100 101 /// Creates a `SslContextBuilder` from a `SSL_CTX`. from_ptr(ptr: *mut SSL_CTX) -> Self102 pub(crate) fn from_ptr(ptr: *mut SSL_CTX) -> Self { 103 SslContextBuilder(SslContext(ptr)) 104 } 105 106 /// Creates a `*mut SSL_CTX` from a `SSL_CTX`. as_ptr_mut(&mut self) -> *mut SSL_CTX107 pub(crate) fn as_ptr_mut(&mut self) -> *mut SSL_CTX { 108 self.0 .0 109 } 110 111 /// Builds a `SslContext`. build(self) -> SslContext112 pub(crate) fn build(self) -> SslContext { 113 self.0 114 } 115 set_min_proto_version(&mut self, version: SslVersion) -> Result<(), ErrorStack>116 pub(crate) fn set_min_proto_version(&mut self, version: SslVersion) -> Result<(), ErrorStack> { 117 let ptr = self.as_ptr_mut(); 118 119 check_ret(unsafe { 120 SSL_CTX_ctrl( 121 ptr, 122 SSL_CTRL_SET_MIN_PROTO_VERSION, 123 version.0 as c_long, 124 ptr::null_mut(), 125 ) 126 } as c_int) 127 .map(|_| ()) 128 } 129 set_max_proto_version(&mut self, version: SslVersion) -> Result<(), ErrorStack>130 pub(crate) fn set_max_proto_version(&mut self, version: SslVersion) -> Result<(), ErrorStack> { 131 let ptr = self.as_ptr_mut(); 132 133 check_ret(unsafe { 134 SSL_CTX_ctrl( 135 ptr, 136 SSL_CTRL_SET_MAX_PROTO_VERSION, 137 version.0 as c_long, 138 ptr::null_mut(), 139 ) 140 } as c_int) 141 .map(|_| ()) 142 } 143 144 /// Loads trusted root certificates from a file.\ 145 /// Uses to Set default locations for trusted CA certificates. 146 /// 147 /// 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>,148 pub(crate) fn set_ca_file<P>(&mut self, file: P) -> Result<(), ErrorStack> 149 where 150 P: AsRef<Path>, 151 { 152 let path = match file.as_ref().as_os_str().to_str() { 153 Some(path) => path, 154 None => return Err(ErrorStack::get()), 155 }; 156 let file = match CString::new(path) { 157 Ok(path) => path, 158 Err(_) => return Err(ErrorStack::get()), 159 }; 160 let ptr = self.as_ptr_mut(); 161 162 check_ret(unsafe { 163 SSL_CTX_load_verify_locations(ptr, file.as_ptr() as *const _, ptr::null()) 164 }) 165 .map(|_| ()) 166 } 167 168 /// Sets the list of supported ciphers for protocols before `TLSv1.3`. set_cipher_list(&mut self, list: &str) -> Result<(), ErrorStack>169 pub(crate) fn set_cipher_list(&mut self, list: &str) -> Result<(), ErrorStack> { 170 let list = match CString::new(list) { 171 Ok(cstr) => cstr, 172 Err(_) => return Err(ErrorStack::get()), 173 }; 174 let ptr = self.as_ptr_mut(); 175 176 check_ret(unsafe { SSL_CTX_set_cipher_list(ptr, list.as_ptr() as *const _) }).map(|_| ()) 177 } 178 179 /// Sets the list of supported ciphers for the `TLSv1.3` protocol. set_cipher_suites(&mut self, list: &str) -> Result<(), ErrorStack>180 pub(crate) fn set_cipher_suites(&mut self, list: &str) -> Result<(), ErrorStack> { 181 let list = match CString::new(list) { 182 Ok(cstr) => cstr, 183 Err(_) => return Err(ErrorStack::get()), 184 }; 185 let ptr = self.as_ptr_mut(); 186 187 check_ret(unsafe { SSL_CTX_set_ciphersuites(ptr, list.as_ptr() as *const _) }).map(|_| ()) 188 } 189 190 /// Loads a leaf certificate from a file. 191 /// 192 /// Only a single certificate will be loaded - use `add_extra_chain_cert` to 193 /// add the remainder of the certificate chain, or 194 /// `set_certificate_chain_file` to load the entire chain from a 195 /// single file. set_certificate_file<P>( &mut self, file: P, file_type: SslFiletype, ) -> Result<(), ErrorStack> where P: AsRef<Path>,196 pub(crate) fn set_certificate_file<P>( 197 &mut self, 198 file: P, 199 file_type: SslFiletype, 200 ) -> Result<(), ErrorStack> 201 where 202 P: AsRef<Path>, 203 { 204 let path = match file.as_ref().as_os_str().to_str() { 205 Some(path) => path, 206 None => return Err(ErrorStack::get()), 207 }; 208 let file = match CString::new(path) { 209 Ok(path) => path, 210 Err(_) => return Err(ErrorStack::get()), 211 }; 212 let ptr = self.as_ptr_mut(); 213 214 check_ret(unsafe { 215 SSL_CTX_use_certificate_file(ptr, file.as_ptr() as *const _, file_type.as_raw()) 216 }) 217 .map(|_| ()) 218 } 219 220 /// Loads a certificate chain from file into ctx. 221 /// The certificates must be in PEM format and must be sorted starting with 222 /// the subject's certificate (actual client or server certificate), 223 /// followed by intermediate CA certificates if applicable, and ending 224 /// at the highest level (root) CA. set_certificate_chain_file<P>(&mut self, file: P) -> Result<(), ErrorStack> where P: AsRef<Path>,225 pub(crate) fn set_certificate_chain_file<P>(&mut self, file: P) -> Result<(), ErrorStack> 226 where 227 P: AsRef<Path>, 228 { 229 let path = match file.as_ref().as_os_str().to_str() { 230 Some(path) => path, 231 None => return Err(ErrorStack::get()), 232 }; 233 let file = match CString::new(path) { 234 Ok(path) => path, 235 Err(_) => return Err(ErrorStack::get()), 236 }; 237 let ptr = self.as_ptr_mut(); 238 239 check_ret(unsafe { SSL_CTX_use_certificate_chain_file(ptr, file.as_ptr() as *const _) }) 240 .map(|_| ()) 241 } 242 243 /// Sets the protocols to sent to the server for Application Layer Protocol 244 /// Negotiation (ALPN). set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack>245 pub(crate) fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { 246 assert!(protocols.len() <= c_uint::max_value() as usize); 247 248 let ptr = self.as_ptr_mut(); 249 match unsafe { SSL_CTX_set_alpn_protos(ptr, protocols.as_ptr(), protocols.len() as c_uint) } 250 { 251 0 => Ok(()), 252 _ => Err(ErrorStack::get()), 253 } 254 } 255 set_verify(&mut self, mode: c_int)256 pub(crate) fn set_verify(&mut self, mode: c_int) { 257 let ptr = self.as_ptr_mut(); 258 unsafe { SSL_CTX_set_verify(ptr, mode, None) }; 259 } 260 set_cert_store(&mut self, cert_store: X509Store)261 pub(crate) fn set_cert_store(&mut self, cert_store: X509Store) { 262 let ptr = self.as_ptr_mut(); 263 unsafe { 264 SSL_CTX_set_cert_store(ptr, cert_store.as_ptr()); 265 mem::forget(cert_store); 266 } 267 } 268 cert_store_mut(&mut self) -> &mut X509StoreRef269 pub(crate) fn cert_store_mut(&mut self) -> &mut X509StoreRef { 270 let ptr = self.as_ptr_mut(); 271 unsafe { X509StoreRef::from_ptr_mut(SSL_CTX_get_cert_store(ptr)) } 272 } 273 } 274