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::{ffi, fmt, ptr, str}; 15 use std::ffi::CString; 16 use std::net::IpAddr; 17 18 use libc::{c_int, c_long, c_uint}; 19 20 use super::bio::BioSlice; 21 use super::error::{error_get_lib, error_get_reason, ErrorStack}; 22 use super::ffi::err::{ERR_clear_error, ERR_peek_last_error}; 23 use super::ffi::pem::PEM_read_bio_X509; 24 use super::ffi::x509::{ 25 d2i_X509, EVP_PKEY_free, X509_NAME_free, X509_NAME_oneline, X509_PUBKEY_free, 26 X509_STORE_CTX_free, X509_STORE_CTX_get0_cert, X509_STORE_add_cert, X509_STORE_free, 27 X509_STORE_new, X509_VERIFY_PARAM_free, X509_VERIFY_PARAM_set1_host, X509_VERIFY_PARAM_set1_ip, 28 X509_VERIFY_PARAM_set_hostflags, X509_get_issuer_name, X509_get_pubkey, X509_get_subject_name, 29 X509_get_version, X509_up_ref, X509_verify, X509_verify_cert_error_string, EVP_PKEY, 30 STACK_X509, X509_NAME, X509_PUBKEY, X509_STORE, X509_STORE_CTX, X509_VERIFY_PARAM, 31 }; 32 use super::foreign::{Foreign, ForeignRef}; 33 use super::stack::Stackof; 34 use super::{check_ptr, check_ret, ssl_init}; 35 use crate::util::c_openssl::ffi::x509::{X509_free, C_X509}; 36 37 foreign_type!( 38 type CStruct = C_X509; 39 fn drop = X509_free; 40 pub(crate) struct X509; 41 pub(crate) struct X509Ref; 42 ); 43 44 foreign_type!( 45 type CStruct = X509_NAME; 46 fn drop = X509_NAME_free; 47 pub(crate) struct X509Name; 48 pub(crate) struct X509NameRef; 49 ); 50 51 foreign_type! { 52 type CStruct = EVP_PKEY; 53 fn drop = EVP_PKEY_free; 54 pub(crate) struct EvpPkey; 55 pub(crate) struct EvpPkeyRef; 56 } 57 58 const ERR_LIB_PEM: c_int = 9; 59 #[cfg(feature = "c_boringssl")] 60 const PEM_R_NO_START_LINE: c_int = 110; 61 #[cfg(feature = "__c_openssl")] 62 const PEM_R_NO_START_LINE: c_int = 108; 63 64 impl X509 { from_pem(pem: &[u8]) -> Result<X509, ErrorStack>65 pub(crate) fn from_pem(pem: &[u8]) -> Result<X509, ErrorStack> { 66 ssl_init(); 67 let bio = BioSlice::from_byte(pem)?; 68 let ptr = check_ptr(unsafe { 69 PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut()) 70 })?; 71 Ok(X509::from_ptr(ptr)) 72 } 73 from_der(der: &[u8]) -> Result<X509, ErrorStack>74 pub(crate) fn from_der(der: &[u8]) -> Result<X509, ErrorStack> { 75 ssl_init(); 76 let len = 77 ::std::cmp::min(der.len(), ::libc::c_long::max_value() as usize) as ::libc::c_long; 78 let ptr = check_ptr(unsafe { d2i_X509(ptr::null_mut(), &mut der.as_ptr(), len) })?; 79 Ok(X509::from_ptr(ptr)) 80 } 81 82 /// Deserializes a list of PEM-formatted certificates. stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack>83 pub(crate) fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> { 84 unsafe { 85 ssl_init(); 86 let bio = BioSlice::from_byte(pem)?; 87 88 let mut certs = vec![]; 89 loop { 90 let r = PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut()); 91 if r.is_null() { 92 let err = ERR_peek_last_error(); 93 if error_get_lib(err) == ERR_LIB_PEM 94 && error_get_reason(err) == PEM_R_NO_START_LINE 95 { 96 ERR_clear_error(); 97 break; 98 } 99 return Err(ErrorStack::get()); 100 } else { 101 certs.push(X509(r)); 102 } 103 } 104 Ok(certs) 105 } 106 } 107 } 108 109 impl X509Ref { get_cert_version(&self) -> c_long110 pub(crate) fn get_cert_version(&self) -> c_long { 111 unsafe { X509_get_version(self.as_ptr() as *const _) } 112 } 113 get_cert_name(&self) -> Result<X509Name, ErrorStack>114 pub(crate) fn get_cert_name(&self) -> Result<X509Name, ErrorStack> { 115 Ok(X509Name(check_ptr(unsafe { 116 X509_get_subject_name(self.as_ptr() as *const _) 117 })?)) 118 } 119 get_issuer_name(&self) -> Result<X509Name, ErrorStack>120 pub(crate) fn get_issuer_name(&self) -> Result<X509Name, ErrorStack> { 121 Ok(X509Name(check_ptr(unsafe { 122 X509_get_issuer_name(self.as_ptr() as *const _) 123 })?)) 124 } 125 get_cert(&self) -> Result<EvpPkey, ErrorStack>126 pub(crate) fn get_cert(&self) -> Result<EvpPkey, ErrorStack> { 127 Ok(EvpPkey(check_ptr(unsafe { 128 X509_get_pubkey(self.as_ptr() as *mut _) 129 })?)) 130 } 131 cmp_certs(&self, pkey: EvpPkey) -> c_int132 pub(crate) fn cmp_certs(&self, pkey: EvpPkey) -> c_int { 133 unsafe { X509_verify(self.as_ptr() as *mut _, pkey.as_ptr()) } 134 } 135 } 136 137 impl X509Name { get_x509_name_info(&self, buf: &mut [u8], size: c_int) -> String138 pub(crate) fn get_x509_name_info(&self, buf: &mut [u8], size: c_int) -> String { 139 unsafe { 140 let _ = X509_NAME_oneline(self.as_ptr() as *mut _, buf.as_mut_ptr() as *mut _, size); 141 let res = str::from_utf8(buf).unwrap_or("").to_string(); 142 res 143 } 144 } 145 } 146 impl Stackof for X509 { 147 type StackType = STACK_X509; 148 } 149 150 impl Clone for X509 { clone(&self) -> Self151 fn clone(&self) -> Self { 152 X509Ref::to_owned(self) 153 } 154 } 155 156 impl ToOwned for X509Ref { 157 type Owned = X509; 158 to_owned(&self) -> Self::Owned159 fn to_owned(&self) -> Self::Owned { 160 unsafe { 161 X509_up_ref(self.as_ptr()); 162 X509::from_ptr(self.as_ptr()) 163 } 164 } 165 } 166 167 #[derive(Copy, Clone, PartialEq, Eq)] 168 pub(crate) struct X509VerifyResult(c_int); 169 170 impl X509VerifyResult { error_string(&self) -> &'static str171 fn error_string(&self) -> &'static str { 172 ssl_init(); 173 unsafe { 174 let s = X509_verify_cert_error_string(self.0 as c_long); 175 str::from_utf8(ffi::CStr::from_ptr(s).to_bytes()).unwrap_or("") 176 } 177 } 178 from_raw(err: c_int) -> X509VerifyResult179 pub(crate) fn from_raw(err: c_int) -> X509VerifyResult { 180 X509VerifyResult(err) 181 } 182 } 183 184 impl fmt::Display for X509VerifyResult { fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result185 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 186 fmt.write_str(self.error_string()) 187 } 188 } 189 190 #[cfg(test)] 191 impl fmt::Debug for X509VerifyResult { fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result192 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 193 fmt.debug_struct("X509VerifyResult") 194 .field("code", &self.0) 195 .field("error", &self.error_string()) 196 .finish() 197 } 198 } 199 200 foreign_type!( 201 type CStruct = X509_STORE; 202 fn drop = X509_STORE_free; 203 pub(crate) struct X509Store; 204 pub(crate) struct X509StoreRef; 205 ); 206 207 impl X509Store { new() -> Result<X509Store, ErrorStack>208 pub(crate) fn new() -> Result<X509Store, ErrorStack> { 209 ssl_init(); 210 Ok(X509Store(check_ptr(unsafe { X509_STORE_new() })?)) 211 } 212 } 213 214 impl X509StoreRef { add_cert(&mut self, cert: X509) -> Result<(), ErrorStack>215 pub(crate) fn add_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { 216 check_ret(unsafe { X509_STORE_add_cert(self.as_ptr(), cert.as_ptr()) }).map(|_| ()) 217 } 218 add_path(&mut self, path: String) -> Result<(), ErrorStack>219 pub(crate) fn add_path(&mut self, path: String) -> Result<(), ErrorStack> { 220 #[cfg(any(feature = "c_openssl_1_1", feature = "c_boringssl"))] 221 use super::ffi::x509::X509_STORE_load_locations; 222 #[cfg(feature = "c_openssl_3_0")] 223 use super::ffi::x509::X509_STORE_load_path; 224 225 let p_slice: &str = &path; 226 let path = match CString::new(p_slice) { 227 Ok(cstr) => cstr, 228 Err(_) => return Err(ErrorStack::get()), 229 }; 230 #[cfg(feature = "c_openssl_3_0")] 231 return check_ret(unsafe { 232 X509_STORE_load_path(self.as_ptr(), path.as_ptr() as *const _) 233 }) 234 .map(|_| ()); 235 #[cfg(any(feature = "c_openssl_1_1", feature = "c_boringssl"))] 236 return check_ret(unsafe { 237 X509_STORE_load_locations(self.as_ptr(), ptr::null(), path.as_ptr() as *const _) 238 }) 239 .map(|_| ()); 240 } 241 } 242 243 foreign_type!( 244 type CStruct = X509_VERIFY_PARAM; 245 fn drop = X509_VERIFY_PARAM_free; 246 pub(crate) struct X509VerifyParam; 247 pub(crate) struct X509VerifyParamRef; 248 ); 249 250 pub(crate) const X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS: c_uint = 0x4; 251 252 impl X509VerifyParamRef { set_hostflags(&mut self, hostflags: c_uint)253 pub(crate) fn set_hostflags(&mut self, hostflags: c_uint) { 254 unsafe { 255 X509_VERIFY_PARAM_set_hostflags(self.as_ptr(), hostflags); 256 } 257 } 258 set_host(&mut self, host: &str) -> Result<(), ErrorStack>259 pub(crate) fn set_host(&mut self, host: &str) -> Result<(), ErrorStack> { 260 let c_host = if host.is_empty() { "\0" } else { host }; 261 check_ret(unsafe { 262 // Must ensure name is NUL-terminated when namelen == 0. 263 X509_VERIFY_PARAM_set1_host(self.as_ptr(), c_host.as_ptr() as *const _, host.len()) 264 }) 265 .map(|_| ()) 266 } 267 set_ip(&mut self, ip_addr: IpAddr) -> Result<(), ErrorStack>268 pub(crate) fn set_ip(&mut self, ip_addr: IpAddr) -> Result<(), ErrorStack> { 269 let mut v = [0u8; 16]; 270 let len = match ip_addr { 271 IpAddr::V4(addr) => { 272 v[..4].copy_from_slice(&addr.octets()); 273 4 274 } 275 IpAddr::V6(addr) => { 276 v.copy_from_slice(&addr.octets()); 277 16 278 } 279 }; 280 check_ret(unsafe { X509_VERIFY_PARAM_set1_ip(self.as_ptr(), v.as_ptr() as *const _, len) }) 281 .map(|_| ()) 282 } 283 } 284 285 foreign_type! { 286 type CStruct = X509_STORE_CTX; 287 fn drop = X509_STORE_CTX_free; 288 pub(crate) struct X509StoreContext; 289 pub(crate) struct X509StoreContextRef; 290 } 291 292 impl X509StoreContextRef { get_current_cert(&self) -> Result<&X509Ref, ErrorStack>293 pub(crate) fn get_current_cert(&self) -> Result<&X509Ref, ErrorStack> { 294 unsafe { 295 Ok(X509Ref::from_ptr(check_ptr(X509_STORE_CTX_get0_cert( 296 self.as_ptr() as *const _, 297 ))?)) 298 } 299 } 300 } 301 302 foreign_type!( 303 type CStruct = X509_PUBKEY; 304 fn drop = X509_PUBKEY_free; 305 pub(crate) struct X509PubKey; 306 pub(crate) struct X509PubKeyRef; 307 ); 308 309 #[cfg(test)] 310 mod ut_x509 { 311 use crate::util::c_openssl::x509::X509; 312 /// UT test cases for `X509::clone`. 313 /// 314 /// # Brief 315 /// 1. Creates a `X509` by calling `X509::from_pem`. 316 /// 2. Creates another `X509` by calling `X509::clone`. 317 /// 3. Checks if the result is as expected. 318 #[test] 319 #[allow(clippy::redundant_clone)] ut_x509_clone()320 fn ut_x509_clone() { 321 let pem = include_bytes!("../../../tests/file/root-ca.pem"); 322 let x509 = X509::from_pem(pem).unwrap(); 323 drop(x509.clone()); 324 } 325 326 /// UT test case for `X509::get_cert_version` and `X509::get_cert` 327 /// 328 /// # Brief 329 /// 1. Creates a `X509` by calling `X509::from_pem`. 330 /// 2. Retrieve the certificate version using `get_cert_version`. 331 /// 3. Verify that the returned version is as expected. 332 #[test] ut_get_cert_version()333 fn ut_get_cert_version() { 334 let pem = include_bytes!("../../../tests/file/root-ca.pem"); 335 let x509 = X509::from_pem(pem).unwrap(); 336 assert!(x509.get_cert().is_ok()); 337 assert_eq!(x509.get_cert_version(), 2); 338 } 339 340 /// UT test case for `X509::cmp_certs`. 341 /// 342 /// # Brief 343 /// 1. Creates a `X509` by calling `X509::from_pem`. 344 /// 2. Retrieves the public key using `get_cert`. 345 /// 3. Compares the certificate using `cmp_certs` and verify that the 346 /// comparison is valid 347 #[test] ut_cmp_certs()348 fn ut_cmp_certs() { 349 let pem = include_bytes!("../../../tests/file/root-ca.pem"); 350 let x509 = X509::from_pem(pem).unwrap(); 351 let key = x509.get_cert().unwrap(); 352 assert!(x509.cmp_certs(key) != 0); 353 } 354 } 355 356 #[cfg(test)] 357 mod ut_x509_verify_result { 358 use super::*; 359 360 /// UT test case for `X509VerifyResult::error_string`. 361 /// 362 /// # Brief 363 /// 1. Creates a `X509VerifyResult` using a known error code. 364 /// 2. Verify that the `error_string` returns a non-empty string. 365 #[test] ut_error_string()366 fn ut_error_string() { 367 let res = X509VerifyResult::from_raw(10); 368 let string = res.error_string(); 369 assert!(!string.is_empty()); 370 } 371 372 /// UT test case for `X509VerifyResult::from_raw`. 373 /// 374 /// # Brief 375 /// 1. Creates a `X509VerifyResult` using a raw error code. 376 /// 2. Verify that the error code is correct. 377 #[test] ut_from_raw()378 fn ut_from_raw() { 379 let code = 20; 380 let res = X509VerifyResult::from_raw(code); 381 assert_eq!(res.0, code); 382 } 383 384 /// UT test code for `fmt::Display` and `fmt::Debug`. 385 /// 386 /// # Brief 387 /// 1. Creates a `X509VerifyResult` using an error code. 388 /// 2. Uses the `fmt::Display` and `fmt::Debug` to format the result as a 389 /// string. 390 /// 3. Verify that the output string is correct. 391 #[test] ut_fmt_display()392 fn ut_fmt_display() { 393 let res = X509VerifyResult::from_raw(10); 394 let fmt_dis = format!("{}", res); 395 assert!(!fmt_dis.is_empty()); 396 let dbg_dis = format!("{:?}", res); 397 assert!(dbg_dis.contains("X509VerifyResult")); 398 assert!(dbg_dis.contains("code")); 399 assert!(dbg_dis.contains("error")); 400 } 401 } 402