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