summaryrefslogtreecommitdiffstats
path: root/modules/md/md_crypt.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/md/md_crypt.c')
-rw-r--r--modules/md/md_crypt.c1334
1 files changed, 1085 insertions, 249 deletions
diff --git a/modules/md/md_crypt.c b/modules/md/md_crypt.c
index e0aac3e..4b2af89 100644
--- a/modules/md/md_crypt.c
+++ b/modules/md/md_crypt.c
@@ -22,19 +22,26 @@
#include <apr_buckets.h>
#include <apr_file_io.h>
#include <apr_strings.h>
+#include <httpd.h>
+#include <http_core.h>
#include <openssl/err.h>
#include <openssl/evp.h>
+#include <openssl/hmac.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/x509v3.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/core_names.h>
+#endif
#include "md.h"
#include "md_crypt.h"
#include "md_json.h"
#include "md_log.h"
#include "md_http.h"
+#include "md_time.h"
#include "md_util.h"
/* getpid for *NIX */
@@ -57,6 +64,17 @@
#define MD_USE_OPENSSL_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
#endif
+#if (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x3050000fL)) || (OPENSSL_VERSION_NUMBER < 0x10100000L)
+/* Missing from LibreSSL < 3.5.0 and only available since OpenSSL v1.1.x */
+#ifndef OPENSSL_NO_CT
+#define OPENSSL_NO_CT
+#endif
+#endif
+
+#ifndef OPENSSL_NO_CT
+#include <openssl/ct.h>
+#endif
+
static int initialized;
struct md_pkey_t {
@@ -142,17 +160,13 @@ apr_status_t md_crypt_init(apr_pool_t *pool)
return APR_SUCCESS;
}
-typedef struct {
- char *data;
- apr_size_t len;
-} buffer_rec;
-
static apr_status_t fwrite_buffer(void *baton, apr_file_t *f, apr_pool_t *p)
{
- buffer_rec *buf = baton;
+ md_data_t *buf = baton;
+ apr_size_t wlen;
(void)p;
- return apr_file_write_full(f, buf->data, buf->len, &buf->len);
+ return apr_file_write_full(f, buf->data, buf->len, &wlen);
}
apr_status_t md_rand_bytes(unsigned char *buf, apr_size_t len, apr_pool_t *p)
@@ -183,8 +197,10 @@ static int pem_passwd(char *buf, int size, int rwflag, void *baton)
size = (int)ctx->pass_len;
}
memcpy(buf, ctx->pass_phrase, (size_t)size);
+ } else {
+ return 0;
}
- return ctx->pass_len;
+ return size;
}
/**************************************************************************************************/
@@ -197,7 +213,8 @@ static int pem_passwd(char *buf, int size, int rwflag, void *baton)
*/
static apr_time_t md_asn1_time_get(const ASN1_TIME* time)
{
-#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10002000L || (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x3060000fL)
/* courtesy: https://stackoverflow.com/questions/10975542/asn1-time-to-time-t-conversion#11263731
* all bugs are mine */
apr_time_exp_t t;
@@ -246,10 +263,98 @@ static apr_time_t md_asn1_time_get(const ASN1_TIME* time)
#endif
}
+apr_time_t md_asn1_generalized_time_get(void *ASN1_GENERALIZEDTIME)
+{
+ return md_asn1_time_get(ASN1_GENERALIZEDTIME);
+}
+
+/**************************************************************************************************/
+/* OID/NID things */
+
+static int get_nid(const char *num, const char *sname, const char *lname)
+{
+ /* Funny API, an OID for a feature might be configured or
+ * maybe not. In the second case, we need to add it. But adding
+ * when it already is there is an error... */
+ int nid = OBJ_txt2nid(num);
+ if (NID_undef == nid) {
+ nid = OBJ_create(num, sname, lname);
+ }
+ return nid;
+}
+
+#define MD_GET_NID(x) get_nid(MD_OID_##x##_NUM, MD_OID_##x##_SNAME, MD_OID_##x##_LNAME)
/**************************************************************************************************/
/* private keys */
+md_pkeys_spec_t *md_pkeys_spec_make(apr_pool_t *p)
+{
+ md_pkeys_spec_t *pks;
+
+ pks = apr_pcalloc(p, sizeof(*pks));
+ pks->p = p;
+ pks->specs = apr_array_make(p, 2, sizeof(md_pkey_spec_t*));
+ return pks;
+}
+
+void md_pkeys_spec_add(md_pkeys_spec_t *pks, md_pkey_spec_t *spec)
+{
+ APR_ARRAY_PUSH(pks->specs, md_pkey_spec_t*) = spec;
+}
+
+void md_pkeys_spec_add_default(md_pkeys_spec_t *pks)
+{
+ md_pkey_spec_t *spec;
+
+ spec = apr_pcalloc(pks->p, sizeof(*spec));
+ spec->type = MD_PKEY_TYPE_DEFAULT;
+ md_pkeys_spec_add(pks, spec);
+}
+
+int md_pkeys_spec_contains_rsa(md_pkeys_spec_t *pks)
+{
+ md_pkey_spec_t *spec;
+ int i;
+ for (i = 0; i < pks->specs->nelts; ++i) {
+ spec = APR_ARRAY_IDX(pks->specs, i, md_pkey_spec_t*);
+ if (MD_PKEY_TYPE_RSA == spec->type) return 1;
+ }
+ return 0;
+}
+
+void md_pkeys_spec_add_rsa(md_pkeys_spec_t *pks, unsigned int bits)
+{
+ md_pkey_spec_t *spec;
+
+ spec = apr_pcalloc(pks->p, sizeof(*spec));
+ spec->type = MD_PKEY_TYPE_RSA;
+ spec->params.rsa.bits = bits;
+ md_pkeys_spec_add(pks, spec);
+}
+
+int md_pkeys_spec_contains_ec(md_pkeys_spec_t *pks, const char *curve)
+{
+ md_pkey_spec_t *spec;
+ int i;
+ for (i = 0; i < pks->specs->nelts; ++i) {
+ spec = APR_ARRAY_IDX(pks->specs, i, md_pkey_spec_t*);
+ if (MD_PKEY_TYPE_EC == spec->type
+ && !apr_strnatcasecmp(curve, spec->params.ec.curve)) return 1;
+ }
+ return 0;
+}
+
+void md_pkeys_spec_add_ec(md_pkeys_spec_t *pks, const char *curve)
+{
+ md_pkey_spec_t *spec;
+
+ spec = apr_pcalloc(pks->p, sizeof(*spec));
+ spec->type = MD_PKEY_TYPE_EC;
+ spec->params.ec.curve = apr_pstrdup(pks->p, curve);
+ md_pkeys_spec_add(pks, spec);
+}
+
md_json_t *md_pkey_spec_to_json(const md_pkey_spec_t *spec, apr_pool_t *p)
{
md_json_t *json = md_json_create(p);
@@ -264,6 +369,12 @@ md_json_t *md_pkey_spec_to_json(const md_pkey_spec_t *spec, apr_pool_t *p)
md_json_setl((long)spec->params.rsa.bits, json, MD_KEY_BITS, NULL);
}
break;
+ case MD_PKEY_TYPE_EC:
+ md_json_sets("EC", json, MD_KEY_TYPE, NULL);
+ if (spec->params.ec.curve) {
+ md_json_sets(spec->params.ec.curve, json, MD_KEY_CURVE, NULL);
+ }
+ break;
default:
md_json_sets("Unsupported", json, MD_KEY_TYPE, NULL);
break;
@@ -272,6 +383,27 @@ md_json_t *md_pkey_spec_to_json(const md_pkey_spec_t *spec, apr_pool_t *p)
return json;
}
+static apr_status_t spec_to_json(void *value, md_json_t *json, apr_pool_t *p, void *baton)
+{
+ md_json_t *jspec;
+
+ (void)baton;
+ jspec = md_pkey_spec_to_json((md_pkey_spec_t*)value, p);
+ return md_json_setj(jspec, json, NULL);
+}
+
+md_json_t *md_pkeys_spec_to_json(const md_pkeys_spec_t *pks, apr_pool_t *p)
+{
+ md_json_t *j;
+
+ if (pks->specs->nelts == 1) {
+ return md_pkey_spec_to_json(md_pkeys_spec_get(pks, 0), p);
+ }
+ j = md_json_create(p);
+ md_json_seta(pks->specs, spec_to_json, (void*)pks, j, "specs", NULL);
+ return md_json_getj(j, "specs", NULL);
+}
+
md_pkey_spec_t *md_pkey_spec_from_json(struct md_json_t *json, apr_pool_t *p)
{
md_pkey_spec_t *spec = apr_pcalloc(p, sizeof(*spec));
@@ -293,29 +425,161 @@ md_pkey_spec_t *md_pkey_spec_from_json(struct md_json_t *json, apr_pool_t *p)
spec->params.rsa.bits = MD_PKEY_RSA_BITS_DEF;
}
}
+ else if (!apr_strnatcasecmp("EC", s)) {
+ spec->type = MD_PKEY_TYPE_EC;
+ s = md_json_gets(json, MD_KEY_CURVE, NULL);
+ if (s) {
+ spec->params.ec.curve = apr_pstrdup(p, s);
+ }
+ else {
+ spec->params.ec.curve = NULL;
+ }
+ }
}
return spec;
}
-int md_pkey_spec_eq(md_pkey_spec_t *spec1, md_pkey_spec_t *spec2)
+static apr_status_t spec_from_json(void **pvalue, md_json_t *json, apr_pool_t *p, void *baton)
+{
+ (void)baton;
+ *pvalue = md_pkey_spec_from_json(json, p);
+ return APR_SUCCESS;
+}
+
+md_pkeys_spec_t *md_pkeys_spec_from_json(struct md_json_t *json, apr_pool_t *p)
+{
+ md_pkeys_spec_t *pks;
+ md_pkey_spec_t *spec;
+
+ pks = md_pkeys_spec_make(p);
+ if (md_json_is(MD_JSON_TYPE_ARRAY, json, NULL)) {
+ md_json_geta(pks->specs, spec_from_json, pks, json, NULL);
+ }
+ else {
+ spec = md_pkey_spec_from_json(json, p);
+ md_pkeys_spec_add(pks, spec);
+ }
+ return pks;
+}
+
+static int pkey_spec_eq(md_pkey_spec_t *s1, md_pkey_spec_t *s2)
{
- if (spec1 == spec2) {
+ if (s1 == s2) {
return 1;
}
- if (spec1 && spec2 && spec1->type == spec2->type) {
- switch (spec1->type) {
+ if (s1 && s2 && s1->type == s2->type) {
+ switch (s1->type) {
case MD_PKEY_TYPE_DEFAULT:
return 1;
case MD_PKEY_TYPE_RSA:
- if (spec1->params.rsa.bits == spec2->params.rsa.bits) {
+ if (s1->params.rsa.bits == s2->params.rsa.bits) {
return 1;
}
break;
+ case MD_PKEY_TYPE_EC:
+ if (s1->params.ec.curve == s2->params.ec.curve) {
+ return 1;
+ }
+ else if (!s1->params.ec.curve || !s2->params.ec.curve) {
+ return 0;
+ }
+ return !strcmp(s1->params.ec.curve, s2->params.ec.curve);
}
}
return 0;
}
+int md_pkeys_spec_eq(md_pkeys_spec_t *pks1, md_pkeys_spec_t *pks2)
+{
+ int i;
+
+ if (pks1 == pks2) {
+ return 1;
+ }
+ if (pks1 && pks2 && pks1->specs->nelts == pks2->specs->nelts) {
+ for(i = 0; i < pks1->specs->nelts; ++i) {
+ if (!pkey_spec_eq(APR_ARRAY_IDX(pks1->specs, i, md_pkey_spec_t *),
+ APR_ARRAY_IDX(pks2->specs, i, md_pkey_spec_t *))) {
+ return 0;
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static md_pkey_spec_t *pkey_spec_clone(apr_pool_t *p, md_pkey_spec_t *spec)
+{
+ md_pkey_spec_t *nspec;
+
+ nspec = apr_pcalloc(p, sizeof(*nspec));
+ nspec->type = spec->type;
+ switch (spec->type) {
+ case MD_PKEY_TYPE_DEFAULT:
+ break;
+ case MD_PKEY_TYPE_RSA:
+ nspec->params.rsa.bits = spec->params.rsa.bits;
+ break;
+ case MD_PKEY_TYPE_EC:
+ nspec->params.ec.curve = apr_pstrdup(p, spec->params.ec.curve);
+ break;
+ }
+ return nspec;
+}
+
+const char *md_pkey_spec_name(const md_pkey_spec_t *spec)
+{
+ if (!spec) return "rsa";
+ switch (spec->type) {
+ case MD_PKEY_TYPE_DEFAULT:
+ case MD_PKEY_TYPE_RSA:
+ return "rsa";
+ case MD_PKEY_TYPE_EC:
+ return spec->params.ec.curve;
+ }
+ return "unknown";
+}
+
+int md_pkeys_spec_is_empty(const md_pkeys_spec_t *pks)
+{
+ return NULL == pks || 0 == pks->specs->nelts;
+}
+
+md_pkeys_spec_t *md_pkeys_spec_clone(apr_pool_t *p, const md_pkeys_spec_t *pks)
+{
+ md_pkeys_spec_t *npks = NULL;
+ md_pkey_spec_t *spec;
+ int i;
+
+ if (pks && pks->specs->nelts > 0) {
+ npks = apr_pcalloc(p, sizeof(*npks));
+ npks->specs = apr_array_make(p, pks->specs->nelts, sizeof(md_pkey_spec_t*));
+ for (i = 0; i < pks->specs->nelts; ++i) {
+ spec = APR_ARRAY_IDX(pks->specs, i, md_pkey_spec_t*);
+ APR_ARRAY_PUSH(npks->specs, md_pkey_spec_t*) = pkey_spec_clone(p, spec);
+ }
+ }
+ return npks;
+}
+
+int md_pkeys_spec_count(const md_pkeys_spec_t *pks)
+{
+ return md_pkeys_spec_is_empty(pks)? 1 : pks->specs->nelts;
+}
+
+static md_pkey_spec_t PkeySpecDef = { MD_PKEY_TYPE_DEFAULT, {{ 0 }} };
+
+md_pkey_spec_t *md_pkeys_spec_get(const md_pkeys_spec_t *pks, int index)
+{
+ if (md_pkeys_spec_is_empty(pks)) {
+ return index == 1? &PkeySpecDef : NULL;
+ }
+ else if (pks && index >= 0 && index < pks->specs->nelts) {
+ return APR_ARRAY_IDX(pks->specs, index, md_pkey_spec_t*);
+ }
+ return NULL;
+}
+
static md_pkey_t *make_pkey(apr_pool_t *p)
{
md_pkey_t *pkey = apr_pcalloc(p, sizeof(*pkey));
@@ -377,13 +641,14 @@ apr_status_t md_pkey_fload(md_pkey_t **ppkey, apr_pool_t *p,
return rv;
}
-static apr_status_t pkey_to_buffer(buffer_rec *buffer, md_pkey_t *pkey, apr_pool_t *p,
+static apr_status_t pkey_to_buffer(md_data_t *buf, 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;
+ apr_status_t rv = APR_SUCCESS;
passwd_ctx ctx;
unsigned long err;
int i;
@@ -392,7 +657,8 @@ static apr_status_t pkey_to_buffer(buffer_rec *buffer, md_pkey_t *pkey, apr_pool
return APR_ENOMEM;
}
if (pass_len > INT_MAX) {
- return APR_EINVAL;
+ rv = APR_EINVAL;
+ goto cleanup;
}
if (pass && pass_len > 0) {
ctx.pass_phrase = pass;
@@ -401,35 +667,42 @@ static apr_status_t pkey_to_buffer(buffer_rec *buffer, md_pkey_t *pkey, apr_pool
cb_baton = &ctx;
cipher = EVP_aes_256_cbc();
if (!cipher) {
- return APR_ENOTIMPL;
+ rv = APR_ENOTIMPL;
+ goto cleanup;
}
}
ERR_clear_error();
+#if 1
+ if (!PEM_write_bio_PKCS8PrivateKey(bio, pkey->pkey, cipher, NULL, 0, cb, cb_baton)) {
+#else
if (!PEM_write_bio_PrivateKey(bio, pkey->pkey, cipher, NULL, 0, cb, cb_baton)) {
- BIO_free(bio);
+#endif
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;
+ rv = APR_EINVAL;
+ goto cleanup;
}
+ md_data_null(buf);
i = BIO_pending(bio);
if (i > 0) {
- buffer->data = apr_palloc(p, (apr_size_t)i + 1);
- i = BIO_read(bio, buffer->data, i);
- buffer->data[i] = '\0';
- buffer->len = (apr_size_t)i;
+ buf->data = apr_palloc(p, (apr_size_t)i);
+ i = BIO_read(bio, (char*)buf->data, i);
+ buf->len = (apr_size_t)i;
}
+
+cleanup:
BIO_free(bio);
- return APR_SUCCESS;
+ return rv;
}
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_rec buffer;
+ md_data_t buffer;
apr_status_t rv;
if (APR_SUCCESS == (rv = pkey_to_buffer(&buffer, pkey, p, pass_phrase, pass_len))) {
@@ -440,6 +713,71 @@ apr_status_t md_pkey_fsave(md_pkey_t *pkey, apr_pool_t *p,
return rv;
}
+apr_status_t md_pkey_read_http(md_pkey_t **ppkey, apr_pool_t *pool,
+ const struct md_http_response_t *res)
+{
+ apr_status_t rv;
+ apr_off_t data_len;
+ char *pem_data;
+ apr_size_t pem_len;
+ md_pkey_t *pkey;
+ BIO *bf;
+ passwd_ctx ctx;
+
+ rv = apr_brigade_length(res->body, 1, &data_len);
+ if (APR_SUCCESS != rv) goto leave;
+ if (data_len > 1024*1024) { /* certs usually are <2k each */
+ rv = APR_EINVAL;
+ goto leave;
+ }
+ rv = apr_brigade_pflatten(res->body, &pem_data, &pem_len, res->req->pool);
+ if (APR_SUCCESS != rv) goto leave;
+
+ if (NULL == (bf = BIO_new_mem_buf(pem_data, (int)pem_len))) {
+ rv = APR_ENOMEM;
+ goto leave;
+ }
+ pkey = make_pkey(pool);
+ ctx.pass_phrase = NULL;
+ ctx.pass_len = 0;
+ ERR_clear_error();
+ pkey->pkey = PEM_read_bio_PrivateKey(bf, NULL, NULL, &ctx);
+ BIO_free(bf);
+
+ if (pkey->pkey == NULL) {
+ unsigned long err = ERR_get_error();
+ rv = APR_EINVAL;
+ md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, pool,
+ "error loading pkey from http response: %s",
+ ERR_error_string(err, NULL));
+ goto leave;
+ }
+ rv = APR_SUCCESS;
+ apr_pool_cleanup_register(pool, pkey, pkey_cleanup, apr_pool_cleanup_null);
+
+leave:
+ *ppkey = (APR_SUCCESS == rv)? pkey : NULL;
+ return rv;
+}
+
+/* Determine the message digest used for signing with the given private key.
+ */
+static const EVP_MD *pkey_get_MD(md_pkey_t *pkey)
+{
+ switch (EVP_PKEY_id(pkey->pkey)) {
+#ifdef NID_ED25519
+ case NID_ED25519:
+ return NULL;
+#endif
+#ifdef NID_ED448
+ case NID_ED448:
+ return NULL;
+#endif
+ default:
+ return EVP_sha256();
+ }
+}
+
static apr_status_t gen_rsa(md_pkey_t **ppkey, apr_pool_t *p, unsigned int bits)
{
EVP_PKEY_CTX *ctx = NULL;
@@ -465,6 +803,143 @@ static apr_status_t gen_rsa(md_pkey_t **ppkey, apr_pool_t *p, unsigned int bits)
return rv;
}
+static apr_status_t check_EC_curve(int nid, apr_pool_t *p) {
+ EC_builtin_curve *curves = NULL;
+ size_t nc, i;
+ int rv = APR_ENOENT;
+
+ nc = EC_get_builtin_curves(NULL, 0);
+ if (NULL == (curves = OPENSSL_malloc(sizeof(*curves) * nc)) ||
+ nc != EC_get_builtin_curves(curves, nc)) {
+ rv = APR_EGENERAL;
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p,
+ "error looking up OpenSSL builtin EC curves");
+ goto leave;
+ }
+ for (i = 0; i < nc; ++i) {
+ if (nid == curves[i].nid) {
+ rv = APR_SUCCESS;
+ break;
+ }
+ }
+leave:
+ OPENSSL_free(curves);
+ return rv;
+}
+
+static apr_status_t gen_ec(md_pkey_t **ppkey, apr_pool_t *p, const char *curve)
+{
+ EVP_PKEY_CTX *ctx = NULL;
+ apr_status_t rv;
+ int curve_nid = NID_undef;
+
+ /* 1. Convert the cure into its registered identifier. Curves can be known under
+ * different names.
+ * 2. Determine, if the curve is supported by OpenSSL (or whatever is linked).
+ * 3. Generate the key, respecting the specific quirks some curves require.
+ */
+ curve_nid = EC_curve_nist2nid(curve);
+ /* In case this fails, try some names from other standards, like SECG */
+#ifdef NID_secp384r1
+ if (NID_undef == curve_nid && !apr_strnatcasecmp("secp384r1", curve)) {
+ curve_nid = NID_secp384r1;
+ curve = EC_curve_nid2nist(curve_nid);
+ }
+#endif
+#ifdef NID_X9_62_prime256v1
+ if (NID_undef == curve_nid && !apr_strnatcasecmp("secp256r1", curve)) {
+ curve_nid = NID_X9_62_prime256v1;
+ curve = EC_curve_nid2nist(curve_nid);
+ }
+#endif
+#ifdef NID_X9_62_prime192v1
+ if (NID_undef == curve_nid && !apr_strnatcasecmp("secp192r1", curve)) {
+ curve_nid = NID_X9_62_prime192v1;
+ curve = EC_curve_nid2nist(curve_nid);
+ }
+#endif
+#if defined(NID_X25519) && (!defined(LIBRESSL_VERSION_NUMBER) || \
+ LIBRESSL_VERSION_NUMBER >= 0x3070000fL)
+ if (NID_undef == curve_nid && !apr_strnatcasecmp("X25519", curve)) {
+ curve_nid = NID_X25519;
+ curve = EC_curve_nid2nist(curve_nid);
+ }
+#endif
+ if (NID_undef == curve_nid) {
+ /* OpenSSL object/curve names */
+ curve_nid = OBJ_sn2nid(curve);
+ }
+ if (NID_undef == curve_nid) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "ec curve unknown: %s", curve);
+ rv = APR_ENOTIMPL; goto leave;
+ }
+
+ *ppkey = make_pkey(p);
+ switch (curve_nid) {
+
+#if defined(NID_X25519) && (!defined(LIBRESSL_VERSION_NUMBER) || \
+ LIBRESSL_VERSION_NUMBER >= 0x3070000fL)
+ case NID_X25519:
+ /* no parameters */
+ if (NULL == (ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL))
+ || EVP_PKEY_keygen_init(ctx) <= 0
+ || EVP_PKEY_keygen(ctx, &(*ppkey)->pkey) <= 0) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, p,
+ "error generate EC key for group: %s", curve);
+ rv = APR_EGENERAL; goto leave;
+ }
+ rv = APR_SUCCESS;
+ break;
+#endif
+
+#if defined(NID_X448) && !defined(LIBRESSL_VERSION_NUMBER)
+ case NID_X448:
+ /* no parameters */
+ if (NULL == (ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X448, NULL))
+ || EVP_PKEY_keygen_init(ctx) <= 0
+ || EVP_PKEY_keygen(ctx, &(*ppkey)->pkey) <= 0) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, p,
+ "error generate EC key for group: %s", curve);
+ rv = APR_EGENERAL; goto leave;
+ }
+ rv = APR_SUCCESS;
+ break;
+#endif
+
+ default:
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ if (APR_SUCCESS != (rv = check_EC_curve(curve_nid, p))) goto leave;
+ if (NULL == (ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL))
+ || EVP_PKEY_paramgen_init(ctx) <= 0
+ || EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, curve_nid) <= 0
+ || EVP_PKEY_CTX_set_ec_param_enc(ctx, OPENSSL_EC_NAMED_CURVE) <= 0
+ || EVP_PKEY_keygen_init(ctx) <= 0
+ || EVP_PKEY_keygen(ctx, &(*ppkey)->pkey) <= 0) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, p,
+ "error generate EC key for group: %s", curve);
+ rv = APR_EGENERAL; goto leave;
+ }
+#else
+ if (APR_SUCCESS != (rv = check_EC_curve(curve_nid, p))) goto leave;
+ if (NULL == (ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL))
+ || EVP_PKEY_keygen_init(ctx) <= 0
+ || EVP_PKEY_CTX_ctrl_str(ctx, "ec_paramgen_curve", curve) <= 0
+ || EVP_PKEY_keygen(ctx, &(*ppkey)->pkey) <= 0) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, p,
+ "error generate EC key for group: %s", curve);
+ rv = APR_EGENERAL; goto leave;
+ }
+#endif
+ rv = APR_SUCCESS;
+ break;
+ }
+
+leave:
+ if (APR_SUCCESS != rv) *ppkey = NULL;
+ EVP_PKEY_CTX_free(ctx);
+ return rv;
+}
+
apr_status_t md_pkey_gen(md_pkey_t **ppkey, apr_pool_t *p, md_pkey_spec_t *spec)
{
md_pkey_type_t ptype = spec? spec->type : MD_PKEY_TYPE_DEFAULT;
@@ -473,6 +948,8 @@ apr_status_t md_pkey_gen(md_pkey_t **ppkey, apr_pool_t *p, md_pkey_spec_t *spec)
return gen_rsa(ppkey, p, MD_PKEY_RSA_BITS_DEF);
case MD_PKEY_TYPE_RSA:
return gen_rsa(ppkey, p, spec->params.rsa.bits);
+ case MD_PKEY_TYPE_EC:
+ return gen_ec(ppkey, p, spec->params.ec.curve);
default:
return APR_ENOTIMPL;
}
@@ -501,59 +978,77 @@ static void RSA_get0_key(const RSA *r,
static const char *bn64(const BIGNUM *b, apr_pool_t *p)
{
if (b) {
- apr_size_t len = (apr_size_t)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);
- }
+ md_data_t buffer;
+
+ md_data_pinit(&buffer, (apr_size_t)BN_num_bytes(b), p);
+ if (buffer.data) {
+ BN_bn2bin(b, (unsigned char *)buffer.data);
+ return md_util_base64url_encode(&buffer, 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;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ const RSA *rsa = EVP_PKEY_get0_RSA(pkey->pkey);
+ if (rsa) {
+ const BIGNUM *e;
+ RSA_get0_key(rsa, NULL, &e, NULL);
+ return bn64(e, p);
+ }
+#else
+ BIGNUM *e = NULL;
+ if (EVP_PKEY_get_bn_param(pkey->pkey, OSSL_PKEY_PARAM_RSA_E, &e)) {
+ const char *e64 = bn64(e, p);
+ BN_free(e);
+ return e64;
}
- RSA_get0_key(rsa, NULL, &e, NULL);
- return bn64(e, p);
+#endif
+ return NULL;
}
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;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ const RSA *rsa = EVP_PKEY_get0_RSA(pkey->pkey);
+ if (rsa) {
+ const BIGNUM *n;
+ RSA_get0_key(rsa, &n, NULL, NULL);
+ return bn64(n, p);
+ }
+#else
+ BIGNUM *n = NULL;
+ if (EVP_PKEY_get_bn_param(pkey->pkey, OSSL_PKEY_PARAM_RSA_N, &n)) {
+ const char *n64 = bn64(n, p);
+ BN_free(n);
+ return n64;
}
- RSA_get0_key(rsa, &n, NULL, NULL);
- return bn64(n, p);
+#endif
+ return NULL;
}
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;
+ md_data_t buffer;
unsigned int blen;
const char *sign64 = NULL;
apr_status_t rv = APR_ENOMEM;
-
- buffer = apr_pcalloc(p, (apr_size_t)EVP_PKEY_size(pkey->pkey));
- if (buffer) {
+
+ md_data_pinit(&buffer, (apr_size_t)EVP_PKEY_size(pkey->pkey), p);
+ if (buffer.data) {
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 (EVP_SignFinal(ctx, (unsigned char*)buffer.data, &blen, pkey->pkey)) {
+ buffer.len = blen;
+ sign64 = md_util_base64url_encode(&buffer, p);
if (sign64) {
rv = APR_SUCCESS;
}
@@ -575,56 +1070,42 @@ apr_status_t md_crypt_sign64(const char **psign64, md_pkey_t *pkey, apr_pool_t *
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)
+static apr_status_t sha256_digest(md_data_t **pdigest, apr_pool_t *p, const md_data_t *buf)
{
EVP_MD_CTX *ctx = NULL;
- unsigned char *buffer;
+ md_data_t *digest;
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;
- }
+ unsigned int dlen;
+
+ digest = md_data_pmake(EVP_MAX_MD_SIZE, p);
+ 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, (unsigned char*)buf->data, buf->len)) {
+ if (EVP_DigestFinal(ctx, (unsigned char*)digest->data, &dlen)) {
+ digest->len = dlen;
+ 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;
+ if (ctx) {
+ EVP_MD_CTX_destroy(ctx);
}
+ *pdigest = (APR_SUCCESS == rv)? digest : NULL;
return rv;
}
-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_digest64(const char **pdigest64, apr_pool_t *p, const md_data_t *d)
{
const char *digest64 = NULL;
- unsigned char *buffer;
- size_t blen;
+ md_data_t *digest;
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))) {
+ if (APR_SUCCESS == (rv = sha256_digest(&digest, p, d))) {
+ if (NULL == (digest64 = md_util_base64url_encode(digest, p))) {
rv = APR_EGENERAL;
}
}
@@ -632,47 +1113,41 @@ apr_status_t md_crypt_sha256_digest64(const char **pdigest64, apr_pool_t *p,
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)
+ const md_data_t *data)
{
- char *dhex = NULL, *cp;
- const char * x;
- unsigned char *buffer;
- size_t blen;
+ md_data_t *digest;
apr_status_t rv;
- unsigned 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];
- }
+ if (APR_SUCCESS == (rv = sha256_digest(&digest, p, data))) {
+ return md_data_to_hex(pdigesthex, 0, p, digest);
}
- *pdigesthex = dhex;
+ *pdigesthex = NULL;
+ return rv;
+}
+
+apr_status_t md_crypt_hmac64(const char **pmac64, const md_data_t *hmac_key,
+ apr_pool_t *p, const char *d, size_t dlen)
+{
+ const char *mac64 = NULL;
+ unsigned char *s;
+ unsigned int digest_len = 0;
+ md_data_t *digest;
+ apr_status_t rv = APR_SUCCESS;
+
+ digest = md_data_pmake(EVP_MAX_MD_SIZE, p);
+ s = HMAC(EVP_sha256(), (const unsigned char*)hmac_key->data, (int)hmac_key->len,
+ (const unsigned char*)d, (size_t)dlen,
+ (unsigned char*)digest->data, &digest_len);
+ if (!s) {
+ rv = APR_EINVAL;
+ goto cleanup;
+ }
+ digest->len = digest_len;
+ mac64 = md_util_base64url_encode(digest, p);
+
+cleanup:
+ *pmac64 = (APR_SUCCESS == rv)? mac64 : NULL;
return rv;
}
@@ -695,26 +1170,47 @@ static apr_status_t cert_cleanup(void *data)
return APR_SUCCESS;
}
-static md_cert_t *make_cert(apr_pool_t *p, X509 *x509)
+md_cert_t *md_cert_wrap(apr_pool_t *p, void *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)
+md_cert_t *md_cert_make(apr_pool_t *p, void *x509)
{
- cert_cleanup(cert);
+ md_cert_t *cert = md_cert_wrap(p, x509);
+ apr_pool_cleanup_register(p, cert, cert_cleanup, apr_pool_cleanup_null);
+ return cert;
}
-void *md_cert_get_X509(struct md_cert_t *cert)
+void *md_cert_get_X509(const md_cert_t *cert)
{
return cert->x509;
}
+const char *md_cert_get_serial_number(const md_cert_t *cert, apr_pool_t *p)
+{
+ const char *s = "";
+ BIGNUM *bn;
+ const char *serial;
+ const ASN1_INTEGER *ai = X509_get_serialNumber(cert->x509);
+ if (ai) {
+ bn = ASN1_INTEGER_to_BN(ai, NULL);
+ serial = BN_bn2hex(bn);
+ s = apr_pstrdup(p, serial);
+ OPENSSL_free((void*)serial);
+ OPENSSL_free((void*)bn);
+ }
+ return s;
+}
+
+int md_certs_are_equal(const md_cert_t *a, const md_cert_t *b)
+{
+ return X509_cmp(a->x509, b->x509) == 0;
+}
+
int md_cert_is_valid_now(const md_cert_t *cert)
{
return ((X509_cmp_current_time(X509_get_notBefore(cert->x509)) < 0)
@@ -726,23 +1222,31 @@ 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)
+apr_time_t md_cert_get_not_after(const md_cert_t *cert)
{
return md_asn1_time_get(X509_get_notAfter(cert->x509));
}
-apr_time_t md_cert_get_not_before(md_cert_t *cert)
+apr_time_t md_cert_get_not_before(const md_cert_t *cert)
{
return md_asn1_time_get(X509_get_notBefore(cert->x509));
}
+md_timeperiod_t md_cert_get_valid(const md_cert_t *cert)
+{
+ md_timeperiod_t p;
+ p.start = md_cert_get_not_before(cert);
+ p.end = md_cert_get_not_after(cert);
+ return p;
+}
+
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;
+ apr_array_header_t *alt_names;
+
+ md_cert_get_alt_names(&alt_names, cert, cert->pool);
+ if (alt_names) {
+ return md_array_str_index(alt_names, domain_name, 0, 0) >= 0;
}
return 0;
}
@@ -760,7 +1264,7 @@ int md_cert_covers_md(md_cert_t *cert, const md_t *md)
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) {
+ if (!md_dns_domains_match(cert->alt_names, name)) {
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, cert->pool,
"md domain %s not covered by cert", name);
return 0;
@@ -774,7 +1278,7 @@ int md_cert_covers_md(md_cert_t *cert, const md_t *md)
return 0;
}
-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_issuers_uri(const char **puri, const md_cert_t *cert, apr_pool_t *p)
{
apr_status_t rv = APR_ENOENT;
STACK_OF(ACCESS_DESCRIPTION) *xinfos;
@@ -801,17 +1305,19 @@ apr_status_t md_cert_get_issuers_uri(const char **puri, md_cert_t *cert, apr_poo
return rv;
}
-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_get_alt_names(apr_array_header_t **pnames, const 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 = X509_get_ext_d2i(cert->x509, NID_subject_alt_name, NULL, NULL);
if (xalt_names) {
GENERAL_NAME *cval;
+ const unsigned char *ip;
+ int len;
names = apr_array_make(p, sk_GENERAL_NAME_num(xalt_names), sizeof(char *));
for (i = 0; i < sk_GENERAL_NAME_num(xalt_names); ++i) {
@@ -819,11 +1325,33 @@ apr_status_t md_cert_get_alt_names(apr_array_header_t **pnames, md_cert_t *cert,
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;
+ case GEN_IPADD:
+ len = ASN1_STRING_length(cval->d.iPAddress);
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ ip = ASN1_STRING_data(cval->d.iPAddress);
+#else
+ ip = ASN1_STRING_get0_data(cval->d.iPAddress);
+#endif
+ if (len == 4) /* IPv4 address */
+ APR_ARRAY_PUSH(names, const char *) = apr_psprintf(p, "%u.%u.%u.%u",
+ ip[0], ip[1], ip[2], ip[3]);
+ else if (len == 16) /* IPv6 address */
+ APR_ARRAY_PUSH(names, const char *) = apr_psprintf(p, "%02x%02x%02x%02x:"
+ "%02x%02x%02x%02x:"
+ "%02x%02x%02x%02x:"
+ "%02x%02x%02x%02x",
+ ip[0], ip[1], ip[2], ip[3],
+ ip[4], ip[5], ip[6], ip[7],
+ ip[8], ip[9], ip[10], ip[11],
+ ip[12], ip[13], ip[14], ip[15]);
+ else {
+ ; /* Unknown address type - Log? Assert? */
+ }
+ break;
default:
break;
}
@@ -848,7 +1376,7 @@ apr_status_t md_cert_fload(md_cert_t **pcert, apr_pool_t *p, const char *fname)
x509 = PEM_read_X509(f, NULL, NULL, NULL);
rv = fclose(f);
if (x509 != NULL) {
- cert = make_cert(p, x509);
+ cert = md_cert_make(p, x509);
}
else {
rv = APR_EINVAL;
@@ -859,7 +1387,7 @@ apr_status_t md_cert_fload(md_cert_t **pcert, apr_pool_t *p, const char *fname)
return rv;
}
-static apr_status_t cert_to_buffer(buffer_rec *buffer, md_cert_t *cert, apr_pool_t *p)
+static apr_status_t cert_to_buffer(md_data_t *buffer, const md_cert_t *cert, apr_pool_t *p)
{
BIO *bio = BIO_new(BIO_s_mem());
int i;
@@ -877,9 +1405,8 @@ static apr_status_t cert_to_buffer(buffer_rec *buffer, md_cert_t *cert, apr_pool
i = BIO_pending(bio);
if (i > 0) {
- buffer->data = apr_palloc(p, (apr_size_t)i + 1);
- i = BIO_read(bio, buffer->data, i);
- buffer->data[i] = '\0';
+ buffer->data = apr_palloc(p, (apr_size_t)i);
+ i = BIO_read(bio, (char*)buffer->data, i);
buffer->len = (apr_size_t)i;
}
BIO_free(bio);
@@ -889,64 +1416,194 @@ static apr_status_t cert_to_buffer(buffer_rec *buffer, md_cert_t *cert, apr_pool
apr_status_t md_cert_fsave(md_cert_t *cert, apr_pool_t *p,
const char *fname, apr_fileperms_t perms)
{
- buffer_rec buffer;
+ md_data_t buffer;
apr_status_t rv;
-
+
+ md_data_null(&buffer);
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)
+apr_status_t md_cert_to_base64url(const char **ps64, const md_cert_t *cert, apr_pool_t *p)
{
- buffer_rec buffer;
+ md_data_t buffer;
apr_status_t rv;
-
+
+ md_data_null(&buffer);
if (APR_SUCCESS == (rv = cert_to_buffer(&buffer, cert, p))) {
- *ps64 = md_util_base64url_encode(buffer.data, buffer.len, p);
+ *ps64 = md_util_base64url_encode(&buffer, p);
return APR_SUCCESS;
}
*ps64 = NULL;
return rv;
}
+apr_status_t md_cert_to_sha256_digest(md_data_t **pdigest, const md_cert_t *cert, apr_pool_t *p)
+{
+ md_data_t *digest;
+ unsigned int dlen;
+
+ digest = md_data_pmake(EVP_MAX_MD_SIZE, p);
+ X509_digest(cert->x509, EVP_sha256(), (unsigned char*)digest->data, &dlen);
+ digest->len = dlen;
+
+ *pdigest = digest;
+ return APR_SUCCESS;
+}
+
+apr_status_t md_cert_to_sha256_fingerprint(const char **pfinger, const md_cert_t *cert, apr_pool_t *p)
+{
+ md_data_t *digest;
+ apr_status_t rv;
+
+ rv = md_cert_to_sha256_digest(&digest, cert, p);
+ if (APR_SUCCESS == rv) {
+ return md_data_to_hex(pfinger, 0, p, digest);
+ }
+ *pfinger = NULL;
+ return rv;
+}
+
+static int md_cert_read_pem(BIO *bf, apr_pool_t *p, md_cert_t **pcert)
+{
+ md_cert_t *cert;
+ X509 *x509;
+ apr_status_t rv = APR_ENOENT;
+
+ ERR_clear_error();
+ x509 = PEM_read_bio_X509(bf, NULL, NULL, NULL);
+ if (x509 == NULL) goto cleanup;
+ cert = md_cert_make(p, x509);
+ rv = APR_SUCCESS;
+cleanup:
+ *pcert = (APR_SUCCESS == rv)? cert : NULL;
+ return rv;
+}
+
+apr_status_t md_cert_read_chain(apr_array_header_t *chain, apr_pool_t *p,
+ const char *pem, apr_size_t pem_len)
+{
+ BIO *bf = NULL;
+ apr_status_t rv = APR_SUCCESS;
+ md_cert_t *cert;
+ int added = 0;
+
+ if (NULL == (bf = BIO_new_mem_buf(pem, (int)pem_len))) {
+ rv = APR_ENOMEM;
+ goto cleanup;
+ }
+ while (APR_SUCCESS == (rv = md_cert_read_pem(bf, chain->pool, &cert))) {
+ APR_ARRAY_PUSH(chain, md_cert_t *) = cert;
+ added = 1;
+ }
+ if (APR_ENOENT == rv && added) {
+ rv = APR_SUCCESS;
+ }
+
+cleanup:
+ md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, rv, p, "read chain with %d certs", chain->nelts);
+ if (bf) BIO_free(bf);
+ 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;
+ char *der;
apr_size_t der_len;
+ md_cert_t *cert = NULL;
apr_status_t rv;
ct = apr_table_get(res->headers, "Content-Type");
- if (!res->body || !ct || strcmp("application/pkix-cert", ct)) {
- return APR_ENOENT;
+ ct = md_util_parse_ct(res->req->pool, ct);
+ if (!res->body || !ct || strcmp("application/pkix-cert", ct)) {
+ rv = APR_ENOENT;
+ goto out;
}
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))) {
+ if (APR_SUCCESS == (rv = apr_brigade_pflatten(res->body, &der, &der_len, res->req->pool))) {
const unsigned char *bf = (const unsigned char*)der;
X509 *x509;
if (NULL == (x509 = d2i_X509(NULL, &bf, (long)der_len))) {
rv = APR_EINVAL;
+ goto out;
}
else {
- *pcert = make_cert(p, x509);
+ cert = md_cert_make(p, x509);
rv = APR_SUCCESS;
+ md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, rv, p,
+ "parsing cert from content-type=%s, content-length=%ld", ct, (long)data_len);
}
}
- md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, "cert parsed");
}
+out:
+ *pcert = (APR_SUCCESS == rv)? cert : NULL;
return rv;
}
-md_cert_state_t md_cert_state_get(md_cert_t *cert)
+apr_status_t md_cert_chain_read_http(struct apr_array_header_t *chain,
+ apr_pool_t *p, const struct md_http_response_t *res)
+{
+ const char *ct = NULL;
+ apr_off_t blen;
+ apr_size_t data_len = 0;
+ char *data;
+ md_cert_t *cert;
+ apr_status_t rv = APR_ENOENT;
+
+ md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, 0, p,
+ "chain_read, processing %d response", res->status);
+ if (APR_SUCCESS != (rv = apr_brigade_length(res->body, 1, &blen))) goto cleanup;
+ if (blen > 1024*1024) { /* certs usually are <2k each */
+ rv = APR_EINVAL;
+ goto cleanup;
+ }
+
+ data_len = (apr_size_t)blen;
+ ct = apr_table_get(res->headers, "Content-Type");
+ if (!res->body || !ct) goto cleanup;
+ ct = md_util_parse_ct(res->req->pool, ct);
+ if (!strcmp("application/pkix-cert", ct)) {
+ rv = md_cert_read_http(&cert, p, res);
+ if (APR_SUCCESS != rv) goto cleanup;
+ APR_ARRAY_PUSH(chain, md_cert_t *) = cert;
+ }
+ else if (!strcmp("application/pem-certificate-chain", ct)
+ || !strncmp("text/plain", ct, sizeof("text/plain")-1)) {
+ /* Some servers seem to think 'text/plain' is sufficient, see #232 */
+ rv = apr_brigade_pflatten(res->body, &data, &data_len, res->req->pool);
+ if (APR_SUCCESS != rv) goto cleanup;
+ rv = md_cert_read_chain(chain, res->req->pool, data, data_len);
+ }
+ else {
+ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p,
+ "attempting to parse certificates from unrecognized content-type: %s", ct);
+ rv = apr_brigade_pflatten(res->body, &data, &data_len, res->req->pool);
+ if (APR_SUCCESS != rv) goto cleanup;
+ rv = md_cert_read_chain(chain, res->req->pool, data, data_len);
+ if (APR_SUCCESS == rv && chain->nelts == 0) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p,
+ "certificate chain response did not contain any certificates "
+ "(suspicious content-type: %s)", ct);
+ rv = APR_ENOENT;
+ }
+ }
+cleanup:
+ md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, rv, p,
+ "parsed certs from content-type=%s, content-length=%ld", ct, (long)data_len);
+ return rv;
+}
+
+md_cert_state_t md_cert_state_get(const md_cert_t *cert)
{
if (cert->x509) {
return md_cert_is_valid_now(cert)? MD_CERT_VALID : MD_CERT_EXPIRED;
@@ -966,7 +1623,7 @@ apr_status_t md_chain_fappend(struct apr_array_header_t *certs, apr_pool_t *p, c
if (rv == APR_SUCCESS) {
ERR_clear_error();
while (NULL != (x509 = PEM_read_X509(f, NULL, NULL, NULL))) {
- cert = make_cert(p, x509);
+ cert = md_cert_make(p, x509);
APR_ARRAY_PUSH(certs, md_cert_t *) = cert;
}
fclose(f);
@@ -1064,18 +1721,22 @@ static apr_status_t add_ext(X509 *x, int nid, const char *value, apr_pool_t *p)
X509V3_CTX ctx;
apr_status_t rv;
+ ERR_clear_error();
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))) {
+ unsigned long err = ERR_get_error();
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "add_ext, create, nid=%d value='%s' "
+ "(lib=%d, reason=%d)", nid, value, ERR_GET_LIB(err), ERR_GET_REASON(err));
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);
-
+ unsigned long err = ERR_get_error();
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "add_ext, add, nid=%d value='%s' "
+ "(lib=%d, reason=%d)", nid, value, ERR_GET_LIB(err), ERR_GET_REASON(err));
}
X509_EXTENSION_free(ext);
return rv;
@@ -1100,116 +1761,114 @@ static apr_status_t sk_add_alt_names(STACK_OF(X509_EXTENSION) *exts,
#define MD_OID_MUST_STAPLE_SNAME "tlsfeature"
#define MD_OID_MUST_STAPLE_LNAME "TLS Feature"
-static int get_must_staple_nid(void)
-{
- /* Funny API, the OID for must staple might be configured or
- * might be not. In the second case, we need to add it. But adding
- * when it already is there is an error... */
- int nid = OBJ_txt2nid(MD_OID_MUST_STAPLE_NUM);
- if (NID_undef == nid) {
- nid = OBJ_create(MD_OID_MUST_STAPLE_NUM,
- MD_OID_MUST_STAPLE_SNAME, MD_OID_MUST_STAPLE_LNAME);
- }
- return nid;
-}
-
-int md_cert_must_staple(md_cert_t *cert)
+int md_cert_must_staple(const md_cert_t *cert)
{
/* In case we do not get the NID for it, we treat this as not set. */
- int nid = get_must_staple_nid();
+ int nid = MD_GET_NID(MUST_STAPLE);
return ((NID_undef != nid)) && X509_get_ext_by_NID(cert->x509, nid, -1) >= 0;
}
-static apr_status_t add_must_staple(STACK_OF(X509_EXTENSION) *exts, const md_t *md, apr_pool_t *p)
+static apr_status_t add_must_staple(STACK_OF(X509_EXTENSION) *exts, const char *name, apr_pool_t *p)
{
+ X509_EXTENSION *x;
+ int nid;
- if (md->must_staple) {
- X509_EXTENSION *x;
- int nid;
-
- nid = get_must_staple_nid();
- if (NID_undef == nid) {
- md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p,
- "%s: unable to get NID for v3 must-staple TLS feature", md->name);
- return APR_ENOTIMPL;
- }
- x = X509V3_EXT_conf_nid(NULL, NULL, nid, (char*)"DER:30:03:02:01:05");
- if (NULL == x) {
- md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p,
- "%s: unable to create x509 extension for must-staple", md->name);
- return APR_EGENERAL;
- }
- sk_X509_EXTENSION_push(exts, x);
+ nid = MD_GET_NID(MUST_STAPLE);
+ if (NID_undef == nid) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p,
+ "%s: unable to get NID for v3 must-staple TLS feature", name);
+ return APR_ENOTIMPL;
+ }
+ x = X509V3_EXT_conf_nid(NULL, NULL, nid, (char*)"DER:30:03:02:01:05");
+ if (NULL == x) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p,
+ "%s: unable to create x509 extension for must-staple", name);
+ 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,
+apr_status_t md_cert_req_create(const char **pcsr_der_64, const char *name,
+ apr_array_header_t *domains, int must_staple,
md_pkey_t *pkey, apr_pool_t *p)
{
- const char *s, *csr_der, *csr_der_64 = NULL;
+ const char *s, *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;
+ md_data_t csr_der;
int csr_der_len;
- assert(md->domains->nelts > 0);
-
+ assert(domains->nelts > 0);
+ md_data_null(&csr_der);
+
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);
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: openssl alloc X509 things", 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);
+ domain = APR_ARRAY_IDX(domains, 0, const unsigned char *);
+ /* Do not set the domain in the CN if it is longer than 64 octets.
+ * Instead, let the CA choose a 'proper' name. At the moment (2021-01), LE will
+ * inspect all SAN names and use one < 64 chars if it can be found. It will fail
+ * otherwise.
+ * The reason we do not check this beforehand is that the restrictions on CNs
+ * are in flux. They used to be authoritative, now browsers no longer do that, but
+ * no one wants to hand out a cert with "google.com" as CN either. So, we leave
+ * it for the CA to decide if and how it hands out a cert for this or fails.
+ * This solves issue where the name is too long, see #227 */
+ if (strlen((const char*)domain) < 64
+ && (!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", 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);
+ if (APR_SUCCESS != (rv = sk_add_alt_names(exts, domains, p))) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: collecting alt names", name);
rv = APR_EGENERAL; goto out;
}
- if (APR_SUCCESS != (rv = add_must_staple(exts, md, p))) {
+ if (must_staple && APR_SUCCESS != (rv = add_must_staple(exts, name, p))) {
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: you requested that a certificate "
"is created with the 'must-staple' extension, however the SSL library was "
"unable to initialized that extension. Please file a bug report on which platform "
"and with which library this happens. To continue before this problem is resolved, "
- "configure 'MDMustStaple off' for your domains", md->name);
+ "configure 'MDMustStaple off' for your domains", 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);
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: adding exts", 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);
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set pkey in csr", 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);
+ if (!X509_REQ_sign(csr, pkey->pkey, pkey_get_MD(pkey))) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: sign csr", 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);
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: der length", name);
rv = APR_EGENERAL; goto out;
}
- s = csr_der = apr_pcalloc(p, (apr_size_t)csr_der_len + 1);
+ csr_der.len = (apr_size_t)csr_der_len;
+ s = csr_der.data = 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);
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: csr der enc", name);
rv = APR_EGENERAL; goto out;
}
- csr_der_64 = md_util_base64url_encode(csr_der, (apr_size_t)csr_der_len, p);
+ csr_der_64 = md_util_base64url_encode(&csr_der, p);
rv = APR_SUCCESS;
out:
@@ -1226,20 +1885,16 @@ out:
return rv;
}
-apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn,
- apr_array_header_t *domains, md_pkey_t *pkey,
- apr_interval_time_t valid_for, apr_pool_t *p)
+static apr_status_t mk_x509(X509 **px, md_pkey_t *pkey, const char *cn,
+ apr_interval_time_t valid_for, apr_pool_t *p)
{
- X509 *x;
+ X509 *x = NULL;
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(domains);
+ int days;
+ apr_status_t rv;
if (NULL == (x = X509_new())
|| NULL == (n = X509_NAME_new())) {
@@ -1247,24 +1902,22 @@ apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn,
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: openssl alloc X509 things", cn);
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", cn);
rv = APR_EGENERAL; goto out;
}
-
- if (1 != X509_set_version(x, 2L)) {
- md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: setting x.509v3", cn);
- 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", cn);
rv = APR_EGENERAL; goto out;
}
- /* set common name and issue */
+ if (1 != X509_set_version(x, 2L)) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: setting x.509v3", cn);
+ rv = APR_EGENERAL; goto out;
+ }
+ /* set common name and issuer */
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)) {
@@ -1272,21 +1925,16 @@ apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn,
rv = APR_EGENERAL; goto out;
}
/* cert are unconstrained (but not very trustworthy) */
- if (APR_SUCCESS != (rv = add_ext(x, NID_basic_constraints, "CA:FALSE, pathlen:0", p))) {
+ if (APR_SUCCESS != (rv = add_ext(x, NID_basic_constraints, "critical,CA:FALSE", p))) {
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set basic constraints ext", cn);
goto out;
}
- /* add the domain as alt name */
- if (APR_SUCCESS != (rv = add_ext(x, NID_subject_alt_name, alt_names(domains, p), p))) {
- md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set alt_name ext", cn);
- 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", cn);
rv = APR_EGENERAL; goto out;
}
-
+ /* validity */
days = (int)((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;
@@ -1295,29 +1943,217 @@ apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn,
rv = APR_EGENERAL; goto out;
}
+out:
+ *px = (APR_SUCCESS == rv)? x : NULL;
+ if (APR_SUCCESS != rv && x) X509_free(x);
+ if (big_rnd) BN_free(big_rnd);
+ if (asn1_rnd) ASN1_INTEGER_free(asn1_rnd);
+ if (n) X509_NAME_free(n);
+ return rv;
+}
+
+apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn,
+ apr_array_header_t *domains, md_pkey_t *pkey,
+ apr_interval_time_t valid_for, apr_pool_t *p)
+{
+ X509 *x;
+ md_cert_t *cert = NULL;
+ apr_status_t rv;
+
+ assert(domains);
+
+ if (APR_SUCCESS != (rv = mk_x509(&x, pkey, cn, valid_for, p))) goto out;
+
+ /* add the domain as alt name */
+ if (APR_SUCCESS != (rv = add_ext(x, NID_subject_alt_name, alt_names(domains, p), p))) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set alt_name ext", cn);
+ goto out;
+ }
+
+ /* keyUsage, ExtendedKeyUsage */
+
+ if (APR_SUCCESS != (rv = add_ext(x, NID_key_usage, "critical,digitalSignature", p))) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set keyUsage", cn);
+ goto out;
+ }
+ if (APR_SUCCESS != (rv = add_ext(x, NID_ext_key_usage, "serverAuth", p))) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set extKeyUsage", cn);
+ goto out;
+ }
+
/* sign with same key */
- if (!X509_sign(x, pkey->pkey, EVP_sha256())) {
+ if (!X509_sign(x, pkey->pkey, pkey_get_MD(pkey))) {
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: sign x509", cn);
rv = APR_EGENERAL; goto out;
}
- cert = make_cert(p, x);
+ cert = md_cert_make(p, x);
+ rv = APR_SUCCESS;
+
+out:
+ *pcert = (APR_SUCCESS == rv)? cert : NULL;
+ if (!cert && x) X509_free(x);
+ return rv;
+}
+
+#define MD_OID_ACME_VALIDATION_NUM "1.3.6.1.5.5.7.1.31"
+#define MD_OID_ACME_VALIDATION_SNAME "pe-acmeIdentifier"
+#define MD_OID_ACME_VALIDATION_LNAME "ACME Identifier"
+
+static int get_acme_validation_nid(void)
+{
+ int nid = OBJ_txt2nid(MD_OID_ACME_VALIDATION_NUM);
+ if (NID_undef == nid) {
+ nid = OBJ_create(MD_OID_ACME_VALIDATION_NUM,
+ MD_OID_ACME_VALIDATION_SNAME, MD_OID_ACME_VALIDATION_LNAME);
+ }
+ return nid;
+}
+
+apr_status_t md_cert_make_tls_alpn_01(md_cert_t **pcert, const char *domain,
+ const char *acme_id, md_pkey_t *pkey,
+ apr_interval_time_t valid_for, apr_pool_t *p)
+{
+ X509 *x;
+ md_cert_t *cert = NULL;
+ const char *alts;
+ apr_status_t rv;
+
+ if (APR_SUCCESS != (rv = mk_x509(&x, pkey, "tls-alpn-01-challenge", valid_for, p))) goto out;
+
+ /* add the domain as alt name */
+ alts = apr_psprintf(p, "DNS:%s", domain);
+ if (APR_SUCCESS != (rv = add_ext(x, NID_subject_alt_name, alts, p))) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set alt_name ext", domain);
+ goto out;
+ }
+
+ if (APR_SUCCESS != (rv = add_ext(x, get_acme_validation_nid(), acme_id, p))) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set pe-acmeIdentifier", domain);
+ goto out;
+ }
+
+ /* sign with same key */
+ if (!X509_sign(x, pkey->pkey, pkey_get_MD(pkey))) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: sign x509", domain);
+ rv = APR_EGENERAL; goto out;
+ }
+
+ cert = md_cert_make(p, x);
rv = APR_SUCCESS;
out:
if (!cert && x) {
X509_free(x);
}
- if (n) {
- X509_NAME_free(n);
+ *pcert = (APR_SUCCESS == rv)? cert : NULL;
+ return rv;
+}
+
+#define MD_OID_CT_SCTS_NUM "1.3.6.1.4.1.11129.2.4.2"
+#define MD_OID_CT_SCTS_SNAME "CT-SCTs"
+#define MD_OID_CT_SCTS_LNAME "CT Certificate SCTs"
+
+#ifndef OPENSSL_NO_CT
+static int get_ct_scts_nid(void)
+{
+ int nid = OBJ_txt2nid(MD_OID_CT_SCTS_NUM);
+ if (NID_undef == nid) {
+ nid = OBJ_create(MD_OID_CT_SCTS_NUM,
+ MD_OID_CT_SCTS_SNAME, MD_OID_CT_SCTS_LNAME);
}
- if (big_rnd) {
- BN_free(big_rnd);
+ return nid;
+}
+#endif
+
+const char *md_nid_get_sname(int nid)
+{
+ return OBJ_nid2sn(nid);
+}
+
+const char *md_nid_get_lname(int nid)
+{
+ return OBJ_nid2ln(nid);
+}
+
+apr_status_t md_cert_get_ct_scts(apr_array_header_t *scts, apr_pool_t *p, const md_cert_t *cert)
+{
+#ifndef OPENSSL_NO_CT
+ int nid, i, idx, critical;
+ STACK_OF(SCT) *sct_list;
+ SCT *sct_handle;
+ md_sct *sct;
+ size_t len;
+ const char *data;
+
+ nid = get_ct_scts_nid();
+ if (NID_undef == nid) return APR_ENOTIMPL;
+
+ idx = -1;
+ while (1) {
+ sct_list = X509_get_ext_d2i(cert->x509, nid, &critical, &idx);
+ if (sct_list) {
+ for (i = 0; i < sk_SCT_num(sct_list); i++) {
+ sct_handle = sk_SCT_value(sct_list, i);
+ if (sct_handle) {
+ sct = apr_pcalloc(p, sizeof(*sct));
+ sct->version = SCT_get_version(sct_handle);
+ sct->timestamp = apr_time_from_msec(SCT_get_timestamp(sct_handle));
+ len = SCT_get0_log_id(sct_handle, (unsigned char**)&data);
+ sct->logid = md_data_make_pcopy(p, data, len);
+ sct->signature_type_nid = SCT_get_signature_nid(sct_handle);
+ len = SCT_get0_signature(sct_handle, (unsigned char**)&data);
+ sct->signature = md_data_make_pcopy(p, data, len);
+
+ APR_ARRAY_PUSH(scts, md_sct*) = sct;
+ }
+ }
+ }
+ if (idx < 0) break;
}
- if (asn1_rnd) {
- ASN1_INTEGER_free(asn1_rnd);
+ md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, 0, p, "ct_sct, found %d SCT extensions", scts->nelts);
+ return APR_SUCCESS;
+#else
+ (void)scts;
+ (void)p;
+ (void)cert;
+ return APR_ENOTIMPL;
+#endif
+}
+
+apr_status_t md_cert_get_ocsp_responder_url(const char **purl, apr_pool_t *p, const md_cert_t *cert)
+{
+ STACK_OF(OPENSSL_STRING) *ssk;
+ apr_status_t rv = APR_SUCCESS;
+ const char *url = NULL;
+
+ ssk = X509_get1_ocsp(md_cert_get_X509(cert));
+ if (!ssk) {
+ rv = APR_ENOENT;
+ goto cleanup;
}
- *pcert = (APR_SUCCESS == rv)? cert : NULL;
+ url = apr_pstrdup(p, sk_OPENSSL_STRING_value(ssk, 0));
+ md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, 0, p, "ocsp responder found '%s'", url);
+
+cleanup:
+ if (ssk) X509_email_free(ssk);
+ *purl = url;
return rv;
}
+apr_status_t md_check_cert_and_pkey(struct apr_array_header_t *certs, md_pkey_t *pkey)
+{
+ const md_cert_t *cert;
+
+ if (certs->nelts == 0) {
+ return APR_ENOENT;
+ }
+
+ cert = APR_ARRAY_IDX(certs, 0, const md_cert_t*);
+
+ if (1 != X509_check_private_key(cert->x509, pkey->pkey)) {
+ return APR_EGENERAL;
+ }
+
+ return APR_SUCCESS;
+}