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 | |
52 | pub fn from_raw(raw: c_int) -> OcspResponseStatus { |
53 | OcspResponseStatus(raw) |
54 | } |
55 | |
56 | #[allow (clippy::trivially_copy_pass_by_ref)] |
57 | 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 | |
70 | pub fn from_raw(raw: c_int) -> OcspCertStatus { |
71 | OcspCertStatus(raw) |
72 | } |
73 | |
74 | #[allow (clippy::trivially_copy_pass_by_ref)] |
75 | 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 | |
102 | pub fn from_raw(raw: c_int) -> OcspRevokedStatus { |
103 | OcspRevokedStatus(raw) |
104 | } |
105 | |
106 | #[allow (clippy::trivially_copy_pass_by_ref)] |
107 | 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)] |
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)] |
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)] |
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)] |
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(op: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)] |
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)] |
288 | 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)] |
296 | pub fn basic(&self) -> Result<OcspBasicResponse, ErrorStack> { |
297 | unsafe { cvt_p(ffi::OCSP_response_get1_basic(self.as_ptr())).map(op: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)] |
311 | pub fn new() -> Result<OcspRequest, ErrorStack> { |
312 | unsafe { |
313 | ffi::init(); |
314 | |
315 | cvt_p(ffi::OCSP_REQUEST_new()).map(op: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)] |
337 | pub fn add_id(&mut self, id: OcspCertId) -> Result<&mut OcspOneReqRef, ErrorStack> { |
338 | unsafe { |
339 | let ptr: *mut OCSP_ONEREQ = cvt_p(ffi::OCSP_request_add0_id(self.as_ptr(), id: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 | |