|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <assert.h>
|
|
Packit |
90a5c9 |
#include <stdlib.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <apr_lib.h>
|
|
Packit |
90a5c9 |
#include <apr_strings.h>
|
|
Packit |
90a5c9 |
#include <apr_buckets.h>
|
|
Packit |
90a5c9 |
#include <apr_hash.h>
|
|
Packit |
90a5c9 |
#include <apr_uri.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "md.h"
|
|
Packit |
90a5c9 |
#include "md_crypt.h"
|
|
Packit |
90a5c9 |
#include "md_json.h"
|
|
Packit |
90a5c9 |
#include "md_jws.h"
|
|
Packit |
90a5c9 |
#include "md_http.h"
|
|
Packit |
90a5c9 |
#include "md_log.h"
|
|
Packit |
90a5c9 |
#include "md_reg.h"
|
|
Packit |
90a5c9 |
#include "md_store.h"
|
|
Packit |
90a5c9 |
#include "md_util.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "md_acme.h"
|
|
Packit |
90a5c9 |
#include "md_acme_acct.h"
|
|
Packit |
90a5c9 |
#include "md_acme_authz.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
md_proto_driver_t *driver;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
const char *phase;
|
|
Packit |
90a5c9 |
int complete;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_pkey_t *privkey; /* the new private key */
|
|
Packit |
90a5c9 |
apr_array_header_t *pubcert; /* the new certificate + chain certs */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_cert_t *cert; /* the new certificate */
|
|
Packit |
90a5c9 |
apr_array_header_t *chain; /* the chain certificates */
|
|
Packit |
90a5c9 |
const char *next_up_link; /* where the next chain cert is */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_acme_t *acme;
|
|
Packit |
90a5c9 |
md_t *md;
|
|
Packit |
90a5c9 |
const md_creds_t *ncreds;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_array_header_t *ca_challenges;
|
|
Packit |
90a5c9 |
md_acme_authz_set_t *authz_set;
|
|
Packit |
90a5c9 |
apr_interval_time_t authz_monitor_timeout;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
const char *csr_der_64;
|
|
Packit |
90a5c9 |
apr_interval_time_t cert_poll_timeout;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
} md_acme_driver_t;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**************************************************************************************************/
|
|
Packit |
90a5c9 |
/* account setup */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t ad_set_acct(md_proto_driver_t *d)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
md_t *md = ad->md;
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
int update = 0, acct_installed = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ad->phase = "setup acme";
|
|
Packit |
90a5c9 |
if (!ad->acme
|
|
Packit |
90a5c9 |
&& APR_SUCCESS != (rv = md_acme_create(&ad->acme, d->p, md->ca_url, d->proxy_url))) {
|
|
Packit |
90a5c9 |
goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ad->phase = "choose account";
|
|
Packit |
90a5c9 |
/* Do we have a staged (modified) account? */
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = md_acme_use_acct_staged(ad->acme, d->store, md, d->p))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "re-using staged account");
|
|
Packit |
90a5c9 |
md->ca_account = MD_ACME_ACCT_STAGED;
|
|
Packit |
90a5c9 |
acct_installed = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (APR_STATUS_IS_ENOENT(rv)) {
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Get an account for the ACME server for this MD */
|
|
Packit |
90a5c9 |
if (md->ca_account && !acct_installed) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "re-use account '%s'", md->ca_account);
|
|
Packit |
90a5c9 |
rv = md_acme_use_acct(ad->acme, d->store, d->p, md->ca_account);
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_ENOENT(rv) || APR_STATUS_IS_EINVAL(rv)) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "rejected %s", md->ca_account);
|
|
Packit |
90a5c9 |
md->ca_account = NULL;
|
|
Packit |
90a5c9 |
update = 1;
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv && !md->ca_account) {
|
|
Packit |
90a5c9 |
/* Find a local account for server, store at MD */
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: looking at existing accounts",
|
|
Packit |
90a5c9 |
d->proto->protocol);
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == md_acme_find_acct(ad->acme, d->store, d->p)) {
|
|
Packit |
90a5c9 |
md->ca_account = md_acme_get_acct_id(ad->acme);
|
|
Packit |
90a5c9 |
update = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv && !md->ca_account) {
|
|
Packit |
90a5c9 |
/* 2.2 No local account exists, create a new one */
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: creating new account",
|
|
Packit |
90a5c9 |
d->proto->protocol);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!ad->md->contacts || apr_is_empty_array(md->contacts)) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, APR_EINVAL, d->p,
|
|
Packit |
90a5c9 |
"no contact information for md %s", md->name);
|
|
Packit |
90a5c9 |
rv = APR_EINVAL;
|
|
Packit |
90a5c9 |
goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = md_acme_create_acct(ad->acme, d->p, md->contacts,
|
|
Packit |
90a5c9 |
md->ca_agreement))
|
|
Packit |
90a5c9 |
&& APR_SUCCESS == (rv = md_acme_acct_save_staged(ad->acme, d->store, md, d->p))) {
|
|
Packit |
90a5c9 |
md->ca_account = MD_ACME_ACCT_STAGED;
|
|
Packit |
90a5c9 |
update = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
out:
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv) {
|
|
Packit |
90a5c9 |
const char *agreement = md_acme_get_agreement(ad->acme);
|
|
Packit |
90a5c9 |
/* Persist the account chosen at the md so we use the same on future runs */
|
|
Packit |
90a5c9 |
if (agreement && !md->ca_agreement) {
|
|
Packit |
90a5c9 |
md->ca_agreement = agreement;
|
|
Packit |
90a5c9 |
update = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (update) {
|
|
Packit |
90a5c9 |
rv = md_save(d->store, d->p, MD_SG_STAGING, ad->md, 0);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**************************************************************************************************/
|
|
Packit |
90a5c9 |
/* authz/challenge setup */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Pre-Req: we have an account for the ACME server that has accepted the current license agreement
|
|
Packit |
90a5c9 |
* For each domain in MD:
|
|
Packit |
90a5c9 |
* - check if there already is a valid AUTHZ resource
|
|
Packit |
90a5c9 |
* - if ot, create an AUTHZ resource with challenge data
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static apr_status_t ad_setup_authz(md_proto_driver_t *d)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
md_t *md = ad->md;
|
|
Packit |
90a5c9 |
md_acme_authz_t *authz;
|
|
Packit |
90a5c9 |
int i, changed;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
assert(ad->md);
|
|
Packit |
90a5c9 |
assert(ad->acme);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ad->phase = "check authz";
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* For each domain in MD: AUTHZ setup
|
|
Packit |
90a5c9 |
* if an AUTHZ resource is known, check if it is still valid
|
|
Packit |
90a5c9 |
* if known AUTHZ resource is not valid, remove, goto 4.1.1
|
|
Packit |
90a5c9 |
* if no AUTHZ available, create a new one for the domain, store it
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
rv = md_acme_authz_set_load(d->store, MD_SG_STAGING, md->name, &ad->authz_set, d->p);
|
|
Packit |
90a5c9 |
if (!ad->authz_set || APR_STATUS_IS_ENOENT(rv)) {
|
|
Packit |
90a5c9 |
ad->authz_set = md_acme_authz_set_create(d->p);
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (APR_SUCCESS != rv) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: loading authz data", md->name);
|
|
Packit |
90a5c9 |
md_acme_authz_set_purge(d->store, MD_SG_STAGING, d->p, md->name);
|
|
Packit |
90a5c9 |
return APR_EAGAIN;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Remove anything we no longer need */
|
|
Packit |
90a5c9 |
for (i = 0; i < ad->authz_set->authzs->nelts; ++i) {
|
|
Packit |
90a5c9 |
authz = APR_ARRAY_IDX(ad->authz_set->authzs, i, md_acme_authz_t*);
|
|
Packit |
90a5c9 |
if (!md_contains(md, authz->domain, 0)) {
|
|
Packit |
90a5c9 |
md_acme_authz_set_remove(ad->authz_set, authz->domain);
|
|
Packit |
90a5c9 |
changed = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Add anything we do not already have */
|
|
Packit |
90a5c9 |
for (i = 0; i < md->domains->nelts && APR_SUCCESS == rv; ++i) {
|
|
Packit |
90a5c9 |
const char *domain = APR_ARRAY_IDX(md->domains, i, const char *);
|
|
Packit |
90a5c9 |
changed = 0;
|
|
Packit |
90a5c9 |
authz = md_acme_authz_set_get(ad->authz_set, domain);
|
|
Packit |
90a5c9 |
if (authz) {
|
|
Packit |
90a5c9 |
/* check valid */
|
|
Packit |
90a5c9 |
rv = md_acme_authz_update(authz, ad->acme, d->store, d->p);
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: updated authz for %s",
|
|
Packit |
90a5c9 |
md->name, domain);
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != rv) {
|
|
Packit |
90a5c9 |
md_acme_authz_set_remove(ad->authz_set, domain);
|
|
Packit |
90a5c9 |
authz = NULL;
|
|
Packit |
90a5c9 |
changed = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (!authz) {
|
|
Packit |
90a5c9 |
/* create new one */
|
|
Packit |
90a5c9 |
rv = md_acme_authz_register(&authz, ad->acme, d->store, domain, d->p);
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: created authz for %s",
|
|
Packit |
90a5c9 |
md->name, domain);
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv) {
|
|
Packit |
90a5c9 |
rv = md_acme_authz_set_add(ad->authz_set, authz);
|
|
Packit |
90a5c9 |
changed = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Save any changes */
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv && changed) {
|
|
Packit |
90a5c9 |
rv = md_acme_authz_set_save(d->store, d->p, MD_SG_STAGING, md->name, ad->authz_set, 0);
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, d->p, "%s: saved", md->name);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Pre-Req: all domains have a AUTHZ resources at the ACME server
|
|
Packit |
90a5c9 |
* For each domain in MD:
|
|
Packit |
90a5c9 |
* - if AUTHZ resource is 'valid' -> continue
|
|
Packit |
90a5c9 |
* - if AUTHZ resource is 'pending':
|
|
Packit |
90a5c9 |
* - find preferred challenge choice
|
|
Packit |
90a5c9 |
* - calculate challenge data for httpd to find
|
|
Packit |
90a5c9 |
* - POST challenge start to ACME server
|
|
Packit |
90a5c9 |
* For each domain in MD where AUTHZ is 'pending', until overall timeout:
|
|
Packit |
90a5c9 |
* - wait a certain time, check status again
|
|
Packit |
90a5c9 |
* If not all AUTHZ are valid, fail
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static apr_status_t ad_start_challenges(md_proto_driver_t *d)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
md_acme_authz_t *authz;
|
|
Packit |
90a5c9 |
int i, changed = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
assert(ad->md);
|
|
Packit |
90a5c9 |
assert(ad->acme);
|
|
Packit |
90a5c9 |
assert(ad->authz_set);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ad->phase = "start challenges";
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (i = 0; i < ad->authz_set->authzs->nelts && APR_SUCCESS == rv; ++i) {
|
|
Packit |
90a5c9 |
authz = APR_ARRAY_IDX(ad->authz_set->authzs, i, md_acme_authz_t*);
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: check AUTHZ for %s",
|
|
Packit |
90a5c9 |
ad->md->name, authz->domain);
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = md_acme_authz_update(authz, ad->acme, d->store, d->p))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, d->p, "%s: check authz for %s",
|
|
Packit |
90a5c9 |
ad->md->name, authz->domain);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
switch (authz->state) {
|
|
Packit |
90a5c9 |
case MD_ACME_AUTHZ_S_VALID:
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case MD_ACME_AUTHZ_S_PENDING:
|
|
Packit |
90a5c9 |
rv = md_acme_authz_respond(authz, ad->acme, d->store, ad->ca_challenges,
|
|
Packit |
90a5c9 |
d->md->pkey_spec, d->p);
|
|
Packit |
90a5c9 |
changed = 1;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
rv = APR_EINVAL;
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p,
|
|
Packit |
90a5c9 |
"%s: unexpected AUTHZ state %d at %s",
|
|
Packit |
90a5c9 |
authz->domain, authz->state, authz->location);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv && changed) {
|
|
Packit |
90a5c9 |
rv = md_acme_authz_set_save(d->store, d->p, MD_SG_STAGING, ad->md->name, ad->authz_set, 0);
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, d->p, "%s: saved", ad->md->name);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t check_challenges(void *baton, int attempt)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_proto_driver_t *d = baton;
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
md_acme_authz_t *authz;
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (i = 0; i < ad->authz_set->authzs->nelts && APR_SUCCESS == rv; ++i) {
|
|
Packit |
90a5c9 |
authz = APR_ARRAY_IDX(ad->authz_set->authzs, i, md_acme_authz_t*);
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: check AUTHZ for %s(%d. attempt)",
|
|
Packit |
90a5c9 |
ad->md->name, authz->domain, attempt);
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = md_acme_authz_update(authz, ad->acme, d->store, d->p))) {
|
|
Packit |
90a5c9 |
switch (authz->state) {
|
|
Packit |
90a5c9 |
case MD_ACME_AUTHZ_S_VALID:
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
case MD_ACME_AUTHZ_S_PENDING:
|
|
Packit |
90a5c9 |
rv = APR_EAGAIN;
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p,
|
|
Packit |
90a5c9 |
"%s: status pending at %s", authz->domain, authz->location);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
rv = APR_EINVAL;
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p,
|
|
Packit |
90a5c9 |
"%s: unexpected AUTHZ state %d at %s",
|
|
Packit |
90a5c9 |
authz->domain, authz->state, authz->location);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t ad_monitor_challenges(md_proto_driver_t *d)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
assert(ad->md);
|
|
Packit |
90a5c9 |
assert(ad->acme);
|
|
Packit |
90a5c9 |
assert(ad->authz_set);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ad->phase = "monitor challenges";
|
|
Packit |
90a5c9 |
rv = md_util_try(check_challenges, d, 0, ad->authz_monitor_timeout, 0, 0, 1);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, rv, d->p,
|
|
Packit |
90a5c9 |
"%s: checked all domain authorizations", ad->md->name);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**************************************************************************************************/
|
|
Packit |
90a5c9 |
/* poll cert */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void get_up_link(md_proto_driver_t *d, apr_table_t *headers)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ad->next_up_link = md_link_find_relation(headers, d->p, "up");
|
|
Packit |
90a5c9 |
if (ad->next_up_link) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, d->p,
|
|
Packit |
90a5c9 |
"server reports up link as %s", ad->next_up_link);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t read_http_cert(md_cert_t **pcert, apr_pool_t *p,
|
|
Packit |
90a5c9 |
const md_http_response_t *res)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = md_cert_read_http(pcert, p, res))
|
|
Packit |
90a5c9 |
&& APR_STATUS_IS_ENOENT(rv)) {
|
|
Packit |
90a5c9 |
rv = APR_EAGAIN;
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p,
|
|
Packit |
90a5c9 |
"cert not in response from %s", res->req->url);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t on_got_cert(md_acme_t *acme, const md_http_response_t *res, void *baton)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_proto_driver_t *d = baton;
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
(void)acme;
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = read_http_cert(&ad->cert, d->p, res))) {
|
|
Packit |
90a5c9 |
rv = md_store_save(d->store, d->p, MD_SG_STAGING, ad->md->name, MD_FN_CERT,
|
|
Packit |
90a5c9 |
MD_SV_CERT, ad->cert, 0);
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "cert parsed and saved");
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv) {
|
|
Packit |
90a5c9 |
get_up_link(d, res->headers);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t get_cert(void *baton, int attempt)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_proto_driver_t *d = baton;
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
(void)attempt;
|
|
Packit |
90a5c9 |
return md_acme_GET(ad->acme, ad->md->cert_url, NULL, NULL, on_got_cert, d);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t ad_cert_poll(md_proto_driver_t *d, int only_once)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
assert(ad->md);
|
|
Packit |
90a5c9 |
assert(ad->acme);
|
|
Packit |
90a5c9 |
assert(ad->md->cert_url);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ad->phase = "poll certificate";
|
|
Packit |
90a5c9 |
if (only_once) {
|
|
Packit |
90a5c9 |
rv = get_cert(d, 0);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
rv = md_util_try(get_cert, d, 1, ad->cert_poll_timeout, 0, 0, 1);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p, "poll for cert at %s", ad->md->cert_url);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**************************************************************************************************/
|
|
Packit |
90a5c9 |
/* cert setup */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t on_init_csr_req(md_acme_req_t *req, void *baton)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_proto_driver_t *d = baton;
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
md_json_t *jpayload;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
jpayload = md_json_create(req->p);
|
|
Packit |
90a5c9 |
md_json_sets("new-cert", jpayload, MD_KEY_RESOURCE, NULL);
|
|
Packit |
90a5c9 |
md_json_sets(ad->csr_der_64, jpayload, MD_KEY_CSR, NULL);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return md_acme_req_body_init(req, jpayload);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t csr_req(md_acme_t *acme, const md_http_response_t *res, void *baton)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_proto_driver_t *d = baton;
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
(void)acme;
|
|
Packit |
90a5c9 |
ad->md->cert_url = apr_table_get(res->headers, "location");
|
|
Packit |
90a5c9 |
if (!ad->md->cert_url) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, APR_EINVAL, d->p,
|
|
Packit |
90a5c9 |
"cert created without giving its location header");
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = md_save(d->store, d->p, MD_SG_STAGING, ad->md, 0))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, APR_EINVAL, d->p,
|
|
Packit |
90a5c9 |
"%s: saving cert url %s", ad->md->name, ad->md->cert_url);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Check if it already was sent with this response */
|
|
Packit |
90a5c9 |
ad->next_up_link = NULL;
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = md_cert_read_http(&ad->cert, d->p, res))) {
|
|
Packit |
90a5c9 |
rv = md_cert_save(d->store, d->p, MD_SG_STAGING, ad->md->name, ad->cert, 0);
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "cert parsed and saved");
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv) {
|
|
Packit |
90a5c9 |
get_up_link(d, res->headers);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (APR_STATUS_IS_ENOENT(rv)) {
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p,
|
|
Packit |
90a5c9 |
"cert not in response, need to poll %s", ad->md->cert_url);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Pre-Req: all domains have been validated by the ACME server, e.g. all have AUTHZ
|
|
Packit |
90a5c9 |
* resources that have status 'valid'
|
|
Packit |
90a5c9 |
* - Setup private key, if not already there
|
|
Packit |
90a5c9 |
* - Generate a CSR with org, contact, etc
|
|
Packit |
90a5c9 |
* - Optionally enable must-staple OCSP extension
|
|
Packit |
90a5c9 |
* - Submit CSR, expect 201 with location
|
|
Packit |
90a5c9 |
* - POLL location for certificate
|
|
Packit |
90a5c9 |
* - store certificate
|
|
Packit |
90a5c9 |
* - retrieve cert chain information from cert
|
|
Packit |
90a5c9 |
* - GET cert chain
|
|
Packit |
90a5c9 |
* - store cert chain
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static apr_status_t ad_setup_certificate(md_proto_driver_t *d)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
md_pkey_t *privkey;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ad->phase = "setup cert privkey";
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = md_pkey_load(d->store, MD_SG_STAGING, ad->md->name, &privkey, d->p);
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_ENOENT(rv)) {
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = md_pkey_gen(&privkey, d->p, d->md->pkey_spec))) {
|
|
Packit |
90a5c9 |
rv = md_pkey_save(d->store, d->p, MD_SG_STAGING, ad->md->name, privkey, 1);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: generate privkey", ad->md->name);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv) {
|
|
Packit |
90a5c9 |
ad->phase = "setup csr";
|
|
Packit |
90a5c9 |
rv = md_cert_req_create(&ad->csr_der_64, ad->md, privkey, d->p);
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: create CSR", ad->md->name);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv) {
|
|
Packit |
90a5c9 |
ad->phase = "submit csr";
|
|
Packit |
90a5c9 |
rv = md_acme_POST(ad->acme, ad->acme->new_cert, on_init_csr_req, NULL, csr_req, d);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv) {
|
|
Packit |
90a5c9 |
if (!ad->cert) {
|
|
Packit |
90a5c9 |
rv = ad_cert_poll(d, 0);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**************************************************************************************************/
|
|
Packit |
90a5c9 |
/* cert chain retrieval */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t on_add_chain(md_acme_t *acme, const md_http_response_t *res, void *baton)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_proto_driver_t *d = baton;
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
md_cert_t *cert;
|
|
Packit |
90a5c9 |
const char *ct;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
(void)acme;
|
|
Packit |
90a5c9 |
ct = apr_table_get(res->headers, "Content-Type");
|
|
Packit |
90a5c9 |
if (ct && !strcmp("application/x-pkcs7-mime", ct)) {
|
|
Packit |
90a5c9 |
/* root cert most likely, end it here */
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = read_http_cert(&cert, d->p, res))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "chain cert parsed");
|
|
Packit |
90a5c9 |
APR_ARRAY_PUSH(ad->chain, md_cert_t *) = cert;
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv) {
|
|
Packit |
90a5c9 |
get_up_link(d, res->headers);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t get_chain(void *baton, int attempt)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_proto_driver_t *d = baton;
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
const char *prev_link = NULL;
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
while (APR_SUCCESS == rv && ad->chain->nelts < 10) {
|
|
Packit |
90a5c9 |
int nelts = ad->chain->nelts;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ad->next_up_link && (!prev_link || strcmp(prev_link, ad->next_up_link))) {
|
|
Packit |
90a5c9 |
prev_link = ad->next_up_link;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p,
|
|
Packit |
90a5c9 |
"next issuer is %s", ad->next_up_link);
|
|
Packit |
90a5c9 |
rv = md_acme_GET(ad->acme, ad->next_up_link, NULL, NULL, on_add_chain, d);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv && nelts == ad->chain->nelts) {
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, d->p,
|
|
Packit |
90a5c9 |
"got chain with %d certs (%d. attempt)", ad->chain->nelts, attempt);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t ad_chain_install(md_proto_driver_t *d)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* We should have that from initial cert retrieval, but if we restarted
|
|
Packit |
90a5c9 |
* or switched child process, we need to retrieve this again from the
|
|
Packit |
90a5c9 |
* certificate resources. */
|
|
Packit |
90a5c9 |
if (!ad->next_up_link) {
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = ad_cert_poll(d, 0))) {
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (!ad->next_up_link) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p,
|
|
Packit |
90a5c9 |
"server reports no link header 'up' for certificate at %s", ad->md->cert_url);
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, d->p,
|
|
Packit |
90a5c9 |
"chain starts at %s", ad->next_up_link);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ad->chain = apr_array_make(d->p, 5, sizeof(md_cert_t *));
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = md_util_try(get_chain, d, 0, ad->cert_poll_timeout, 0, 0, 0))) {
|
|
Packit |
90a5c9 |
rv = md_store_save(d->store, d->p, MD_SG_STAGING, ad->md->name, MD_FN_CHAIN,
|
|
Packit |
90a5c9 |
MD_SV_CHAIN, ad->chain, 0);
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "chain fetched and saved");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**************************************************************************************************/
|
|
Packit |
90a5c9 |
/* ACME driver init */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t acme_driver_init(md_proto_driver_t *d)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad;
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ad = apr_pcalloc(d->p, sizeof(*ad));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
d->baton = ad;
|
|
Packit |
90a5c9 |
ad->driver = d;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ad->authz_monitor_timeout = apr_time_from_sec(30);
|
|
Packit |
90a5c9 |
ad->cert_poll_timeout = apr_time_from_sec(30);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* We can only support challenges if the server is reachable from the outside
|
|
Packit |
90a5c9 |
* via port 80 and/or 443. These ports might be mapped for httpd to something
|
|
Packit |
90a5c9 |
* else, but a mapping needs to exist. */
|
|
Packit |
90a5c9 |
ad->ca_challenges = apr_array_make(d->p, 3, sizeof(const char *));
|
|
Packit |
90a5c9 |
if (d->challenge) {
|
|
Packit |
90a5c9 |
/* we have been told to use this type */
|
|
Packit |
90a5c9 |
APR_ARRAY_PUSH(ad->ca_challenges, const char*) = apr_pstrdup(d->p, d->challenge);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (d->md->ca_challenges && d->md->ca_challenges->nelts > 0) {
|
|
Packit |
90a5c9 |
/* pre-configured set for this managed domain */
|
|
Packit |
90a5c9 |
apr_array_cat(ad->ca_challenges, d->md->ca_challenges);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* free to chose. Add all we support and see what we get offered */
|
|
Packit |
90a5c9 |
APR_ARRAY_PUSH(ad->ca_challenges, const char*) = MD_AUTHZ_TYPE_HTTP01;
|
|
Packit |
90a5c9 |
APR_ARRAY_PUSH(ad->ca_challenges, const char*) = MD_AUTHZ_TYPE_TLSSNI01;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!d->can_http && !d->can_https) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, d->p, "%s: the server seems neither "
|
|
Packit |
90a5c9 |
"reachable via http (port 80) nor https (port 443). The ACME protocol "
|
|
Packit |
90a5c9 |
"needs at least one of those so the CA can talk to the server and verify "
|
|
Packit |
90a5c9 |
"a domain ownership.", d->md->name);
|
|
Packit |
90a5c9 |
return APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!d->can_http) {
|
|
Packit |
90a5c9 |
ad->ca_challenges = md_array_str_remove(d->p, ad->ca_challenges, MD_AUTHZ_TYPE_HTTP01, 0);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (!d->can_https) {
|
|
Packit |
90a5c9 |
ad->ca_challenges = md_array_str_remove(d->p, ad->ca_challenges, MD_AUTHZ_TYPE_TLSSNI01, 0);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (apr_is_empty_array(ad->ca_challenges)) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, d->p, "%s: specific CA challenge methods "
|
|
Packit |
90a5c9 |
"have been configured, but the server is unable to use any of those. "
|
|
Packit |
90a5c9 |
"For 'http-01' it needs to be reachable on port 80, for 'tls-sni-01'"
|
|
Packit |
90a5c9 |
" port 443 is needed.", d->md->name);
|
|
Packit |
90a5c9 |
return APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, d->p, "%s: init driver", d->md->name);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**************************************************************************************************/
|
|
Packit |
90a5c9 |
/* ACME staging */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t acme_stage(md_proto_driver_t *d)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
int reset_staging = d->reset;
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
int renew = 1;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (md_log_is_level(d->p, MD_LOG_DEBUG)) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, d->p, "%s: staging started, "
|
|
Packit |
90a5c9 |
"state=%d, can_http=%d, can_https=%d, challenges='%s'",
|
|
Packit |
90a5c9 |
d->md->name, d->md->state, d->can_http, d->can_https,
|
|
Packit |
90a5c9 |
apr_array_pstrcat(d->p, ad->ca_challenges, ' '));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!reset_staging) {
|
|
Packit |
90a5c9 |
rv = md_load(d->store, MD_SG_STAGING, d->md->name, &ad->md, d->p);
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv) {
|
|
Packit |
90a5c9 |
/* So, we have a copy in staging, but is it a recent or an old one? */
|
|
Packit |
90a5c9 |
if (md_is_newer(d->store, MD_SG_DOMAINS, MD_SG_STAGING, d->md->name, d->p)) {
|
|
Packit |
90a5c9 |
reset_staging = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (APR_STATUS_IS_ENOENT(rv)) {
|
|
Packit |
90a5c9 |
reset_staging = 1;
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, d->p,
|
|
Packit |
90a5c9 |
"%s: checked staging area, will%s reset",
|
|
Packit |
90a5c9 |
d->md->name, reset_staging? "" : " not");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (reset_staging) {
|
|
Packit |
90a5c9 |
/* reset the staging area for this domain */
|
|
Packit |
90a5c9 |
rv = md_store_purge(d->store, d->p, MD_SG_STAGING, d->md->name);
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != rv && !APR_STATUS_IS_ENOENT(rv)) {
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
ad->md = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ad->md && ad->md->state == MD_S_MISSING) {
|
|
Packit |
90a5c9 |
/* There is config information missing. It makes no sense to drive this MD further */
|
|
Packit |
90a5c9 |
rv = APR_INCOMPLETE;
|
|
Packit |
90a5c9 |
goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ad->md) {
|
|
Packit |
90a5c9 |
/* staging in progress. look for new ACME account information collected there */
|
|
Packit |
90a5c9 |
rv = md_reg_creds_get(&ad->ncreds, d->reg, MD_SG_STAGING, d->md, d->p);
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: checked creds", d->md->name);
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_ENOENT(rv)) {
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Find out where we're at with this managed domain */
|
|
Packit |
90a5c9 |
if (ad->ncreds && ad->ncreds->privkey && ad->ncreds->pubcert) {
|
|
Packit |
90a5c9 |
/* There is a full set staged, to be loaded */
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p, "%s: all data staged", d->md->name);
|
|
Packit |
90a5c9 |
renew = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (renew) {
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = md_acme_create(&ad->acme, d->p, d->md->ca_url, d->proxy_url))
|
|
Packit |
90a5c9 |
|| APR_SUCCESS != (rv = md_acme_setup(ad->acme))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p, "%s: setup ACME(%s)",
|
|
Packit |
90a5c9 |
d->md->name, d->md->ca_url);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!ad->md) {
|
|
Packit |
90a5c9 |
/* re-initialize staging */
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p, "%s: setup staging", d->md->name);
|
|
Packit |
90a5c9 |
md_store_purge(d->store, d->p, MD_SG_STAGING, d->md->name);
|
|
Packit |
90a5c9 |
ad->md = md_copy(d->p, d->md);
|
|
Packit |
90a5c9 |
ad->md->cert_url = NULL; /* do not retrieve the old cert */
|
|
Packit |
90a5c9 |
rv = md_save(d->store, d->p, MD_SG_STAGING, ad->md, 0);
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: save staged md",
|
|
Packit |
90a5c9 |
ad->md->name);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv && !ad->cert) {
|
|
Packit |
90a5c9 |
md_cert_load(d->store, MD_SG_STAGING, ad->md->name, &ad->cert, d->p);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv && !ad->cert) {
|
|
Packit |
90a5c9 |
ad->phase = "get certificate";
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p, "%s: need certificate", d->md->name);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Chose (or create) and ACME account to use */
|
|
Packit |
90a5c9 |
rv = ad_set_acct(d);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Check that the account agreed to the terms-of-service, otherwise
|
|
Packit |
90a5c9 |
* requests for new authorizations are denied. ToS may change during the
|
|
Packit |
90a5c9 |
* lifetime of an account */
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv) {
|
|
Packit |
90a5c9 |
const char *required;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ad->phase = "check agreement";
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p,
|
|
Packit |
90a5c9 |
"%s: check Terms-of-Service agreement", d->md->name);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = md_acme_check_agreement(ad->acme, d->p, ad->md->ca_agreement, &required);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_INCOMPLETE(rv) && required) {
|
|
Packit |
90a5c9 |
/* The CA wants the user to agree to Terms-of-Services. Until the user
|
|
Packit |
90a5c9 |
* has reconfigured and restarted the server, this MD cannot be
|
|
Packit |
90a5c9 |
* driven further */
|
|
Packit |
90a5c9 |
ad->md->state = MD_S_MISSING;
|
|
Packit |
90a5c9 |
md_save(d->store, d->p, MD_SG_STAGING, ad->md, 0);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p,
|
|
Packit |
90a5c9 |
"%s: the CA requires you to accept the terms-of-service "
|
|
Packit |
90a5c9 |
"as specified in <%s>. "
|
|
Packit |
90a5c9 |
"Please read the document that you find at that URL and, "
|
|
Packit |
90a5c9 |
"if you agree to the conditions, configure "
|
|
Packit |
90a5c9 |
"\"MDCertificateAgreement url\" "
|
|
Packit |
90a5c9 |
"with exactly that URL in your Apache. "
|
|
Packit |
90a5c9 |
"Then (graceful) restart the server to activate.",
|
|
Packit |
90a5c9 |
ad->md->name, required);
|
|
Packit |
90a5c9 |
goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If we know a cert's location, try to get it. Previous download might
|
|
Packit |
90a5c9 |
* have failed. If server 404 it, we clear our memory of it. */
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv && ad->md->cert_url) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p,
|
|
Packit |
90a5c9 |
"%s: polling certificate", d->md->name);
|
|
Packit |
90a5c9 |
rv = ad_cert_poll(d, 1);
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_ENOENT(rv)) {
|
|
Packit |
90a5c9 |
/* Server reports to know nothing about it. */
|
|
Packit |
90a5c9 |
ad->md->cert_url = NULL;
|
|
Packit |
90a5c9 |
rv = md_reg_update(d->reg, d->p, ad->md->name, ad->md, MD_UPD_CERT_URL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv && !ad->cert) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p,
|
|
Packit |
90a5c9 |
"%s: setup new authorization", d->md->name);
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = ad_setup_authz(d))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: setup authz resource",
|
|
Packit |
90a5c9 |
ad->md->name);
|
|
Packit |
90a5c9 |
goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p,
|
|
Packit |
90a5c9 |
"%s: setup new challenges", d->md->name);
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = ad_start_challenges(d))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: start challenges",
|
|
Packit |
90a5c9 |
ad->md->name);
|
|
Packit |
90a5c9 |
goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p,
|
|
Packit |
90a5c9 |
"%s: monitoring challenge status", d->md->name);
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = ad_monitor_challenges(d))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: monitor challenges",
|
|
Packit |
90a5c9 |
ad->md->name);
|
|
Packit |
90a5c9 |
goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p,
|
|
Packit |
90a5c9 |
"%s: creating certificate request", d->md->name);
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = ad_setup_certificate(d))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: setup certificate",
|
|
Packit |
90a5c9 |
ad->md->name);
|
|
Packit |
90a5c9 |
goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p,
|
|
Packit |
90a5c9 |
"%s: received certificate", d->md->name);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv && !ad->chain) {
|
|
Packit |
90a5c9 |
/* have we created this already? */
|
|
Packit |
90a5c9 |
md_chain_load(d->store, MD_SG_STAGING, ad->md->name, &ad->chain, d->p);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv && !ad->chain) {
|
|
Packit |
90a5c9 |
ad->phase = "install chain";
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p,
|
|
Packit |
90a5c9 |
"%s: retrieving certificate chain", d->md->name);
|
|
Packit |
90a5c9 |
rv = ad_chain_install(d);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv && !ad->pubcert) {
|
|
Packit |
90a5c9 |
/* have we created this already? */
|
|
Packit |
90a5c9 |
md_pubcert_load(d->store, MD_SG_STAGING, ad->md->name, &ad->pubcert, d->p);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv && !ad->pubcert) {
|
|
Packit |
90a5c9 |
/* combine cert + chain into the pubcert */
|
|
Packit |
90a5c9 |
ad->pubcert = apr_array_make(d->p, ad->chain->nelts + 1, sizeof(md_cert_t*));
|
|
Packit |
90a5c9 |
APR_ARRAY_PUSH(ad->pubcert, md_cert_t *) = ad->cert;
|
|
Packit |
90a5c9 |
apr_array_cat(ad->pubcert, ad->chain);
|
|
Packit |
90a5c9 |
rv = md_pubcert_save(d->store, d->p, MD_SG_STAGING, ad->md->name, ad->pubcert, 0);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv && ad->cert) {
|
|
Packit |
90a5c9 |
apr_time_t now = apr_time_now();
|
|
Packit |
90a5c9 |
apr_interval_time_t max_delay, delay_activation;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* determine when this cert should be activated */
|
|
Packit |
90a5c9 |
d->stage_valid_from = md_cert_get_not_before(ad->cert);
|
|
Packit |
90a5c9 |
if (d->md->state == MD_S_COMPLETE && d->md->expires > now) {
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* The MD is complete and un-expired. This is a renewal run.
|
|
Packit |
90a5c9 |
* Give activation 24 hours leeway (if we have that time) to
|
|
Packit |
90a5c9 |
* accommodate for clients with somewhat weird clocks.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
delay_activation = apr_time_from_sec(MD_SECS_PER_DAY);
|
|
Packit |
90a5c9 |
if (delay_activation > (max_delay = d->md->expires - now)) {
|
|
Packit |
90a5c9 |
delay_activation = max_delay;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
d->stage_valid_from += delay_activation;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
out:
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t acme_driver_stage(md_proto_driver_t *d)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ad->phase = "ACME staging";
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = acme_stage(d))) {
|
|
Packit |
90a5c9 |
ad->phase = "staging done";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: %s, %s",
|
|
Packit |
90a5c9 |
d->md->name, d->proto->protocol, ad->phase);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**************************************************************************************************/
|
|
Packit |
90a5c9 |
/* ACME preload */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t acme_preload(md_store_t *store, md_store_group_t load_group,
|
|
Packit |
90a5c9 |
const char *name, const char *proxy_url, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
md_pkey_t *privkey, *acct_key;
|
|
Packit |
90a5c9 |
md_t *md;
|
|
Packit |
90a5c9 |
apr_array_header_t *pubcert;
|
|
Packit |
90a5c9 |
struct md_acme_acct_t *acct;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "%s: preload start", name);
|
|
Packit |
90a5c9 |
/* Load all data which will be taken into the DOMAIN storage group.
|
|
Packit |
90a5c9 |
* This serves several purposes:
|
|
Packit |
90a5c9 |
* 1. It's a format check on the input data.
|
|
Packit |
90a5c9 |
* 2. We write back what we read, creating data with our own access permissions
|
|
Packit |
90a5c9 |
* 3. We ignore any other accumulated data in STAGING
|
|
Packit |
90a5c9 |
* 4. Once TMP is verified, we can swap/archive groups with a rename
|
|
Packit |
90a5c9 |
* 5. Reading/Writing the data will apply/remove any group specific data encryption.
|
|
Packit |
90a5c9 |
* With the exemption that DOMAINS and TMP must apply the same policy/keys.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = md_load(store, MD_SG_STAGING, name, &md, p))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading md json", name);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = md_pkey_load(store, MD_SG_STAGING, name, &privkey, p))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading staging private key", name);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = md_pubcert_load(store, MD_SG_STAGING, name, &pubcert, p))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading pubcert", name);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* See if staging holds a new or modified account data */
|
|
Packit |
90a5c9 |
rv = md_acme_acct_load(&acct, &acct_key, store, MD_SG_STAGING, name, p);
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_ENOENT(rv)) {
|
|
Packit |
90a5c9 |
acct = NULL;
|
|
Packit |
90a5c9 |
acct_key = NULL;
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (APR_SUCCESS != rv) {
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Remove any authz information we have here or in MD_SG_CHALLENGES */
|
|
Packit |
90a5c9 |
md_acme_authz_set_purge(store, MD_SG_STAGING, p, name);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p,
|
|
Packit |
90a5c9 |
"%s: staged data load, purging tmp space", name);
|
|
Packit |
90a5c9 |
rv = md_store_purge(store, p, load_group, name);
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != rv) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: error purging preload storage", name);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (acct) {
|
|
Packit |
90a5c9 |
md_acme_t *acme;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = md_acme_create(&acme, p, md->ca_url, proxy_url))
|
|
Packit |
90a5c9 |
|| APR_SUCCESS != (rv = md_acme_acct_save(store, p, acme, acct, acct_key))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: error saving acct", name);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
md->ca_account = acct->id;
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: saved ACME account %s",
|
|
Packit |
90a5c9 |
name, acct->id);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = md_save(store, p, load_group, md, 1))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving md json", name);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = md_pubcert_save(store, p, load_group, name, pubcert, 1))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving cert chain", name);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = md_pkey_save(store, p, load_group, name, privkey, 1))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving private key", name);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t acme_driver_preload(md_proto_driver_t *d, md_store_group_t group)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_acme_driver_t *ad = d->baton;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ad->phase = "ACME preload";
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = acme_preload(d->store, group, d->md->name, d->proxy_url, d->p))) {
|
|
Packit |
90a5c9 |
ad->phase = "preload done";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: %s, %s",
|
|
Packit |
90a5c9 |
d->md->name, d->proto->protocol, ad->phase);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static md_proto_t ACME_PROTO = {
|
|
Packit |
90a5c9 |
MD_PROTO_ACME, acme_driver_init, acme_driver_stage, acme_driver_preload
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_acme_protos_add(apr_hash_t *protos, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
(void)p;
|
|
Packit |
90a5c9 |
apr_hash_set(protos, MD_PROTO_ACME, sizeof(MD_PROTO_ACME)-1, &ACME_PROTO);
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|