httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ic...@apache.org
Subject svn commit: r1804123 [3/6] - in /httpd/httpd/branches/trunk-md: ./ modules/http2/ modules/md/
Date Fri, 04 Aug 2017 13:47:26 GMT
Added: httpd/httpd/branches/trunk-md/modules/md/md_core.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/trunk-md/modules/md/md_core.c?rev=1804123&view=auto
==============================================================================
--- httpd/httpd/branches/trunk-md/modules/md/md_core.c (added)
+++ httpd/httpd/branches/trunk-md/modules/md/md_core.c Fri Aug  4 13:47:25 2017
@@ -0,0 +1,321 @@
+/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed 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 <stdlib.h>
+
+#include <apr_lib.h>
+#include <apr_strings.h>
+#include <apr_tables.h>
+#include <apr_time.h>
+#include <apr_date.h>
+
+#include "md_json.h"
+#include "md.h"
+#include "md_log.h"
+#include "md_store.h"
+#include "md_util.h"
+
+
+int md_contains(const md_t *md, const char *domain)
+{
+   return md_array_str_index(md->domains, domain, 0, 0) >= 0;
+}
+
+const char *md_common_name(const md_t *md1, const md_t *md2)
+{
+    int i;
+    
+    if (md1 == NULL || md1->domains == NULL
+        || md2 == NULL || md2->domains == NULL) {
+        return NULL;
+    }
+    
+    for (i = 0; i < md1->domains->nelts; ++i) {
+        const char *name1 = APR_ARRAY_IDX(md1->domains, i, const char*);
+        if (md_contains(md2, name1)) {
+            return name1;
+        }
+    }
+    return NULL;
+}
+
+int md_domains_overlap(const md_t *md1, const md_t *md2)
+{
+    return md_common_name(md1, md2) != NULL;
+}
+
+apr_size_t md_common_name_count(const md_t *md1, const md_t *md2)
+{
+    int i;
+    apr_size_t hits;
+    
+    if (md1 == NULL || md1->domains == NULL
+        || md2 == NULL || md2->domains == NULL) {
+        return 0;
+    }
+    
+    hits = 0;
+    for (i = 0; i < md1->domains->nelts; ++i) {
+        const char *name1 = APR_ARRAY_IDX(md1->domains, i, const char*);
+        if (md_contains(md2, name1)) {
+            ++hits;
+        }
+    }
+    return hits;
+}
+
+md_t *md_create_empty(apr_pool_t *p)
+{
+    md_t *md = apr_pcalloc(p, sizeof(*md));
+    if (md) {
+        md->domains = apr_array_make(p, 5, sizeof(const char *));
+        md->contacts = apr_array_make(p, 5, sizeof(const char *));
+        md->drive_mode = MD_DRIVE_DEFAULT;
+        md->defn_name = "unknown";
+        md->defn_line_number = 0;
+    }
+    return md;
+}
+
+int md_equal_domains(const md_t *md1, const md_t *md2)
+{
+    int i;
+    if (md1->domains->nelts == md2->domains->nelts) {
+        for (i = 0; i < md1->domains->nelts; ++i) {
+            const char *name1 = APR_ARRAY_IDX(md1->domains, i, const char*);
+            if (!md_contains(md2, name1)) {
+                return 0;
+            }
+        }
+        return 1;
+    }
+    return 0;
+}
+
+int md_contains_domains(const md_t *md1, const md_t *md2)
+{
+    int i;
+    if (md1->domains->nelts >= md2->domains->nelts) {
+        for (i = 0; i < md2->domains->nelts; ++i) {
+            const char *name2 = APR_ARRAY_IDX(md2->domains, i, const char*);
+            if (!md_contains(md1, name2)) {
+                return 0;
+            }
+        }
+        return 1;
+    }
+    return 0;
+}
+
+md_t *md_find_closest_match(apr_array_header_t *mds, const md_t *md)
+{
+    md_t *candidate, *m;
+    apr_size_t cand_n, n;
+    int i;
+    
+    candidate = md_get_by_name(mds, md->name);
+    if (!candidate) {
+        /* try to find an instance that contains all domain names from md */ 
+        for (i = 0; i < mds->nelts; ++i) {
+            m = APR_ARRAY_IDX(mds, i, md_t *);
+            if (md_contains_domains(m, md)) {
+                return m;
+            }
+        }
+        /* no matching name and no md in the list has all domains.
+         * We consider that managed domain as closest match that contains at least one
+         * domain name from md, ONLY if there is no other one that also has.
+         */
+        cand_n = 0;
+        for (i = 0; i < mds->nelts; ++i) {
+            m = APR_ARRAY_IDX(mds, i, md_t *);
+            n = md_common_name_count(md, m);
+            if (n > cand_n) {
+                candidate = m;
+                cand_n = n;
+            }
+        }
+    }
+    return candidate;
+}
+
+md_t *md_get_by_name(struct apr_array_header_t *mds, const char *name)
+{
+    int i;
+    for (i = 0; i < mds->nelts; ++i) {
+        md_t *md = APR_ARRAY_IDX(mds, i, md_t *);
+        if (!strcmp(name, md->name)) {
+            return md;
+        }
+    }
+    return NULL;
+}
+
+md_t *md_get_by_domain(struct apr_array_header_t *mds, const char *domain)
+{
+    int i;
+    for (i = 0; i < mds->nelts; ++i) {
+        md_t *md = APR_ARRAY_IDX(mds, i, md_t *);
+        if (md_contains(md, domain)) {
+            return md;
+        }
+    }
+    return NULL;
+}
+
+md_t *md_get_by_dns_overlap(struct apr_array_header_t *mds, const md_t *md)
+{
+    int i;
+    for (i = 0; i < mds->nelts; ++i) {
+        md_t *o = APR_ARRAY_IDX(mds, i, md_t *);
+        if (strcmp(o->name, md->name) && md_common_name(o, md)) {
+            return o;
+        }
+    }
+    return NULL;
+}
+
+const char *md_create(md_t **pmd, apr_pool_t *p, apr_array_header_t *domains)
+{
+    md_t *md;
+    
+    if (domains->nelts <= 0) {
+        return "needs at least one domain name";
+    }
+    
+    md = md_create_empty(p);
+    if (!md) {
+        return "not enough memory";
+    }
+
+    md->domains = md_array_str_compact(p, domains, 0);
+    md->name = APR_ARRAY_IDX(md->domains, 0, const char *);
+    
+    *pmd = md;
+    return NULL;   
+}
+
+/**************************************************************************************************/
+/* lifetime */
+
+md_t *md_copy(apr_pool_t *p, const md_t *src)
+{
+    md_t *md;
+    
+    md = apr_pcalloc(p, sizeof(*md));
+    if (md) {
+        memcpy(md, src, sizeof(*md));
+        md->domains = apr_array_copy(p, src->domains);
+        md->contacts = apr_array_copy(p, src->contacts);
+        if (src->ca_challenges) {
+            md->ca_challenges = apr_array_copy(p, src->ca_challenges);
+        }
+    }    
+    return md;   
+}
+
+md_t *md_clone(apr_pool_t *p, const md_t *src)
+{
+    md_t *md;
+    
+    md = apr_pcalloc(p, sizeof(*md));
+    if (md) {
+        md->state = src->state;
+        md->name = apr_pstrdup(p, src->name);
+        md->drive_mode = src->drive_mode;
+        md->domains = md_array_str_compact(p, src->domains, 0);
+        md->renew_window = src->renew_window;
+        md->contacts = md_array_str_clone(p, src->contacts);
+        if (src->ca_url) md->ca_url = apr_pstrdup(p, src->ca_url);
+        if (src->ca_proto) md->ca_proto = apr_pstrdup(p, src->ca_proto);
+        if (src->ca_account) md->ca_account = apr_pstrdup(p, src->ca_account);
+        if (src->ca_agreement) md->ca_agreement = apr_pstrdup(p, src->ca_agreement);
+        if (src->defn_name) md->defn_name = apr_pstrdup(p, src->defn_name);
+        if (src->cert_url) md->cert_url = apr_pstrdup(p, src->cert_url);
+        md->defn_line_number = src->defn_line_number;
+        if (src->ca_challenges) {
+            md->ca_challenges = md_array_str_clone(p, src->ca_challenges);
+        }
+    }    
+    return md;   
+}
+
+/**************************************************************************************************/
+/* format conversion */
+
+md_json_t *md_to_json(const md_t *md, apr_pool_t *p)
+{
+    md_json_t *json = md_json_create(p);
+    if (json) {
+        apr_array_header_t *domains = md_array_str_compact(p, md->domains, 0);
+        md_json_sets(md->name, json, MD_KEY_NAME, NULL);
+        md_json_setsa(domains, json, MD_KEY_DOMAINS, NULL);
+        md_json_setsa(md->contacts, json, MD_KEY_CONTACTS, NULL);
+        md_json_sets(md->ca_account, json, MD_KEY_CA, MD_KEY_ACCOUNT, NULL);
+        md_json_sets(md->ca_proto, json, MD_KEY_CA, MD_KEY_PROTO, NULL);
+        md_json_sets(md->ca_url, json, MD_KEY_CA, MD_KEY_URL, NULL);
+        md_json_sets(md->ca_agreement, json, MD_KEY_CA, MD_KEY_AGREEMENT, NULL);
+        if (md->cert_url) {
+            md_json_sets(md->cert_url, json, MD_KEY_CERT, MD_KEY_URL, NULL);
+        }
+        md_json_setl(md->state, json, MD_KEY_STATE, NULL);
+        md_json_setl(md->drive_mode, json, MD_KEY_DRIVE_MODE, NULL);
+        if (md->expires > 0) {
+            char *ts = apr_pcalloc(p, APR_RFC822_DATE_LEN);
+            apr_rfc822_date(ts, md->expires);
+            md_json_sets(ts, json, MD_KEY_CERT, MD_KEY_EXPIRES, NULL);
+        }
+        md_json_setl(apr_time_sec(md->renew_window), json, MD_KEY_RENEW_WINDOW, NULL);
+        if (md->ca_challenges && md->ca_challenges->nelts > 0) {
+            apr_array_header_t *na;
+            na = md_array_str_compact(p, md->ca_challenges, 0);
+            md_json_setsa(na, json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL);
+        }
+        return json;
+    }
+    return NULL;
+}
+
+md_t *md_from_json(md_json_t *json, apr_pool_t *p)
+{
+    const char *s;
+    md_t *md = md_create_empty(p);
+    if (md) {
+        md->name = md_json_dups(p, json, MD_KEY_NAME, NULL);            
+        md_json_dupsa(md->domains, p, json, MD_KEY_DOMAINS, NULL);
+        md_json_dupsa(md->contacts, p, json, MD_KEY_CONTACTS, NULL);
+        md->ca_account = md_json_dups(p, json, MD_KEY_CA, MD_KEY_ACCOUNT, NULL);
+        md->ca_proto = md_json_dups(p, json, MD_KEY_CA, MD_KEY_PROTO, NULL);
+        md->ca_url = md_json_dups(p, json, MD_KEY_CA, MD_KEY_URL, NULL);
+        md->ca_agreement = md_json_dups(p, json, MD_KEY_CA, MD_KEY_AGREEMENT, NULL);
+        md->cert_url = md_json_dups(p, json, MD_KEY_CERT, MD_KEY_URL, NULL);
+        md->state = (int)md_json_getl(json, MD_KEY_STATE, NULL);
+        md->drive_mode = (int)md_json_getl(json, MD_KEY_DRIVE_MODE, NULL);
+        md->domains = md_array_str_compact(p, md->domains, 0);
+        s = md_json_dups(p, json, MD_KEY_CERT, MD_KEY_EXPIRES, NULL);
+        if (s && *s) {
+            md->expires = apr_date_parse_rfc(s);
+        }
+        md->renew_window = apr_time_from_sec(md_json_getl(json, MD_KEY_RENEW_WINDOW, NULL));
+        if (md_json_has_key(json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL)) {
+            md->ca_challenges = apr_array_make(p, 5, sizeof(const char*));
+            md_json_dupsa(md->ca_challenges, p, json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL);
+        }
+        return md;
+    }
+    return NULL;
+}
+

Added: httpd/httpd/branches/trunk-md/modules/md/md_crypt.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/trunk-md/modules/md/md_crypt.c?rev=1804123&view=auto
==============================================================================
--- httpd/httpd/branches/trunk-md/modules/md/md_crypt.c (added)
+++ httpd/httpd/branches/trunk-md/modules/md/md_crypt.c Fri Aug  4 13:47:25 2017
@@ -0,0 +1,1128 @@
+/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed 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 <stdlib.h>
+
+#include <apr_lib.h>
+#include <apr_buckets.h>
+#include <apr_file_io.h>
+#include <apr_strings.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+#include <openssl/rsa.h>
+#include <openssl/x509v3.h>
+
+#include "md.h"
+#include "md_crypt.h"
+#include "md_log.h"
+#include "md_http.h"
+#include "md_util.h"
+
+/* getpid for *NIX */
+#if APR_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* getpid for Windows */
+#if APR_HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+static int initialized;
+
+struct md_pkey_t {
+    apr_pool_t *pool;
+    EVP_PKEY   *pkey;
+};
+
+#ifdef MD_HAVE_ARC4RANDOM
+
+static void seed_RAND(int pid)
+{
+    char seed[128];
+    arc4random_buf(seed, sizeof(seed));
+    RAND_seed(seed, sizeof(seed));
+}
+
+#else /* ifdef MD_HAVE_ARC4RANDOM */
+
+static int rand_choosenum(int l, int h)
+{
+    int i;
+    char buf[50];
+
+    apr_snprintf(buf, sizeof(buf), "%.0f",
+                 (((double)(rand()%RAND_MAX)/RAND_MAX)*(h-l)));
+    i = atoi(buf)+1;
+    if (i < l) i = l;
+    if (i > h) i = h;
+    return i;
+}
+
+static void seed_RAND(int pid)
+{   
+    unsigned char stackdata[256];
+    /* stolen from mod_ssl/ssl_engine_rand.c */
+    apr_size_t n, l;
+    struct {
+        time_t t;
+        pid_t pid;
+    } my_seed;
+    
+    /*
+     * seed in the current time (usually just 4 bytes)
+     */
+    my_seed.t = time(NULL);
+    
+    /*
+     * seed in the current process id (usually just 4 bytes)
+     */
+    my_seed.pid = pid;
+    
+    l = sizeof(my_seed);
+    RAND_seed((unsigned char *)&my_seed, l);
+    
+    /*
+     * seed in some current state of the run-time stack (128 bytes)
+     */
+#if HAVE_VALGRIND && 0
+    if (ssl_running_on_valgrind) {
+        VALGRIND_MAKE_MEM_DEFINED(stackdata, sizeof(stackdata));
+    }
+#endif
+    n = rand_choosenum(0, sizeof(stackdata)-128-1);
+    RAND_seed(stackdata+n, 128);
+}
+
+#endif /*ifdef MD_HAVE_ARC4RANDOM (else part) */
+
+
+apr_status_t md_crypt_init(apr_pool_t *pool)
+{
+    (void)pool;
+    
+    if (!initialized) {
+        int pid = getpid();
+        
+        ERR_load_crypto_strings();
+        OpenSSL_add_all_algorithms();
+        
+        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, 0, pool, "initializing RAND"); 
+        while (!RAND_status()) {
+            seed_RAND(pid);
+	}
+
+        initialized = 1;
+    }
+    return APR_SUCCESS;
+}
+
+typedef struct {
+    char *data;
+    apr_size_t len;
+} buffer;
+
+static apr_status_t fwrite_buffer(void *baton, apr_file_t *f, apr_pool_t *p) 
+{
+    buffer *buf = baton;
+    return apr_file_write_full(f, buf->data, buf->len, &buf->len);
+}
+
+apr_status_t md_rand_bytes(unsigned char *buf, apr_size_t len, apr_pool_t *p)
+{
+    apr_status_t rv;
+    
+    if (len > INT_MAX) {
+        return APR_ENOTIMPL;
+    }
+    if (APR_SUCCESS == (rv = md_crypt_init(p))) {
+        RAND_bytes((unsigned char*)buf, (int)len);
+    }
+    return rv;
+}
+
+typedef struct {
+    const char *pass_phrase;
+    int pass_len;
+} passwd_ctx;
+
+static int pem_passwd(char *buf, int size, int rwflag, void *baton)
+{
+    passwd_ctx *ctx = baton;
+    if (ctx->pass_len > 0) {
+        if (ctx->pass_len < size) {
+            size = (int)ctx->pass_len;
+        }
+        memcpy(buf, ctx->pass_phrase, size);
+    }
+    return ctx->pass_len;
+}
+
+/**************************************************************************************************/
+/* private keys */
+
+static md_pkey_t *make_pkey(apr_pool_t *p) 
+{
+    md_pkey_t *pkey = apr_pcalloc(p, sizeof(*pkey));
+    pkey->pool = p;
+    return pkey;
+}
+
+static apr_status_t pkey_cleanup(void *data)
+{
+    md_pkey_t *pkey = data;
+    if (pkey->pkey) {
+        EVP_PKEY_free(pkey->pkey);
+        pkey->pkey = NULL;
+    }
+    return APR_SUCCESS;
+}
+
+void md_pkey_free(md_pkey_t *pkey)
+{
+    pkey_cleanup(pkey);
+}
+
+void *md_pkey_get_EVP_PKEY(struct md_pkey_t *pkey)
+{
+    return pkey->pkey;
+}
+
+apr_status_t md_pkey_fload(md_pkey_t **ppkey, apr_pool_t *p, 
+                           const char *key, apr_size_t key_len,
+                           const char *fname)
+{
+    apr_status_t rv = APR_ENOENT;
+    md_pkey_t *pkey;
+    BIO *bf;
+    passwd_ctx ctx;
+    
+    pkey =  make_pkey(p);
+    if (NULL != (bf = BIO_new_file(fname, "r"))) {
+        ctx.pass_phrase = key;
+        ctx.pass_len = (int)key_len;
+        
+        ERR_clear_error();
+        pkey->pkey = PEM_read_bio_PrivateKey(bf, NULL, pem_passwd, &ctx);
+        BIO_free(bf);
+        
+        if (pkey->pkey != NULL) {
+            rv = APR_SUCCESS;
+            apr_pool_cleanup_register(p, pkey, pkey_cleanup, apr_pool_cleanup_null);
+        }
+        else {
+            long err = ERR_get_error();
+            rv = APR_EINVAL;
+            md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p, 
+                          "error loading pkey %s: %s (pass phrase was %snull)", fname,
+                          ERR_error_string(err, NULL), key? "not " : ""); 
+        }
+    }
+    *ppkey = (APR_SUCCESS == rv)? pkey : NULL;
+    return rv;
+}
+
+static apr_status_t pkey_to_buffer(buffer *buffer, md_pkey_t *pkey, apr_pool_t *p,
+                                   const char *pass, apr_size_t pass_len)
+{
+    BIO *bio = BIO_new(BIO_s_mem());
+    const EVP_CIPHER *cipher = NULL;
+    pem_password_cb *cb = NULL;
+    void *cb_baton = NULL;
+    passwd_ctx ctx;
+    unsigned long err;
+    
+    if (!bio) {
+        return APR_ENOMEM;
+    }
+    if (pass_len > INT_MAX) {
+        return APR_EINVAL;
+    }
+    if (pass && pass_len > 0) {
+        ctx.pass_phrase = pass;
+        ctx.pass_len = (int)pass_len;
+        cb = pem_passwd;
+        cb_baton = &ctx;
+        cipher = EVP_aes_256_cbc();
+        if (!cipher) {
+            return APR_ENOTIMPL;
+        }
+    }
+    
+    ERR_clear_error();
+    if (!PEM_write_bio_PrivateKey(bio, pkey->pkey, cipher, NULL, 0, cb, cb_baton)) {
+        BIO_free(bio);
+        err = ERR_get_error();
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "PEM_write key: %ld %s", 
+                      err, ERR_error_string(err, NULL)); 
+        return APR_EINVAL;
+    }
+
+    buffer->len = BIO_pending(bio);
+    if (buffer->len > 0) {
+        buffer->data = apr_palloc(p, buffer->len+1);
+        buffer->len = BIO_read(bio, buffer->data, (int)buffer->len);
+        buffer->data[buffer->len] = '\0';
+    }
+    BIO_free(bio);
+    return APR_SUCCESS;
+}
+
+apr_status_t md_pkey_fsave(md_pkey_t *pkey, apr_pool_t *p, 
+                           const char *pass_phrase, apr_size_t pass_len,
+                           const char *fname, apr_fileperms_t perms)
+{
+    buffer buffer;
+    apr_status_t rv;
+    
+    if (APR_SUCCESS == (rv = pkey_to_buffer(&buffer, pkey, p, pass_phrase, pass_len))) {
+        return md_util_freplace(fname, perms, p, fwrite_buffer, &buffer); 
+    }
+    md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "save pkey %s (%s pass phrase, len=%d)",
+                  fname, pass_len > 0? "with" : "without", (int)pass_len); 
+    return rv;
+}
+
+apr_status_t md_pkey_gen_rsa(md_pkey_t **ppkey, apr_pool_t *p, int bits)
+{
+    EVP_PKEY_CTX *ctx = NULL;
+    apr_status_t rv;
+    
+    *ppkey = make_pkey(p);
+    ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+    if (ctx 
+        && EVP_PKEY_keygen_init(ctx) >= 0
+        && EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) >= 0
+        && EVP_PKEY_keygen(ctx, &(*ppkey)->pkey) >= 0) {
+        rv = APR_SUCCESS;
+    }
+    else {
+        md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, p, "unable to generate new key"); 
+        *ppkey = NULL;
+        rv = APR_EGENERAL;
+    }
+    
+    if (ctx != NULL) {
+        EVP_PKEY_CTX_free(ctx);
+    }
+    return rv;
+}
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+
+#ifndef NID_tlsfeature
+#define NID_tlsfeature          1020
+#endif
+
+static void RSA_get0_key(const RSA *r,
+                         const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
+{
+    if (n != NULL)
+        *n = r->n;
+    if (e != NULL)
+        *e = r->e;
+    if (d != NULL)
+        *d = r->d;
+}
+
+#endif
+
+static const char *bn64(const BIGNUM *b, apr_pool_t *p) 
+{
+    if (b) {
+         int len = BN_num_bytes(b);
+         char *buffer = apr_pcalloc(p, len);
+         if (buffer) {
+            BN_bn2bin(b, (unsigned char *)buffer);
+            return md_util_base64url_encode(buffer, len, p);
+         }
+    }
+    return NULL;
+}
+
+const char *md_pkey_get_rsa_e64(md_pkey_t *pkey, apr_pool_t *p)
+{
+    const BIGNUM *e;
+    RSA *rsa = EVP_PKEY_get1_RSA(pkey->pkey);
+    
+    if (!rsa) {
+        return NULL;
+    }
+    RSA_get0_key(rsa, NULL, &e, NULL);
+    return bn64(e, p);
+}
+
+const char *md_pkey_get_rsa_n64(md_pkey_t *pkey, apr_pool_t *p)
+{
+    const BIGNUM *n;
+    RSA *rsa = EVP_PKEY_get1_RSA(pkey->pkey);
+    
+    if (!rsa) {
+        return NULL;
+    }
+    RSA_get0_key(rsa, &n, NULL, NULL);
+    return bn64(n, p);
+}
+
+apr_status_t md_crypt_sign64(const char **psign64, md_pkey_t *pkey, apr_pool_t *p, 
+                             const char *d, size_t dlen)
+{
+    EVP_MD_CTX *ctx = NULL;
+    char *buffer;
+    unsigned int blen;
+    const char *sign64 = NULL;
+    apr_status_t rv = APR_ENOMEM;
+    
+    buffer = apr_pcalloc(p, EVP_PKEY_size(pkey->pkey));
+    if (buffer) {
+        ctx = EVP_MD_CTX_create();
+        if (ctx) {
+            rv = APR_ENOTIMPL;
+            if (EVP_SignInit_ex(ctx, EVP_sha256(), NULL)) {
+                rv = APR_EGENERAL;
+                if (EVP_SignUpdate(ctx, d, dlen)) {
+                    if (EVP_SignFinal(ctx, (unsigned char*)buffer, &blen, pkey->pkey)) {
+                        sign64 = md_util_base64url_encode(buffer, blen, p);
+                        if (sign64) {
+                            rv = APR_SUCCESS;
+                        }
+                    }
+                }
+            }
+        }
+        
+        if (ctx) {
+            EVP_MD_CTX_destroy(ctx);
+        }
+    }
+    
+    if (rv != APR_SUCCESS) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p, "signing"); 
+    }
+    
+    *psign64 = sign64;
+    return rv;
+}
+
+static apr_status_t sha256_digest(unsigned char **pdigest, size_t *pdigest_len,
+                                  apr_pool_t *p, const char *d, size_t dlen)
+{
+    EVP_MD_CTX *ctx = NULL;
+    unsigned char *buffer;
+    apr_status_t rv = APR_ENOMEM;
+    unsigned int blen;
+    
+    buffer = apr_pcalloc(p, EVP_MAX_MD_SIZE);
+    if (buffer) {
+        ctx = EVP_MD_CTX_create();
+        if (ctx) {
+            rv = APR_ENOTIMPL;
+            if (EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)) {
+                rv = APR_EGENERAL;
+                if (EVP_DigestUpdate(ctx, d, dlen)) {
+                    if (EVP_DigestFinal(ctx, buffer, &blen)) {
+                        rv = APR_SUCCESS;
+                    }
+                }
+            }
+        }
+        
+        if (ctx) {
+            EVP_MD_CTX_destroy(ctx);
+        }
+    }
+    
+    if (APR_SUCCESS == rv) {
+        *pdigest = buffer;
+        *pdigest_len = blen;
+    }
+    else {
+        md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p, "digest"); 
+        *pdigest = NULL;
+        *pdigest_len = 0;
+    }
+    return rv;
+}
+
+apr_status_t md_crypt_sha256_digest64(const char **pdigest64, apr_pool_t *p, 
+                                      const char *d, size_t dlen)
+{
+    const char *digest64 = NULL;
+    unsigned char *buffer;
+    size_t blen;
+    apr_status_t rv;
+    
+    if (APR_SUCCESS == (rv = sha256_digest(&buffer, &blen, p, d, dlen))) {
+        if (NULL == (digest64 = md_util_base64url_encode((const char*)buffer, blen, p))) {
+            rv = APR_EGENERAL;
+        }
+    }
+    *pdigest64 = digest64;
+    return rv;
+}
+
+static const char * const hex_const[] = {
+    "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", 
+    "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", 
+    "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", 
+    "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", 
+    "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", 
+    "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", 
+    "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", 
+    "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", 
+    "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", 
+    "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", 
+    "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", 
+    "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", 
+    "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", 
+    "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df", 
+    "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", 
+    "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff", 
+};
+
+apr_status_t md_crypt_sha256_digest_hex(const char **pdigesthex, apr_pool_t *p, 
+                                        const char *d, size_t dlen)
+{
+    char *dhex = NULL, *cp;
+    const char * x;
+    unsigned char *buffer;
+    size_t blen;
+    apr_status_t rv;
+    int i;
+    
+    if (APR_SUCCESS == (rv = sha256_digest(&buffer, &blen, p, d, dlen))) {
+        cp = dhex = apr_pcalloc(p,  2 * blen + 1);
+        if (!dhex) {
+            rv = APR_EGENERAL;
+        }
+        for (i = 0; i < blen; ++i, cp += 2) {
+            x = hex_const[buffer[i]];
+            cp[0] = x[0];
+            cp[1] = x[1];
+        }
+    }
+    *pdigesthex = dhex;
+    return rv;
+}
+
+/**************************************************************************************************/
+/* certificates */
+
+struct md_cert_t {
+    apr_pool_t *pool;
+    X509 *x509;
+    apr_array_header_t *alt_names;
+};
+
+static apr_status_t cert_cleanup(void *data)
+{
+    md_cert_t *cert = data;
+    if (cert->x509) {
+        X509_free(cert->x509);
+        cert->x509 = NULL;
+    }
+    return APR_SUCCESS;
+}
+
+static md_cert_t *make_cert(apr_pool_t *p, X509 *x509) 
+{
+    md_cert_t *cert = apr_pcalloc(p, sizeof(*cert));
+    cert->pool = p;
+    cert->x509 = x509;
+    apr_pool_cleanup_register(p, cert, cert_cleanup, apr_pool_cleanup_null);
+    
+    return cert;
+}
+
+void md_cert_free(md_cert_t *cert)
+{
+    cert_cleanup(cert);
+}
+
+void *md_cert_get_X509(struct md_cert_t *cert)
+{
+    return cert->x509;
+}
+
+int md_cert_is_valid_now(const md_cert_t *cert)
+{
+    return ((X509_cmp_current_time(X509_get_notBefore(cert->x509)) < 0)
+            && (X509_cmp_current_time(X509_get_notAfter(cert->x509)) > 0));
+}
+
+int md_cert_has_expired(const md_cert_t *cert)
+{
+    return (X509_cmp_current_time(X509_get_notAfter(cert->x509)) <= 0);
+}
+
+apr_time_t md_cert_get_not_after(md_cert_t *cert)
+{
+    int secs, days;
+    apr_time_t time = apr_time_now();
+    ASN1_TIME *not_after = X509_get_notAfter(cert->x509);
+    
+    if (ASN1_TIME_diff(&days, &secs, NULL, not_after)) {
+        time += apr_time_from_sec((days * MD_SECS_PER_DAY) + secs); 
+    }
+    return time;
+}
+
+int md_cert_covers_domain(md_cert_t *cert, const char *domain_name)
+{
+    if (!cert->alt_names) {
+        md_cert_get_alt_names(&cert->alt_names, cert, cert->pool);
+    }
+    if (cert->alt_names) {
+        return md_array_str_index(cert->alt_names, domain_name, 0, 0) >= 0;
+    }
+    return 0;
+}
+
+int md_cert_covers_md(md_cert_t *cert, const md_t *md)
+{
+    const char *name;
+    int i;
+    
+    if (!cert->alt_names) {
+        md_cert_get_alt_names(&cert->alt_names, cert, cert->pool);
+    }
+    if (cert->alt_names) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE4, 0, cert->pool, "cert has %d alt names",
+                      cert->alt_names->nelts); 
+        for (i = 0; i < md->domains->nelts; ++i) {
+            name = APR_ARRAY_IDX(md->domains, i, const char *);
+            if (md_array_str_index(cert->alt_names, name, 0, 0) < 0) {
+                md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, cert->pool, 
+                              "md domain %s not covered by cert", name);
+                return 0;
+            }
+        }
+        return 1;
+    }
+    else {
+        md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, cert->pool, "cert has NO alt names");
+    }
+    return 0;
+}
+
+apr_status_t md_cert_get_issuers_uri(const char **puri, md_cert_t *cert, apr_pool_t *p)
+{
+    int i, ext_idx, nid = NID_info_access;
+    X509_EXTENSION *ext;
+    X509V3_EXT_METHOD *ext_cls;
+    void *ext_data;
+    const char *uri = NULL;
+    apr_status_t rv = APR_ENOENT;
+    
+    /* Waddle through x509  API history to get someone that may be able
+     * to hand us the issuer url for the cert chain */
+    ext_idx = X509_get_ext_by_NID(cert->x509, nid, -1);
+    ext = (ext_idx >= 0)? X509_get_ext(cert->x509, ext_idx) : NULL;
+    ext_cls = ext? (X509V3_EXT_METHOD*)X509V3_EXT_get(ext) : NULL;
+    if (ext_cls && (ext_data = X509_get_ext_d2i(cert->x509, nid, 0, 0))) {
+        CONF_VALUE *cval;
+        STACK_OF(CONF_VALUE) *ext_vals = ext_cls->i2v(ext_cls, ext_data, 0);
+        
+        for (i = 0; i < sk_CONF_VALUE_num(ext_vals); ++i) {
+            cval = sk_CONF_VALUE_value(ext_vals, i);
+            if (!strcmp("CA Issuers - URI", cval->name)) {
+                uri = apr_pstrdup(p, cval->value);
+                rv = APR_SUCCESS;
+                break;
+            }
+        }
+    } 
+    *puri = (APR_SUCCESS == rv)? uri : NULL;
+    return rv;
+}
+
+apr_status_t md_cert_get_alt_names(apr_array_header_t **pnames, md_cert_t *cert, apr_pool_t *p)
+{
+    apr_array_header_t *names;
+    apr_status_t rv = APR_ENOENT;
+    STACK_OF(GENERAL_NAME) *xalt_names;
+    unsigned char *buf;
+    int i;
+    
+    xalt_names = (GENERAL_NAMES*)X509_get_ext_d2i(cert->x509, NID_subject_alt_name, NULL, NULL);
+    if (xalt_names) {
+        GENERAL_NAME *cval;
+        
+        names = apr_array_make(p, sk_GENERAL_NAME_num(xalt_names), sizeof(char *));
+        for (i = 0; i < sk_GENERAL_NAME_num(xalt_names); ++i) {
+            cval = sk_GENERAL_NAME_value(xalt_names, i);
+            switch (cval->type) {
+                case GEN_DNS:
+                case GEN_URI:
+                case GEN_IPADD:
+                    ASN1_STRING_to_UTF8(&buf, cval->d.ia5);
+                    APR_ARRAY_PUSH(names, const char *) = apr_pstrdup(p, (char*)buf);
+                    OPENSSL_free(buf);
+                    break;
+                default:
+                    break;
+            }
+        }
+        rv = APR_SUCCESS;
+    }
+    *pnames = (APR_SUCCESS == rv)? names : NULL;
+    return rv;
+}
+
+apr_status_t md_cert_fload(md_cert_t **pcert, apr_pool_t *p, const char *fname)
+{
+    FILE *f;
+    apr_status_t rv;
+    md_cert_t *cert;
+    X509 *x509;
+    
+    rv = md_util_fopen(&f, fname, "r");
+    if (rv == APR_SUCCESS) {
+    
+        x509 = PEM_read_X509(f, NULL, NULL, NULL);
+        rv = fclose(f);
+        if (x509 != NULL) {
+            cert =  make_cert(p, x509);
+        }
+        else {
+            rv = APR_EINVAL;
+        }
+    }
+
+    *pcert = (APR_SUCCESS == rv)? cert : NULL;
+    return rv;
+}
+
+static apr_status_t cert_to_buffer(buffer *buffer, md_cert_t *cert, apr_pool_t *p)
+{
+    BIO *bio = BIO_new(BIO_s_mem());
+    
+    if (!bio) {
+        return APR_ENOMEM;
+    }
+
+    ERR_clear_error();
+    PEM_write_bio_X509(bio, cert->x509);
+    if (ERR_get_error() > 0) {
+        BIO_free(bio);
+        return APR_EINVAL;
+    }
+
+    buffer->len = BIO_pending(bio);
+    if (buffer->len > 0) {
+        buffer->data = apr_palloc(p, buffer->len+1);
+        buffer->len = BIO_read(bio, buffer->data, (int)buffer->len);
+        buffer->data[buffer->len] = '\0';
+    }
+    BIO_free(bio);
+    return APR_SUCCESS;
+}
+
+apr_status_t md_cert_fsave(md_cert_t *cert, apr_pool_t *p, 
+                           const char *fname, apr_fileperms_t perms)
+{
+    buffer buffer;
+    apr_status_t rv;
+    
+    if (APR_SUCCESS == (rv = cert_to_buffer(&buffer, cert, p))) {
+        return md_util_freplace(fname, perms, p, fwrite_buffer, &buffer); 
+    }
+    return rv;
+}
+
+apr_status_t md_cert_to_base64url(const char **ps64, md_cert_t *cert, apr_pool_t *p)
+{
+    buffer buffer;
+    apr_status_t rv;
+    
+    if (APR_SUCCESS == (rv = cert_to_buffer(&buffer, cert, p))) {
+        *ps64 = md_util_base64url_encode(buffer.data, buffer.len, p);
+        return APR_SUCCESS;
+    }
+    *ps64 = NULL;
+    return rv;
+}
+
+apr_status_t md_cert_read_http(md_cert_t **pcert, apr_pool_t *p, 
+                               const md_http_response_t *res)
+{
+    const char *ct;
+    apr_off_t data_len;
+    apr_size_t der_len;
+    apr_status_t rv;
+    
+    ct = apr_table_get(res->headers, "Content-Type");
+    if (!res->body || !ct  || strcmp("application/pkix-cert", ct)) {
+        return APR_ENOENT;
+    }
+    
+    if (APR_SUCCESS == (rv = apr_brigade_length(res->body, 1, &data_len))) {
+        char *der;
+        if (data_len > 1024*1024) { /* certs usually are <2k each */
+            return APR_EINVAL;
+        }
+        if (APR_SUCCESS == (rv = apr_brigade_pflatten(res->body, &der, &der_len, p))) {
+            const unsigned char *bf = (const unsigned char*)der;
+            X509 *x509;
+            
+            if (NULL == (x509 = d2i_X509(NULL, &bf, der_len))) {
+                rv = APR_EINVAL;
+            }
+            else {
+                *pcert = make_cert(p, x509);
+                rv = APR_SUCCESS;
+            }
+        }
+        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, "cert parsed");
+    }
+    return rv;
+}
+
+md_cert_state_t md_cert_state_get(md_cert_t *cert)
+{
+    if (cert->x509) {
+        return md_cert_is_valid_now(cert)? MD_CERT_VALID : MD_CERT_EXPIRED;
+    }
+    return MD_CERT_UNKNOWN;
+}
+
+apr_status_t md_chain_fload(apr_array_header_t **pcerts, apr_pool_t *p, const char *fname)
+{
+    FILE *f;
+    apr_status_t rv;
+    apr_array_header_t *certs = NULL;
+    X509 *x509;
+    md_cert_t *cert;
+    unsigned long err;
+    
+    rv = md_util_fopen(&f, fname, "r");
+    if (rv == APR_SUCCESS) {
+        certs = apr_array_make(p, 5, sizeof(md_cert_t *));
+        
+        ERR_clear_error();
+        while (NULL != (x509 = PEM_read_X509(f, NULL, NULL, NULL))) {
+            cert = make_cert(p, x509);
+            APR_ARRAY_PUSH(certs, md_cert_t *) = cert;
+        }
+        fclose(f);
+        
+        if (0 < (err =  ERR_get_error())
+            && !(ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
+            /* not the expected one when no more PEM encodings are found */
+            rv = APR_EINVAL;
+            goto out;
+        }
+        
+        if (certs->nelts == 0) {
+            /* Did not find any. This is acceptable unless the file has a certain size
+             * when we no longer accept it as empty chain file. Something seems to be
+             * wrong then. */
+            apr_finfo_t info;
+            if (APR_SUCCESS == apr_stat(&info, fname, APR_FINFO_SIZE, p) && info.size >= 1024) {
+                /* "Too big for a moon." */
+                rv = APR_EINVAL;
+                md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, 
+                              "no certificates in non-empty chain %s", fname);
+                goto out;
+            }
+        }        
+    }
+out:
+    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, "read chain file %s, found %d certs", 
+                  fname, certs? certs->nelts : 0);
+    *pcerts = (APR_SUCCESS == rv)? certs : NULL;
+    return rv;
+}
+
+apr_status_t md_chain_fsave(apr_array_header_t *certs, apr_pool_t *p, 
+                            const char *fname, apr_fileperms_t perms)
+{
+    FILE *f;
+    apr_status_t rv;
+    const md_cert_t *cert;
+    unsigned long err = 0;
+    int i;
+    
+    rv = md_util_fopen(&f, fname, "w");
+    if (rv == APR_SUCCESS) {
+        apr_file_perms_set(fname, perms);
+        ERR_clear_error();
+        for (i = 0; i < certs->nelts; ++i) {
+            cert = APR_ARRAY_IDX(certs, i, const md_cert_t *);
+            assert(cert->x509);
+            
+            PEM_write_X509(f, cert->x509);
+            
+            if (0 < (err = ERR_get_error())) {
+                break;
+            }
+            
+        }
+        rv = fclose(f);
+        if (err) {
+            rv = APR_EINVAL;
+        }
+    }
+    return rv;
+}
+
+/**************************************************************************************************/
+/* certificate signing requests */
+
+static const char *alt_name(const char *domain, apr_pool_t *p)
+{
+    return apr_psprintf(p, "DNS:%s", domain);
+}
+
+static const char *alt_names(apr_array_header_t *domains, apr_pool_t *p)
+{
+    const char *alts = "", *sep = "", *domain;
+    int i;
+    
+    for (i = 0; i < domains->nelts; ++i) {
+        domain = APR_ARRAY_IDX(domains, i, const char *);
+        alts = apr_psprintf(p, "%s%sDNS:%s", alts, sep, domain);
+        sep = ",";
+    }
+    return alts;
+}
+
+static apr_status_t add_ext(X509 *x, int nid, const char *value, apr_pool_t *p)
+{
+    X509_EXTENSION *ext = NULL;
+    X509V3_CTX ctx;
+    apr_status_t rv;
+
+    X509V3_set_ctx_nodb(&ctx);
+    X509V3_set_ctx(&ctx, x, x, NULL, NULL, 0);
+    if (NULL == (ext = X509V3_EXT_conf_nid(NULL, &ctx, nid, (char*)value))) {
+        return APR_EGENERAL;
+    }
+    
+    ERR_clear_error();
+    rv = X509_add_ext(x, ext, -1)? APR_SUCCESS : APR_EINVAL;
+    if (APR_SUCCESS != rv) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "add_ext nid=%dd value='%s'", 
+                      nid, value); 
+        
+    }
+    X509_EXTENSION_free(ext);
+    return rv;
+}
+
+static apr_status_t sk_add_alt_names(STACK_OF(X509_EXTENSION) *exts,
+                                     apr_array_header_t *domains, apr_pool_t *p)
+{
+    if (domains->nelts > 0) {
+        X509_EXTENSION *x;
+        
+        x = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, (char*)alt_names(domains, p));
+        if (NULL == x) {
+            return APR_EGENERAL;
+        }
+        sk_X509_EXTENSION_push(exts, x);
+    }
+    return APR_SUCCESS;
+}
+
+static apr_status_t add_must_staple(STACK_OF(X509_EXTENSION) *exts, const md_t *md, apr_pool_t *p)
+{
+    
+    if (md->must_staple) {
+        X509_EXTENSION *x = X509V3_EXT_conf_nid(NULL, NULL, 
+                                                NID_tlsfeature, (char*)"DER:30:03:02:01:05");
+        if (NULL == x) {
+            return APR_EGENERAL;
+        }
+        sk_X509_EXTENSION_push(exts, x);
+    }
+    return APR_SUCCESS;
+}
+
+apr_status_t md_cert_req_create(const char **pcsr_der_64, const md_t *md, 
+                                md_pkey_t *pkey, apr_pool_t *p)
+{
+    const char *s, *csr_der, *csr_der_64 = NULL;
+    const unsigned char *domain;
+    X509_REQ *csr;
+    X509_NAME *n = NULL;
+    STACK_OF(X509_EXTENSION) *exts = NULL;
+    apr_status_t rv;
+    int csr_der_len;
+    
+    assert(md->domains->nelts > 0);
+    
+    if (NULL == (csr = X509_REQ_new()) 
+        || NULL == (exts = sk_X509_EXTENSION_new_null())
+        || NULL == (n = X509_NAME_new())) {
+        rv = APR_ENOMEM;
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: openssl alloc X509 things", md->name);
+        goto out; 
+    }
+
+    /* subject name == first domain */
+    domain = APR_ARRAY_IDX(md->domains, 0, const unsigned char *);
+    if (!X509_NAME_add_entry_by_txt(n, "CN", MBSTRING_ASC, domain, -1, -1, 0)
+        || !X509_REQ_set_subject_name(csr, n)) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: REQ name add entry", md->name);
+        rv = APR_EGENERAL; goto out;
+    }
+    /* collect extensions, such as alt names and must staple */
+    if (APR_SUCCESS != (rv = sk_add_alt_names(exts, md->domains, p))) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: collecting alt names", md->name);
+        rv = APR_EGENERAL; goto out;
+    }
+    if (APR_SUCCESS != (rv = add_must_staple(exts, md, p))) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: must staple", md->name);
+        rv = APR_EGENERAL; goto out;
+    }
+    /* add extensions to csr */
+    if (sk_X509_EXTENSION_num(exts) > 0 && !X509_REQ_add_extensions(csr, exts)) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: adding exts", md->name);
+        rv = APR_EGENERAL; goto out;
+    }
+    /* add our key */
+    if (!X509_REQ_set_pubkey(csr, pkey->pkey)) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set pkey in csr", md->name);
+        rv = APR_EGENERAL; goto out;
+    }
+    /* sign, der encode and base64url encode */
+    if (!X509_REQ_sign(csr, pkey->pkey, EVP_sha256())) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: sign csr", md->name);
+        rv = APR_EGENERAL; goto out;
+    }
+    if ((csr_der_len = i2d_X509_REQ(csr, NULL)) < 0) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: der length", md->name);
+        rv = APR_EGENERAL; goto out;
+    }
+    s = csr_der = apr_pcalloc(p, csr_der_len + 1);
+    if (i2d_X509_REQ(csr, (unsigned char**)&s) != csr_der_len) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: csr der enc", md->name);
+        rv = APR_EGENERAL; goto out;
+    }
+    csr_der_64 = md_util_base64url_encode(csr_der, csr_der_len, p);
+    rv = APR_SUCCESS;
+    
+out:
+    if (exts) {
+        sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+    }
+    if (csr) {
+        X509_REQ_free(csr);
+    }
+    if (n) {
+        X509_NAME_free(n);
+    }
+    *pcsr_der_64 = (APR_SUCCESS == rv)? csr_der_64 : NULL;
+    return rv;
+}
+
+apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn, 
+                               const char *domain, md_pkey_t *pkey,
+                               apr_interval_time_t valid_for, apr_pool_t *p)
+{
+    X509 *x;
+    X509_NAME *n = NULL;
+    md_cert_t *cert = NULL;
+    apr_status_t rv;
+    int days;
+    BIGNUM *big_rnd = NULL;
+    ASN1_INTEGER *asn1_rnd = NULL;
+    unsigned char rnd[20];
+    
+    assert(domain);
+    
+    if (NULL == (x = X509_new()) 
+        || NULL == (n = X509_NAME_new())) {
+        rv = APR_ENOMEM;
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: openssl alloc X509 things", domain);
+        goto out; 
+    }
+    
+    if (APR_SUCCESS != (rv = md_rand_bytes(rnd, sizeof(rnd), p))
+        || !(big_rnd = BN_bin2bn(rnd, sizeof(rnd), NULL))
+        || !(asn1_rnd = BN_to_ASN1_INTEGER(big_rnd, NULL))) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: setup random serial", domain);
+        rv = APR_EGENERAL; goto out;
+    } 
+    if (!X509_set_serialNumber(x, asn1_rnd)) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: set serial number", domain);
+        rv = APR_EGENERAL; goto out;
+    }
+    /* set common name and issue */
+    if (!X509_NAME_add_entry_by_txt(n, "CN", MBSTRING_ASC, (const unsigned char*)cn, -1, -1, 0)
+        || !X509_set_subject_name(x, n)
+        || !X509_set_issuer_name(x, n)) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: name add entry", domain);
+        rv = APR_EGENERAL; goto out;
+    }
+    /* cert are uncontrained (but not very trustworthy) */
+    if (APR_SUCCESS != (rv = add_ext(x, NID_basic_constraints, "CA:TRUE, pathlen:0", p))) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set basic constraints ext", domain);
+        goto out;
+    }
+    /* add the domain as alt name */
+    if (APR_SUCCESS != (rv = add_ext(x, NID_subject_alt_name, alt_name(domain, p), p))) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set alt_name ext", domain);
+        goto out;
+    }
+    /* add our key */
+    if (!X509_set_pubkey(x, pkey->pkey)) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set pkey in x509", domain);
+        rv = APR_EGENERAL; goto out;
+    }
+    
+    days = ((apr_time_sec(valid_for) + MD_SECS_PER_DAY - 1)/ MD_SECS_PER_DAY);
+    if (!X509_set_notBefore(x, ASN1_TIME_set(NULL, time(NULL)))) {
+        rv = APR_EGENERAL; goto out;
+    }
+    if (!X509_set_notAfter(x, ASN1_TIME_adj(NULL, time(NULL), days, 0))) {
+        rv = APR_EGENERAL; goto out;
+    }
+
+    /* sign with same key */
+    if (!X509_sign(x, pkey->pkey, EVP_sha256())) {
+        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: sign x509", domain);
+        rv = APR_EGENERAL; goto out;
+    }
+
+    cert = make_cert(p, x);
+    rv = APR_SUCCESS;
+    
+out:
+    if (!cert && x) {
+        X509_free(x);
+    }
+    if (n) {
+        X509_NAME_free(n);
+    }
+    if (big_rnd) {
+        BN_free(big_rnd);
+    }
+    if (asn1_rnd) {
+        ASN1_INTEGER_free(asn1_rnd);
+    }
+    *pcert = (APR_SUCCESS == rv)? cert : NULL;
+    return rv;
+}
+

Added: httpd/httpd/branches/trunk-md/modules/md/md_crypt.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/trunk-md/modules/md/md_crypt.h?rev=1804123&view=auto
==============================================================================
--- httpd/httpd/branches/trunk-md/modules/md/md_crypt.h (added)
+++ httpd/httpd/branches/trunk-md/modules/md/md_crypt.h Fri Aug  4 13:47:25 2017
@@ -0,0 +1,110 @@
+/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed 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.
+ */
+
+#ifndef mod_md_md_crypt_h
+#define mod_md_md_crypt_h
+
+#include <apr_file_io.h>
+
+struct apr_array_header_t;
+struct md_t;
+struct md_http_response_t;
+struct md_cert_t;
+struct md_pkey_t;
+
+/**************************************************************************************************/
+/* random */
+
+apr_status_t md_rand_bytes(unsigned char *buf, apr_size_t len, apr_pool_t *p);
+
+/**************************************************************************************************/
+/* digests */
+apr_status_t md_crypt_sha256_digest64(const char **pdigest64, apr_pool_t *p, 
+                                      const char *d, size_t dlen);
+apr_status_t md_crypt_sha256_digest_hex(const char **pdigesthex, apr_pool_t *p, 
+                                        const char *d, size_t dlen);
+
+/**************************************************************************************************/
+/* private keys */
+
+typedef struct md_pkey_t md_pkey_t;
+
+apr_status_t md_crypt_init(apr_pool_t *pool);
+
+apr_status_t md_pkey_gen_rsa(md_pkey_t **ppkey, apr_pool_t *p, int bits);
+void md_pkey_free(md_pkey_t *pkey);
+
+const char *md_pkey_get_rsa_e64(md_pkey_t *pkey, apr_pool_t *p);
+const char *md_pkey_get_rsa_n64(md_pkey_t *pkey, apr_pool_t *p);
+
+apr_status_t md_pkey_fload(md_pkey_t **ppkey, apr_pool_t *p, 
+                           const char *pass_phrase, apr_size_t pass_len,
+                           const char *fname);
+apr_status_t md_pkey_fsave(md_pkey_t *pkey, apr_pool_t *p, 
+                           const char *pass_phrase, apr_size_t pass_len, 
+                           const char *fname, apr_fileperms_t perms);
+
+apr_status_t md_crypt_sign64(const char **psign64, md_pkey_t *pkey, apr_pool_t *p, 
+                             const char *d, size_t dlen);
+
+void *md_cert_get_X509(struct md_cert_t *cert);
+void *md_pkey_get_EVP_PKEY(struct md_pkey_t *pkey);
+
+/**************************************************************************************************/
+/* X509 certificates */
+
+typedef struct md_cert_t md_cert_t;
+
+typedef enum {
+    MD_CERT_UNKNOWN,
+    MD_CERT_VALID,
+    MD_CERT_EXPIRED
+} md_cert_state_t;
+
+void md_cert_free(md_cert_t *cert);
+
+apr_status_t md_cert_fload(md_cert_t **pcert, apr_pool_t *p, const char *fname);
+apr_status_t md_cert_fsave(md_cert_t *cert, apr_pool_t *p, 
+                           const char *fname, apr_fileperms_t perms);
+
+apr_status_t md_cert_read_http(md_cert_t **pcert, apr_pool_t *pool, 
+                               const struct md_http_response_t *res);
+
+md_cert_state_t md_cert_state_get(md_cert_t *cert);
+int md_cert_is_valid_now(const md_cert_t *cert);
+int md_cert_has_expired(const md_cert_t *cert);
+int md_cert_covers_domain(md_cert_t *cert, const char *domain_name);
+int md_cert_covers_md(md_cert_t *cert, const struct md_t *md);
+apr_time_t md_cert_get_not_after(md_cert_t *cert);
+
+apr_status_t md_cert_get_issuers_uri(const char **puri, md_cert_t *cert, apr_pool_t *p);
+apr_status_t md_cert_get_alt_names(apr_array_header_t **pnames, md_cert_t *cert, apr_pool_t *p);
+
+apr_status_t md_cert_to_base64url(const char **ps64, md_cert_t *cert, apr_pool_t *p);
+apr_status_t md_cert_from_base64url(md_cert_t **pcert, const char *s64, apr_pool_t *p);
+
+apr_status_t md_chain_fload(struct apr_array_header_t **pcerts, 
+                            apr_pool_t *p, const char *fname);
+apr_status_t md_chain_fsave(struct apr_array_header_t *certs, 
+                            apr_pool_t *p, const char *fname, apr_fileperms_t perms);
+
+apr_status_t md_cert_req_create(const char **pcsr_der_64, const struct md_t *md, 
+                                md_pkey_t *pkey, apr_pool_t *p);
+
+apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn, 
+                               const char *domain, md_pkey_t *pkey,
+                               apr_interval_time_t valid_for, apr_pool_t *p);
+
+#endif /* md_crypt_h */

Added: httpd/httpd/branches/trunk-md/modules/md/md_curl.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/trunk-md/modules/md/md_curl.c?rev=1804123&view=auto
==============================================================================
--- httpd/httpd/branches/trunk-md/modules/md/md_curl.c (added)
+++ httpd/httpd/branches/trunk-md/modules/md/md_curl.c Fri Aug  4 13:47:25 2017
@@ -0,0 +1,301 @@
+/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed 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 <curl/curl.h>
+
+#include <apr_lib.h>
+#include <apr_strings.h>
+#include <apr_buckets.h>
+
+#include "md_http.h"
+#include "md_log.h"
+#include "md_curl.h"
+
+/**************************************************************************************************/
+/* md_http curl implementation */
+
+
+static apr_status_t curl_status(int curl_code)
+{
+    switch (curl_code) {
+        case CURLE_OK:                   return APR_SUCCESS;
+        case CURLE_UNSUPPORTED_PROTOCOL: return APR_ENOTIMPL; 
+        case CURLE_NOT_BUILT_IN:         return APR_ENOTIMPL; 
+        case CURLE_URL_MALFORMAT:        return APR_EINVAL;
+        case CURLE_COULDNT_RESOLVE_PROXY:return APR_ECONNREFUSED;
+        case CURLE_COULDNT_RESOLVE_HOST: return APR_ECONNREFUSED;
+        case CURLE_COULDNT_CONNECT:      return APR_ECONNREFUSED;
+        case CURLE_REMOTE_ACCESS_DENIED: return APR_EACCES;
+        case CURLE_OUT_OF_MEMORY:        return APR_ENOMEM;
+        case CURLE_OPERATION_TIMEDOUT:   return APR_TIMEUP;
+        case CURLE_SSL_CONNECT_ERROR:    return APR_ECONNABORTED;
+        case CURLE_AGAIN:                return APR_EAGAIN;
+        default:                         return APR_EGENERAL;
+    }
+}
+
+static size_t req_data_cb(void *data, size_t len, size_t nmemb, void *baton)
+{
+    apr_bucket_brigade *body = baton;
+    size_t blen, read_len = 0, max_len = len * nmemb;
+    const char *bdata;
+    apr_bucket *b;
+    apr_status_t rv;
+    
+    while (body && !APR_BRIGADE_EMPTY(body) && max_len > 0) {
+        b = APR_BRIGADE_FIRST(body);
+        if (APR_BUCKET_IS_METADATA(b)) {
+            if (APR_BUCKET_IS_EOS(b)) {
+                body = NULL;
+            }
+        }
+        else {
+            rv = apr_bucket_read(b, &bdata, &blen, APR_BLOCK_READ);
+            if (rv == APR_SUCCESS) {
+                if (blen > max_len) {
+                    apr_bucket_split(b, max_len);
+                    blen = max_len;
+                }
+                memcpy(data, bdata, blen);
+                read_len += blen;
+                max_len -= blen;
+            }
+            else {
+                body = NULL;
+                if (!APR_STATUS_IS_EOF(rv)) {
+                    /* everything beside EOF is an error */
+                    read_len = CURL_READFUNC_ABORT;
+                }
+            }
+            
+        }
+        apr_bucket_delete(b);
+    }
+    
+    return read_len;
+}
+
+static size_t resp_data_cb(void *data, size_t len, size_t nmemb, void *baton)
+{
+    md_http_response_t *res = baton;
+    size_t blen = len * nmemb;
+    apr_status_t rv;
+    
+    if (res->body) {
+        if (res->req->resp_limit) {
+            apr_off_t body_len = 0;
+            apr_brigade_length(res->body, 0, &body_len);
+            if (body_len + len > res->req->resp_limit) {
+                return 0; /* signal curl failure */
+            }
+        }
+        rv = apr_brigade_write(res->body, NULL, NULL, (const char *)data, blen);
+        if (rv != APR_SUCCESS) {
+            /* returning anything != blen will make CURL fail this */
+            return 0;
+        }
+    }
+    return blen;
+}
+
+static size_t header_cb(void *buffer, size_t elen, size_t nmemb, void *baton)
+{
+    md_http_response_t *res = baton;
+    size_t len, clen = elen * nmemb;
+    const char *name = NULL, *value = "", *b = buffer;
+    int i;
+    
+    len = (clen && b[clen-1] == '\n')? clen-1 : clen;
+    len = (len && b[len-1] == '\r')? len-1 : len;
+    for (i = 0; i < len; ++i) {
+        if (b[i] == ':') {
+            name = apr_pstrndup(res->req->pool, b, i);
+            ++i;
+            while (i < len && b[i] == ' ') {
+                ++i;
+            }
+            if (i < len) {
+                value = apr_pstrndup(res->req->pool, b+i, len - i);
+            }
+            break;
+        }
+    }
+    
+    if (name != NULL) {
+        apr_table_add(res->headers, name, value);
+    }
+    return clen;
+}
+
+static apr_status_t curl_init(md_http_request_t *req)
+{
+    CURL *curl = curl_easy_init();
+    if (!curl) {
+        return APR_EGENERAL;
+    }
+    
+    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_cb);
+    curl_easy_setopt(curl, CURLOPT_HEADERDATA, NULL);
+    curl_easy_setopt(curl, CURLOPT_READFUNCTION, req_data_cb);
+    curl_easy_setopt(curl, CURLOPT_READDATA, NULL);
+    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, resp_data_cb);
+    curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
+    
+    req->internals = curl;
+    return APR_SUCCESS;
+}
+
+typedef struct {
+    md_http_request_t *req;
+    struct curl_slist *hdrs;
+    apr_status_t rv;
+} curlify_hdrs_ctx;
+
+static int curlify_headers(void *baton, const char *key, const char *value)
+{
+    curlify_hdrs_ctx *ctx = baton;
+    const char *s;
+    
+    if (strchr(key, '\r') || strchr(key, '\n')
+        || strchr(value, '\r') || strchr(value, '\n')) {
+        ctx->rv = APR_EINVAL;
+        return 0;
+    }
+    s = apr_psprintf(ctx->req->pool, "%s: %s", key, value);
+    ctx->hdrs = curl_slist_append(ctx->hdrs, s);
+    return 1;
+}
+
+static apr_status_t curl_perform(md_http_request_t *req)
+{
+    apr_status_t rv = APR_SUCCESS;
+    int curle;
+    md_http_response_t *res;
+    CURL *curl;
+    struct curl_slist *req_hdrs = NULL;
+
+    rv = curl_init(req);
+    curl = req->internals;
+    
+    res = apr_pcalloc(req->pool, sizeof(*res));
+    
+    res->req = req;
+    res->rv = APR_SUCCESS;
+    res->status = 400;
+    res->headers = apr_table_make(req->pool, 5);
+    res->body = apr_brigade_create(req->pool, req->bucket_alloc);
+    
+    curl_easy_setopt(curl, CURLOPT_URL, req->url);
+    if (!apr_strnatcasecmp("GET", req->method)) {
+        /* nop */
+    }
+    else if (!apr_strnatcasecmp("HEAD", req->method)) {
+        curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
+    }
+    else if (!apr_strnatcasecmp("POST", req->method)) {
+        curl_easy_setopt(curl, CURLOPT_POST, 1L);
+    }
+    else {
+        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, req->method);
+    }
+    curl_easy_setopt(curl, CURLOPT_HEADERDATA, res);
+    curl_easy_setopt(curl, CURLOPT_READDATA, req->body);
+    curl_easy_setopt(curl, CURLOPT_WRITEDATA, res);
+    
+    if (req->user_agent) {
+        curl_easy_setopt(curl, CURLOPT_USERAGENT, req->user_agent);
+    }
+    if (!apr_is_empty_table(req->headers)) {
+        curlify_hdrs_ctx ctx;
+        
+        ctx.req = req;
+        ctx.hdrs = NULL;
+        ctx.rv = APR_SUCCESS;
+        apr_table_do(curlify_headers, &ctx, req->headers, NULL);
+        req_hdrs = ctx.hdrs;
+        if (ctx.rv == APR_SUCCESS) {
+            curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req_hdrs);
+        }
+    }
+    
+    md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, req->pool, 
+                  "request %ld --> %s %s", req->id, req->method, req->url);
+    
+    if (md_log_is_level(req->pool, MD_LOG_TRACE3)) {
+        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+    }
+    
+    curle = curl_easy_perform(curl);
+    res->rv = curl_status(curle);
+    
+    if (APR_SUCCESS == res->rv) {
+        long l;
+        res->rv = curl_status(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &l));
+        if (APR_SUCCESS == res->rv) {
+            res->status = (int)l;
+        }
+        md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, res->rv, req->pool, 
+                      "request %ld <-- %d", req->id, res->status);
+    }
+    else {
+        md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, res->rv, req->pool, 
+                      "request %ld failed(%d): %s", req->id, curle, curl_easy_strerror(curle));
+    }
+    
+    if (req->cb) {
+        res->rv = req->cb(res);
+    }
+    
+    rv = res->rv;
+    md_http_req_destroy(req);
+    if (req_hdrs) {
+        curl_slist_free_all(req_hdrs);
+    }
+    
+    return rv;
+}
+
+static int initialized;
+
+static apr_status_t md_curl_init(void) {
+    if (!initialized) {
+        initialized = 1;
+        curl_global_init(CURL_GLOBAL_DEFAULT);
+    }
+    return APR_SUCCESS;
+}
+
+static void curl_req_cleanup(md_http_request_t *req) 
+{
+    if (req->internals) {
+        curl_easy_cleanup(req->internals);
+        req->internals = NULL;
+    }
+}
+
+static md_http_impl_t impl = {
+    md_curl_init,
+    curl_req_cleanup,
+    curl_perform
+};
+
+md_http_impl_t * md_curl_get_impl(apr_pool_t *p)
+{
+    /* trigger early global curl init, before we are down a rabbit hole */
+    md_curl_init();
+    return &impl;
+}

Added: httpd/httpd/branches/trunk-md/modules/md/md_curl.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/trunk-md/modules/md/md_curl.h?rev=1804123&view=auto
==============================================================================
--- httpd/httpd/branches/trunk-md/modules/md/md_curl.h (added)
+++ httpd/httpd/branches/trunk-md/modules/md/md_curl.h Fri Aug  4 13:47:25 2017
@@ -0,0 +1,23 @@
+/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed 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.
+ */
+
+#ifndef md_curl_h
+#define md_curl_h
+
+struct md_http_impl;
+
+struct md_http_impl_t * md_curl_get_impl(apr_pool_t *p);
+
+#endif /* md_curl_h */

Added: httpd/httpd/branches/trunk-md/modules/md/md_http.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/trunk-md/modules/md/md_http.c?rev=1804123&view=auto
==============================================================================
--- httpd/httpd/branches/trunk-md/modules/md/md_http.c (added)
+++ httpd/httpd/branches/trunk-md/modules/md/md_http.c Fri Aug  4 13:47:25 2017
@@ -0,0 +1,237 @@
+/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de)
+ * Licensed 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 <apr_lib.h>
+#include <apr_strings.h>
+#include <apr_buckets.h>
+
+#include "md_http.h"
+#include "md_log.h"
+
+struct md_http_t {
+    apr_pool_t *pool;
+    apr_bucket_alloc_t *bucket_alloc;
+    apr_off_t resp_limit;
+    md_http_impl_t *impl;
+    const char *user_agent;
+};
+
+static md_http_impl_t *cur_impl;
+static int cur_init_done;
+
+void md_http_use_implementation(md_http_impl_t *impl)
+{
+    if (cur_impl != impl) {
+        cur_impl = impl;
+        cur_init_done = 0;
+    }
+}
+
+static long next_req_id;
+
+apr_status_t md_http_create(md_http_t **phttp, apr_pool_t *p, const char *user_agent)
+{
+    md_http_t *http;
+    apr_status_t rv = APR_SUCCESS;
+
+    if (!cur_impl) {
+        *phttp = NULL;
+        return APR_ENOTIMPL;
+    }
+    
+    if (!cur_init_done) {
+        if (APR_SUCCESS == (rv = cur_impl->init())) {
+            cur_init_done = 1;
+        }
+        else {
+            return rv;
+        }
+    }
+    
+    http = apr_pcalloc(p, sizeof(*http));
+    http->pool = p;
+    http->impl = cur_impl;
+    http->user_agent = apr_pstrdup(p, user_agent);
+    http->bucket_alloc = apr_bucket_alloc_create(p);
+    if (!http->bucket_alloc) {
+        return APR_EGENERAL;
+    }
+    *phttp = http;
+    return APR_SUCCESS;
+}
+
+void md_http_set_response_limit(md_http_t *http, apr_off_t resp_limit)
+{
+    http->resp_limit = resp_limit;
+}
+
+static apr_status_t req_create(md_http_request_t **preq, md_http_t *http, 
+                               const char *method, const char *url, struct apr_table_t *headers,
+                               md_http_cb *cb, void *baton)
+{
+    md_http_request_t *req;
+    apr_pool_t *pool;
+    apr_status_t rv;
+    
+    rv = apr_pool_create(&pool, http->pool);
+    if (rv != APR_SUCCESS) {
+        return rv;
+    }
+    
+    req = apr_pcalloc(pool, sizeof(*req));
+    req->id = next_req_id++;
+    req->pool = pool;
+    req->bucket_alloc = http->bucket_alloc;
+    req->http = http;
+    req->method = method;
+    req->url = url;
+    req->headers = headers? apr_table_copy(req->pool, headers) : apr_table_make(req->pool, 5);
+    req->resp_limit = http->resp_limit;
+    req->cb = cb;
+    req->baton = baton;
+    req->user_agent = http->user_agent;
+
+    *preq = req;
+    return rv;
+}
+
+void md_http_req_destroy(md_http_request_t *req) 
+{
+    if (req->internals) {
+        req->http->impl->req_cleanup(req);
+        req->internals = NULL;
+    }
+    apr_pool_destroy(req->pool);
+}
+
+static apr_status_t schedule(md_http_request_t *req, 
+                             apr_bucket_brigade *body, int detect_clen,
+                             long *preq_id) 
+{
+    apr_status_t rv;
+    
+    req->body = body;
+    req->body_len = body? -1 : 0;
+
+    if (req->body && detect_clen) {
+        rv = apr_brigade_length(req->body, 1, &req->body_len);
+        if (rv != APR_SUCCESS) {
+            md_http_req_destroy(req);
+            return rv;
+        }
+    }
+    
+    if (req->body_len == 0 && apr_strnatcasecmp("GET", req->method)) {
+        apr_table_setn(req->headers, "Content-Length", "0");
+    }
+    else if (req->body_len > 0) {
+        apr_table_setn(req->headers, "Content-Length", apr_off_t_toa(req->pool, req->body_len));
+    }
+    
+    if (preq_id) {
+        *preq_id = req->id;
+    }
+    
+    /* we send right away */
+    rv = req->http->impl->perform(req);
+    
+    return rv;
+}
+
+apr_status_t md_http_GET(struct md_http_t *http, 
+                         const char *url, struct apr_table_t *headers,
+                         md_http_cb *cb, void *baton, long *preq_id)
+{
+    md_http_request_t *req;
+    apr_status_t rv;
+    
+    rv = req_create(&req, http, "GET", url, headers, cb, baton);
+    if (rv != APR_SUCCESS) {
+        return rv;
+    }
+    
+    return schedule(req, NULL, 0, preq_id);
+}
+
+apr_status_t md_http_HEAD(struct md_http_t *http, 
+                          const char *url, struct apr_table_t *headers,
+                          md_http_cb *cb, void *baton, long *preq_id)
+{
+    md_http_request_t *req;
+    apr_status_t rv;
+    
+    rv = req_create(&req, http, "HEAD", url, headers, cb, baton);
+    if (rv != APR_SUCCESS) {
+        return rv;
+    }
+    
+    return schedule(req, NULL, 0, preq_id);
+}
+
+apr_status_t md_http_POST(struct md_http_t *http, const char *url, 
+                          struct apr_table_t *headers, const char *content_type, 
+                          apr_bucket_brigade *body,
+                          md_http_cb *cb, void *baton, long *preq_id)
+{
+    md_http_request_t *req;
+    apr_status_t rv;
+    
+    rv = req_create(&req, http, "POST", url, headers, cb, baton);
+    if (rv != APR_SUCCESS) {
+        return rv;
+    }
+    
+    if (content_type) {
+        apr_table_set(req->headers, "Content-Type", content_type); 
+    }
+    return schedule(req, body, 1, preq_id);
+}
+
+apr_status_t md_http_POSTd(md_http_t *http, const char *url, 
+                           struct apr_table_t *headers, const char *content_type, 
+                           const char *data, size_t data_len, 
+                           md_http_cb *cb, void *baton, long *preq_id)
+{
+    md_http_request_t *req;
+    apr_status_t rv;
+    apr_bucket_brigade *body = NULL;
+    
+    rv = req_create(&req, http, "POST", url, headers, cb, baton);
+    if (rv != APR_SUCCESS) {
+        return rv;
+    }
+
+    if (data && data_len > 0) {
+        body = apr_brigade_create(req->pool, req->http->bucket_alloc);
+        rv = apr_brigade_write(body, NULL, NULL, data, data_len);
+        if (rv != APR_SUCCESS) {
+            md_http_req_destroy(req);
+            return rv;
+        }
+    }
+    
+    if (content_type) {
+        apr_table_set(req->headers, "Content-Type", content_type); 
+    }
+     
+    return schedule(req, body, 1, preq_id);
+}
+
+apr_status_t md_http_await(md_http_t *http, long req_id)
+{
+    return APR_SUCCESS;
+}
+

Added: httpd/httpd/branches/trunk-md/modules/md/md_http.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/trunk-md/modules/md/md_http.h?rev=1804123&view=auto
==============================================================================
--- httpd/httpd/branches/trunk-md/modules/md/md_http.h (added)
+++ httpd/httpd/branches/trunk-md/modules/md/md_http.h Fri Aug  4 13:47:25 2017
@@ -0,0 +1,99 @@
+/* Copyright 2017 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed 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.
+ */
+
+#ifndef mod_md_md_http_h
+#define mod_md_md_http_h
+
+struct apr_table_t;
+struct apr_bucket_brigade;
+struct apr_bucket_alloc_t;
+
+typedef struct md_http_t md_http_t;
+
+typedef struct md_http_request_t md_http_request_t;
+typedef struct md_http_response_t md_http_response_t;
+
+typedef apr_status_t md_http_cb(const md_http_response_t *res);
+
+struct md_http_request_t {
+    long id;
+    md_http_t *http;
+    apr_pool_t *pool;
+    struct apr_bucket_alloc_t *bucket_alloc;
+    const char *method;
+    const char *url;
+    const char *user_agent;
+    apr_table_t *headers;
+    struct apr_bucket_brigade *body;
+    apr_off_t body_len;
+    apr_off_t resp_limit;
+    md_http_cb *cb;
+    void *baton;
+    void *internals;
+};
+
+struct md_http_response_t {
+    md_http_request_t *req;
+    apr_status_t rv;
+    int status;
+    apr_table_t *headers;
+    struct apr_bucket_brigade *body;
+};
+
+apr_status_t md_http_create(md_http_t **phttp, apr_pool_t *p, const char *user_agent);
+
+void md_http_set_response_limit(md_http_t *http, apr_off_t resp_limit);
+
+apr_status_t md_http_GET(md_http_t *http, 
+                         const char *url, struct apr_table_t *headers,
+                         md_http_cb *cb, void *baton, long *preq_id);
+
+apr_status_t md_http_HEAD(md_http_t *http, 
+                          const char *url, struct apr_table_t *headers,
+                          md_http_cb *cb, void *baton, long *preq_id);
+
+apr_status_t md_http_POST(md_http_t *http, const char *url, 
+                          struct apr_table_t *headers, const char *content_type, 
+                          struct apr_bucket_brigade *body,
+                          md_http_cb *cb, void *baton, long *preq_id);
+
+apr_status_t md_http_POSTd(md_http_t *http, const char *url, 
+                           struct apr_table_t *headers, const char *content_type, 
+                           const char *data, size_t data_len, 
+                           md_http_cb *cb, void *baton, long *preq_id);
+
+apr_status_t md_http_await(md_http_t *http, long req_id);
+
+void md_http_req_destroy(md_http_request_t *req);
+
+/**************************************************************************************************/
+/* interface to implementation */
+
+typedef apr_status_t md_http_init_cb(void);
+typedef void md_http_req_cleanup_cb(md_http_request_t *req);
+typedef apr_status_t md_http_perform_cb(md_http_request_t *req);
+
+typedef struct md_http_impl_t md_http_impl_t;
+struct md_http_impl_t {
+    md_http_init_cb *init;
+    md_http_req_cleanup_cb *req_cleanup;
+    md_http_perform_cb *perform;
+};
+
+void md_http_use_implementation(md_http_impl_t *impl);
+
+
+
+#endif /* md_http_h */



Mime
View raw message