From cd391ffdaf0107cc0820e3284f60822a13d13da3 Mon Sep 17 00:00:00 2001 From: Rodolphe Breard Date: Wed, 8 May 2019 16:08:36 +0200 Subject: [PATCH] Fix the OpenSSL time parsing ACMEd was not able to parse some possible time representation, which could cause a certificate not being renewed at all. This commit support all current known outputs. https://github.com/openssl/openssl/blob/master/crypto/asn1/a_time.c --- CHANGELOG.md | 3 +++ acmed/src/certificate.rs | 44 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f798ec..f17fa35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - In challenge hooks, the `algorithm` template variable has been removed. +### Fixed +- In some cases, ACMEd was unable to parse a certificate's expiration date. + ## [0.3.0] - 2019-04-30 diff --git a/acmed/src/certificate.rs b/acmed/src/certificate.rs index 238f020..c0cf333 100644 --- a/acmed/src/certificate.rs +++ b/acmed/src/certificate.rs @@ -3,18 +3,35 @@ use crate::config::{Account, HookType}; use crate::hooks::{self, ChallengeHookData, Hook, PostOperationHookData}; use crate::storage::{certificate_files_exists, get_certificate}; use acme_common::error::Error; -use log::debug; +use log::{debug, trace}; use openssl::x509::X509; use std::collections::HashSet; use std::fmt; use time::{strptime, Duration}; +// OpenSSL ASN1_TIME_print madness +// The real fix would be to add Asn1TimeRef access in the openssl crate. +// +// https://github.com/sfackler/rust-openssl/issues/687 +// https://github.com/sfackler/rust-openssl/pull/673 fn parse_openssl_time_string(time: &str) -> Result { - let formats = ["%b %d %T %Y", "%b %d %T %Y", "%b %d %T %Y"]; + debug!("Parsing OpenSSL time: \"{}\"", time); + let formats = [ + "%b %d %T %Y %Z", + "%b %d %T %Y %Z", + "%b %d %T %Y", + "%b %d %T %Y", + "%b %d %T.%f %Y %Z", + "%b %d %T.%f %Y %Z", + "%b %d %T.%f %Y", + "%b %d %T.%f %Y", + ]; for fmt in formats.iter() { if let Ok(t) = strptime(time, fmt) { + trace!("Format \"{}\" matches", fmt); return Ok(t); } + trace!("Format \"{}\" does not match", fmt); } let msg = format!("invalid time string: {}", time); Err(msg.into()) @@ -223,3 +240,26 @@ impl Certificate { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::parse_openssl_time_string; + + #[test] + fn test_parse_openssl_time() { + let time_str_lst = [ + "May 7 18:34:07 2024", + "May 7 18:34:07 2024 GMT", + "May 17 18:34:07 2024", + "May 17 18:34:07 2024 GMT", + "May 7 18:34:07.922661874 2024", + "May 7 18:34:07.922661874 2024 GMT", + "May 17 18:34:07.922661874 2024", + "May 17 18:34:07.922661874 2024 GMT", + ]; + for time_str in time_str_lst.iter() { + let time_res = parse_openssl_time_string(time_str); + assert!(time_res.is_ok()); + } + } +}