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 #[cfg(feature = "c_openssl_3_0")] 16 use std::ffi::CString; 17 use std::net::IpAddr; 18 19 use libc::{c_int, c_long, c_uint}; 20 21 use super::bio::BioSlice; 22 use super::error::{error_get_lib, error_get_reason, ErrorStack}; 23 use super::ffi::err::{ERR_clear_error, ERR_peek_last_error}; 24 use super::ffi::pem::PEM_read_bio_X509; 25 #[cfg(feature = "c_openssl_3_0")] 26 use super::ffi::x509::X509_STORE_load_path; 27 use super::ffi::x509::{ 28 d2i_X509, EVP_PKEY_free, X509_NAME_free, X509_NAME_oneline, X509_PUBKEY_free, 29 X509_STORE_CTX_free, X509_STORE_CTX_get0_cert, X509_STORE_add_cert, X509_STORE_free, 30 X509_STORE_new, X509_VERIFY_PARAM_free, X509_VERIFY_PARAM_set1_host, X509_VERIFY_PARAM_set1_ip, 31 X509_VERIFY_PARAM_set_hostflags, X509_get_issuer_name, X509_get_pubkey, X509_get_subject_name, 32 X509_get_version, X509_up_ref, X509_verify, X509_verify_cert_error_string, EVP_PKEY, 33 STACK_X509, X509_NAME, X509_PUBKEY, X509_STORE, X509_STORE_CTX, X509_VERIFY_PARAM, 34 }; 35 use super::foreign::{Foreign, ForeignRef}; 36 use super::stack::Stackof; 37 use super::{check_ptr, check_ret, ssl_init}; 38 use crate::util::c_openssl::ffi::x509::{X509_free, C_X509}; 39 40 foreign_type!( 41 type CStruct = C_X509; 42 fn drop = X509_free; 43 pub(crate) struct X509; 44 pub(crate) struct X509Ref; 45 ); 46 47 foreign_type!( 48 type CStruct = X509_NAME; 49 fn drop = X509_NAME_free; 50 pub(crate) struct X509Name; 51 pub(crate) struct X509NameRef; 52 ); 53 54 foreign_type! { 55 type CStruct = EVP_PKEY; 56 fn drop = EVP_PKEY_free; 57 pub(crate) struct EvpPkey; 58 pub(crate) struct EvpPkeyRef; 59 } 60 61 const ERR_LIB_PEM: c_int = 9; 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 100 return Err(ErrorStack::get()); 101 } else { 102 certs.push(X509(r)); 103 } 104 } 105 Ok(certs) 106 } 107 } 108 } 109 110 impl X509Ref { get_cert_version(&self) -> c_long111 pub(crate) fn get_cert_version(&self) -> c_long { 112 unsafe { X509_get_version(self.as_ptr() as *const _) } 113 } 114 get_cert_name(&self) -> Result<X509Name, ErrorStack>115 pub(crate) fn get_cert_name(&self) -> Result<X509Name, ErrorStack> { 116 Ok(X509Name(check_ptr(unsafe { 117 X509_get_subject_name(self.as_ptr() as *const _) 118 })?)) 119 } 120 get_issuer_name(&self) -> Result<X509Name, ErrorStack>121 pub(crate) fn get_issuer_name(&self) -> Result<X509Name, ErrorStack> { 122 Ok(X509Name(check_ptr(unsafe { 123 X509_get_issuer_name(self.as_ptr() as *const _) 124 })?)) 125 } 126 get_cert(&self) -> Result<EvpPkey, ErrorStack>127 pub(crate) fn get_cert(&self) -> Result<EvpPkey, ErrorStack> { 128 Ok(EvpPkey(check_ptr(unsafe { 129 X509_get_pubkey(self.as_ptr() as *mut _) 130 })?)) 131 } 132 cmp_certs(&self, pkey: EvpPkey) -> c_int133 pub(crate) fn cmp_certs(&self, pkey: EvpPkey) -> c_int { 134 unsafe { X509_verify(self.as_ptr() as *mut _, pkey.as_ptr()) } 135 } 136 } 137 138 impl X509Name { get_x509_name_info(&self, buf: &mut [u8], size: c_int) -> String139 pub(crate) fn get_x509_name_info(&self, buf: &mut [u8], size: c_int) -> String { 140 unsafe { 141 let _ = X509_NAME_oneline(self.as_ptr() as *mut _, buf.as_mut_ptr() as *mut _, size); 142 let res = str::from_utf8(buf).unwrap_or("").to_string(); 143 res 144 } 145 } 146 } 147 impl Stackof for X509 { 148 type StackType = STACK_X509; 149 } 150 151 impl Clone for X509 { clone(&self) -> Self152 fn clone(&self) -> Self { 153 X509Ref::to_owned(self) 154 } 155 } 156 157 impl ToOwned for X509Ref { 158 type Owned = X509; 159 to_owned(&self) -> Self::Owned160 fn to_owned(&self) -> Self::Owned { 161 unsafe { 162 X509_up_ref(self.as_ptr()); 163 X509::from_ptr(self.as_ptr()) 164 } 165 } 166 } 167 168 #[derive(Copy, Clone, PartialEq, Eq)] 169 pub(crate) struct X509VerifyResult(c_int); 170 171 impl X509VerifyResult { error_string(&self) -> &'static str172 fn error_string(&self) -> &'static str { 173 ssl_init(); 174 unsafe { 175 let s = X509_verify_cert_error_string(self.0 as c_long); 176 str::from_utf8(ffi::CStr::from_ptr(s).to_bytes()).unwrap_or("") 177 } 178 } 179 from_raw(err: c_int) -> X509VerifyResult180 pub(crate) fn from_raw(err: c_int) -> X509VerifyResult { 181 X509VerifyResult(err) 182 } 183 } 184 185 impl fmt::Display for X509VerifyResult { fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result186 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 187 fmt.write_str(self.error_string()) 188 } 189 } 190 191 #[cfg(test)] 192 impl fmt::Debug for X509VerifyResult { fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result193 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 194 fmt.debug_struct("X509VerifyResult") 195 .field("code", &self.0) 196 .field("error", &self.error_string()) 197 .finish() 198 } 199 } 200 201 foreign_type!( 202 type CStruct = X509_STORE; 203 fn drop = X509_STORE_free; 204 pub(crate) struct X509Store; 205 pub(crate) struct X509StoreRef; 206 ); 207 208 impl X509Store { new() -> Result<X509Store, ErrorStack>209 pub(crate) fn new() -> Result<X509Store, ErrorStack> { 210 ssl_init(); 211 Ok(X509Store(check_ptr(unsafe { X509_STORE_new() })?)) 212 } 213 } 214 215 impl X509StoreRef { add_cert(&mut self, cert: X509) -> Result<(), ErrorStack>216 pub(crate) fn add_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { 217 check_ret(unsafe { X509_STORE_add_cert(self.as_ptr(), cert.as_ptr()) }).map(|_| ()) 218 } 219 220 #[cfg(feature = "c_openssl_3_0")] add_path(&mut self, path: String) -> Result<(), ErrorStack>221 pub(crate) fn add_path(&mut self, path: String) -> Result<(), ErrorStack> { 222 let p_slice: &str = &path; 223 let path = match CString::new(p_slice) { 224 Ok(cstr) => cstr, 225 Err(_) => return Err(ErrorStack::get()), 226 }; 227 check_ret(unsafe { X509_STORE_load_path(self.as_ptr(), path.as_ptr() as *const _) }) 228 .map(|_| ()) 229 } 230 } 231 232 foreign_type!( 233 type CStruct = X509_VERIFY_PARAM; 234 fn drop = X509_VERIFY_PARAM_free; 235 pub(crate) struct X509VerifyParam; 236 pub(crate) struct X509VerifyParamRef; 237 ); 238 239 pub(crate) const X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS: c_uint = 0x4; 240 241 impl X509VerifyParamRef { set_hostflags(&mut self, hostflags: c_uint)242 pub(crate) fn set_hostflags(&mut self, hostflags: c_uint) { 243 unsafe { 244 X509_VERIFY_PARAM_set_hostflags(self.as_ptr(), hostflags); 245 } 246 } 247 set_host(&mut self, host: &str) -> Result<(), ErrorStack>248 pub(crate) fn set_host(&mut self, host: &str) -> Result<(), ErrorStack> { 249 let c_host = if host.is_empty() { "\0" } else { host }; 250 check_ret(unsafe { 251 // Must ensure name is NUL-terminated when namelen == 0. 252 X509_VERIFY_PARAM_set1_host(self.as_ptr(), c_host.as_ptr() as *const _, host.len()) 253 }) 254 .map(|_| ()) 255 } 256 set_ip(&mut self, ip_addr: IpAddr) -> Result<(), ErrorStack>257 pub(crate) fn set_ip(&mut self, ip_addr: IpAddr) -> Result<(), ErrorStack> { 258 let mut v = [0u8; 16]; 259 let len = match ip_addr { 260 IpAddr::V4(addr) => { 261 v[..4].copy_from_slice(&addr.octets()); 262 4 263 } 264 IpAddr::V6(addr) => { 265 v.copy_from_slice(&addr.octets()); 266 16 267 } 268 }; 269 check_ret(unsafe { X509_VERIFY_PARAM_set1_ip(self.as_ptr(), v.as_ptr() as *const _, len) }) 270 .map(|_| ()) 271 } 272 } 273 274 foreign_type! { 275 type CStruct = X509_STORE_CTX; 276 fn drop = X509_STORE_CTX_free; 277 pub(crate) struct X509StoreContext; 278 pub(crate) struct X509StoreContextRef; 279 } 280 281 impl X509StoreContextRef { get_current_cert(&self) -> Result<&X509Ref, ErrorStack>282 pub(crate) fn get_current_cert(&self) -> Result<&X509Ref, ErrorStack> { 283 unsafe { 284 Ok(X509Ref::from_ptr(check_ptr(X509_STORE_CTX_get0_cert( 285 self.as_ptr() as *const _, 286 ))?)) 287 } 288 } 289 } 290 291 foreign_type!( 292 type CStruct = X509_PUBKEY; 293 fn drop = X509_PUBKEY_free; 294 pub(crate) struct X509PubKey; 295 pub(crate) struct X509PubKeyRef; 296 ); 297 298 #[cfg(test)] 299 mod ut_x509 { 300 301 /// UT test cases for `X509::clone`. 302 /// 303 /// # Brief 304 /// 1. Creates a `X509` by calling `X509::from_pem`. 305 /// 2. Creates another `X509` by calling `X509::clone`. 306 /// 3. Checks if the result is as expected. 307 #[test] 308 #[allow(clippy::redundant_clone)] ut_x509_clone()309 fn ut_x509_clone() { 310 use crate::util::c_openssl::x509::X509; 311 312 let pem = include_bytes!("../../../tests/file/root-ca.pem"); 313 let x509 = X509::from_pem(pem).unwrap(); 314 drop(x509.clone()); 315 } 316 } 317