1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-07 04:02:58 +03:00
Files
apache/modules/md/md_time.c
Stefan Eissing e3928f2b27 *) mod_md: v2.4.0 with improvements and bugfixes
- MDPrivateKeys allows the specification of several types. Beside "RSA" plus 
     optional key lengths elliptic curves can be configured. This means you can 
     have multiple certificates for a Managed Domain with different key types.
     With ```MDPrivateKeys secp384r1 rsa2048``` you get one ECDSA  and one RSA 
     certificate and all modern client will use the shorter ECDSA, while older 
     client will get the RSA certificate.
     Many thanks to @tlhackque who pushed and helped on this.
     - Support added for MDomains consisting of a wildcard. Configuring 
     ```MDomain *.host.net``` will match all virtual hosts matching that pattern 
     and obtain one certificate for it (assuming you have 'dns-01' challenge 
     support configured). Addresses #239.
     - Removed support for ACMEv1 servers. The only known installation used to 
     be Let's Encrypt which has disabled that version more than a year ago for 
     new accounts.
     - Andreas Ulm (<https://github.com/root360-AndreasUlm>) implemented the 
     ```renewing``` call to ```MDMessageCmd``` that can deny a certificate 
     renewal attempt. This is useful in clustered installations, as 
     discussed in #233).
     - New event ```challenge-setup:<type>:<domain>```, triggered when the 
     challenge data for a domain has been created. This is invoked before the 
     ACME server is told to check for it. The type is one of the ACME challenge 
     types. This is invoked for every DNS name in a MDomain.
     - The max delay for retries has been raised to daily (this is like all 
     retries jittered somewhat to avoid repeats at fixed time of day).
     - Certain error codes reported by the ACME server that indicate a problem 
     with the configured data now immediately switch to daily retries. For 
     example: if the ACME server rejects a contact email or a domain name, 
     frequent retries will most likely not solve the problem. But daily retries 
     still make sense as there might be an error at the server and un-supervised 
     certificate renewal is the goal. Refs #222.
     - Test case and work around for domain names > 64 octets. Fixes #227.
     When the first DNS name of an MD is longer than 63 octets, the certificate
     request will not contain a CN field, but leave it up to the CA to choose one.
     Currently, Lets Encrypt looks for a shorter name in the SAN list given and
     fails the request if none is found. But it is really up to the CA (and what
     browsers/libs accept here) and may change over the years. That is why
     the decision is best made at the CA.
     - Retry delays now have a random +/-[0-50]% modification applied to let 
     retries from several servers spread out more, should they have been 
     restarted at the same time of day.
     - Fixed several places where the 'badNonce' return code from an ACME server 
     was not handled correctly. The test server 'pebble' simulates this behaviour 
     by default and helps nicely in verifying this behaviour. Thanks, pebble!
     - Set the default `MDActivationDelay` to 0. This was confusing to users that
     new certificates were deemed not usably before a day of delay. When clocks are
     correct, using a new certificate right away should not pose a problem.
     - When handling ACME authorization resources, the module no longer requires 
     the server to return a "Location" header, as was necessary in ACMEv1. 
     Fixes #216.
     - Fixed a theoretical uninitialized read when testing for JSON error responses 
     from the ACME CA. Reported at <https://bz.apache.org/bugzilla/show_bug.cgi?id=64297>.
     - ACME problem reports from CAs that include parameters in the Content-Type 
     header are handled correctly. (Previously, the problem text would not be 
     reported and retries could exist CA limits.)
     - Account Update transactions to V2 CAs now use the correct POST-AS-GET method.  
     Previously, an empty JSON object was sent - which apparently LE accepted, 
     but others reject.



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1887337 13f79535-47bb-0310-9956-ffa450edef68
2021-03-08 18:05:50 +00:00

326 lines
9.1 KiB
C

/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <apr_lib.h>
#include <apr_strings.h>
#include <apr_time.h>
#include "md.h"
#include "md_time.h"
apr_time_t md_timeperiod_length(const md_timeperiod_t *period)
{
return (period->start < period->end)? (period->end - period->start) : 0;
}
int md_timeperiod_contains(const md_timeperiod_t *period, apr_time_t time)
{
return md_timeperiod_has_started(period, time)
&& !md_timeperiod_has_ended(period, time);
}
int md_timeperiod_has_started(const md_timeperiod_t *period, apr_time_t time)
{
return (time >= period->start);
}
int md_timeperiod_has_ended(const md_timeperiod_t *period, apr_time_t time)
{
return (time >= period->start) && (time <= period->end);
}
apr_interval_time_t md_timeperiod_remaining(const md_timeperiod_t *period, apr_time_t time)
{
if (time < period->start) return md_timeperiod_length(period);
if (time < period->end) return period->end - time;
return 0;
}
char *md_timeperiod_print(apr_pool_t *p, const md_timeperiod_t *period)
{
char tstart[APR_RFC822_DATE_LEN];
char tend[APR_RFC822_DATE_LEN];
apr_rfc822_date(tstart, period->start);
apr_rfc822_date(tend, period->end);
return apr_pstrcat(p, tstart, " - ", tend, NULL);
}
static const char *duration_print(apr_pool_t *p, int roughly, apr_interval_time_t duration)
{
const char *s = "", *sep = "";
long days = (long)(apr_time_sec(duration) / MD_SECS_PER_DAY);
int rem = (int)(apr_time_sec(duration) % MD_SECS_PER_DAY);
s = roughly? "~" : "";
if (days > 0) {
s = apr_psprintf(p, "%s%ld days", s, days);
if (roughly) return s;
sep = " ";
}
if (rem > 0) {
int hours = (rem / MD_SECS_PER_HOUR);
rem = (rem % MD_SECS_PER_HOUR);
if (hours > 0) {
s = apr_psprintf(p, "%s%s%d hours", s, sep, hours);
if (roughly) return s;
sep = " ";
}
if (rem > 0) {
int minutes = (rem / 60);
rem = (rem % 60);
if (minutes > 0) {
s = apr_psprintf(p, "%s%s%d minutes", s, sep, minutes);
if (roughly) return s;
sep = " ";
}
if (rem > 0) {
s = apr_psprintf(p, "%s%s%d seconds", s, sep, rem);
if (roughly) return s;
sep = " ";
}
}
}
else if (days == 0) {
s = "0 seconds";
if (duration != 0) {
s = apr_psprintf(p, "%d ms", (int)apr_time_msec(duration));
}
}
return s;
}
const char *md_duration_print(apr_pool_t *p, apr_interval_time_t duration)
{
return duration_print(p, 0, duration);
}
const char *md_duration_roughly(apr_pool_t *p, apr_interval_time_t duration)
{
return duration_print(p, 1, duration);
}
static const char *duration_format(apr_pool_t *p, apr_interval_time_t duration)
{
const char *s = "0";
int units = (int)(apr_time_sec(duration) / MD_SECS_PER_DAY);
int rem = (int)(apr_time_sec(duration) % MD_SECS_PER_DAY);
if (rem == 0) {
s = apr_psprintf(p, "%dd", units);
}
else {
units = (int)(apr_time_sec(duration) / MD_SECS_PER_HOUR);
rem = (int)(apr_time_sec(duration) % MD_SECS_PER_HOUR);
if (rem == 0) {
s = apr_psprintf(p, "%dh", units);
}
else {
units = (int)(apr_time_sec(duration) / 60);
rem = (int)(apr_time_sec(duration) % 60);
if (rem == 0) {
s = apr_psprintf(p, "%dmi", units);
}
else {
units = (int)(apr_time_sec(duration));
rem = (int)(apr_time_msec(duration) % 1000);
if (rem == 0) {
s = apr_psprintf(p, "%ds", units);
}
else {
s = apr_psprintf(p, "%dms", (int)(apr_time_msec(duration)));
}
}
}
}
return s;
}
const char *md_duration_format(apr_pool_t *p, apr_interval_time_t duration)
{
return duration_format(p, duration);
}
apr_status_t md_duration_parse(apr_interval_time_t *ptimeout, const char *value,
const char *def_unit)
{
char *endp;
apr_int64_t n;
n = apr_strtoi64(value, &endp, 10);
if (errno) {
return errno;
}
if (!endp || !*endp) {
if (!def_unit) def_unit = "s";
}
else if (endp == value) {
return APR_EINVAL;
}
else {
def_unit = endp;
}
switch (*def_unit) {
case 'D':
case 'd':
*ptimeout = apr_time_from_sec(n * MD_SECS_PER_DAY);
break;
case 's':
case 'S':
*ptimeout = (apr_interval_time_t) apr_time_from_sec(n);
break;
case 'h':
case 'H':
/* Time is in hours */
*ptimeout = (apr_interval_time_t) apr_time_from_sec(n * MD_SECS_PER_HOUR);
break;
case 'm':
case 'M':
switch (*(++def_unit)) {
/* Time is in milliseconds */
case 's':
case 'S':
*ptimeout = (apr_interval_time_t) n * 1000;
break;
/* Time is in minutes */
case 'i':
case 'I':
*ptimeout = (apr_interval_time_t) apr_time_from_sec(n * 60);
break;
default:
return APR_EGENERAL;
}
break;
default:
return APR_EGENERAL;
}
return APR_SUCCESS;
}
static apr_status_t percentage_parse(const char *value, int *ppercent)
{
char *endp;
apr_int64_t n;
n = apr_strtoi64(value, &endp, 10);
if (errno) {
return errno;
}
if (*endp == '%') {
if (n < 0) {
return APR_BADARG;
}
*ppercent = (int)n;
return APR_SUCCESS;
}
return APR_EINVAL;
}
apr_status_t md_timeslice_create(md_timeslice_t **pts, apr_pool_t *p,
apr_interval_time_t norm, apr_interval_time_t len)
{
md_timeslice_t *ts;
ts = apr_pcalloc(p, sizeof(*ts));
ts->norm = norm;
ts->len = len;
*pts = ts;
return APR_SUCCESS;
}
const char *md_timeslice_parse(md_timeslice_t **pts, apr_pool_t *p,
const char *val, apr_interval_time_t norm)
{
md_timeslice_t *ts;
int percent = 0;
*pts = NULL;
if (!val) {
return "cannot parse NULL value";
}
ts = apr_pcalloc(p, sizeof(*ts));
if (md_duration_parse(&ts->len, val, "d") == APR_SUCCESS) {
*pts = ts;
return NULL;
}
else {
switch (percentage_parse(val, &percent)) {
case APR_SUCCESS:
ts->norm = norm;
ts->len = apr_time_from_sec((apr_time_sec(norm) * percent / 100L));
*pts = ts;
return NULL;
case APR_BADARG:
return "percent must be less than 100";
}
}
return "has unrecognized format";
}
const char *md_timeslice_format(const md_timeslice_t *ts, apr_pool_t *p) {
if (ts->norm > 0) {
int percent = (int)(((long)apr_time_sec(ts->len)) * 100L
/ ((long)apr_time_sec(ts->norm)));
return apr_psprintf(p, "%d%%", percent);
}
return duration_format(p, ts->len);
}
md_timeperiod_t md_timeperiod_slice_before_end(const md_timeperiod_t *period,
const md_timeslice_t *ts)
{
md_timeperiod_t r;
apr_time_t duration = ts->len;
if (ts->norm > 0) {
int percent = (int)(((long)apr_time_sec(ts->len)) * 100L
/ ((long)apr_time_sec(ts->norm)));
apr_time_t plen = md_timeperiod_length(period);
if (apr_time_sec(plen) > 100) {
duration = apr_time_from_sec(apr_time_sec(plen) * percent / 100);
}
else {
duration = plen * percent / 100;
}
}
r.start = period->end - duration;
r.end = period->end;
return r;
}
int md_timeslice_eq(const md_timeslice_t *ts1, const md_timeslice_t *ts2)
{
if (ts1 == ts2) return 1;
if (!ts1 || !ts2) return 0;
return (ts1->norm == ts2->norm) && (ts1->len == ts2->len);
}
md_timeperiod_t md_timeperiod_common(const md_timeperiod_t *a, const md_timeperiod_t *b)
{
md_timeperiod_t c;
c.start = (a->start > b->start)? a->start : b->start;
c.end = (a->end < b->end)? a->end : b->end;
if (c.start > c.end) {
c.start = c.end = 0;
}
return c;
}