You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2019/06/24 16:04:33 UTC
svn commit: r1862013 [1/8] - in /httpd/httpd/trunk: ./ modules/md/
Author: icing
Date: Mon Jun 24 16:04:32 2019
New Revision: 1862013
URL: http://svn.apache.org/viewvc?rev=1862013&view=rev
Log:
*) mod_md: bringing over v2.0.6 from github.
- supports the ACMEv2 protocol
- supports the new challenge method 'tls-alpn-01'
- supports command configuration to setup/teardown 'dns-01' challenges
- supports wildcard certificates when dns challenges are configured
- ACMEv2 is the new default and will be used on the next certificate renewal,
unless another MDCertificateAuthority is configured
- challenge type 'tls-sni-01' has been removed as CAs do not offer this any longer
- a domain exposes its status at https://<domain>/.httpd/certificate-status
- Managed Domains are now in Apache's 'server-status' page
- A new handler 'md-status' exposes verbose status information in JSON format
- new directives "MDCertificateFile" and "MDCertificateKeyFile" to configure a
Managed Domain that uses static files. Auto-renewal is turned off for those.
- new MDMessageCmd that is invoked on several events: 'renewed', 'expiring' and
'errored'. New 'MDWarnWindow' directive to configure when expiration warnings
shall be issued.
- ACMEv2 endpoints use the GET via empty POST way of accessing resources, see
announcement by Let's Encrypt:
https://community.letsencrypt.org/t/acme-v2-scheduled-deprecation-of-unauthenticated-resource-gets/74380
Added:
httpd/httpd/trunk/modules/md/md_acme_drive.h
httpd/httpd/trunk/modules/md/md_acme_order.c
httpd/httpd/trunk/modules/md/md_acme_order.h
httpd/httpd/trunk/modules/md/md_acmev1_drive.c
httpd/httpd/trunk/modules/md/md_acmev1_drive.h
httpd/httpd/trunk/modules/md/md_acmev2_drive.c
httpd/httpd/trunk/modules/md/md_acmev2_drive.h
httpd/httpd/trunk/modules/md/md_result.c
httpd/httpd/trunk/modules/md/md_result.h
httpd/httpd/trunk/modules/md/md_status.c
httpd/httpd/trunk/modules/md/md_status.h
httpd/httpd/trunk/modules/md/md_time.c
httpd/httpd/trunk/modules/md/md_time.h
httpd/httpd/trunk/modules/md/mod_md_drive.c
httpd/httpd/trunk/modules/md/mod_md_drive.h
httpd/httpd/trunk/modules/md/mod_md_status.c
httpd/httpd/trunk/modules/md/mod_md_status.h
Modified:
httpd/httpd/trunk/CHANGES
httpd/httpd/trunk/modules/md/config2.m4
httpd/httpd/trunk/modules/md/md.h
httpd/httpd/trunk/modules/md/md_acme.c
httpd/httpd/trunk/modules/md/md_acme.h
httpd/httpd/trunk/modules/md/md_acme_acct.c
httpd/httpd/trunk/modules/md/md_acme_acct.h
httpd/httpd/trunk/modules/md/md_acme_authz.c
httpd/httpd/trunk/modules/md/md_acme_authz.h
httpd/httpd/trunk/modules/md/md_acme_drive.c
httpd/httpd/trunk/modules/md/md_core.c
httpd/httpd/trunk/modules/md/md_crypt.c
httpd/httpd/trunk/modules/md/md_crypt.h
httpd/httpd/trunk/modules/md/md_curl.c
httpd/httpd/trunk/modules/md/md_http.c
httpd/httpd/trunk/modules/md/md_http.h
httpd/httpd/trunk/modules/md/md_json.c
httpd/httpd/trunk/modules/md/md_json.h
httpd/httpd/trunk/modules/md/md_jws.c
httpd/httpd/trunk/modules/md/md_reg.c
httpd/httpd/trunk/modules/md/md_reg.h
httpd/httpd/trunk/modules/md/md_store.c
httpd/httpd/trunk/modules/md/md_store.h
httpd/httpd/trunk/modules/md/md_store_fs.c
httpd/httpd/trunk/modules/md/md_util.c
httpd/httpd/trunk/modules/md/md_util.h
httpd/httpd/trunk/modules/md/md_version.h
httpd/httpd/trunk/modules/md/mod_md.c
httpd/httpd/trunk/modules/md/mod_md.h
httpd/httpd/trunk/modules/md/mod_md_config.c
httpd/httpd/trunk/modules/md/mod_md_config.h
httpd/httpd/trunk/modules/md/mod_md_os.c
Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1862013&r1=1862012&r2=1862013&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Mon Jun 24 16:04:32 2019
@@ -1,6 +1,27 @@
-*- coding: utf-8 -*-
Changes with Apache 2.5.1
+ *) mod_md: bringing over v2.0.6 from github.
+ - supports the ACMEv2 protocol
+ - supports the new challenge method 'tls-alpn-01'
+ - supports command configuration to setup/teardown 'dns-01' challenges
+ - supports wildcard certificates when dns challenges are configured
+ - ACMEv2 is the new default and will be used on the next certificate renewal,
+ unless another MDCertificateAuthority is configured
+ - challenge type 'tls-sni-01' has been removed as CAs do not offer this any longer
+ - a domain exposes its status at https://<domain>/.httpd/certificate-status
+ - Managed Domains are now in Apache's 'server-status' page
+ - A new handler 'md-status' exposes verbose status information in JSON format
+ - new directives "MDCertificateFile" and "MDCertificateKeyFile" to configure a
+ Managed Domain that uses static files. Auto-renewal is turned off for those.
+ - new MDMessageCmd that is invoked on several events: 'renewed', 'expiring' and
+ 'errored'. New 'MDWarnWindow' directive to configure when expiration warnings
+ shall be issued.
+ - ACMEv2 endpoints use the GET via empty POST way of accessing resources, see
+ announcement by Let's Encrypt:
+ https://community.letsencrypt.org/t/acme-v2-scheduled-deprecation-of-unauthenticated-resource-gets/74380
+ [Stefan Eissing]
+
*) mod_ssl: use OPENSSL_init_ssl() to initialise OpenSSL on versions 1.1+.
[Graham Leggett]
Modified: httpd/httpd/trunk/modules/md/config2.m4
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/config2.m4?rev=1862013&r1=1862012&r2=1862013&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/config2.m4 (original)
+++ httpd/httpd/trunk/modules/md/config2.m4 Mon Jun 24 16:04:32 2019
@@ -140,6 +140,9 @@ md_acme.lo dnl
md_acme_acct.lo dnl
md_acme_authz.lo dnl
md_acme_drive.lo dnl
+md_acmev1_drive.lo dnl
+md_acmev2_drive.lo dnl
+md_acme_order.lo dnl
md_core.lo dnl
md_curl.lo dnl
md_crypt.lo dnl
@@ -147,13 +150,18 @@ md_http.lo dnl
md_json.lo dnl
md_jws.lo dnl
md_log.lo dnl
+md_result.lo dnl
md_reg.lo dnl
+md_status.lo dnl
md_store.lo dnl
md_store_fs.lo dnl
+md_time.lo dnl
md_util.lo dnl
mod_md.lo dnl
mod_md_config.lo dnl
+mod_md_drive.lo dnl
mod_md_os.lo dnl
+mod_md_status.lo dnl
"
# Ensure that other modules can pick up mod_md.h
Modified: httpd/httpd/trunk/modules/md/md.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md.h?rev=1862013&r1=1862012&r2=1862013&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md.h (original)
+++ httpd/httpd/trunk/modules/md/md.h Mon Jun 24 16:04:32 2019
@@ -17,6 +17,7 @@
#ifndef mod_md_md_h
#define mod_md_md_h
+#include "md_time.h"
#include "md_version.h"
struct apr_array_header_t;
@@ -37,13 +38,19 @@ struct md_pkey_spec_t;
#define MD_HSTS_HEADER "Strict-Transport-Security"
#define MD_HSTS_MAX_AGE_DEFAULT 15768000
+#define PROTO_ACME_TLS_1 "acme-tls/1"
+
+#define MD_TIME_LIFE_NORM (apr_time_from_sec(100 * MD_SECS_PER_DAY))
+#define MD_TIME_RENEW_WINDOW_DEF (apr_time_from_sec(33 * MD_SECS_PER_DAY))
+#define MD_TIME_WARN_WINDOW_DEF (apr_time_from_sec(10 * MD_SECS_PER_DAY))
+
typedef enum {
- MD_S_UNKNOWN, /* MD has not been analysed yet */
- MD_S_INCOMPLETE, /* MD is missing necessary information, cannot go live */
- MD_S_COMPLETE, /* MD has all necessary information, can go live */
- MD_S_EXPIRED, /* MD is complete, but credentials have expired */
- MD_S_ERROR, /* MD data is flawed, unable to be processed as is */
- MD_S_MISSING, /* MD is missing config information, cannot proceed */
+ MD_S_UNKNOWN = 0, /* MD has not been analysed yet */
+ MD_S_INCOMPLETE = 1, /* MD is missing necessary information, cannot go live */
+ MD_S_COMPLETE = 2, /* MD has all necessary information, can go live */
+ MD_S_EXPIRED_DEPRECATED = 3, /* deprecated */
+ MD_S_ERROR = 4, /* MD data is flawed, unable to be processed as is */
+ MD_S_MISSING_INFORMATION = 5, /* User has not agreed to ToS */
} md_state_t;
typedef enum {
@@ -73,11 +80,11 @@ typedef enum {
} md_store_group_t;
typedef enum {
- MD_DRIVE_DEFAULT = -1, /* default value */
- MD_DRIVE_MANUAL, /* manually triggered transmission of credentials */
- MD_DRIVE_AUTO, /* automatic process performed by httpd */
- MD_DRIVE_ALWAYS, /* always driven by httpd, even if not used in any vhost */
-} md_drive_mode_t;
+ MD_RENEW_DEFAULT = -1, /* default value */
+ MD_RENEW_MANUAL, /* manually triggered renewal of certificate */
+ MD_RENEW_AUTO, /* automatic process performed by httpd */
+ MD_RENEW_ALWAYS, /* always renewed by httpd, even if not necessary */
+} md_renew_mode_t;
typedef struct md_t md_t;
struct md_t {
@@ -88,35 +95,46 @@ struct md_t {
int transitive; /* != 0 iff VirtualHost names/aliases are auto-added */
md_require_t require_https; /* Iff https: is required for this MD */
- int drive_mode; /* mode of obtaining credentials */
+ int renew_mode; /* mode of obtaining credentials */
struct md_pkey_spec_t *pkey_spec;/* specification for generating new private keys */
int must_staple; /* certificates should set the OCSP Must Staple extension */
- apr_interval_time_t renew_norm; /* if > 0, normalized cert lifetime */
- apr_interval_time_t renew_window;/* time before expiration that starts renewal */
+ const md_timeslice_t *renew_window; /* time before expiration that starts renewal */
+ const md_timeslice_t *warn_window; /* time before expiration that warnings are sent out */
const char *ca_url; /* url of CA certificate service */
const char *ca_proto; /* protocol used vs CA (e.g. ACME) */
const char *ca_account; /* account used at CA */
const char *ca_agreement; /* accepted agreement uri between CA and user */
struct apr_array_header_t *ca_challenges; /* challenge types configured for this MD */
-
+ const char *cert_file; /* != NULL iff pubcert file explicitly configured */
+ const char *pkey_file; /* != NULL iff privkey file explicitly configured */
+
md_state_t state; /* state of this MD */
- apr_time_t valid_from; /* When the credentials start to be valid. 0 if unknown */
- apr_time_t expires; /* When the credentials expire. 0 if unknown */
- const char *cert_url; /* url where cert has been created, remember during drive */
+
+ struct apr_array_header_t *acme_tls_1_domains; /* domains supporting "acme-tls/1" protocol */
const struct md_srv_conf_t *sc; /* server config where it was defined or NULL */
const char *defn_name; /* config file this MD was defined */
unsigned defn_line_number; /* line number of definition */
+
+ const char *configured_name; /* name this MD was configured with, if different */
};
#define MD_KEY_ACCOUNT "account"
+#define MD_KEY_ACME_TLS_1 "acme-tls/1"
+#define MD_KEY_ACTIVITY "activity"
#define MD_KEY_AGREEMENT "agreement"
+#define MD_KEY_AUTHORIZATIONS "authorizations"
#define MD_KEY_BITS "bits"
#define MD_KEY_CA "ca"
#define MD_KEY_CA_URL "ca-url"
#define MD_KEY_CERT "cert"
+#define MD_KEY_CERT_FILE "cert-file"
+#define MD_KEY_CERTIFICATE "certificate"
+#define MD_KEY_CHALLENGE "challenge"
#define MD_KEY_CHALLENGES "challenges"
+#define MD_KEY_CMD_DNS01 "cmd-dns-01"
+#define MD_KEY_COMPLETE "complete"
#define MD_KEY_CONTACT "contact"
#define MD_KEY_CONTACTS "contacts"
#define MD_KEY_CSR "csr"
@@ -125,46 +143,67 @@ struct md_t {
#define MD_KEY_DIR "dir"
#define MD_KEY_DOMAIN "domain"
#define MD_KEY_DOMAINS "domains"
-#define MD_KEY_DRIVE_MODE "drive-mode"
+#define MD_KEY_ENTRIES "entries"
+#define MD_KEY_ERRORED "errored"
#define MD_KEY_ERRORS "errors"
#define MD_KEY_EXPIRES "expires"
+#define MD_KEY_FINALIZE "finalize"
+#define MD_KEY_FINISHED "finished"
#define MD_KEY_HTTP "http"
#define MD_KEY_HTTPS "https"
#define MD_KEY_ID "id"
#define MD_KEY_IDENTIFIER "identifier"
#define MD_KEY_KEY "key"
#define MD_KEY_KEYAUTHZ "keyAuthorization"
+#define MD_KEY_LAST "last"
+#define MD_KEY_LAST_RUN "last-run"
#define MD_KEY_LOCATION "location"
+#define MD_KEY_LOG "log"
+#define MD_KEY_MDS "managed-domains"
+#define MD_KEY_MESSAGE "message"
#define MD_KEY_MUST_STAPLE "must-staple"
#define MD_KEY_NAME "name"
+#define MD_KEY_NEXT_RUN "next-run"
+#define MD_KEY_NOTIFIED "notified"
+#define MD_KEY_ORDERS "orders"
#define MD_KEY_PERMANENT "permanent"
#define MD_KEY_PKEY "privkey"
-#define MD_KEY_PROCESSED "processed"
+#define MD_KEY_PKEY_FILE "pkey-file"
+#define MD_KEY_PROBLEM "problem"
#define MD_KEY_PROTO "proto"
+#define MD_KEY_READY "ready"
#define MD_KEY_REGISTRATION "registration"
#define MD_KEY_RENEW "renew"
+#define MD_KEY_RENEW_MODE "renew-mode"
+#define MD_KEY_RENEWAL "renewal"
+#define MD_KEY_RENEWING "renewing"
#define MD_KEY_RENEW_WINDOW "renew-window"
#define MD_KEY_REQUIRE_HTTPS "require-https"
#define MD_KEY_RESOURCE "resource"
+#define MD_KEY_SERIAL "serial"
+#define MD_KEY_SHA256_FINGERPRINT "sha256-fingerprint"
#define MD_KEY_STATE "state"
#define MD_KEY_STATUS "status"
#define MD_KEY_STORE "store"
#define MD_KEY_TEMPORARY "temporary"
#define MD_KEY_TOKEN "token"
+#define MD_KEY_TOTAL "total"
#define MD_KEY_TRANSITIVE "transitive"
#define MD_KEY_TYPE "type"
#define MD_KEY_URL "url"
#define MD_KEY_URI "uri"
-#define MD_KEY_VALID_FROM "validFrom"
+#define MD_KEY_VALID_FROM "valid-from"
+#define MD_KEY_VALID_UNTIL "valid-until"
#define MD_KEY_VALUE "value"
#define MD_KEY_VERSION "version"
+#define MD_KEY_WHEN "when"
+#define MD_KEY_WARN_WINDOW "warn-window"
#define MD_FN_MD "md.json"
#define MD_FN_JOB "job.json"
#define MD_FN_PRIVKEY "privkey.pem"
#define MD_FN_PUBCERT "pubcert.pem"
#define MD_FN_CERT "cert.pem"
-#define MD_FN_CHAIN "chain.pem"
#define MD_FN_HTTPD_JSON "httpd.json"
#define MD_FN_FALLBACK_PKEY "fallback-privkey.pem"
@@ -226,7 +265,7 @@ md_t *md_get_by_dns_overlap(struct apr_a
* Find the managed domain in the list that, for the given md,
* has the same name, or the most number of overlaps in domains
*/
-md_t *md_find_closest_match(apr_array_header_t *mds, const md_t *md);
+md_t *md_find_closest_match(struct apr_array_header_t *mds, const md_t *md);
/**
* Create and empty md record, structures initialized.
@@ -248,11 +287,6 @@ md_t *md_clone(apr_pool_t *p, const md_t
*/
md_t *md_copy(apr_pool_t *p, const md_t *src);
-/**
- * Create a merged md with the settings of add overlaying the ones from base.
- */
-md_t *md_merge(apr_pool_t *p, const md_t *add, const md_t *base);
-
/**
* Convert the managed domain into a JSON representation and vice versa.
*
@@ -261,30 +295,26 @@ md_t *md_merge(apr_pool_t *p, const md_t
struct md_json_t *md_to_json (const md_t *md, apr_pool_t *p);
md_t *md_from_json(struct md_json_t *json, apr_pool_t *p);
-/**
- * Determine if MD should renew its cert (if it has one)
- */
-int md_should_renew(const md_t *md);
+int md_is_covered_by_alt_names(const md_t *md, const struct apr_array_header_t* alt_names);
+
+#define LE_ACMEv1_PROD "https://acme-v01.api.letsencrypt.org/directory"
+#define LE_ACMEv1_STAGING "https://acme-staging.api.letsencrypt.org/directory"
+
+#define LE_ACMEv2_PROD "https://acme-v02.api.letsencrypt.org/directory"
+#define LE_ACMEv2_STAGING "https://acme-staging-v02.api.letsencrypt.org/directory"
+
/**************************************************************************************************/
/* domain credentials */
-typedef struct md_creds_t md_creds_t;
-struct md_creds_t {
- struct md_pkey_t *privkey;
- struct apr_array_header_t *pubcert; /* complete md_cert* chain */
- struct md_cert_t *cert;
- int expired;
+typedef struct md_pubcert_t md_pubcert_t;
+struct md_pubcert_t {
+ struct apr_array_header_t *certs; /* chain of const md_cert*, leaf cert first */
+ struct apr_array_header_t *alt_names; /* alt-names of leaf cert */
+ const char *cert_file; /* file path of chain */
+ const char *key_file; /* file path of key for leaf cert */
};
-/* TODO: not sure this is a good idea, testing some readability and debuggabiltiy of
- * cascaded apr_status_t checks. */
-#define MD_CHK_VARS const char *md_chk_
-#define MD_LAST_CHK md_chk_
-#define MD_CHK_STEP(c, status, s) (md_chk_ = s, (void)md_chk_, status == (rv = (c)))
-#define MD_CHK(c, status) MD_CHK_STEP(c, status, #c)
-#define MD_IS_ERR(c, err) (md_chk_ = #c, APR_STATUS_IS_##err((rv = (c))))
-#define MD_CHK_SUCCESS(c) MD_CHK(c, APR_SUCCESS)
-#define MD_OK(c) MD_CHK_SUCCESS(c)
+#define MD_OK(c) (APR_SUCCESS == (rv = c))
#endif /* mod_md_md_h */
Modified: httpd/httpd/trunk/modules/md/md_acme.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_acme.c?rev=1862013&r1=1862012&r2=1862013&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_acme.c (original)
+++ httpd/httpd/trunk/modules/md/md_acme.c Mon Jun 24 16:04:32 2019
@@ -30,6 +30,7 @@
#include "md_http.h"
#include "md_log.h"
#include "md_store.h"
+#include "md_result.h"
#include "md_util.h"
#include "md_version.h"
@@ -37,7 +38,7 @@
#include "md_acme_acct.h"
-static const char *base_product;
+static const char *base_product= "-";
typedef struct acme_problem_status_t acme_problem_status_t;
@@ -85,91 +86,6 @@ static apr_status_t problem_status_get(c
return APR_EGENERAL;
}
-apr_status_t md_acme_init(apr_pool_t *p, const char *base)
-{
- base_product = base;
- return md_crypt_init(p);
-}
-
-apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url,
- const char *proxy_url)
-{
- md_acme_t *acme;
- const char *err = NULL;
- apr_status_t rv;
- apr_uri_t uri_parsed;
- size_t len;
-
- if (!url) {
- md_log_perror(MD_LOG_MARK, MD_LOG_ERR, APR_EINVAL, p, "create ACME without url");
- return APR_EINVAL;
- }
-
- if (APR_SUCCESS != (rv = md_util_abs_uri_check(p, url, &err))) {
- md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "invalid ACME uri (%s): %s", err, url);
- return rv;
- }
-
- acme = apr_pcalloc(p, sizeof(*acme));
- acme->url = url;
- acme->p = p;
- acme->user_agent = apr_psprintf(p, "%s mod_md/%s",
- base_product, MOD_MD_VERSION);
- acme->proxy_url = proxy_url? apr_pstrdup(p, proxy_url) : NULL;
- acme->max_retries = 3;
-
- if (APR_SUCCESS != (rv = apr_uri_parse(p, url, &uri_parsed))) {
- md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "parsing ACME uri: %s", url);
- return APR_EINVAL;
- }
-
- len = strlen(uri_parsed.hostname);
- acme->sname = (len <= 16)? uri_parsed.hostname : apr_pstrdup(p, uri_parsed.hostname + len - 16);
-
- *pacme = (APR_SUCCESS == rv)? acme : NULL;
- return rv;
-}
-
-apr_status_t md_acme_setup(md_acme_t *acme)
-{
- apr_status_t rv;
- md_json_t *json;
-
- assert(acme->url);
- if (!acme->http && APR_SUCCESS != (rv = md_http_create(&acme->http, acme->p,
- acme->user_agent, acme->proxy_url))) {
- return rv;
- }
- md_http_set_response_limit(acme->http, 1024*1024);
-
- md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, acme->p, "get directory from %s", acme->url);
-
- rv = md_acme_get_json(&json, acme, acme->url, acme->p);
- if (APR_SUCCESS == rv) {
- acme->new_authz = md_json_gets(json, "new-authz", NULL);
- acme->new_cert = md_json_gets(json, "new-cert", NULL);
- acme->new_reg = md_json_gets(json, "new-reg", NULL);
- acme->revoke_cert = md_json_gets(json, "revoke-cert", NULL);
- if (acme->new_authz && acme->new_cert && acme->new_reg && acme->revoke_cert) {
- return APR_SUCCESS;
- }
- md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, acme->p,
- "Unable to understand ACME server response. Wrong ACME protocol version?");
- rv = APR_EINVAL;
- }
- else {
- md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, acme->p, "unsuccessful in contacting ACME "
- "server at %s. If this problem persists, please check your network "
- "connectivity from your Apache server to the ACME server. Also, older "
- "servers might have trouble verifying the certificates of the ACME "
- "server. You can check if you are able to contact it manually via the "
- "curl command. Sometimes, the ACME server might be down for maintenance, "
- "so failing to contact it is not an immediate problem. mod_md will "
- "continue retrying this.", acme->url);
- }
- return rv;
-}
-
/**************************************************************************************************/
/* acme requests */
@@ -195,16 +111,6 @@ static apr_status_t http_update_nonce(co
return res->rv;
}
-static apr_status_t md_acme_new_nonce(md_acme_t *acme)
-{
- apr_status_t rv;
- long id;
-
- rv = md_http_HEAD(acme->http, acme->new_reg, NULL, http_update_nonce, acme, &id);
- md_http_await(acme->http, id);
- return rv;
-}
-
static md_acme_req_t *md_acme_req_create(md_acme_t *acme, const char *method, const char *url)
{
apr_pool_t *pool;
@@ -232,31 +138,26 @@ static md_acme_req_t *md_acme_req_create
return NULL;
}
req->max_retries = acme->max_retries;
-
+ req->result = md_result_make(req->p, APR_SUCCESS);
return req;
}
-apr_status_t md_acme_req_body_init(md_acme_req_t *req, md_json_t *jpayload)
+static apr_status_t acmev1_new_nonce(md_acme_t *acme)
{
- const char *payload;
- size_t payload_len;
-
- if (!req->acme->acct) {
- return APR_EINVAL;
- }
+ return md_http_HEAD(acme->http, acme->api.v1.new_reg, NULL, http_update_nonce, acme);
+}
- payload = md_json_writep(jpayload, req->p, MD_JSON_FMT_COMPACT);
- if (!payload) {
- return APR_EINVAL;
- }
+static apr_status_t acmev2_new_nonce(md_acme_t *acme)
+{
+ return md_http_HEAD(acme->http, acme->api.v2.new_nonce, NULL, http_update_nonce, acme);
+}
- payload_len = strlen(payload);
- md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, req->p,
- "acct payload(len=%" APR_SIZE_T_FMT "): %s", payload_len, payload);
- return md_jws_sign(&req->req_json, req->p, payload, payload_len,
- req->prot_hdrs, req->acme->acct_key, NULL);
-}
+apr_status_t md_acme_init(apr_pool_t *p, const char *base, int init_ssl)
+{
+ base_product = base;
+ return init_ssl? md_crypt_init(p) : APR_SUCCESS;
+}
static apr_status_t inspect_problem(md_acme_req_t *req, const md_http_response_t *res)
{
@@ -274,6 +175,7 @@ static apr_status_t inspect_problem(md_a
ptype = md_json_gets(problem, MD_KEY_TYPE, NULL);
pdetail = md_json_gets(problem, MD_KEY_DETAIL, NULL);
req->rv = problem_status_get(ptype);
+ md_result_problem_set(req->result, req->rv, ptype, pdetail);
if (APR_STATUS_IS_EAGAIN(req->rv)) {
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, req->rv, req->p,
@@ -298,7 +200,9 @@ static apr_status_t inspect_problem(md_a
default:
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, req->p,
"acme problem unknown: http status %d", res->status);
- return APR_EGENERAL;
+ md_result_printf(req->result, APR_EGENERAL, "unexpected http status: %d",
+ res->status);
+ return req->result->status;
}
}
return res->rv;
@@ -307,9 +211,73 @@ static apr_status_t inspect_problem(md_a
/**************************************************************************************************/
/* ACME requests with nonce handling */
-static apr_status_t md_acme_req_done(md_acme_req_t *req)
+static apr_status_t acmev1_req_init(md_acme_req_t *req, md_json_t *jpayload)
+{
+ const char *payload;
+ size_t payload_len;
+
+ if (!req->acme->acct) {
+ return APR_EINVAL;
+ }
+ if (jpayload) {
+ payload = md_json_writep(jpayload, req->p, MD_JSON_FMT_COMPACT);
+ if (!payload) {
+ return APR_EINVAL;
+ }
+ }
+ else {
+ payload = "";
+ }
+
+ payload_len = strlen(payload);
+ md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, req->p,
+ "acme payload(len=%" APR_SIZE_T_FMT "): %s", payload_len, payload);
+ return md_jws_sign(&req->req_json, req->p, payload, payload_len,
+ req->prot_hdrs, req->acme->acct_key, NULL);
+}
+
+static apr_status_t acmev2_req_init(md_acme_req_t *req, md_json_t *jpayload)
+{
+ const char *payload;
+ size_t payload_len;
+
+ if (!req->acme->acct) {
+ return APR_EINVAL;
+ }
+ if (jpayload) {
+ payload = md_json_writep(jpayload, req->p, MD_JSON_FMT_COMPACT);
+ if (!payload) {
+ return APR_EINVAL;
+ }
+ }
+ else {
+ payload = "";
+ }
+
+ payload_len = strlen(payload);
+ md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, req->p,
+ "acme payload(len=%" APR_SIZE_T_FMT "): %s", payload_len, payload);
+ return md_jws_sign(&req->req_json, req->p, payload, payload_len,
+ req->prot_hdrs, req->acme->acct_key, req->acme->acct->url);
+}
+
+apr_status_t md_acme_req_body_init(md_acme_req_t *req, md_json_t *payload)
{
- apr_status_t rv = req->rv;
+ return req->acme->req_init_fn(req, payload);
+}
+
+static apr_status_t md_acme_req_done(md_acme_req_t *req, apr_status_t rv)
+{
+ if (req->result->status != APR_SUCCESS) {
+ if (req->on_err) {
+ req->on_err(req, req->result, req->baton);
+ }
+ }
+ /* An error in rv superceeds the result->status */
+ if (APR_SUCCESS != rv) req->result->status = rv;
+ rv = req->result->status;
+ /* transfer results into the acme's central result for longer life and later inspection */
+ md_result_dup(req->acme->last, req->result);
if (req->p) {
apr_pool_destroy(req->p);
}
@@ -361,9 +329,10 @@ static apr_status_t on_response(const md
if (!processed) {
rv = APR_EINVAL;
- md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, req->p,
- "response: %d, content-type=%s", res->status,
- apr_table_get(res->headers, "Content-Type"));
+ md_result_printf(req->result, rv, "unable to process the response: "
+ "http-status=%d, content-type=%s",
+ res->status, apr_table_get(res->headers, "Content-Type"));
+ md_result_log(req->result, MD_LOG_ERR);
}
}
else if (APR_EAGAIN == (rv = inspect_problem(req, res))) {
@@ -372,84 +341,111 @@ static apr_status_t on_response(const md
}
out:
- md_acme_req_done(req);
+ md_acme_req_done(req, rv);
return rv;
}
+static apr_status_t acmev2_GET_as_POST_init(md_acme_req_t *req, void *baton)
+{
+ (void)baton;
+ return md_acme_req_body_init(req, NULL);
+}
+
static apr_status_t md_acme_req_send(md_acme_req_t *req)
{
apr_status_t rv;
md_acme_t *acme = req->acme;
const char *body = NULL;
+ md_result_t *result;
assert(acme->url);
+ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, req->p,
+ "sending req: %s %s", req->method, req->url);
+ md_result_reset(req->acme->last);
+ result = md_result_make(req->p, APR_SUCCESS);
+
+ /* Whom are we talking to? */
+ if (acme->version == MD_ACME_VERSION_UNKNOWN) {
+ rv = md_acme_setup(acme, result);
+ if (APR_SUCCESS != rv) goto leave;
+ }
+
+ if (!strcmp("GET", req->method) && !req->on_init && !req->req_json
+ && MD_ACME_VERSION_MAJOR(acme->version) > 1) {
+ /* See <https://ietf-wg-acme.github.io/acme/draft-ietf-acme-acme.html#rfc.section.6.3>
+ * and <https://mailarchive.ietf.org/arch/msg/acme/sotffSQ0OWV-qQJodLwWYWcEVKI>
+ * and <https://community.letsencrypt.org/t/acme-v2-scheduled-deprecation-of-unauthenticated-resource-gets/74380>
+ * We implement this change in ACMEv2 and higher as keeping the md_acme_GET() methods,
+ * but switching them to POSTs with a empty, JWS signed, body when we call
+ * our HTTP client. */
+ req->method = "POST";
+ req->on_init = acmev2_GET_as_POST_init;
+ }
+
+ /* Besides GET/HEAD, we always need a fresh nonce */
if (strcmp("GET", req->method) && strcmp("HEAD", req->method)) {
- if (!acme->new_authz) {
- if (APR_SUCCESS != (rv = md_acme_setup(acme))) {
- return rv;
- }
- }
- if (!acme->nonce) {
- if (APR_SUCCESS != (rv = md_acme_new_nonce(acme))) {
- md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, req->p,
- "error retrieving new nonce from ACME server");
- return rv;
- }
+ if (acme->version == MD_ACME_VERSION_UNKNOWN) {
+ rv = md_acme_setup(acme, result);
+ if (APR_SUCCESS != rv) goto leave;
+ }
+ if (!acme->nonce && (APR_SUCCESS != (rv = acme->new_nonce_fn(acme)))) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, req->p,
+ "error retrieving new nonce from ACME server");
+ goto leave;
}
apr_table_set(req->prot_hdrs, "nonce", acme->nonce);
+ if (MD_ACME_VERSION_MAJOR(acme->version) > 1) {
+ apr_table_set(req->prot_hdrs, "url", req->url);
+ }
acme->nonce = NULL;
}
rv = req->on_init? req->on_init(req, req->baton) : APR_SUCCESS;
+ if (APR_SUCCESS != rv) goto leave;
- if ((rv == APR_SUCCESS) && req->req_json) {
+ if (req->req_json) {
body = md_json_writep(req->req_json, req->p, MD_JSON_FMT_INDENT);
if (!body) {
- rv = APR_EINVAL;
+ rv = APR_EINVAL; goto leave;
}
}
- if (rv == APR_SUCCESS) {
- long id = 0;
-
- if (body && md_log_is_level(req->p, MD_LOG_TRACE2)) {
- md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, 0, req->p,
- "req: POST %s, body:\n%s", req->url, body);
- }
- else {
- md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, req->p,
- "req: POST %s", req->url);
- }
- if (!strcmp("GET", req->method)) {
- rv = md_http_GET(req->acme->http, req->url, NULL, on_response, req, &id);
- }
- else if (!strcmp("POST", req->method)) {
- rv = md_http_POSTd(req->acme->http, req->url, NULL, "application/json",
- body, body? strlen(body) : 0, on_response, req, &id);
- }
- else if (!strcmp("HEAD", req->method)) {
- rv = md_http_HEAD(req->acme->http, req->url, NULL, on_response, req, &id);
- }
- else {
- md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, req->p,
- "HTTP method %s against: %s", req->method, req->url);
- rv = APR_ENOTIMPL;
- }
- md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, req->p, "req sent");
- md_http_await(acme->http, id);
-
- if (APR_EAGAIN == rv && req->max_retries > 0) {
- --req->max_retries;
- return md_acme_req_send(req);
- }
- req = NULL;
+ if (body && md_log_is_level(req->p, MD_LOG_TRACE2)) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, 0, req->p,
+ "req: %s %s, body:\n%s", req->method, req->url, body);
}
-
- if (req) {
- md_acme_req_done(req);
+ else {
+ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, req->p,
+ "req: %s %s", req->method, req->url);
+ }
+
+ if (!strcmp("GET", req->method)) {
+ rv = md_http_GET(req->acme->http, req->url, NULL, on_response, req);
+ }
+ else if (!strcmp("POST", req->method)) {
+ rv = md_http_POSTd(req->acme->http, req->url, NULL, "application/jose+json",
+ body, body? strlen(body) : 0, on_response, req);
}
+ else if (!strcmp("HEAD", req->method)) {
+ rv = md_http_HEAD(req->acme->http, req->url, NULL, on_response, req);
+ }
+ else {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, req->p,
+ "HTTP method %s against: %s", req->method, req->url);
+ rv = APR_ENOTIMPL;
+ }
+ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, req->p, "req sent");
+
+ if (APR_EAGAIN == rv && req->max_retries > 0) {
+ --req->max_retries;
+ rv = md_acme_req_send(req);
+ }
+ req = NULL;
+
+leave:
+ if (req) md_acme_req_done(req, rv);
return rv;
}
@@ -457,6 +453,7 @@ apr_status_t md_acme_POST(md_acme_t *acm
md_acme_req_init_cb *on_init,
md_acme_req_json_cb *on_json,
md_acme_req_res_cb *on_res,
+ md_acme_req_err_cb *on_err,
void *baton)
{
md_acme_req_t *req;
@@ -469,6 +466,7 @@ apr_status_t md_acme_POST(md_acme_t *acm
req->on_init = on_init;
req->on_json = on_json;
req->on_res = on_res;
+ req->on_err = on_err;
req->baton = baton;
return md_acme_req_send(req);
@@ -478,6 +476,7 @@ apr_status_t md_acme_GET(md_acme_t *acme
md_acme_req_init_cb *on_init,
md_acme_req_json_cb *on_json,
md_acme_req_res_cb *on_res,
+ md_acme_req_err_cb *on_err,
void *baton)
{
md_acme_req_t *req;
@@ -490,11 +489,22 @@ apr_status_t md_acme_GET(md_acme_t *acme
req->on_init = on_init;
req->on_json = on_json;
req->on_res = on_res;
+ req->on_err = on_err;
req->baton = baton;
return md_acme_req_send(req);
}
+void md_acme_report_result(md_acme_t *acme, apr_status_t rv, struct md_result_t *result)
+{
+ if (acme->last->status == APR_SUCCESS) {
+ md_result_set(result, rv, NULL);
+ }
+ else {
+ md_result_problem_set(result, acme->last->status, acme->last->problem, acme->last->detail);
+ }
+}
+
/**************************************************************************************************/
/* GET JSON */
@@ -524,8 +534,255 @@ apr_status_t md_acme_get_json(struct md_
ctx.pool = p;
ctx.json = NULL;
- rv = md_acme_GET(acme, url, NULL, on_got_json, NULL, &ctx);
+ rv = md_acme_GET(acme, url, NULL, on_got_json, NULL, NULL, &ctx);
*pjson = (APR_SUCCESS == rv)? ctx.json : NULL;
return rv;
}
+/**************************************************************************************************/
+/* Generic ACME operations */
+
+void md_acme_clear_acct(md_acme_t *acme)
+{
+ acme->acct_id = NULL;
+ acme->acct = NULL;
+ acme->acct_key = NULL;
+}
+
+const char *md_acme_acct_id_get(md_acme_t *acme)
+{
+ return acme->acct_id;
+}
+
+const char *md_acme_acct_url_get(md_acme_t *acme)
+{
+ return acme->acct? acme->acct->url : NULL;
+}
+
+apr_status_t md_acme_use_acct(md_acme_t *acme, md_store_t *store,
+ apr_pool_t *p, const char *acct_id)
+{
+ md_acme_acct_t *acct;
+ md_pkey_t *pkey;
+ apr_status_t rv;
+
+ if (APR_SUCCESS == (rv = md_acme_acct_load(&acct, &pkey,
+ store, MD_SG_ACCOUNTS, acct_id, acme->p))) {
+ if (acct->ca_url && !strcmp(acct->ca_url, acme->url)) {
+ acme->acct_id = apr_pstrdup(p, acct_id);
+ acme->acct = acct;
+ acme->acct_key = pkey;
+ rv = md_acme_acct_validate(acme, store, p);
+ }
+ else {
+ /* account is from a nother server or, more likely, from another
+ * protocol endpoint on the same server */
+ rv = APR_ENOENT;
+ }
+ }
+ return rv;
+}
+
+apr_status_t md_acme_save_acct(md_acme_t *acme, apr_pool_t *p, md_store_t *store)
+{
+ return md_acme_acct_save(store, p, acme, &acme->acct_id, acme->acct, acme->acct_key);
+}
+
+static apr_status_t acmev1_POST_new_account(md_acme_t *acme,
+ md_acme_req_init_cb *on_init,
+ md_acme_req_json_cb *on_json,
+ md_acme_req_res_cb *on_res,
+ md_acme_req_err_cb *on_err,
+ void *baton)
+{
+ return md_acme_POST(acme, acme->api.v1.new_reg, on_init, on_json, on_res, on_err, baton);
+}
+
+static apr_status_t acmev2_POST_new_account(md_acme_t *acme,
+ md_acme_req_init_cb *on_init,
+ md_acme_req_json_cb *on_json,
+ md_acme_req_res_cb *on_res,
+ md_acme_req_err_cb *on_err,
+ void *baton)
+{
+ return md_acme_POST(acme, acme->api.v2.new_account, on_init, on_json, on_res, on_err, baton);
+}
+
+apr_status_t md_acme_POST_new_account(md_acme_t *acme,
+ md_acme_req_init_cb *on_init,
+ md_acme_req_json_cb *on_json,
+ md_acme_req_res_cb *on_res,
+ md_acme_req_err_cb *on_err,
+ void *baton)
+{
+ return acme->post_new_account_fn(acme, on_init, on_json, on_res, on_err, baton);
+}
+
+/**************************************************************************************************/
+/* ACME setup */
+
+apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url,
+ const char *proxy_url)
+{
+ md_acme_t *acme;
+ const char *err = NULL;
+ apr_status_t rv;
+ apr_uri_t uri_parsed;
+ size_t len;
+
+ if (!url) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, APR_EINVAL, p, "create ACME without url");
+ return APR_EINVAL;
+ }
+
+ if (APR_SUCCESS != (rv = md_util_abs_uri_check(p, url, &err))) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "invalid ACME uri (%s): %s", err, url);
+ return rv;
+ }
+
+ acme = apr_pcalloc(p, sizeof(*acme));
+ acme->url = url;
+ acme->p = p;
+ acme->user_agent = apr_psprintf(p, "%s mod_md/%s",
+ base_product, MOD_MD_VERSION);
+ acme->proxy_url = proxy_url? apr_pstrdup(p, proxy_url) : NULL;
+ acme->max_retries = 3;
+
+ if (APR_SUCCESS != (rv = apr_uri_parse(p, url, &uri_parsed))) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "parsing ACME uri: %s", url);
+ return APR_EINVAL;
+ }
+
+ len = strlen(uri_parsed.hostname);
+ acme->sname = (len <= 16)? uri_parsed.hostname : apr_pstrdup(p, uri_parsed.hostname + len - 16);
+ acme->version = MD_ACME_VERSION_UNKNOWN;
+ acme->last = md_result_make(acme->p, APR_SUCCESS);
+
+ *pacme = (APR_SUCCESS == rv)? acme : NULL;
+ return rv;
+}
+
+typedef struct {
+ md_acme_t *acme;
+ md_result_t *result;
+} update_dir_ctx;
+
+static apr_status_t update_directory(const md_http_response_t *res)
+{
+ md_http_request_t *req = res->req;
+ md_acme_t *acme = ((update_dir_ctx *)req->baton)->acme;
+ md_result_t *result = ((update_dir_ctx *)req->baton)->result;
+ apr_status_t rv = res->rv;
+ md_json_t *json;
+ const char *s;
+
+ if (APR_SUCCESS != rv) goto leave;
+ md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, req->pool, "directory lookup response: %d", res->status);
+ if (res->status == 503) {
+ md_result_printf(result, APR_EAGAIN,
+ "The ACME server at <%s> reports that Service is Unavailable (503). This "
+ "may happen during maintenance for short periods of time.", acme->url);
+ md_result_log(result, MD_LOG_INFO);
+ rv = result->status;
+ goto leave;
+ }
+ else if (res->status < 200 || res->status >= 300) {
+ md_result_printf(result, APR_EAGAIN,
+ "The ACME server at <%s> responded with HTTP status %d. This "
+ "is unusual. Please verify that the URL is correct and that you can indeed "
+ "make request from the server to it by other means, e.g. invoking curl/wget.",
+ acme->url, res->status);
+ goto leave;
+ }
+
+ rv = md_json_read_http(&json, req->pool, res);
+ if (APR_SUCCESS != rv) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, req->pool, "reading JSON body");
+ goto leave;
+ }
+
+ if (md_log_is_level(acme->p, MD_LOG_TRACE2)) {
+ s = md_json_writep(json, req->pool, MD_JSON_FMT_INDENT);
+ md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, rv, req->pool,
+ "response: %s", s ? s : "<failed to serialize!>");
+ }
+
+ /* What have we got? */
+ if ((s = md_json_dups(acme->p, json, "new-authz", NULL))) {
+ acme->api.v1.new_authz = s;
+ acme->api.v1.new_cert = md_json_dups(acme->p, json, "new-cert", NULL);
+ acme->api.v1.new_reg = md_json_dups(acme->p, json, "new-reg", NULL);
+ acme->api.v1.revoke_cert = md_json_dups(acme->p, json, "revoke-cert", NULL);
+ if (acme->api.v1.new_authz && acme->api.v1.new_cert
+ && acme->api.v1.new_reg && acme->api.v1.revoke_cert) {
+ acme->version = MD_ACME_VERSION_1;
+ }
+ acme->ca_agreement = md_json_dups(acme->p, json, "meta", "terms-of-service", NULL);
+ acme->new_nonce_fn = acmev1_new_nonce;
+ acme->req_init_fn = acmev1_req_init;
+ acme->post_new_account_fn = acmev1_POST_new_account;
+ }
+ else if ((s = md_json_dups(acme->p, json, "newAccount", NULL))) {
+ acme->api.v2.new_account = s;
+ acme->api.v2.new_order = md_json_dups(acme->p, json, "newOrder", NULL);
+ acme->api.v2.revoke_cert = md_json_dups(acme->p, json, "revokeCert", NULL);
+ acme->api.v2.key_change = md_json_dups(acme->p, json, "keyChange", NULL);
+ acme->api.v2.new_nonce = md_json_dups(acme->p, json, "newNonce", NULL);
+ if (acme->api.v2.new_account && acme->api.v2.new_order
+ && acme->api.v2.revoke_cert && acme->api.v2.key_change
+ && acme->api.v2.new_nonce) {
+ acme->version = MD_ACME_VERSION_2;
+ }
+ acme->ca_agreement = md_json_dups(acme->p, json, "meta", "termsOfService", NULL);
+ acme->new_nonce_fn = acmev2_new_nonce;
+ acme->req_init_fn = acmev2_req_init;
+ acme->post_new_account_fn = acmev2_POST_new_account;
+ }
+
+ if (MD_ACME_VERSION_UNKNOWN == acme->version) {
+ md_result_printf(result, APR_EINVAL,
+ "Unable to understand ACME server response from <%s>. "
+ "Wrong ACME protocol version or link?", acme->url);
+ md_result_log(result, MD_LOG_WARNING);
+ rv = result->status;
+ }
+leave:
+ return rv;
+}
+
+apr_status_t md_acme_setup(md_acme_t *acme, md_result_t *result)
+{
+ apr_status_t rv;
+ update_dir_ctx ctx;
+
+ assert(acme->url);
+ acme->version = MD_ACME_VERSION_UNKNOWN;
+
+ if (!acme->http && APR_SUCCESS != (rv = md_http_create(&acme->http, acme->p,
+ acme->user_agent, acme->proxy_url))) {
+ return rv;
+ }
+ md_http_set_response_limit(acme->http, 1024*1024);
+
+ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, acme->p, "get directory from %s", acme->url);
+
+ ctx.acme = acme;
+ ctx.result = result;
+ rv = md_http_GET(acme->http, acme->url, NULL, update_directory, &ctx);
+
+ if (APR_SUCCESS != rv && APR_SUCCESS == result->status) {
+ /* If the result reports no error, we never got a response from the server */
+ md_result_printf(result, rv,
+ "Unsuccessful in contacting ACME server at <%s>. If this problem persists, "
+ "please check your network connectivity from your Apache server to the "
+ "ACME server. Also, older servers might have trouble verifying the certificates "
+ "of the ACME server. You can check if you are able to contact it manually via the "
+ "curl command. Sometimes, the ACME server might be down for maintenance, "
+ "so failing to contact it is not an immediate problem. Apache will "
+ "continue retrying this.", acme->url);
+ md_result_log(result, MD_LOG_WARNING);
+ }
+ return rv;
+}
+
+
Modified: httpd/httpd/trunk/modules/md/md_acme.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/md/md_acme.h?rev=1862013&r1=1862012&r2=1862013&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/md/md_acme.h (original)
+++ httpd/httpd/trunk/modules/md/md_acme.h Mon Jun 24 16:04:32 2019
@@ -26,14 +26,21 @@ struct md_json_t;
struct md_pkey_t;
struct md_t;
struct md_acme_acct_t;
-struct md_proto_t;
+struct md_acmev2_acct_t;
struct md_store_t;
+struct md_result_t;
#define MD_PROTO_ACME "ACME"
#define MD_AUTHZ_CHA_HTTP_01 "http-01"
#define MD_AUTHZ_CHA_SNI_01 "tls-sni-01"
+#define MD_ACME_VERSION_UNKNOWN 0x0
+#define MD_ACME_VERSION_1 0x010000
+#define MD_ACME_VERSION_2 0x020000
+
+#define MD_ACME_VERSION_MAJOR(i) (((i)&0xFF0000) >> 16)
+
typedef enum {
MD_ACME_S_UNKNOWN, /* MD has not been analysed yet */
MD_ACME_S_REGISTERED, /* MD is registered at CA, but not more */
@@ -46,30 +53,90 @@ typedef enum {
typedef struct md_acme_t md_acme_t;
+typedef struct md_acme_req_t md_acme_req_t;
+/**
+ * Request callback on a successful HTTP response (status 2xx).
+ */
+typedef apr_status_t md_acme_req_res_cb(md_acme_t *acme,
+ const struct md_http_response_t *res, void *baton);
+
+/**
+ * Request callback to initialize before sending. May be invoked more than once in
+ * case of retries.
+ */
+typedef apr_status_t md_acme_req_init_cb(md_acme_req_t *req, void *baton);
+
+/**
+ * Request callback on a successful response (HTTP response code 2xx) and content
+ * type matching application/.*json.
+ */
+typedef apr_status_t md_acme_req_json_cb(md_acme_t *acme, apr_pool_t *p,
+ const apr_table_t *headers,
+ struct md_json_t *jbody, void *baton);
+
+/**
+ * Request callback on detected errors.
+ */
+typedef apr_status_t md_acme_req_err_cb(md_acme_req_t *req,
+ const struct md_result_t *result, void *baton);
+
+
+typedef apr_status_t md_acme_new_nonce_fn(md_acme_t *acme);
+typedef apr_status_t md_acme_req_init_fn(md_acme_req_t *req, struct md_json_t *jpayload);
+
+typedef apr_status_t md_acme_post_fn(md_acme_t *acme,
+ md_acme_req_init_cb *on_init,
+ md_acme_req_json_cb *on_json,
+ md_acme_req_res_cb *on_res,
+ md_acme_req_err_cb *on_err,
+ void *baton);
+
struct md_acme_t {
const char *url; /* directory url of the ACME service */
const char *sname; /* short name for the service, not necessarily unique */
apr_pool_t *p;
const char *user_agent;
const char *proxy_url;
- struct md_acme_acct_t *acct;
- struct md_pkey_t *acct_key;
- const char *new_authz;
- const char *new_cert;
- const char *new_reg;
- const char *revoke_cert;
+ const char *acct_id; /* local storage id account was loaded from or NULL */
+ struct md_acme_acct_t *acct; /* account at ACME server to use for requests */
+ struct md_pkey_t *acct_key; /* private RSA key belonging to account */
+
+ int version; /* as detected from the server */
+ union {
+ struct {
+ const char *new_authz;
+ const char *new_cert;
+ const char *new_reg;
+ const char *revoke_cert;
+
+ } v1;
+ struct {
+ const char *new_account;
+ const char *new_order;
+ const char *key_change;
+ const char *revoke_cert;
+ const char *new_nonce;
+ } v2;
+ } api;
+ const char *ca_agreement;
+ const char *acct_name;
+
+ md_acme_new_nonce_fn *new_nonce_fn;
+ md_acme_req_init_fn *req_init_fn;
+ md_acme_post_fn *post_new_account_fn;
struct md_http_t *http;
const char *nonce;
int max_retries;
+ struct md_result_t *last; /* result of last request */
};
/**
* Global init, call once at start up.
*/
-apr_status_t md_acme_init(apr_pool_t *pool, const char *base_version);
+apr_status_t md_acme_init(apr_pool_t *pool, const char *base_version, int init_ssl);
/**
* Create a new ACME server instance. If path is not NULL, will use that directory
@@ -89,16 +156,31 @@ apr_status_t md_acme_create(md_acme_t **
*
* @param acme the ACME server to contact
*/
-apr_status_t md_acme_setup(md_acme_t *acme);
+apr_status_t md_acme_setup(md_acme_t *acme, struct md_result_t *result);
+
+void md_acme_report_result(md_acme_t *acme, apr_status_t rv, struct md_result_t *result);
/**************************************************************************************************/
/* account handling */
-#define MD_ACME_ACCT_STAGED "staged"
+/**
+ * Clear any existing account data from acme instance.
+ */
+void md_acme_clear_acct(md_acme_t *acme);
+
+apr_status_t md_acme_POST_new_account(md_acme_t *acme,
+ md_acme_req_init_cb *on_init,
+ md_acme_req_json_cb *on_json,
+ md_acme_req_res_cb *on_res,
+ md_acme_req_err_cb *on_err,
+ void *baton);
-apr_status_t md_acme_acct_load(struct md_acme_acct_t **pacct, struct md_pkey_t **ppkey,
- struct md_store_t *store, md_store_group_t group,
- const char *name, apr_pool_t *p);
+/**
+ * Get the local name of the account currently used by the acme instance.
+ * Will be NULL if no account has been setup successfully.
+ */
+const char *md_acme_acct_id_get(md_acme_t *acme);
+const char *md_acme_acct_url_get(md_acme_t *acme);
/**
* Specify the account to use by name in local store. On success, the account
@@ -107,14 +189,11 @@ apr_status_t md_acme_acct_load(struct md
apr_status_t md_acme_use_acct(md_acme_t *acme, struct md_store_t *store,
apr_pool_t *p, const char *acct_id);
-apr_status_t md_acme_use_acct_staged(md_acme_t *acme, struct md_store_t *store,
- md_t *md, apr_pool_t *p);
-
/**
* Get the local name of the account currently used by the acme instance.
* Will be NULL if no account has been setup successfully.
*/
-const char *md_acme_get_acct_id(md_acme_t *acme);
+const char *md_acme_acct_id_get(md_acme_t *acme);
/**
* Agree to the given Terms-of-Service url for the current account.
@@ -136,71 +215,16 @@ apr_status_t md_acme_agree(md_acme_t *ac
apr_status_t md_acme_check_agreement(md_acme_t *acme, apr_pool_t *p,
const char *agreement, const char **prequired);
-/**
- * Get the ToS agreement for current account.
- */
-const char *md_acme_get_agreement(md_acme_t *acme);
-
-
-/**
- * Find an existing account in the local store. On APR_SUCCESS, the acme
- * instance will have a current, validated account to use.
- */
-apr_status_t md_acme_find_acct(md_acme_t *acme, struct md_store_t *store, apr_pool_t *p);
-
-/**
- * Create a new account at the ACME server. The
- * new account is the one used by the acme instance afterwards, on success.
- */
-apr_status_t md_acme_create_acct(md_acme_t *acme, apr_pool_t *p, apr_array_header_t *contacts,
- const char *agreement);
-
-apr_status_t md_acme_acct_save(struct md_store_t *store, apr_pool_t *p, md_acme_t *acme,
- struct md_acme_acct_t *acct, struct md_pkey_t *acct_key);
+apr_status_t md_acme_save_acct(md_acme_t *acme, apr_pool_t *p, struct md_store_t *store);
-apr_status_t md_acme_save(md_acme_t *acme, struct md_store_t *store, apr_pool_t *p);
-
-apr_status_t md_acme_acct_save_staged(md_acme_t *acme, struct md_store_t *store,
- md_t *md, apr_pool_t *p);
-
-/**
- * Delete the current account at the ACME server and remove it from store.
- */
-apr_status_t md_acme_delete_acct(md_acme_t *acme, struct md_store_t *store, apr_pool_t *p);
-
/**
- * Delete the account from the local store without contacting the ACME server.
+ * Deactivate the current account at the ACME server..
*/
-apr_status_t md_acme_unstore_acct(struct md_store_t *store, apr_pool_t *p, const char *acct_id);
+apr_status_t md_acme_acct_deactivate(md_acme_t *acme, apr_pool_t *p);
/**************************************************************************************************/
/* request handling */
-/**
- * Request callback on a successful HTTP response (status 2xx).
- */
-typedef apr_status_t md_acme_req_res_cb(md_acme_t *acme,
- const struct md_http_response_t *res, void *baton);
-
-/**
- * A request against an ACME server
- */
-typedef struct md_acme_req_t md_acme_req_t;
-
-/**
- * Request callback to initialize before sending. May be invoked more than once in
- * case of retries.
- */
-typedef apr_status_t md_acme_req_init_cb(md_acme_req_t *req, void *baton);
-
-/**
- * Request callback on a successful response (HTTP response code 2xx) and content
- * type matching application/.*json.
- */
-typedef apr_status_t md_acme_req_json_cb(md_acme_t *acme, apr_pool_t *p,
- const apr_table_t *headers,
- struct md_json_t *jbody, void *baton);
-
struct md_acme_req_t {
md_acme_t *acme; /* the ACME server to talk to */
apr_pool_t *p; /* pool for the request duration */
@@ -218,14 +242,19 @@ struct md_acme_req_t {
md_acme_req_init_cb *on_init; /* callback to initialize the request before submit */
md_acme_req_json_cb *on_json; /* callback on successful JSON response */
md_acme_req_res_cb *on_res; /* callback on generic HTTP response */
+ md_acme_req_err_cb *on_err; /* callback on encountered error */
int max_retries; /* how often this might be retried */
void *baton; /* userdata for callbacks */
+ struct md_result_t *result; /* result of this request */
};
+apr_status_t md_acme_req_body_init(md_acme_req_t *req, struct md_json_t *payload);
+
apr_status_t md_acme_GET(md_acme_t *acme, const char *url,
md_acme_req_init_cb *on_init,
md_acme_req_json_cb *on_json,
md_acme_req_res_cb *on_res,
+ md_acme_req_err_cb *on_err,
void *baton);
/**
* Perform a POST against the ACME url. If a on_json callback is given and
@@ -245,14 +274,9 @@ apr_status_t md_acme_POST(md_acme_t *acm
md_acme_req_init_cb *on_init,
md_acme_req_json_cb *on_json,
md_acme_req_res_cb *on_res,
+ md_acme_req_err_cb *on_err,
void *baton);
-apr_status_t md_acme_GET(md_acme_t *acme, const char *url,
- md_acme_req_init_cb *on_init,
- md_acme_req_json_cb *on_json,
- md_acme_req_res_cb *on_res,
- void *baton);
-
/**
* Retrieve a JSON resource from the ACME server
*/