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