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