| 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 as c_ulong; |
| 22 | const NO_INTERN = ffi::OCSP_NOINTERN as c_ulong; |
| 23 | const NO_CHAIN = ffi::OCSP_NOCHAIN as c_ulong; |
| 24 | const NO_VERIFY = ffi::OCSP_NOVERIFY as c_ulong; |
| 25 | const NO_EXPLICIT = ffi::OCSP_NOEXPLICIT as c_ulong; |
| 26 | const NO_CA_SIGN = ffi::OCSP_NOCASIGN as c_ulong; |
| 27 | const NO_DELEGATED = ffi::OCSP_NODELEGATED as c_ulong; |
| 28 | const NO_CHECKS = ffi::OCSP_NOCHECKS as c_ulong; |
| 29 | const TRUST_OTHER = ffi::OCSP_TRUSTOTHER as c_ulong; |
| 30 | const RESPID_KEY = ffi::OCSP_RESPID_KEY as c_ulong; |
| 31 | const NO_TIME = ffi::OCSP_NOTIME as c_ulong; |
| 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 OcspStatus<'_> { |
| 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.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 | |