|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <assert.h>
|
|
Packit |
90a5c9 |
#include <stdio.h>
|
|
Packit |
90a5c9 |
#include <stdlib.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <apr_lib.h>
|
|
Packit |
90a5c9 |
#include <apr_buckets.h>
|
|
Packit |
90a5c9 |
#include <apr_file_io.h>
|
|
Packit |
90a5c9 |
#include <apr_strings.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <openssl/err.h>
|
|
Packit |
90a5c9 |
#include <openssl/evp.h>
|
|
Packit |
90a5c9 |
#include <openssl/pem.h>
|
|
Packit |
90a5c9 |
#include <openssl/rand.h>
|
|
Packit |
90a5c9 |
#include <openssl/rsa.h>
|
|
Packit |
90a5c9 |
#include <openssl/x509v3.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "md.h"
|
|
Packit |
90a5c9 |
#include "md_crypt.h"
|
|
Packit |
90a5c9 |
#include "md_json.h"
|
|
Packit |
90a5c9 |
#include "md_log.h"
|
|
Packit |
90a5c9 |
#include "md_http.h"
|
|
Packit |
90a5c9 |
#include "md_util.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* getpid for *NIX */
|
|
Packit |
90a5c9 |
#if APR_HAVE_SYS_TYPES_H
|
|
Packit |
90a5c9 |
#include <sys/types.h>
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
#if APR_HAVE_UNISTD_H
|
|
Packit |
90a5c9 |
#include <unistd.h>
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* getpid for Windows */
|
|
Packit |
90a5c9 |
#if APR_HAVE_PROCESS_H
|
|
Packit |
90a5c9 |
#include <process.h>
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#if defined(LIBRESSL_VERSION_NUMBER)
|
|
Packit |
90a5c9 |
/* Missing from LibreSSL */
|
|
Packit |
90a5c9 |
#define MD_USE_OPENSSL_PRE_1_1_API (LIBRESSL_VERSION_NUMBER < 0x2070000f)
|
|
Packit |
90a5c9 |
#else /* defined(LIBRESSL_VERSION_NUMBER) */
|
|
Packit |
90a5c9 |
#define MD_USE_OPENSSL_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int initialized;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
struct md_pkey_t {
|
|
Packit |
90a5c9 |
apr_pool_t *pool;
|
|
Packit |
90a5c9 |
EVP_PKEY *pkey;
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#ifdef MD_HAVE_ARC4RANDOM
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void seed_RAND(int pid)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char seed[128];
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
(void)pid;
|
|
Packit |
90a5c9 |
arc4random_buf(seed, sizeof(seed));
|
|
Packit |
90a5c9 |
RAND_seed(seed, sizeof(seed));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#else /* ifdef MD_HAVE_ARC4RANDOM */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int rand_choosenum(int l, int h)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
char buf[50];
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_snprintf(buf, sizeof(buf), "%.0f",
|
|
Packit |
90a5c9 |
(((double)(rand()%RAND_MAX)/RAND_MAX)*(h-l)));
|
|
Packit |
90a5c9 |
i = atoi(buf)+1;
|
|
Packit |
90a5c9 |
if (i < l) i = l;
|
|
Packit |
90a5c9 |
if (i > h) i = h;
|
|
Packit |
90a5c9 |
return i;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void seed_RAND(int pid)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
unsigned char stackdata[256];
|
|
Packit |
90a5c9 |
/* stolen from mod_ssl/ssl_engine_rand.c */
|
|
Packit |
90a5c9 |
int n;
|
|
Packit |
90a5c9 |
struct {
|
|
Packit |
90a5c9 |
time_t t;
|
|
Packit |
90a5c9 |
pid_t pid;
|
|
Packit |
90a5c9 |
} my_seed;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* seed in the current time (usually just 4 bytes)
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
my_seed.t = time(NULL);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* seed in the current process id (usually just 4 bytes)
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
my_seed.pid = pid;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
RAND_seed((unsigned char *)&my_seed, sizeof(my_seed));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* seed in some current state of the run-time stack (128 bytes)
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
n = rand_choosenum(0, sizeof(stackdata)-128-1);
|
|
Packit |
90a5c9 |
RAND_seed(stackdata+n, 128);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#endif /*ifdef MD_HAVE_ARC4RANDOM (else part) */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_crypt_init(apr_pool_t *pool)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
(void)pool;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!initialized) {
|
|
Packit |
90a5c9 |
int pid = getpid();
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ERR_load_crypto_strings();
|
|
Packit |
90a5c9 |
OpenSSL_add_all_algorithms();
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE2, 0, pool, "initializing RAND");
|
|
Packit |
90a5c9 |
while (!RAND_status()) {
|
|
Packit |
90a5c9 |
seed_RAND(pid);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
initialized = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
char *data;
|
|
Packit |
90a5c9 |
apr_size_t len;
|
|
Packit |
90a5c9 |
} buffer_rec;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t fwrite_buffer(void *baton, apr_file_t *f, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
buffer_rec *buf = baton;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
(void)p;
|
|
Packit |
90a5c9 |
return apr_file_write_full(f, buf->data, buf->len, &buf->len);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_rand_bytes(unsigned char *buf, apr_size_t len, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (len > INT_MAX) {
|
|
Packit |
90a5c9 |
return APR_ENOTIMPL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = md_crypt_init(p))) {
|
|
Packit |
90a5c9 |
RAND_bytes((unsigned char*)buf, (int)len);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
const char *pass_phrase;
|
|
Packit |
90a5c9 |
int pass_len;
|
|
Packit |
90a5c9 |
} passwd_ctx;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int pem_passwd(char *buf, int size, int rwflag, void *baton)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
passwd_ctx *ctx = baton;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
(void)rwflag;
|
|
Packit |
90a5c9 |
if (ctx->pass_len > 0) {
|
|
Packit |
90a5c9 |
if (ctx->pass_len < size) {
|
|
Packit |
90a5c9 |
size = (int)ctx->pass_len;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
memcpy(buf, ctx->pass_phrase, (size_t)size);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return ctx->pass_len;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**************************************************************************************************/
|
|
Packit |
90a5c9 |
/* date time things */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Get the apr time (micro seconds, since 1970) from an ASN1 time, as stored in X509
|
|
Packit |
90a5c9 |
* certificates. OpenSSL now has a utility function, but other *SSL derivatives have
|
|
Packit |
90a5c9 |
* not caughts up yet or chose to ignore. An alternative is implemented, we prefer
|
|
Packit |
90a5c9 |
* however the *SSL to maintain such things.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static apr_time_t md_asn1_time_get(const ASN1_TIME* time)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
|
|
Packit |
90a5c9 |
/* courtesy: https://stackoverflow.com/questions/10975542/asn1-time-to-time-t-conversion#11263731
|
|
Packit |
90a5c9 |
* all bugs are mine */
|
|
Packit |
90a5c9 |
apr_time_exp_t t;
|
|
Packit |
90a5c9 |
apr_time_t ts;
|
|
Packit |
90a5c9 |
const char* str = (const char*) time->data;
|
|
Packit |
90a5c9 |
apr_size_t i = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
memset(&t, 0, sizeof(t));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (time->type == V_ASN1_UTCTIME) {/* two digit year */
|
|
Packit |
90a5c9 |
t.tm_year = (str[i++] - '0') * 10;
|
|
Packit |
90a5c9 |
t.tm_year += (str[i++] - '0');
|
|
Packit |
90a5c9 |
if (t.tm_year < 70)
|
|
Packit |
90a5c9 |
t.tm_year += 100;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (time->type == V_ASN1_GENERALIZEDTIME) {/* four digit year */
|
|
Packit |
90a5c9 |
t.tm_year = (str[i++] - '0') * 1000;
|
|
Packit |
90a5c9 |
t.tm_year+= (str[i++] - '0') * 100;
|
|
Packit |
90a5c9 |
t.tm_year+= (str[i++] - '0') * 10;
|
|
Packit |
90a5c9 |
t.tm_year+= (str[i++] - '0');
|
|
Packit |
90a5c9 |
t.tm_year -= 1900;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
t.tm_mon = (str[i++] - '0') * 10;
|
|
Packit |
90a5c9 |
t.tm_mon += (str[i++] - '0') - 1; /* -1 since January is 0 not 1. */
|
|
Packit |
90a5c9 |
t.tm_mday = (str[i++] - '0') * 10;
|
|
Packit |
90a5c9 |
t.tm_mday+= (str[i++] - '0');
|
|
Packit |
90a5c9 |
t.tm_hour = (str[i++] - '0') * 10;
|
|
Packit |
90a5c9 |
t.tm_hour+= (str[i++] - '0');
|
|
Packit |
90a5c9 |
t.tm_min = (str[i++] - '0') * 10;
|
|
Packit |
90a5c9 |
t.tm_min += (str[i++] - '0');
|
|
Packit |
90a5c9 |
t.tm_sec = (str[i++] - '0') * 10;
|
|
Packit |
90a5c9 |
t.tm_sec += (str[i++] - '0');
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == apr_time_exp_gmt_get(&ts, &t)) {
|
|
Packit |
90a5c9 |
return ts;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
#else
|
|
Packit |
90a5c9 |
int secs, days;
|
|
Packit |
90a5c9 |
apr_time_t ts = apr_time_now();
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ASN1_TIME_diff(&days, &secs, NULL, time)) {
|
|
Packit |
90a5c9 |
ts += apr_time_from_sec((days * MD_SECS_PER_DAY) + secs);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return ts;
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**************************************************************************************************/
|
|
Packit |
90a5c9 |
/* private keys */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_json_t *md_pkey_spec_to_json(const md_pkey_spec_t *spec, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_json_t *json = md_json_create(p);
|
|
Packit |
90a5c9 |
if (json) {
|
|
Packit |
90a5c9 |
switch (spec->type) {
|
|
Packit |
90a5c9 |
case MD_PKEY_TYPE_DEFAULT:
|
|
Packit |
90a5c9 |
md_json_sets("Default", json, MD_KEY_TYPE, NULL);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
case MD_PKEY_TYPE_RSA:
|
|
Packit |
90a5c9 |
md_json_sets("RSA", json, MD_KEY_TYPE, NULL);
|
|
Packit |
90a5c9 |
if (spec->params.rsa.bits >= MD_PKEY_RSA_BITS_MIN) {
|
|
Packit |
90a5c9 |
md_json_setl((long)spec->params.rsa.bits, json, MD_KEY_BITS, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
md_json_sets("Unsupported", json, MD_KEY_TYPE, NULL);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return json;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_pkey_spec_t *md_pkey_spec_from_json(struct md_json_t *json, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_pkey_spec_t *spec = apr_pcalloc(p, sizeof(*spec));
|
|
Packit |
90a5c9 |
const char *s;
|
|
Packit |
90a5c9 |
long l;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (spec) {
|
|
Packit |
90a5c9 |
s = md_json_gets(json, MD_KEY_TYPE, NULL);
|
|
Packit |
90a5c9 |
if (!s || !apr_strnatcasecmp("Default", s)) {
|
|
Packit |
90a5c9 |
spec->type = MD_PKEY_TYPE_DEFAULT;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!apr_strnatcasecmp("RSA", s)) {
|
|
Packit |
90a5c9 |
spec->type = MD_PKEY_TYPE_RSA;
|
|
Packit |
90a5c9 |
l = md_json_getl(json, MD_KEY_BITS, NULL);
|
|
Packit |
90a5c9 |
if (l >= MD_PKEY_RSA_BITS_MIN) {
|
|
Packit |
90a5c9 |
spec->params.rsa.bits = (unsigned int)l;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
spec->params.rsa.bits = MD_PKEY_RSA_BITS_DEF;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return spec;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
int md_pkey_spec_eq(md_pkey_spec_t *spec1, md_pkey_spec_t *spec2)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (spec1 == spec2) {
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (spec1 && spec2 && spec1->type == spec2->type) {
|
|
Packit |
90a5c9 |
switch (spec1->type) {
|
|
Packit |
90a5c9 |
case MD_PKEY_TYPE_DEFAULT:
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
case MD_PKEY_TYPE_RSA:
|
|
Packit |
90a5c9 |
if (spec1->params.rsa.bits == spec2->params.rsa.bits) {
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static md_pkey_t *make_pkey(apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_pkey_t *pkey = apr_pcalloc(p, sizeof(*pkey));
|
|
Packit |
90a5c9 |
pkey->pool = p;
|
|
Packit |
90a5c9 |
return pkey;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t pkey_cleanup(void *data)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_pkey_t *pkey = data;
|
|
Packit |
90a5c9 |
if (pkey->pkey) {
|
|
Packit |
90a5c9 |
EVP_PKEY_free(pkey->pkey);
|
|
Packit |
90a5c9 |
pkey->pkey = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
void md_pkey_free(md_pkey_t *pkey)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
pkey_cleanup(pkey);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
void *md_pkey_get_EVP_PKEY(struct md_pkey_t *pkey)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return pkey->pkey;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_pkey_fload(md_pkey_t **ppkey, apr_pool_t *p,
|
|
Packit |
90a5c9 |
const char *key, apr_size_t key_len,
|
|
Packit |
90a5c9 |
const char *fname)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_ENOENT;
|
|
Packit |
90a5c9 |
md_pkey_t *pkey;
|
|
Packit |
90a5c9 |
BIO *bf;
|
|
Packit |
90a5c9 |
passwd_ctx ctx;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
pkey = make_pkey(p);
|
|
Packit |
90a5c9 |
if (NULL != (bf = BIO_new_file(fname, "r"))) {
|
|
Packit |
90a5c9 |
ctx.pass_phrase = key;
|
|
Packit |
90a5c9 |
ctx.pass_len = (int)key_len;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ERR_clear_error();
|
|
Packit |
90a5c9 |
pkey->pkey = PEM_read_bio_PrivateKey(bf, NULL, pem_passwd, &ctx;;
|
|
Packit |
90a5c9 |
BIO_free(bf);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (pkey->pkey != NULL) {
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
apr_pool_cleanup_register(p, pkey, pkey_cleanup, apr_pool_cleanup_null);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
unsigned long err = ERR_get_error();
|
|
Packit |
90a5c9 |
rv = APR_EINVAL;
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p,
|
|
Packit |
90a5c9 |
"error loading pkey %s: %s (pass phrase was %snull)", fname,
|
|
Packit |
90a5c9 |
ERR_error_string(err, NULL), key? "not " : "");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*ppkey = (APR_SUCCESS == rv)? pkey : NULL;
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t pkey_to_buffer(buffer_rec *buffer, md_pkey_t *pkey, apr_pool_t *p,
|
|
Packit |
90a5c9 |
const char *pass, apr_size_t pass_len)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
BIO *bio = BIO_new(BIO_s_mem());
|
|
Packit |
90a5c9 |
const EVP_CIPHER *cipher = NULL;
|
|
Packit |
90a5c9 |
pem_password_cb *cb = NULL;
|
|
Packit |
90a5c9 |
void *cb_baton = NULL;
|
|
Packit |
90a5c9 |
passwd_ctx ctx;
|
|
Packit |
90a5c9 |
unsigned long err;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!bio) {
|
|
Packit |
90a5c9 |
return APR_ENOMEM;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (pass_len > INT_MAX) {
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (pass && pass_len > 0) {
|
|
Packit |
90a5c9 |
ctx.pass_phrase = pass;
|
|
Packit |
90a5c9 |
ctx.pass_len = (int)pass_len;
|
|
Packit |
90a5c9 |
cb = pem_passwd;
|
|
Packit |
90a5c9 |
cb_baton = &ctx;
|
|
Packit |
90a5c9 |
cipher = EVP_aes_256_cbc();
|
|
Packit |
90a5c9 |
if (!cipher) {
|
|
Packit |
90a5c9 |
return APR_ENOTIMPL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ERR_clear_error();
|
|
Packit |
90a5c9 |
if (!PEM_write_bio_PrivateKey(bio, pkey->pkey, cipher, NULL, 0, cb, cb_baton)) {
|
|
Packit |
90a5c9 |
BIO_free(bio);
|
|
Packit |
90a5c9 |
err = ERR_get_error();
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "PEM_write key: %ld %s",
|
|
Packit |
90a5c9 |
err, ERR_error_string(err, NULL));
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
i = BIO_pending(bio);
|
|
Packit |
90a5c9 |
if (i > 0) {
|
|
Packit |
90a5c9 |
buffer->data = apr_palloc(p, (apr_size_t)i + 1);
|
|
Packit |
90a5c9 |
i = BIO_read(bio, buffer->data, i);
|
|
Packit |
90a5c9 |
buffer->data[i] = '\0';
|
|
Packit |
90a5c9 |
buffer->len = (apr_size_t)i;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
BIO_free(bio);
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_pkey_fsave(md_pkey_t *pkey, apr_pool_t *p,
|
|
Packit |
90a5c9 |
const char *pass_phrase, apr_size_t pass_len,
|
|
Packit |
90a5c9 |
const char *fname, apr_fileperms_t perms)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
buffer_rec buffer;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = pkey_to_buffer(&buffer, pkey, p, pass_phrase, pass_len))) {
|
|
Packit |
90a5c9 |
return md_util_freplace(fname, perms, p, fwrite_buffer, &buffer);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "save pkey %s (%s pass phrase, len=%d)",
|
|
Packit |
90a5c9 |
fname, pass_len > 0? "with" : "without", (int)pass_len);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t gen_rsa(md_pkey_t **ppkey, apr_pool_t *p, unsigned int bits)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
EVP_PKEY_CTX *ctx = NULL;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
*ppkey = make_pkey(p);
|
|
Packit |
90a5c9 |
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
|
|
Packit |
90a5c9 |
if (ctx
|
|
Packit |
90a5c9 |
&& EVP_PKEY_keygen_init(ctx) >= 0
|
|
Packit |
90a5c9 |
&& EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, (int)bits) >= 0
|
|
Packit |
90a5c9 |
&& EVP_PKEY_keygen(ctx, &(*ppkey)->pkey) >= 0) {
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, p, "error generate pkey RSA %d", bits);
|
|
Packit |
90a5c9 |
*ppkey = NULL;
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ctx != NULL) {
|
|
Packit |
90a5c9 |
EVP_PKEY_CTX_free(ctx);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_pkey_gen(md_pkey_t **ppkey, apr_pool_t *p, md_pkey_spec_t *spec)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_pkey_type_t ptype = spec? spec->type : MD_PKEY_TYPE_DEFAULT;
|
|
Packit |
90a5c9 |
switch (ptype) {
|
|
Packit |
90a5c9 |
case MD_PKEY_TYPE_DEFAULT:
|
|
Packit |
90a5c9 |
return gen_rsa(ppkey, p, MD_PKEY_RSA_BITS_DEF);
|
|
Packit |
90a5c9 |
case MD_PKEY_TYPE_RSA:
|
|
Packit |
90a5c9 |
return gen_rsa(ppkey, p, spec->params.rsa.bits);
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
return APR_ENOTIMPL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#if MD_USE_OPENSSL_PRE_1_1_API || (defined(LIBRESSL_VERSION_NUMBER) && \
|
|
Packit |
90a5c9 |
LIBRESSL_VERSION_NUMBER < 0x2070000f)
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#ifndef NID_tlsfeature
|
|
Packit |
90a5c9 |
#define NID_tlsfeature 1020
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void RSA_get0_key(const RSA *r,
|
|
Packit |
90a5c9 |
const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (n != NULL)
|
|
Packit |
90a5c9 |
*n = r->n;
|
|
Packit |
90a5c9 |
if (e != NULL)
|
|
Packit |
90a5c9 |
*e = r->e;
|
|
Packit |
90a5c9 |
if (d != NULL)
|
|
Packit |
90a5c9 |
*d = r->d;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *bn64(const BIGNUM *b, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (b) {
|
|
Packit |
90a5c9 |
apr_size_t len = (apr_size_t)BN_num_bytes(b);
|
|
Packit |
90a5c9 |
char *buffer = apr_pcalloc(p, len);
|
|
Packit |
90a5c9 |
if (buffer) {
|
|
Packit |
90a5c9 |
BN_bn2bin(b, (unsigned char *)buffer);
|
|
Packit |
90a5c9 |
return md_util_base64url_encode(buffer, len, p);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
const char *md_pkey_get_rsa_e64(md_pkey_t *pkey, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const BIGNUM *e;
|
|
Packit |
90a5c9 |
RSA *rsa = EVP_PKEY_get1_RSA(pkey->pkey);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!rsa) {
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
RSA_get0_key(rsa, NULL, &e, NULL);
|
|
Packit |
90a5c9 |
return bn64(e, p);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
const char *md_pkey_get_rsa_n64(md_pkey_t *pkey, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const BIGNUM *n;
|
|
Packit |
90a5c9 |
RSA *rsa = EVP_PKEY_get1_RSA(pkey->pkey);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!rsa) {
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
RSA_get0_key(rsa, &n, NULL, NULL);
|
|
Packit |
90a5c9 |
return bn64(n, p);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_crypt_sign64(const char **psign64, md_pkey_t *pkey, apr_pool_t *p,
|
|
Packit |
90a5c9 |
const char *d, size_t dlen)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
EVP_MD_CTX *ctx = NULL;
|
|
Packit |
90a5c9 |
char *buffer;
|
|
Packit |
90a5c9 |
unsigned int blen;
|
|
Packit |
90a5c9 |
const char *sign64 = NULL;
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_ENOMEM;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
buffer = apr_pcalloc(p, (apr_size_t)EVP_PKEY_size(pkey->pkey));
|
|
Packit |
90a5c9 |
if (buffer) {
|
|
Packit |
90a5c9 |
ctx = EVP_MD_CTX_create();
|
|
Packit |
90a5c9 |
if (ctx) {
|
|
Packit |
90a5c9 |
rv = APR_ENOTIMPL;
|
|
Packit |
90a5c9 |
if (EVP_SignInit_ex(ctx, EVP_sha256(), NULL)) {
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL;
|
|
Packit |
90a5c9 |
if (EVP_SignUpdate(ctx, d, dlen)) {
|
|
Packit |
90a5c9 |
if (EVP_SignFinal(ctx, (unsigned char*)buffer, &blen, pkey->pkey)) {
|
|
Packit |
90a5c9 |
sign64 = md_util_base64url_encode(buffer, blen, p);
|
|
Packit |
90a5c9 |
if (sign64) {
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ctx) {
|
|
Packit |
90a5c9 |
EVP_MD_CTX_destroy(ctx);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p, "signing");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
*psign64 = sign64;
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t sha256_digest(unsigned char **pdigest, size_t *pdigest_len,
|
|
Packit |
90a5c9 |
apr_pool_t *p, const char *d, size_t dlen)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
EVP_MD_CTX *ctx = NULL;
|
|
Packit |
90a5c9 |
unsigned char *buffer;
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_ENOMEM;
|
|
Packit |
90a5c9 |
unsigned int blen;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
buffer = apr_pcalloc(p, EVP_MAX_MD_SIZE);
|
|
Packit |
90a5c9 |
if (buffer) {
|
|
Packit |
90a5c9 |
ctx = EVP_MD_CTX_create();
|
|
Packit |
90a5c9 |
if (ctx) {
|
|
Packit |
90a5c9 |
rv = APR_ENOTIMPL;
|
|
Packit |
90a5c9 |
if (EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)) {
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL;
|
|
Packit |
90a5c9 |
if (EVP_DigestUpdate(ctx, d, dlen)) {
|
|
Packit |
90a5c9 |
if (EVP_DigestFinal(ctx, buffer, &blen)) {
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ctx) {
|
|
Packit |
90a5c9 |
EVP_MD_CTX_destroy(ctx);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == rv) {
|
|
Packit |
90a5c9 |
*pdigest = buffer;
|
|
Packit |
90a5c9 |
*pdigest_len = blen;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p, "digest");
|
|
Packit |
90a5c9 |
*pdigest = NULL;
|
|
Packit |
90a5c9 |
*pdigest_len = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_crypt_sha256_digest64(const char **pdigest64, apr_pool_t *p,
|
|
Packit |
90a5c9 |
const char *d, size_t dlen)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *digest64 = NULL;
|
|
Packit |
90a5c9 |
unsigned char *buffer;
|
|
Packit |
90a5c9 |
size_t blen;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = sha256_digest(&buffer, &blen, p, d, dlen))) {
|
|
Packit |
90a5c9 |
if (NULL == (digest64 = md_util_base64url_encode((const char*)buffer, blen, p))) {
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*pdigest64 = digest64;
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char * const hex_const[] = {
|
|
Packit |
90a5c9 |
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
|
|
Packit |
90a5c9 |
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f",
|
|
Packit |
90a5c9 |
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f",
|
|
Packit |
90a5c9 |
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f",
|
|
Packit |
90a5c9 |
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f",
|
|
Packit |
90a5c9 |
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f",
|
|
Packit |
90a5c9 |
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f",
|
|
Packit |
90a5c9 |
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f",
|
|
Packit |
90a5c9 |
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
|
|
Packit |
90a5c9 |
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f",
|
|
Packit |
90a5c9 |
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
|
|
Packit |
90a5c9 |
"b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf",
|
|
Packit |
90a5c9 |
"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf",
|
|
Packit |
90a5c9 |
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df",
|
|
Packit |
90a5c9 |
"e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef",
|
|
Packit |
90a5c9 |
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff",
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_crypt_sha256_digest_hex(const char **pdigesthex, apr_pool_t *p,
|
|
Packit |
90a5c9 |
const char *d, size_t dlen)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char *dhex = NULL, *cp;
|
|
Packit |
90a5c9 |
const char * x;
|
|
Packit |
90a5c9 |
unsigned char *buffer;
|
|
Packit |
90a5c9 |
size_t blen;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
unsigned int i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = sha256_digest(&buffer, &blen, p, d, dlen))) {
|
|
Packit |
90a5c9 |
cp = dhex = apr_pcalloc(p, 2 * blen + 1);
|
|
Packit |
90a5c9 |
if (!dhex) {
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
for (i = 0; i < blen; ++i, cp += 2) {
|
|
Packit |
90a5c9 |
x = hex_const[buffer[i]];
|
|
Packit |
90a5c9 |
cp[0] = x[0];
|
|
Packit |
90a5c9 |
cp[1] = x[1];
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*pdigesthex = dhex;
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**************************************************************************************************/
|
|
Packit |
90a5c9 |
/* certificates */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
struct md_cert_t {
|
|
Packit |
90a5c9 |
apr_pool_t *pool;
|
|
Packit |
90a5c9 |
X509 *x509;
|
|
Packit |
90a5c9 |
apr_array_header_t *alt_names;
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t cert_cleanup(void *data)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_cert_t *cert = data;
|
|
Packit |
90a5c9 |
if (cert->x509) {
|
|
Packit |
90a5c9 |
X509_free(cert->x509);
|
|
Packit |
90a5c9 |
cert->x509 = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static md_cert_t *make_cert(apr_pool_t *p, X509 *x509)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_cert_t *cert = apr_pcalloc(p, sizeof(*cert));
|
|
Packit |
90a5c9 |
cert->pool = p;
|
|
Packit |
90a5c9 |
cert->x509 = x509;
|
|
Packit |
90a5c9 |
apr_pool_cleanup_register(p, cert, cert_cleanup, apr_pool_cleanup_null);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return cert;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
void md_cert_free(md_cert_t *cert)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
cert_cleanup(cert);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
void *md_cert_get_X509(struct md_cert_t *cert)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return cert->x509;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
int md_cert_is_valid_now(const md_cert_t *cert)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return ((X509_cmp_current_time(X509_get_notBefore(cert->x509)) < 0)
|
|
Packit |
90a5c9 |
&& (X509_cmp_current_time(X509_get_notAfter(cert->x509)) > 0));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
int md_cert_has_expired(const md_cert_t *cert)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return (X509_cmp_current_time(X509_get_notAfter(cert->x509)) <= 0);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_time_t md_cert_get_not_after(md_cert_t *cert)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return md_asn1_time_get(X509_get_notAfter(cert->x509));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_time_t md_cert_get_not_before(md_cert_t *cert)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return md_asn1_time_get(X509_get_notBefore(cert->x509));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
int md_cert_covers_domain(md_cert_t *cert, const char *domain_name)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (!cert->alt_names) {
|
|
Packit |
90a5c9 |
md_cert_get_alt_names(&cert->alt_names, cert, cert->pool);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (cert->alt_names) {
|
|
Packit |
90a5c9 |
return md_array_str_index(cert->alt_names, domain_name, 0, 0) >= 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
int md_cert_covers_md(md_cert_t *cert, const md_t *md)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *name;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!cert->alt_names) {
|
|
Packit |
90a5c9 |
md_cert_get_alt_names(&cert->alt_names, cert, cert->pool);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (cert->alt_names) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE4, 0, cert->pool, "cert has %d alt names",
|
|
Packit |
90a5c9 |
cert->alt_names->nelts);
|
|
Packit |
90a5c9 |
for (i = 0; i < md->domains->nelts; ++i) {
|
|
Packit |
90a5c9 |
name = APR_ARRAY_IDX(md->domains, i, const char *);
|
|
Packit |
90a5c9 |
if (md_array_str_index(cert->alt_names, name, 0, 0) < 0) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, cert->pool,
|
|
Packit |
90a5c9 |
"md domain %s not covered by cert", name);
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, cert->pool, "cert has NO alt names");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_cert_get_issuers_uri(const char **puri, md_cert_t *cert, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_ENOENT;
|
|
Packit |
90a5c9 |
STACK_OF(ACCESS_DESCRIPTION) *xinfos;
|
|
Packit |
90a5c9 |
const char *uri = NULL;
|
|
Packit |
90a5c9 |
unsigned char *buf;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
xinfos = X509_get_ext_d2i(cert->x509, NID_info_access, NULL, NULL);
|
|
Packit |
90a5c9 |
if (xinfos) {
|
|
Packit |
90a5c9 |
for (i = 0; i < sk_ACCESS_DESCRIPTION_num(xinfos); i++) {
|
|
Packit |
90a5c9 |
ACCESS_DESCRIPTION *val = sk_ACCESS_DESCRIPTION_value(xinfos, i);
|
|
Packit |
90a5c9 |
if (OBJ_obj2nid(val->method) == NID_ad_ca_issuers
|
|
Packit |
90a5c9 |
&& val->location && val->location->type == GEN_URI) {
|
|
Packit |
90a5c9 |
ASN1_STRING_to_UTF8(&buf, val->location->d.uniformResourceIdentifier);
|
|
Packit |
90a5c9 |
uri = apr_pstrdup(p, (char *)buf);
|
|
Packit |
90a5c9 |
OPENSSL_free(buf);
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
sk_ACCESS_DESCRIPTION_pop_free(xinfos, ACCESS_DESCRIPTION_free);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*puri = (APR_SUCCESS == rv)? uri : NULL;
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_cert_get_alt_names(apr_array_header_t **pnames, md_cert_t *cert, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_array_header_t *names;
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_ENOENT;
|
|
Packit |
90a5c9 |
STACK_OF(GENERAL_NAME) *xalt_names;
|
|
Packit |
90a5c9 |
unsigned char *buf;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
xalt_names = X509_get_ext_d2i(cert->x509, NID_subject_alt_name, NULL, NULL);
|
|
Packit |
90a5c9 |
if (xalt_names) {
|
|
Packit |
90a5c9 |
GENERAL_NAME *cval;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
names = apr_array_make(p, sk_GENERAL_NAME_num(xalt_names), sizeof(char *));
|
|
Packit |
90a5c9 |
for (i = 0; i < sk_GENERAL_NAME_num(xalt_names); ++i) {
|
|
Packit |
90a5c9 |
cval = sk_GENERAL_NAME_value(xalt_names, i);
|
|
Packit |
90a5c9 |
switch (cval->type) {
|
|
Packit |
90a5c9 |
case GEN_DNS:
|
|
Packit |
90a5c9 |
case GEN_URI:
|
|
Packit |
90a5c9 |
case GEN_IPADD:
|
|
Packit |
90a5c9 |
ASN1_STRING_to_UTF8(&buf, cval->d.ia5);
|
|
Packit |
90a5c9 |
APR_ARRAY_PUSH(names, const char *) = apr_pstrdup(p, (char*)buf);
|
|
Packit |
90a5c9 |
OPENSSL_free(buf);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
sk_GENERAL_NAME_pop_free(xalt_names, GENERAL_NAME_free);
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*pnames = (APR_SUCCESS == rv)? names : NULL;
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_cert_fload(md_cert_t **pcert, apr_pool_t *p, const char *fname)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
FILE *f;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
md_cert_t *cert;
|
|
Packit |
90a5c9 |
X509 *x509;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = md_util_fopen(&f, fname, "r");
|
|
Packit |
90a5c9 |
if (rv == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
x509 = PEM_read_X509(f, NULL, NULL, NULL);
|
|
Packit |
90a5c9 |
rv = fclose(f);
|
|
Packit |
90a5c9 |
if (x509 != NULL) {
|
|
Packit |
90a5c9 |
cert = make_cert(p, x509);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
rv = APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
*pcert = (APR_SUCCESS == rv)? cert : NULL;
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t cert_to_buffer(buffer_rec *buffer, md_cert_t *cert, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
BIO *bio = BIO_new(BIO_s_mem());
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!bio) {
|
|
Packit |
90a5c9 |
return APR_ENOMEM;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ERR_clear_error();
|
|
Packit |
90a5c9 |
PEM_write_bio_X509(bio, cert->x509);
|
|
Packit |
90a5c9 |
if (ERR_get_error() > 0) {
|
|
Packit |
90a5c9 |
BIO_free(bio);
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
i = BIO_pending(bio);
|
|
Packit |
90a5c9 |
if (i > 0) {
|
|
Packit |
90a5c9 |
buffer->data = apr_palloc(p, (apr_size_t)i + 1);
|
|
Packit |
90a5c9 |
i = BIO_read(bio, buffer->data, i);
|
|
Packit |
90a5c9 |
buffer->data[i] = '\0';
|
|
Packit |
90a5c9 |
buffer->len = (apr_size_t)i;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
BIO_free(bio);
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_cert_fsave(md_cert_t *cert, apr_pool_t *p,
|
|
Packit |
90a5c9 |
const char *fname, apr_fileperms_t perms)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
buffer_rec buffer;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = cert_to_buffer(&buffer, cert, p))) {
|
|
Packit |
90a5c9 |
return md_util_freplace(fname, perms, p, fwrite_buffer, &buffer);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_cert_to_base64url(const char **ps64, md_cert_t *cert, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
buffer_rec buffer;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = cert_to_buffer(&buffer, cert, p))) {
|
|
Packit |
90a5c9 |
*ps64 = md_util_base64url_encode(buffer.data, buffer.len, p);
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*ps64 = NULL;
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_cert_read_http(md_cert_t **pcert, apr_pool_t *p,
|
|
Packit |
90a5c9 |
const md_http_response_t *res)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *ct;
|
|
Packit |
90a5c9 |
apr_off_t data_len;
|
|
Packit |
90a5c9 |
apr_size_t der_len;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ct = apr_table_get(res->headers, "Content-Type");
|
|
Packit |
90a5c9 |
if (!res->body || !ct || strcmp("application/pkix-cert", ct)) {
|
|
Packit |
90a5c9 |
return APR_ENOENT;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = apr_brigade_length(res->body, 1, &data_len))) {
|
|
Packit |
90a5c9 |
char *der;
|
|
Packit |
90a5c9 |
if (data_len > 1024*1024) { /* certs usually are <2k each */
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == (rv = apr_brigade_pflatten(res->body, &der, &der_len, p))) {
|
|
Packit |
90a5c9 |
const unsigned char *bf = (const unsigned char*)der;
|
|
Packit |
90a5c9 |
X509 *x509;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (NULL == (x509 = d2i_X509(NULL, &bf, (long)der_len))) {
|
|
Packit |
90a5c9 |
rv = APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
*pcert = make_cert(p, x509);
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, "cert parsed");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_cert_state_t md_cert_state_get(md_cert_t *cert)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (cert->x509) {
|
|
Packit |
90a5c9 |
return md_cert_is_valid_now(cert)? MD_CERT_VALID : MD_CERT_EXPIRED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return MD_CERT_UNKNOWN;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_chain_fappend(struct apr_array_header_t *certs, apr_pool_t *p, const char *fname)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
FILE *f;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
X509 *x509;
|
|
Packit |
90a5c9 |
md_cert_t *cert;
|
|
Packit |
90a5c9 |
unsigned long err;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = md_util_fopen(&f, fname, "r");
|
|
Packit |
90a5c9 |
if (rv == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ERR_clear_error();
|
|
Packit |
90a5c9 |
while (NULL != (x509 = PEM_read_X509(f, NULL, NULL, NULL))) {
|
|
Packit |
90a5c9 |
cert = make_cert(p, x509);
|
|
Packit |
90a5c9 |
APR_ARRAY_PUSH(certs, md_cert_t *) = cert;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
fclose(f);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (0 < (err = ERR_get_error())
|
|
Packit |
90a5c9 |
&& !(ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
|
|
Packit |
90a5c9 |
/* not the expected one when no more PEM encodings are found */
|
|
Packit |
90a5c9 |
rv = APR_EINVAL;
|
|
Packit |
90a5c9 |
goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (certs->nelts == 0) {
|
|
Packit |
90a5c9 |
/* Did not find any. This is acceptable unless the file has a certain size
|
|
Packit |
90a5c9 |
* when we no longer accept it as empty chain file. Something seems to be
|
|
Packit |
90a5c9 |
* wrong then. */
|
|
Packit |
90a5c9 |
apr_finfo_t info;
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == apr_stat(&info, fname, APR_FINFO_SIZE, p) && info.size >= 1024) {
|
|
Packit |
90a5c9 |
/* "Too big for a moon." */
|
|
Packit |
90a5c9 |
rv = APR_EINVAL;
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p,
|
|
Packit |
90a5c9 |
"no certificates in non-empty chain %s", fname);
|
|
Packit |
90a5c9 |
goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
out:
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, "read chain file %s, found %d certs",
|
|
Packit |
90a5c9 |
fname, certs? certs->nelts : 0);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_chain_fload(apr_array_header_t **pcerts, apr_pool_t *p, const char *fname)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_array_header_t *certs;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
certs = apr_array_make(p, 5, sizeof(md_cert_t *));
|
|
Packit |
90a5c9 |
rv = md_chain_fappend(certs, p, fname);
|
|
Packit |
90a5c9 |
*pcerts = (APR_SUCCESS == rv)? certs : NULL;
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_chain_fsave(apr_array_header_t *certs, apr_pool_t *p,
|
|
Packit |
90a5c9 |
const char *fname, apr_fileperms_t perms)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
FILE *f;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
const md_cert_t *cert;
|
|
Packit |
90a5c9 |
unsigned long err = 0;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
(void)p;
|
|
Packit |
90a5c9 |
rv = md_util_fopen(&f, fname, "w");
|
|
Packit |
90a5c9 |
if (rv == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
apr_file_perms_set(fname, perms);
|
|
Packit |
90a5c9 |
ERR_clear_error();
|
|
Packit |
90a5c9 |
for (i = 0; i < certs->nelts; ++i) {
|
|
Packit |
90a5c9 |
cert = APR_ARRAY_IDX(certs, i, const md_cert_t *);
|
|
Packit |
90a5c9 |
assert(cert->x509);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
PEM_write_X509(f, cert->x509);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (0 < (err = ERR_get_error())) {
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
rv = fclose(f);
|
|
Packit |
90a5c9 |
if (err) {
|
|
Packit |
90a5c9 |
rv = APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**************************************************************************************************/
|
|
Packit |
90a5c9 |
/* certificate signing requests */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *alt_names(apr_array_header_t *domains, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *alts = "", *sep = "", *domain;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (i = 0; i < domains->nelts; ++i) {
|
|
Packit |
90a5c9 |
domain = APR_ARRAY_IDX(domains, i, const char *);
|
|
Packit |
90a5c9 |
alts = apr_psprintf(p, "%s%sDNS:%s", alts, sep, domain);
|
|
Packit |
90a5c9 |
sep = ",";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return alts;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t add_ext(X509 *x, int nid, const char *value, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
X509_EXTENSION *ext = NULL;
|
|
Packit |
90a5c9 |
X509V3_CTX ctx;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
X509V3_set_ctx_nodb(&ctx;;
|
|
Packit |
90a5c9 |
X509V3_set_ctx(&ctx, x, x, NULL, NULL, 0);
|
|
Packit |
90a5c9 |
if (NULL == (ext = X509V3_EXT_conf_nid(NULL, &ctx, nid, (char*)value))) {
|
|
Packit |
90a5c9 |
return APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ERR_clear_error();
|
|
Packit |
90a5c9 |
rv = X509_add_ext(x, ext, -1)? APR_SUCCESS : APR_EINVAL;
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != rv) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "add_ext nid=%dd value='%s'",
|
|
Packit |
90a5c9 |
nid, value);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
X509_EXTENSION_free(ext);
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t sk_add_alt_names(STACK_OF(X509_EXTENSION) *exts,
|
|
Packit |
90a5c9 |
apr_array_header_t *domains, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (domains->nelts > 0) {
|
|
Packit |
90a5c9 |
X509_EXTENSION *x;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
x = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, (char*)alt_names(domains, p));
|
|
Packit |
90a5c9 |
if (NULL == x) {
|
|
Packit |
90a5c9 |
return APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
sk_X509_EXTENSION_push(exts, x);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#define MD_OID_MUST_STAPLE_NUM "1.3.6.1.5.5.7.1.24"
|
|
Packit |
90a5c9 |
#define MD_OID_MUST_STAPLE_SNAME "tlsfeature"
|
|
Packit |
90a5c9 |
#define MD_OID_MUST_STAPLE_LNAME "TLS Feature"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int get_must_staple_nid(void)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
/* Funny API, the OID for must staple might be configured or
|
|
Packit |
90a5c9 |
* might be not. In the second case, we need to add it. But adding
|
|
Packit |
90a5c9 |
* when it already is there is an error... */
|
|
Packit |
90a5c9 |
int nid = OBJ_txt2nid(MD_OID_MUST_STAPLE_NUM);
|
|
Packit |
90a5c9 |
if (NID_undef == nid) {
|
|
Packit |
90a5c9 |
nid = OBJ_create(MD_OID_MUST_STAPLE_NUM,
|
|
Packit |
90a5c9 |
MD_OID_MUST_STAPLE_SNAME, MD_OID_MUST_STAPLE_LNAME);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return nid;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
int md_cert_must_staple(md_cert_t *cert)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
/* In case we do not get the NID for it, we treat this as not set. */
|
|
Packit |
90a5c9 |
int nid = get_must_staple_nid();
|
|
Packit |
90a5c9 |
return ((NID_undef != nid)) && X509_get_ext_by_NID(cert->x509, nid, -1) >= 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t add_must_staple(STACK_OF(X509_EXTENSION) *exts, const md_t *md, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (md->must_staple) {
|
|
Packit |
90a5c9 |
X509_EXTENSION *x;
|
|
Packit |
90a5c9 |
int nid;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
nid = get_must_staple_nid();
|
|
Packit |
90a5c9 |
if (NID_undef == nid) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p,
|
|
Packit |
90a5c9 |
"%s: unable to get NID for v3 must-staple TLS feature", md->name);
|
|
Packit |
90a5c9 |
return APR_ENOTIMPL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
x = X509V3_EXT_conf_nid(NULL, NULL, nid, (char*)"DER:30:03:02:01:05");
|
|
Packit |
90a5c9 |
if (NULL == x) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p,
|
|
Packit |
90a5c9 |
"%s: unable to create x509 extension for must-staple", md->name);
|
|
Packit |
90a5c9 |
return APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
sk_X509_EXTENSION_push(exts, x);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_cert_req_create(const char **pcsr_der_64, const md_t *md,
|
|
Packit |
90a5c9 |
md_pkey_t *pkey, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *s, *csr_der, *csr_der_64 = NULL;
|
|
Packit |
90a5c9 |
const unsigned char *domain;
|
|
Packit |
90a5c9 |
X509_REQ *csr;
|
|
Packit |
90a5c9 |
X509_NAME *n = NULL;
|
|
Packit |
90a5c9 |
STACK_OF(X509_EXTENSION) *exts = NULL;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
int csr_der_len;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
assert(md->domains->nelts > 0);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (NULL == (csr = X509_REQ_new())
|
|
Packit |
90a5c9 |
|| NULL == (exts = sk_X509_EXTENSION_new_null())
|
|
Packit |
90a5c9 |
|| NULL == (n = X509_NAME_new())) {
|
|
Packit |
90a5c9 |
rv = APR_ENOMEM;
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: openssl alloc X509 things", md->name);
|
|
Packit |
90a5c9 |
goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* subject name == first domain */
|
|
Packit |
90a5c9 |
domain = APR_ARRAY_IDX(md->domains, 0, const unsigned char *);
|
|
Packit |
90a5c9 |
if (!X509_NAME_add_entry_by_txt(n, "CN", MBSTRING_ASC, domain, -1, -1, 0)
|
|
Packit |
90a5c9 |
|| !X509_REQ_set_subject_name(csr, n)) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: REQ name add entry", md->name);
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* collect extensions, such as alt names and must staple */
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = sk_add_alt_names(exts, md->domains, p))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: collecting alt names", md->name);
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = add_must_staple(exts, md, p))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: you requested that a certificate "
|
|
Packit |
90a5c9 |
"is created with the 'must-staple' extension, however the SSL library was "
|
|
Packit |
90a5c9 |
"unable to initialized that extension. Please file a bug report on which platform "
|
|
Packit |
90a5c9 |
"and with which library this happens. To continue before this problem is resolved, "
|
|
Packit |
90a5c9 |
"configure 'MDMustStaple off' for your domains", md->name);
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* add extensions to csr */
|
|
Packit |
90a5c9 |
if (sk_X509_EXTENSION_num(exts) > 0 && !X509_REQ_add_extensions(csr, exts)) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: adding exts", md->name);
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* add our key */
|
|
Packit |
90a5c9 |
if (!X509_REQ_set_pubkey(csr, pkey->pkey)) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set pkey in csr", md->name);
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* sign, der encode and base64url encode */
|
|
Packit |
90a5c9 |
if (!X509_REQ_sign(csr, pkey->pkey, EVP_sha256())) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: sign csr", md->name);
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if ((csr_der_len = i2d_X509_REQ(csr, NULL)) < 0) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: der length", md->name);
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
s = csr_der = apr_pcalloc(p, (apr_size_t)csr_der_len + 1);
|
|
Packit |
90a5c9 |
if (i2d_X509_REQ(csr, (unsigned char**)&s) != csr_der_len) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: csr der enc", md->name);
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
csr_der_64 = md_util_base64url_encode(csr_der, (apr_size_t)csr_der_len, p);
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
out:
|
|
Packit |
90a5c9 |
if (exts) {
|
|
Packit |
90a5c9 |
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (csr) {
|
|
Packit |
90a5c9 |
X509_REQ_free(csr);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (n) {
|
|
Packit |
90a5c9 |
X509_NAME_free(n);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*pcsr_der_64 = (APR_SUCCESS == rv)? csr_der_64 : NULL;
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn,
|
|
Packit |
90a5c9 |
apr_array_header_t *domains, md_pkey_t *pkey,
|
|
Packit |
90a5c9 |
apr_interval_time_t valid_for, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
X509 *x;
|
|
Packit |
90a5c9 |
X509_NAME *n = NULL;
|
|
Packit |
90a5c9 |
md_cert_t *cert = NULL;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
int days;
|
|
Packit |
90a5c9 |
BIGNUM *big_rnd = NULL;
|
|
Packit |
90a5c9 |
ASN1_INTEGER *asn1_rnd = NULL;
|
|
Packit |
90a5c9 |
unsigned char rnd[20];
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
assert(domains);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (NULL == (x = X509_new())
|
|
Packit |
90a5c9 |
|| NULL == (n = X509_NAME_new())) {
|
|
Packit |
90a5c9 |
rv = APR_ENOMEM;
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: openssl alloc X509 things", cn);
|
|
Packit |
90a5c9 |
goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = md_rand_bytes(rnd, sizeof(rnd), p))
|
|
Packit |
90a5c9 |
|| !(big_rnd = BN_bin2bn(rnd, sizeof(rnd), NULL))
|
|
Packit |
90a5c9 |
|| !(asn1_rnd = BN_to_ASN1_INTEGER(big_rnd, NULL))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: setup random serial", cn);
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (1 != X509_set_version(x, 2L)) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: setting x.509v3", cn);
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!X509_set_serialNumber(x, asn1_rnd)) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: set serial number", cn);
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* set common name and issue */
|
|
Packit |
90a5c9 |
if (!X509_NAME_add_entry_by_txt(n, "CN", MBSTRING_ASC, (const unsigned char*)cn, -1, -1, 0)
|
|
Packit |
90a5c9 |
|| !X509_set_subject_name(x, n)
|
|
Packit |
90a5c9 |
|| !X509_set_issuer_name(x, n)) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: name add entry", cn);
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* cert are unconstrained (but not very trustworthy) */
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = add_ext(x, NID_basic_constraints, "CA:FALSE, pathlen:0", p))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set basic constraints ext", cn);
|
|
Packit |
90a5c9 |
goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* add the domain as alt name */
|
|
Packit |
90a5c9 |
if (APR_SUCCESS != (rv = add_ext(x, NID_subject_alt_name, alt_names(domains, p), p))) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set alt_name ext", cn);
|
|
Packit |
90a5c9 |
goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* add our key */
|
|
Packit |
90a5c9 |
if (!X509_set_pubkey(x, pkey->pkey)) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set pkey in x509", cn);
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
days = (int)((apr_time_sec(valid_for) + MD_SECS_PER_DAY - 1)/ MD_SECS_PER_DAY);
|
|
Packit |
90a5c9 |
if (!X509_set_notBefore(x, ASN1_TIME_set(NULL, time(NULL)))) {
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (!X509_set_notAfter(x, ASN1_TIME_adj(NULL, time(NULL), days, 0))) {
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* sign with same key */
|
|
Packit |
90a5c9 |
if (!X509_sign(x, pkey->pkey, EVP_sha256())) {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: sign x509", cn);
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL; goto out;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
cert = make_cert(p, x);
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
out:
|
|
Packit |
90a5c9 |
if (!cert && x) {
|
|
Packit |
90a5c9 |
X509_free(x);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (n) {
|
|
Packit |
90a5c9 |
X509_NAME_free(n);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (big_rnd) {
|
|
Packit |
90a5c9 |
BN_free(big_rnd);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (asn1_rnd) {
|
|
Packit |
90a5c9 |
ASN1_INTEGER_free(asn1_rnd);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*pcert = (APR_SUCCESS == rv)? cert : NULL;
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|