1 use bitflags::bitflags;
2 use foreign_types::ForeignTypeRef;
3 use libc::{c_int, c_long, c_ulong};
4 use std::mem;
5 use std::ptr;
6 
7 use crate::asn1::Asn1GeneralizedTimeRef;
8 use crate::error::ErrorStack;
9 use crate::hash::MessageDigest;
10 use crate::stack::StackRef;
11 use crate::util::ForeignTypeRefExt;
12 use crate::x509::store::X509StoreRef;
13 use crate::x509::{X509Ref, X509};
14 use crate::{cvt, cvt_p};
15 use openssl_macros::corresponds;
16 
17 bitflags! {
18     #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
19     #[repr(transparent)]
20     pub struct OcspFlag: c_ulong {
21         const NO_CERTS = ffi::OCSP_NOCERTS;
22         const NO_INTERN = ffi::OCSP_NOINTERN;
23         const NO_CHAIN = ffi::OCSP_NOCHAIN;
24         const NO_VERIFY = ffi::OCSP_NOVERIFY;
25         const NO_EXPLICIT = ffi::OCSP_NOEXPLICIT;
26         const NO_CA_SIGN = ffi::OCSP_NOCASIGN;
27         const NO_DELEGATED = ffi::OCSP_NODELEGATED;
28         const NO_CHECKS = ffi::OCSP_NOCHECKS;
29         const TRUST_OTHER = ffi::OCSP_TRUSTOTHER;
30         const RESPID_KEY = ffi::OCSP_RESPID_KEY;
31         const NO_TIME = ffi::OCSP_NOTIME;
32     }
33 }
34 
35 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
36 pub struct OcspResponseStatus(c_int);
37 
38 impl OcspResponseStatus {
39     pub const SUCCESSFUL: OcspResponseStatus =
40         OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_SUCCESSFUL);
41     pub const MALFORMED_REQUEST: OcspResponseStatus =
42         OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_MALFORMEDREQUEST);
43     pub const INTERNAL_ERROR: OcspResponseStatus =
44         OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_INTERNALERROR);
45     pub const TRY_LATER: OcspResponseStatus =
46         OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_TRYLATER);
47     pub const SIG_REQUIRED: OcspResponseStatus =
48         OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_SIGREQUIRED);
49     pub const UNAUTHORIZED: OcspResponseStatus =
50         OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_UNAUTHORIZED);
51 
from_raw(raw: c_int) -> OcspResponseStatus52     pub fn from_raw(raw: c_int) -> OcspResponseStatus {
53         OcspResponseStatus(raw)
54     }
55 
56     #[allow(clippy::trivially_copy_pass_by_ref)]
as_raw(&self) -> c_int57     pub fn as_raw(&self) -> c_int {
58         self.0
59     }
60 }
61 
62 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
63 pub struct OcspCertStatus(c_int);
64 
65 impl OcspCertStatus {
66     pub const GOOD: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_GOOD);
67     pub const REVOKED: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_REVOKED);
68     pub const UNKNOWN: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_UNKNOWN);
69 
from_raw(raw: c_int) -> OcspCertStatus70     pub fn from_raw(raw: c_int) -> OcspCertStatus {
71         OcspCertStatus(raw)
72     }
73 
74     #[allow(clippy::trivially_copy_pass_by_ref)]
as_raw(&self) -> c_int75     pub fn as_raw(&self) -> c_int {
76         self.0
77     }
78 }
79 
80 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
81 pub struct OcspRevokedStatus(c_int);
82 
83 impl OcspRevokedStatus {
84     pub const NO_STATUS: OcspRevokedStatus = OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_NOSTATUS);
85     pub const UNSPECIFIED: OcspRevokedStatus =
86         OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_UNSPECIFIED);
87     pub const KEY_COMPROMISE: OcspRevokedStatus =
88         OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_KEYCOMPROMISE);
89     pub const CA_COMPROMISE: OcspRevokedStatus =
90         OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CACOMPROMISE);
91     pub const AFFILIATION_CHANGED: OcspRevokedStatus =
92         OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_AFFILIATIONCHANGED);
93     pub const STATUS_SUPERSEDED: OcspRevokedStatus =
94         OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_SUPERSEDED);
95     pub const STATUS_CESSATION_OF_OPERATION: OcspRevokedStatus =
96         OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CESSATIONOFOPERATION);
97     pub const STATUS_CERTIFICATE_HOLD: OcspRevokedStatus =
98         OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CERTIFICATEHOLD);
99     pub const REMOVE_FROM_CRL: OcspRevokedStatus =
100         OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_REMOVEFROMCRL);
101 
from_raw(raw: c_int) -> OcspRevokedStatus102     pub fn from_raw(raw: c_int) -> OcspRevokedStatus {
103         OcspRevokedStatus(raw)
104     }
105 
106     #[allow(clippy::trivially_copy_pass_by_ref)]
as_raw(&self) -> c_int107     pub fn as_raw(&self) -> c_int {
108         self.0
109     }
110 }
111 
112 pub struct OcspStatus<'a> {
113     /// The overall status of the response.
114     pub status: OcspCertStatus,
115     /// If `status` is `CERT_STATUS_REVOKED`, the reason for the revocation.
116     pub reason: OcspRevokedStatus,
117     /// If `status` is `CERT_STATUS_REVOKED`, the time at which the certificate was revoked.
118     pub revocation_time: Option<&'a Asn1GeneralizedTimeRef>,
119     /// The time that this revocation check was performed.
120     pub this_update: &'a Asn1GeneralizedTimeRef,
121     /// The time at which this revocation check expires.
122     pub next_update: &'a Asn1GeneralizedTimeRef,
123 }
124 
125 impl<'a> OcspStatus<'a> {
126     /// Checks validity of the `this_update` and `next_update` fields.
127     ///
128     /// The `nsec` parameter specifies an amount of slack time that will be used when comparing
129     /// those times with the current time to account for delays and clock skew.
130     ///
131     /// The `maxsec` parameter limits the maximum age of the `this_update` parameter to prohibit
132     /// very old responses.
133     #[corresponds(OCSP_check_validity)]
check_validity(&self, nsec: u32, maxsec: Option<u32>) -> Result<(), ErrorStack>134     pub fn check_validity(&self, nsec: u32, maxsec: Option<u32>) -> Result<(), ErrorStack> {
135         unsafe {
136             cvt(ffi::OCSP_check_validity(
137                 self.this_update.as_ptr(),
138                 self.next_update.as_ptr(),
139                 nsec as c_long,
140                 maxsec.map(|n| n as c_long).unwrap_or(-1),
141             ))
142             .map(|_| ())
143         }
144     }
145 }
146 
147 foreign_type_and_impl_send_sync! {
148     type CType = ffi::OCSP_BASICRESP;
149     fn drop = ffi::OCSP_BASICRESP_free;
150 
151     pub struct OcspBasicResponse;
152     pub struct OcspBasicResponseRef;
153 }
154 
155 impl OcspBasicResponseRef {
156     /// Verifies the validity of the response.
157     ///
158     /// The `certs` parameter contains a set of certificates that will be searched when locating the
159     /// OCSP response signing certificate. Some responders do not include this in the response.
160     #[corresponds(OCSP_basic_verify)]
verify( &self, certs: &StackRef<X509>, store: &X509StoreRef, flags: OcspFlag, ) -> Result<(), ErrorStack>161     pub fn verify(
162         &self,
163         certs: &StackRef<X509>,
164         store: &X509StoreRef,
165         flags: OcspFlag,
166     ) -> Result<(), ErrorStack> {
167         unsafe {
168             cvt(ffi::OCSP_basic_verify(
169                 self.as_ptr(),
170                 certs.as_ptr(),
171                 store.as_ptr(),
172                 flags.bits(),
173             ))
174             .map(|_| ())
175         }
176     }
177 
178     /// Looks up the status for the specified certificate ID.
179     #[corresponds(OCSP_resp_find_status)]
find_status<'a>(&'a self, id: &OcspCertIdRef) -> Option<OcspStatus<'a>>180     pub fn find_status<'a>(&'a self, id: &OcspCertIdRef) -> Option<OcspStatus<'a>> {
181         unsafe {
182             let mut status = ffi::V_OCSP_CERTSTATUS_UNKNOWN;
183             let mut reason = ffi::OCSP_REVOKED_STATUS_NOSTATUS;
184             let mut revocation_time = ptr::null_mut();
185             let mut this_update = ptr::null_mut();
186             let mut next_update = ptr::null_mut();
187 
188             let r = ffi::OCSP_resp_find_status(
189                 self.as_ptr(),
190                 id.as_ptr(),
191                 &mut status,
192                 &mut reason,
193                 &mut revocation_time,
194                 &mut this_update,
195                 &mut next_update,
196             );
197             if r == 1 {
198                 let revocation_time = Asn1GeneralizedTimeRef::from_const_ptr_opt(revocation_time);
199 
200                 Some(OcspStatus {
201                     status: OcspCertStatus(status),
202                     reason: OcspRevokedStatus(status),
203                     revocation_time,
204                     this_update: Asn1GeneralizedTimeRef::from_ptr(this_update),
205                     next_update: Asn1GeneralizedTimeRef::from_ptr(next_update),
206                 })
207             } else {
208                 None
209             }
210         }
211     }
212 }
213 
214 foreign_type_and_impl_send_sync! {
215     type CType = ffi::OCSP_CERTID;
216     fn drop = ffi::OCSP_CERTID_free;
217 
218     pub struct OcspCertId;
219     pub struct OcspCertIdRef;
220 }
221 
222 impl OcspCertId {
223     /// Constructs a certificate ID for certificate `subject`.
224     #[corresponds(OCSP_cert_to_id)]
from_cert( digest: MessageDigest, subject: &X509Ref, issuer: &X509Ref, ) -> Result<OcspCertId, ErrorStack>225     pub fn from_cert(
226         digest: MessageDigest,
227         subject: &X509Ref,
228         issuer: &X509Ref,
229     ) -> Result<OcspCertId, ErrorStack> {
230         unsafe {
231             cvt_p(ffi::OCSP_cert_to_id(
232                 digest.as_ptr(),
233                 subject.as_ptr(),
234                 issuer.as_ptr(),
235             ))
236             .map(OcspCertId)
237         }
238     }
239 }
240 
241 foreign_type_and_impl_send_sync! {
242     type CType = ffi::OCSP_RESPONSE;
243     fn drop = ffi::OCSP_RESPONSE_free;
244 
245     pub struct OcspResponse;
246     pub struct OcspResponseRef;
247 }
248 
249 impl OcspResponse {
250     /// Creates an OCSP response from the status and optional body.
251     ///
252     /// A body should only be provided if `status` is `RESPONSE_STATUS_SUCCESSFUL`.
253     #[corresponds(OCSP_response_create)]
create( status: OcspResponseStatus, body: Option<&OcspBasicResponseRef>, ) -> Result<OcspResponse, ErrorStack>254     pub fn create(
255         status: OcspResponseStatus,
256         body: Option<&OcspBasicResponseRef>,
257     ) -> Result<OcspResponse, ErrorStack> {
258         unsafe {
259             ffi::init();
260 
261             cvt_p(ffi::OCSP_response_create(
262                 status.as_raw(),
263                 body.map(|r| r.as_ptr()).unwrap_or(ptr::null_mut()),
264             ))
265             .map(OcspResponse)
266         }
267     }
268 
269     from_der! {
270         /// Deserializes a DER-encoded OCSP response.
271         #[corresponds(d2i_OCSP_RESPONSE)]
272         from_der,
273         OcspResponse,
274         ffi::d2i_OCSP_RESPONSE
275     }
276 }
277 
278 impl OcspResponseRef {
279     to_der! {
280         /// Serializes the response to its standard DER encoding.
281         #[corresponds(i2d_OCSP_RESPONSE)]
282         to_der,
283         ffi::i2d_OCSP_RESPONSE
284     }
285 
286     /// Returns the status of the response.
287     #[corresponds(OCSP_response_status)]
status(&self) -> OcspResponseStatus288     pub fn status(&self) -> OcspResponseStatus {
289         unsafe { OcspResponseStatus(ffi::OCSP_response_status(self.as_ptr())) }
290     }
291 
292     /// Returns the basic response.
293     ///
294     /// This will only succeed if `status()` returns `RESPONSE_STATUS_SUCCESSFUL`.
295     #[corresponds(OCSP_response_get1_basic)]
basic(&self) -> Result<OcspBasicResponse, ErrorStack>296     pub fn basic(&self) -> Result<OcspBasicResponse, ErrorStack> {
297         unsafe { cvt_p(ffi::OCSP_response_get1_basic(self.as_ptr())).map(OcspBasicResponse) }
298     }
299 }
300 
301 foreign_type_and_impl_send_sync! {
302     type CType = ffi::OCSP_REQUEST;
303     fn drop = ffi::OCSP_REQUEST_free;
304 
305     pub struct OcspRequest;
306     pub struct OcspRequestRef;
307 }
308 
309 impl OcspRequest {
310     #[corresponds(OCSP_REQUEST_new)]
new() -> Result<OcspRequest, ErrorStack>311     pub fn new() -> Result<OcspRequest, ErrorStack> {
312         unsafe {
313             ffi::init();
314 
315             cvt_p(ffi::OCSP_REQUEST_new()).map(OcspRequest)
316         }
317     }
318 
319     from_der! {
320         /// Deserializes a DER-encoded OCSP request.
321         #[corresponds(d2i_OCSP_REQUEST)]
322         from_der,
323         OcspRequest,
324         ffi::d2i_OCSP_REQUEST
325     }
326 }
327 
328 impl OcspRequestRef {
329     to_der! {
330         /// Serializes the request to its standard DER encoding.
331         #[corresponds(i2d_OCSP_REQUEST)]
332         to_der,
333         ffi::i2d_OCSP_REQUEST
334     }
335 
336     #[corresponds(OCSP_request_add0_id)]
add_id(&mut self, id: OcspCertId) -> Result<&mut OcspOneReqRef, ErrorStack>337     pub fn add_id(&mut self, id: OcspCertId) -> Result<&mut OcspOneReqRef, ErrorStack> {
338         unsafe {
339             let ptr = cvt_p(ffi::OCSP_request_add0_id(self.as_ptr(), id.as_ptr()))?;
340             mem::forget(id);
341             Ok(OcspOneReqRef::from_ptr_mut(ptr))
342         }
343     }
344 }
345 
346 foreign_type_and_impl_send_sync! {
347     type CType = ffi::OCSP_ONEREQ;
348     fn drop = ffi::OCSP_ONEREQ_free;
349 
350     pub struct OcspOneReq;
351     pub struct OcspOneReqRef;
352 }
353