Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Apache/modules/md/   (Apache Software Stiftung Version 2.4.65©)  Datei vom 16.4.2022 mit Größe 24 kB image not shown  

Quelle  md_acme_acct.c   Sprache: C

 
/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

 
#include <assert.h>
#include <stdio.h>

#include <apr_lib.h>
#include <apr_file_info.h>
#include <apr_file_io.h>
#include <apr_fnmatch.h>
#include <apr_hash.h>
#include <apr_strings.h>
#include <apr_tables.h>

#include "md.h"
#include "md_crypt.h"
#include "md_json.h"
#include "md_jws.h"
#include "md_log.h"
#include "md_result.h"
#include "md_store.h"
#include "md_util.h"
#include "md_version.h"

#include "md_acme.h"
#include "md_acme_acct.h"

static apr_status_t acct_make(md_acme_acct_t **pacct, apr_pool_t *p, 
                              const char *ca_url, apr_array_header_t *contacts)
{
    md_acme_acct_t *acct;
    
    acct = apr_pcalloc(p, sizeof(*acct));
    acct->ca_url = ca_url;
    if (!contacts || apr_is_empty_array(contacts)) {
        acct->contacts = apr_array_make(p, 5, sizeof(const char *));
    }
    else {
        acct->contacts = apr_array_copy(p, contacts);
    }
    
    *pacct = acct;
    return APR_SUCCESS;
}


static const char *mk_acct_id(apr_pool_t *p, md_acme_t *acme, int i)
{
    return apr_psprintf(p, "ACME-%s-%04d", acme->sname, i);
}

static const char *mk_acct_pattern(apr_pool_t *p, md_acme_t *acme)
{
    return apr_psprintf(p, "ACME-%s-*", acme->sname);
}
 
/**************************************************************************************************/
/* json load/save */

static md_acme_acct_st acct_st_from_str(const char *s) 
{
    if (s) {
        if (!strcmp("valid", s)) {
            return MD_ACME_ACCT_ST_VALID;
        }
        else if (!strcmp("deactivated", s)) {
            return MD_ACME_ACCT_ST_DEACTIVATED;
        }
        else if (!strcmp("revoked", s)) {
            return MD_ACME_ACCT_ST_REVOKED;
        }
    }
    return MD_ACME_ACCT_ST_UNKNOWN;
}

md_json_t *md_acme_acct_to_json(md_acme_acct_t *acct, apr_pool_t *p)
{
    md_json_t *jacct;
    const char *s;

    assert(acct);
    jacct = md_json_create(p);
    switch (acct->status) {
        case MD_ACME_ACCT_ST_VALID:
            s = "valid";
            break;
        case MD_ACME_ACCT_ST_DEACTIVATED:
            s = "deactivated";
            break;
        case MD_ACME_ACCT_ST_REVOKED:
            s = "revoked";
            break;
        default:
            s = NULL;
            break;
    }    
    if (s) md_json_sets(s, jacct, MD_KEY_STATUS, NULL);
    if (acct->url) md_json_sets(acct->url, jacct, MD_KEY_URL, NULL);
    if (acct->ca_url) md_json_sets(acct->ca_url, jacct, MD_KEY_CA_URL, NULL);
    if (acct->contacts) md_json_setsa(acct->contacts, jacct, MD_KEY_CONTACT, NULL);
    if (acct->registration) md_json_setj(acct->registration, jacct, MD_KEY_REGISTRATION, NULL);
    if (acct->agreement) md_json_sets(acct->agreement, jacct, MD_KEY_AGREEMENT, NULL);
    if (acct->orders) md_json_sets(acct->orders, jacct, MD_KEY_ORDERS, NULL);
    if (acct->eab_kid) md_json_sets(acct->eab_kid, jacct, MD_KEY_EAB, MD_KEY_KID, NULL);
    if (acct->eab_hmac) md_json_sets(acct->eab_hmac, jacct, MD_KEY_EAB, MD_KEY_HMAC, NULL);

    return jacct;
}

apr_status_t md_acme_acct_from_json(md_acme_acct_t **pacct, md_json_t *json, apr_pool_t *p)
{
    apr_status_t rv = APR_EINVAL;
    md_acme_acct_t *acct;
    md_acme_acct_st status = MD_ACME_ACCT_ST_UNKNOWN;
    const char *ca_url, *url;
    apr_array_header_t *contacts;
    
    if (md_json_has_key(json, MD_KEY_STATUS, NULL)) {
        status = acct_st_from_str(md_json_gets(json, MD_KEY_STATUS, NULL));
    }

    url = md_json_gets(json, MD_KEY_URL, NULL);
    if (!url) {
        md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "account has no url");
        goto leave;
    }

    ca_url = md_json_gets(json, MD_KEY_CA_URL, NULL);
    if (!ca_url) {
        md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "account has no CA url: %s", url);
        goto leave;
    }
    
    contacts = apr_array_make(p, 5, sizeof(const char *));
    if (md_json_has_key(json, MD_KEY_CONTACT, NULL)) {
        md_json_getsa(contacts, json, MD_KEY_CONTACT, NULL);
    }
    else {
        md_json_getsa(contacts, json, MD_KEY_REGISTRATION, MD_KEY_CONTACT, NULL);
    }
    rv = acct_make(&acct, p, ca_url, contacts);
    if (APR_SUCCESS != rv) goto leave;

    acct->status = status;
    acct->url = url;
    acct->agreement = md_json_gets(json, MD_KEY_AGREEMENT, NULL);
    if (!acct->agreement) {
        /* backward compatible check */
        acct->agreement = md_json_gets(json, "terms-of-service", NULL);
    }
    acct->orders = md_json_gets(json, MD_KEY_ORDERS, NULL);
    if (md_json_has_key(json, MD_KEY_EAB, MD_KEY_KID, NULL)
        && md_json_has_key(json, MD_KEY_EAB, MD_KEY_HMAC, NULL)) {
        acct->eab_kid = md_json_gets(json, MD_KEY_EAB, MD_KEY_KID, NULL);
        acct->eab_hmac = md_json_gets(json, MD_KEY_EAB, MD_KEY_HMAC, NULL);
    }

leave:
    *pacct = (APR_SUCCESS == rv)? acct : NULL;
    return rv;
}

apr_status_t md_acme_acct_save(md_store_t *store, apr_pool_t *p, md_acme_t *acme, 
                               const char **pid, md_acme_acct_t *acct, md_pkey_t *acct_key)
{
    md_json_t *jacct;
    apr_status_t rv;
    int i;
    const char *id = pid? *pid : NULL;
    
    jacct = md_acme_acct_to_json(acct, p);
    if (id) {
        rv = md_store_save(store, p, MD_SG_ACCOUNTS, id, MD_FN_ACCOUNT, MD_SV_JSON, jacct, 0);
    }
    else {
        rv = APR_EAGAIN;
        for (i = 0; i < 1000 && APR_SUCCESS != rv; ++i) {
            id = mk_acct_id(p, acme, i);
            rv = md_store_save(store, p, MD_SG_ACCOUNTS, id, MD_FN_ACCOUNT, MD_SV_JSON, jacct, 1);
        }
    }
    if (APR_SUCCESS == rv) {
        if (pid) *pid = id;
        rv = md_store_save(store, p, MD_SG_ACCOUNTS, id, MD_FN_ACCT_KEY, MD_SV_PKEY, acct_key, 0);
    }
    return rv;
}

apr_status_t md_acme_acct_load(md_acme_acct_t **pacct, md_pkey_t **ppkey,
                               md_store_t *store, md_store_group_t group, 
                               const char *name, apr_pool_t *p)
{
    md_json_t *json;
    apr_status_t rv;

    rv = md_store_load_json(store, group, name, MD_FN_ACCOUNT, &json, p);
    if (APR_STATUS_IS_ENOENT(rv)) {
        goto out;
    }
    if (APR_SUCCESS != rv) {
        md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "error reading account: %s", name);
        goto out;
    }
    
    rv = md_acme_acct_from_json(pacct, json, p);
    if (APR_SUCCESS == rv) {
        rv = md_store_load(store, group, name, MD_FN_ACCT_KEY, MD_SV_PKEY, (void**)ppkey, p);
        if (APR_SUCCESS != rv) {
            md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "loading key: %s", name);
            goto out;
        }
    }
out:
    if (APR_SUCCESS != rv) {
        *pacct = NULL;
        *ppkey = NULL;
    } 
    return rv;
}

/**************************************************************************************************/
/* Lookup */

int md_acme_acct_matches_url(md_acme_acct_t *acct, const char *url)
{
    /* The ACME url must match exactly */
    if (!url || !acct->ca_url || strcmp(acct->ca_url, url)) return 0;
    return 1;
}

int md_acme_acct_matches_md(md_acme_acct_t *acct, const md_t *md)
{
    if (!md_acme_acct_matches_url(acct, md->ca_effective)) return 0;
    /* if eab values are not mentioned, we match an account regardless
     * if it was registered with eab or not */

    if (!md->ca_eab_kid || !md->ca_eab_hmac) {
        /* No eab only acceptable when no eab is asked for.
         * This prevents someone that has no external account binding
         * to re-use an account from another MDomain that was created
         * with a binding. */

        return !acct->eab_kid || !acct->eab_hmac;
    }
    /* But of eab is asked for, we need an acct that matches exactly.
     * When someone configures a new EAB and we need
     * to created a new account for it. */

    if (!acct->eab_kid || !acct->eab_hmac) return 0;
    return !strcmp(acct->eab_kid, md->ca_eab_kid)
           && !strcmp(acct->eab_hmac, md->ca_eab_hmac);
}

typedef struct {
    apr_pool_t *p;
    const md_t *md;
    const char *id;
} find_ctx;

static int find_acct(void *baton, const char *name, const char *aspect,
                     md_store_vtype_t vtype, void *value, apr_pool_t *ptemp)
{
    find_ctx *ctx = baton;
    md_acme_acct_t *acct;
    apr_status_t rv;

    (void)aspect;
    (void)ptemp;
    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, ctx->p, "account candidate %s/%s", name, aspect); 
    if (MD_SV_JSON == vtype) {
        rv = md_acme_acct_from_json(&acct, (md_json_t*)value, ptemp);
        if (APR_SUCCESS != rv) goto cleanup;

        if (MD_ACME_ACCT_ST_VALID == acct->status
            && (!ctx->md || md_acme_acct_matches_md(acct, ctx->md))) {
            md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, ctx->p, 
                          "found account %s for %s: %s, status=%d",
                          acct->id, ctx->md->ca_effective, aspect, acct->status);
            ctx->id = apr_pstrdup(ctx->p, name);
            return 0;
        }
    }
cleanup:
    return 1;
}

static apr_status_t acct_find(const char **pid, md_acme_acct_t **pacct, md_pkey_t **ppkey, 
                              md_store_t *store, md_store_group_t group,
                              const char *name_pattern,
                              const md_t *md, apr_pool_t *p)
{
    apr_status_t rv;
    find_ctx ctx;

    memset(&ctx, 0, sizeof(ctx));
    ctx.p = p;
    ctx.md = md;

    rv = md_store_iter(find_acct, &ctx, store, p, group, name_pattern, MD_FN_ACCOUNT, MD_SV_JSON);
    if (ctx.id) {
        *pid = ctx.id;
        rv = md_acme_acct_load(pacct, ppkey, store, group, ctx.id, p);
        md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "acct_find: got account %s", ctx.id);
    }
    else {
        *pacct = NULL;
        rv = APR_ENOENT;
        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, p, "acct_find: none found"); 
    }
    return rv;
}

static apr_status_t acct_find_and_verify(md_store_t *store, md_store_group_t group, 
                                         const char *name_pattern,
                                         md_acme_t *acme, const md_t *md,
                                         apr_pool_t *p)
{
    md_acme_acct_t *acct;
    md_pkey_t *pkey;
    const char *id;
    apr_status_t rv;

    rv = acct_find(&id, &acct, &pkey, store, group, name_pattern, md, p);
    if (APR_SUCCESS == rv) {
        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, p, "acct_find_and_verify: found %s",
                      id);
        acme->acct_id = (MD_SG_STAGING == group)? NULL : id;
        acme->acct = acct;
        acme->acct_key = pkey;
        rv = md_acme_acct_validate(acme, (MD_SG_STAGING == group)? NULL : store, p);
        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, p, "acct_find_and_verify: verified %s",
                      id);

        if (APR_SUCCESS != rv) {
            acme->acct_id = NULL;
            acme->acct = NULL;
            acme->acct_key = NULL;
            if (APR_STATUS_IS_ENOENT(rv)) {
                /* verification failed and account has been disabled.
                   Indicate to caller that he may try again. */

                rv = APR_EAGAIN;
            }
        }
    }
    return rv;
}

apr_status_t md_acme_find_acct_for_md(md_acme_t *acme, md_store_t *store, const md_t *md)
{
    apr_status_t rv;
    
    while (APR_EAGAIN == (rv = acct_find_and_verify(store, MD_SG_ACCOUNTS, 
                                                    mk_acct_pattern(acme->p, acme), 
                                                    acme, md, acme->p))) {
        /* nop */
    }
    
    if (APR_STATUS_IS_ENOENT(rv)) {
        /* No suitable account found in MD_SG_ACCOUNTS. Maybe a new account
         * can already be found in MD_SG_STAGING? */

        md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, acme->p, 
                      "no account found, looking in STAGING");
        rv = acct_find_and_verify(store, MD_SG_STAGING, "*", acme, md, acme->p);
        if (APR_EAGAIN == rv) {
            rv = APR_ENOENT;
        }
    }
    return rv;
}

apr_status_t md_acme_acct_id_for_md(const char **pid, md_store_t *store,
                                    md_store_group_t group, const md_t *md,
                                    apr_pool_t *p)
{
    apr_status_t rv;
    find_ctx ctx;

    memset(&ctx, 0, sizeof(ctx));
    ctx.p = p;
    ctx.md = md;

    rv = md_store_iter(find_acct, &ctx, store, p, group, "*", MD_FN_ACCOUNT, MD_SV_JSON);
    if (ctx.id) {
        *pid = ctx.id;
        rv = APR_SUCCESS;
    }
    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "acct_id_for_md %s -> %s", md->name, *pid);
    return rv;
}

/**************************************************************************************************/
/* acct operation context */
typedef struct {
    md_acme_t *acme;
    apr_pool_t *p;
    const char *agreement;
    const char *eab_kid;
    const char *eab_hmac;
} acct_ctx_t;

/**************************************************************************************************/
/* acct update */

static apr_status_t on_init_acct_upd(md_acme_req_t *req, void *baton)
{
    (void)baton;
    return md_acme_req_body_init(req, NULL);


static apr_status_t acct_upd(md_acme_t *acme, apr_pool_t *p, 
                             const apr_table_t *hdrs, md_json_t *body, void *baton)
{
    acct_ctx_t *ctx = baton;
    apr_status_t rv = APR_SUCCESS;
    md_acme_acct_t *acct = acme->acct;

    if (md_log_is_level(p, MD_LOG_TRACE2)) {
        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, 0, acme->p, "acct update response: %s",
                      md_json_writep(body, p, MD_JSON_FMT_COMPACT));
    }

    if (!acct->url) {
        const char *location = apr_table_get(hdrs, "location");
        if (!location) {
            md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, APR_EINVAL, p, "new acct without location");
            return APR_EINVAL;
        }
        acct->url = apr_pstrdup(ctx->p, location);
    }
    
    apr_array_clear(acct->contacts);
    md_json_dupsa(acct->contacts, acme->p, body, MD_KEY_CONTACT, NULL);
    if (md_json_has_key(body, MD_KEY_STATUS, NULL)) {
        acct->status = acct_st_from_str(md_json_gets(body, MD_KEY_STATUS, NULL));
    }
    if (md_json_has_key(body, MD_KEY_AGREEMENT, NULL)) {
        acct->agreement = md_json_dups(acme->p, body, MD_KEY_AGREEMENT, NULL);
    }
    if (md_json_has_key(body, MD_KEY_ORDERS, NULL)) {
        acct->orders = md_json_dups(acme->p, body, MD_KEY_ORDERS, NULL);
    }
    if (ctx->eab_kid && ctx->eab_hmac) {
        acct->eab_kid = ctx->eab_kid;
        acct->eab_hmac = ctx->eab_hmac;
    }
    acct->registration = md_json_clone(ctx->p, body);
    
    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "updated acct %s", acct->url);
    return rv;
}

apr_status_t md_acme_acct_update(md_acme_t *acme)
{
    acct_ctx_t ctx;
    
    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, acme->p, "acct update");
    if (!acme->acct) {
        return APR_EINVAL;
    }
    memset(&ctx, 0, sizeof(ctx));
    ctx.acme = acme;
    ctx.p = acme->p;
    return md_acme_POST(acme, acme->acct->url, on_init_acct_upd, acct_upd, NULL, NULL, &ctx);
}

apr_status_t md_acme_acct_validate(md_acme_t *acme, md_store_t *store, apr_pool_t *p)
{
    apr_status_t rv;
    
    if (APR_SUCCESS != (rv = md_acme_acct_update(acme))) {
        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, acme->p,
                      "acct update failed for %s", acme->acct->url);
        if (APR_EINVAL == rv && (acme->acct->agreement || !acme->ca_agreement)) {
            /* Sadly, some proprietary ACME servers choke on empty POSTs
             * on accounts. Try a faked ToS agreement. */

            md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, acme->p,
                          "trying acct update via ToS agreement");
            rv = md_acme_agree(acme, p, "accepted");
        }
        if (acme->acct && (APR_ENOENT == rv || APR_EACCES == rv || APR_EINVAL == rv)) {
            if (MD_ACME_ACCT_ST_VALID == acme->acct->status) {
                acme->acct->status = MD_ACME_ACCT_ST_UNKNOWN;
                if (store) {
                    md_acme_acct_save(store, p, acme, &acme->acct_id, acme->acct, acme->acct_key); 
                }
            }
            acme->acct = NULL;
            acme->acct_key = NULL;
            rv = APR_ENOENT;
        }
    }
    return rv;
}

/**************************************************************************************************/
/* Register a new account */

static apr_status_t get_eab(md_json_t **peab, md_acme_req_t *req, const char *kid,
                            const char *hmac64, md_pkey_t *account_key,
                            const char *url)
{
    md_json_t *eab, *prot_fields, *jwk;
    md_data_t payload, hmac_key;
    apr_status_t rv;

    prot_fields = md_json_create(req->p);
    md_json_sets(url, prot_fields, "url", NULL);
    md_json_sets(kid, prot_fields, "kid", NULL);

    rv = md_jws_get_jwk(&jwk, req->p, account_key);
    if (APR_SUCCESS != rv) goto cleanup;

    md_data_null(&payload);
    payload.data = md_json_writep(jwk, req->p, MD_JSON_FMT_COMPACT);
    if (!payload.data) {
        rv = APR_EINVAL;
        goto cleanup;
    }
    payload.len = strlen(payload.data);

    md_util_base64url_decode(&hmac_key, hmac64, req->p);
    if (!hmac_key.len) {
        rv = APR_EINVAL;
        md_result_problem_set(req->result, rv, "apache:eab-hmac-invalid",
                              "external account binding HMAC value is not valid base64", NULL);
        goto cleanup;
    }

    rv = md_jws_hmac(&eab, req->p, &payload, prot_fields, &hmac_key);
    if (APR_SUCCESS != rv) {
        md_result_problem_set(req->result, rv, "apache:eab-hmac-fail",
                              "external account binding MAC could not be computed", NULL);
    }

cleanup:
    *peab = (APR_SUCCESS == rv)? eab : NULL;
    return rv;
}

static apr_status_t on_init_acct_new(md_acme_req_t *req, void *baton)
{
    acct_ctx_t *ctx = baton;
    md_json_t *jpayload, *jeab;
    apr_status_t rv;

    jpayload = md_json_create(req->p);
    md_json_setsa(ctx->acme->acct->contacts, jpayload, MD_KEY_CONTACT, NULL);
    if (ctx->agreement) {
        md_json_setb(1, jpayload, "termsOfServiceAgreed", NULL);
    }
    if (ctx->eab_kid && ctx->eab_hmac) {
        rv = get_eab(&jeab, req, ctx->eab_kid, ctx->eab_hmac,
                     req->acme->acct_key, req->url);
        if (APR_SUCCESS != rv) goto cleanup;
        md_json_setj(jeab, jpayload, "externalAccountBinding", NULL);
    }
    rv = md_acme_req_body_init(req, jpayload);

cleanup:
    return rv;


apr_status_t md_acme_acct_register(md_acme_t *acme, md_store_t *store,
                                   const md_t *md, apr_pool_t *p)
{
    apr_status_t rv;
    md_pkey_t *pkey;
    const char *err = NULL, *uri;
    md_pkey_spec_t spec;
    int i;
    acct_ctx_t ctx;
    
    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "create new account");

    memset(&ctx, 0, sizeof(ctx));
    ctx.acme = acme;
    ctx.p = p;
    /* The agreement URL is submitted when the ACME server announces Terms-of-Service
     * in its directory meta data. The magic value "accepted" will always use the
     * advertised URL. */

    ctx.agreement = NULL;
    if (acme->ca_agreement && md->ca_agreement) {
        ctx.agreement = !strcmp("accepted", md->ca_agreement)?
            acme->ca_agreement : md->ca_agreement;
    }
    
    if (ctx.agreement) {
        if (APR_SUCCESS != (rv = md_util_abs_uri_check(acme->p, ctx.agreement, &err))) {
            md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, 
                          "invalid agreement uri (%s): %s", err, ctx.agreement);
            goto out;
        }
    }
    ctx.eab_kid = md->ca_eab_kid;
    ctx.eab_hmac = md->ca_eab_hmac;
    
    for (i = 0; i < md->contacts->nelts; ++i) {
        uri = APR_ARRAY_IDX(md->contacts, i, const char *);
        if (APR_SUCCESS != (rv = md_util_abs_uri_check(acme->p, uri, &err))) {
            md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, 
                          "invalid contact uri (%s): %s", err, uri);
            goto out;
        }
    }
    
    /* If there is no key selected yet, try to find an existing one for the same host. 
     * Let's Encrypt identifies accounts by their key for their ACMEv1 and v2 services.
     * Although the account appears on both services with different urls, it is 
     * internally the same one.
     * I think this is beneficial if someone migrates from ACMEv1 to v2 and not a leak
     * of identifying information.
     */

    if (!acme->acct_key) {
        find_ctx fctx;

        memset(&fctx, 0, sizeof(fctx));
        fctx.p = p;
        fctx.md = md;

        md_store_iter(find_acct, &fctx, store, p, MD_SG_ACCOUNTS, 
                      mk_acct_pattern(p, acme), MD_FN_ACCOUNT, MD_SV_JSON);
        if (fctx.id) {
            rv = md_store_load(store, MD_SG_ACCOUNTS, fctx.id, MD_FN_ACCT_KEY, MD_SV_PKEY,
                               (void**)&acme->acct_key, p);
            if (APR_SUCCESS == rv) {
                md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p,
                              "reusing key from account %s", fctx.id);
            }
            else {
                acme->acct_key = NULL;
            }
        }
    }
    
    /* If we still have no key, generate a new one */
    if (!acme->acct_key) {
        spec.type = MD_PKEY_TYPE_RSA;
        spec.params.rsa.bits = MD_ACME_ACCT_PKEY_BITS;
        
        if (APR_SUCCESS != (rv = md_pkey_gen(&pkey, acme->p, &spec))) goto out;
        acme->acct_key = pkey;
        md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "created new account key");
    }
    
    if (APR_SUCCESS != (rv = acct_make(&acme->acct,  p, acme->url, md->contacts))) goto out;
    rv = md_acme_POST_new_account(acme,  on_init_acct_new, acct_upd, NULL, NULL, &ctx);
    if (APR_SUCCESS == rv) {
        md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, p, 
                      "registered new account %s", acme->acct->url);
    }

out:    
    if (APR_SUCCESS != rv && acme->acct) {
        acme->acct = NULL;
    }
    return rv;
}

/**************************************************************************************************/
/* Deactivate the account */

static apr_status_t on_init_acct_del(md_acme_req_t *req, void *baton)
{
    md_json_t *jpayload;

    (void)baton;
    jpayload = md_json_create(req->p);
    md_json_sets("deactivated", jpayload, MD_KEY_STATUS, NULL);
    return md_acme_req_body_init(req, jpayload);


apr_status_t md_acme_acct_deactivate(md_acme_t *acme, apr_pool_t *p)
{
    md_acme_acct_t *acct = acme->acct;
    acct_ctx_t ctx;
    
    (void)p;
    if (!acct) {
        return APR_EINVAL;
    }
    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, acme->p, "delete account %s from %s"
                  acct->url, acct->ca_url);
    memset(&ctx, 0, sizeof(ctx));
    ctx.acme = acme;
    ctx.p = p;
    return md_acme_POST(acme, acct->url, on_init_acct_del, acct_upd, NULL, NULL, &ctx);
}

/**************************************************************************************************/
/* terms-of-service */

static apr_status_t on_init_agree_tos(md_acme_req_t *req, void *baton)
{
    acct_ctx_t *ctx = baton;
    md_json_t *jpayload;

    jpayload = md_json_create(req->p);
    if (ctx->acme->acct->agreement) {
        md_json_setb(1, jpayload, "termsOfServiceAgreed", NULL);
    }
    return md_acme_req_body_init(req, jpayload);


apr_status_t md_acme_agree(md_acme_t *acme, apr_pool_t *p, const char *agreement)
{
    acct_ctx_t ctx;
    
    acme->acct->agreement = agreement;
    if (!strcmp("accepted", agreement) && acme->ca_agreement) {
        acme->acct->agreement = acme->ca_agreement;
    }
    
    memset(&ctx, 0, sizeof(ctx));
    ctx.acme = acme;
    ctx.p = p;
    return md_acme_POST(acme, acme->acct->url, on_init_agree_tos, acct_upd, NULL, NULL, &ctx);
}

apr_status_t md_acme_check_agreement(md_acme_t *acme, apr_pool_t *p, 
                                     const char *agreement, const char **prequired)
{
    apr_status_t rv = APR_SUCCESS;
    
    /* We used to really check if the account agreement and the one indicated in meta
     * are the very same. However, LE is happy if the account has agreed to a ToS in 
     * the past and does not require a renewed acceptance.
     */

    *prequired = NULL;
    if (!acme->acct->agreement && acme->ca_agreement) {
        if (agreement) {
            rv = md_acme_agree(acme, p, acme->ca_agreement);
        }
        else {
            *prequired = acme->ca_agreement;
            rv = APR_INCOMPLETE;
        }
    }
    return rv;
}        

Messung V0.5
C=96 H=94 G=94

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.