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