mirror of
https://github.com/apache/httpd.git
synced 2025-08-01 07:26:57 +03:00
* mod_ssl_openssl.h: Make it the first openssl to be included openssl header, selecting the OpenSSL api based on OPENSSL_API_COMPAT eventually. * ssl_private.h; Define OPENSSL_API_COMPAT to version 1.1.1 (last one supporting EGINE_ API) before including mod_ssl_openssl.h to enable the ENGINE_ api (TODO: switch to new "providers" api before the ENGINE_ api is abandonned..). mod_ssl.h is now implicitely included from there. Fix preprocessor "#define FOO (COND)" to "#if COND #define FOO 1 #else #define FOO 0". Define MODSSL_HAVE_ENGINE_API iff OPENSSL_API_COMPAT < 3.0 (otherwise all the engine features are disabled, only "builtin" is accepted). Define HAVE_SRP iff OPENSSL_API_COMPAT < 3.0 (no replacement for this api above, so it might not be implemenentedain httpd anymore at some point..). Define X509_get_not{Before,After} if missing to the non deprecated version. New modssl_set_io_callbacks() to factorize compat code for io callbacks. ssl_dh_GetParamFromFile() becomes modssl_dh_from_file() for openssl < 3.0 and modssl_dh_pkey_from_file() for openssl >= 3.0. * mod_ssl.c, mod_ssl_ct.c, ssl_util_stapling: Including "ssl_private.h" only is suited/enough now. * mod_ssl_ct.c, ssl_ct_log_config: Use EVP api with openssl >= 3 instead of the deprecated SHA256 one. * ssl_engine_config.c(ssl_cmd_SSLCryptoDevice): Disabled engines (besides NULL/"builtin"/NULL) unless MODSSL_HAVE_ENGINE_API. * ssl_engine_init: New compat modssl_runtime_lib_version() to address deprecated SSLeay(). ssl_init_Engine() does nothing unless MODSSL_HAVE_ENGINE_API. Simplify ssl_init_server_certs() (less #ifdef-ery) with scoped local vars. Compat loading DH parameters and EC curve from cert. * ssl_engine_io.c, ssl_engine_kernel.c: Implement common modssl_set_io_callbacks() and use it. * ssl_engine_pphrase(modssl_load_engine_keypair): Depend on MODSSL_HAVE_ENGINE_API, or return ENOTIMPL. * ssl_util.c(modssl_is_engine_id): No engine supported unless MODSSL_HAVE_ENGINE_API. * ssl_util_ssl.c(modssl_dh_pkey_from_file, modssl_ec_group_from_file): Compat with openssl >= 3.0. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1908537 13f79535-47bb-0310-9956-ffa450edef68
450 lines
14 KiB
C
450 lines
14 KiB
C
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
* (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "apr_dbd.h"
|
|
#include "apr_escape.h"
|
|
#include "apr_strings.h"
|
|
|
|
#include "httpd.h"
|
|
#include "http_log.h"
|
|
#include "http_main.h"
|
|
|
|
#include "ssl_ct_sct.h"
|
|
#include "ssl_ct_log_config.h"
|
|
#include "ssl_ct_util.h"
|
|
|
|
APLOG_USE_MODULE(ssl_ct);
|
|
|
|
int log_config_readable(apr_pool_t *p, const char *logconfig,
|
|
const char **msg)
|
|
{
|
|
const apr_dbd_driver_t *driver;
|
|
apr_dbd_t *handle;
|
|
apr_status_t rv;
|
|
apr_dbd_results_t *res;
|
|
int rc;
|
|
|
|
rv = apr_dbd_get_driver(p, "sqlite3", &driver);
|
|
if (rv != APR_SUCCESS) {
|
|
if (msg) {
|
|
*msg = "SQLite3 driver cannot be loaded";
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
rv = apr_dbd_open(driver, p, logconfig, &handle);
|
|
if (rv != APR_SUCCESS) {
|
|
return 0;
|
|
}
|
|
|
|
/* is there a cheaper way? */
|
|
res = NULL;
|
|
rc = apr_dbd_select(driver, p, handle, &res,
|
|
"SELECT * FROM loginfo WHERE id = 0", 0);
|
|
|
|
apr_dbd_close(driver, handle);
|
|
|
|
if (rc != 0) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static apr_status_t public_key_cleanup(void *data)
|
|
{
|
|
EVP_PKEY *pubkey = data;
|
|
|
|
EVP_PKEY_free(pubkey);
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
static apr_status_t read_public_key(apr_pool_t *p, const char *pubkey_fname,
|
|
EVP_PKEY **ppkey)
|
|
{
|
|
apr_status_t rv;
|
|
EVP_PKEY *pubkey;
|
|
FILE *pubkeyf;
|
|
|
|
*ppkey = NULL;
|
|
|
|
rv = ctutil_fopen(pubkey_fname, "r", &pubkeyf);
|
|
if (rv != APR_SUCCESS) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
|
|
APLOGNO(02751) "could not open log public key file %s",
|
|
pubkey_fname);
|
|
return rv;
|
|
}
|
|
|
|
pubkey = PEM_read_PUBKEY(pubkeyf, NULL, NULL, NULL);
|
|
if (!pubkey) {
|
|
fclose(pubkeyf);
|
|
rv = APR_EINVAL;
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
|
|
APLOGNO(02752) "PEM_read_PUBKEY() failed to process "
|
|
"public key file %s",
|
|
pubkey_fname);
|
|
return rv;
|
|
}
|
|
|
|
fclose(pubkeyf);
|
|
|
|
*ppkey = pubkey;
|
|
|
|
apr_pool_cleanup_register(p, (void *)pubkey, public_key_cleanup,
|
|
apr_pool_cleanup_null);
|
|
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
static void digest_public_key(EVP_PKEY *pubkey, unsigned char digest[LOG_ID_SIZE])
|
|
{
|
|
int len = i2d_PUBKEY(pubkey, NULL);
|
|
unsigned char *val = ap_malloc(len);
|
|
unsigned char *tmp = val;
|
|
|
|
ap_assert(LOG_ID_SIZE == SHA256_DIGEST_LENGTH);
|
|
|
|
i2d_PUBKEY(pubkey, &tmp);
|
|
#if OPENSSL_VERSION_NUMBER < 0x30000000
|
|
{
|
|
SHA256_CTX sha256ctx;
|
|
SHA256_Init(&sha256ctx);
|
|
SHA256_Update(&sha256ctx, (unsigned char *)val, len);
|
|
SHA256_Final(digest, &sha256ctx);
|
|
}
|
|
#else
|
|
{
|
|
EVP_MD_CTX *md_ctx;
|
|
unsigned int dlen = 0;
|
|
md_ctx = EVP_MD_CTX_create();
|
|
ap_assert(md_ctx != NULL);
|
|
ap_assert(EVP_DigestInit_ex(md_ctx, EVP_sha256(), NULL));
|
|
ap_assert(EVP_DigestUpdate(md_ctx, val, len));
|
|
ap_assert(EVP_DigestFinal_ex(md_ctx, digest, &dlen));
|
|
ap_assert(dlen == SHA256_DIGEST_LENGTH);
|
|
EVP_MD_CTX_destroy(md_ctx);
|
|
}
|
|
#endif
|
|
free(val);
|
|
}
|
|
|
|
static apr_status_t parse_log_url(apr_pool_t *p, const char *lu, apr_uri_t *puri)
|
|
{
|
|
apr_status_t rv;
|
|
apr_uri_t uri;
|
|
|
|
rv = apr_uri_parse(p, lu, &uri);
|
|
if (rv == APR_SUCCESS) {
|
|
if (!uri.scheme
|
|
|| !uri.hostname
|
|
|| !uri.path) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
|
|
APLOGNO(02753) "Error in log url \"%s\": URL can't be "
|
|
"parsed or is missing required elements", lu);
|
|
rv = APR_EINVAL;
|
|
}
|
|
if (strcmp(uri.scheme, "http")) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
|
|
APLOGNO(02754) "Error in log url \"%s\": Only scheme "
|
|
"\"http\" (instead of \"%s\") is currently "
|
|
"accepted",
|
|
lu, uri.scheme);
|
|
rv = APR_EINVAL;
|
|
}
|
|
if (strcmp(uri.path, "/")) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
|
|
APLOGNO(02755) "Error in log url \"%s\": Only path "
|
|
"\"/\" (instead of \"%s\") is currently accepted",
|
|
lu, uri.path);
|
|
rv = APR_EINVAL;
|
|
}
|
|
}
|
|
if (rv == APR_SUCCESS) {
|
|
*puri = uri;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
static apr_status_t parse_time_str(apr_pool_t *p, const char *time_str,
|
|
apr_time_t *time)
|
|
{
|
|
apr_int64_t val;
|
|
const char *end;
|
|
|
|
errno = 0;
|
|
val = apr_strtoi64(time_str, (char **)&end, 10);
|
|
if (errno || *end != '\0') {
|
|
return APR_EINVAL;
|
|
}
|
|
|
|
*time = apr_time_from_msec(val);
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
/* The log_config array should have already been allocated from p. */
|
|
apr_status_t save_log_config_entry(apr_array_header_t *log_config,
|
|
apr_pool_t *p,
|
|
const char *log_id,
|
|
const char *pubkey_fname,
|
|
const char *distrusted_str,
|
|
const char *min_time_str,
|
|
const char *max_time_str,
|
|
const char *url)
|
|
{
|
|
apr_size_t len;
|
|
apr_status_t rv;
|
|
apr_time_t min_time, max_time;
|
|
apr_uri_t uri;
|
|
char *computed_log_id = NULL, *log_id_bin = NULL;
|
|
ct_log_config *newconf, **pnewconf;
|
|
int distrusted;
|
|
EVP_PKEY *public_key;
|
|
|
|
if (!distrusted_str) {
|
|
distrusted = DISTRUSTED_UNSET;
|
|
}
|
|
else if (!strcmp(distrusted_str, "1")) {
|
|
distrusted = DISTRUSTED;
|
|
}
|
|
else if (!strcmp(distrusted_str, "0")) {
|
|
distrusted = TRUSTED;
|
|
}
|
|
else {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
|
|
APLOGNO(02756) "Trusted status \"%s\" not valid",
|
|
distrusted_str);
|
|
return APR_EINVAL;
|
|
}
|
|
|
|
if (log_id) {
|
|
rv = apr_unescape_hex(NULL, log_id, strlen(log_id), 0, &len);
|
|
if (rv != 0 || len != 32) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
|
|
APLOGNO(02757) "Log id \"%s\" not valid", log_id);
|
|
log_id_bin = apr_palloc(p, len);
|
|
apr_unescape_hex(log_id_bin, log_id, strlen(log_id), 0, NULL);
|
|
}
|
|
}
|
|
|
|
if (pubkey_fname) {
|
|
rv = read_public_key(p, pubkey_fname, &public_key);
|
|
if (rv != APR_SUCCESS) {
|
|
return rv;
|
|
}
|
|
}
|
|
else {
|
|
public_key = NULL;
|
|
}
|
|
|
|
if (min_time_str) {
|
|
rv = parse_time_str(p, min_time_str, &min_time);
|
|
if (rv) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
|
|
APLOGNO(02758) "Invalid min time \"%s\"", min_time_str);
|
|
return rv;
|
|
}
|
|
}
|
|
else {
|
|
min_time = 0;
|
|
}
|
|
|
|
if (max_time_str) {
|
|
rv = parse_time_str(p, max_time_str, &max_time);
|
|
if (rv) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
|
|
APLOGNO(02759) "Invalid max time \"%s\"", max_time_str);
|
|
return rv;
|
|
}
|
|
}
|
|
else {
|
|
max_time = 0;
|
|
}
|
|
|
|
if (url) {
|
|
rv = parse_log_url(p, url, &uri);
|
|
if (rv != APR_SUCCESS) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
newconf = apr_pcalloc(p, sizeof(ct_log_config));
|
|
pnewconf = (ct_log_config **)apr_array_push(log_config);
|
|
*pnewconf = newconf;
|
|
|
|
newconf->distrusted = distrusted;
|
|
newconf->public_key = public_key;
|
|
|
|
if (newconf->public_key) {
|
|
computed_log_id = apr_palloc(p, LOG_ID_SIZE);
|
|
digest_public_key(newconf->public_key,
|
|
(unsigned char *)computed_log_id);
|
|
}
|
|
|
|
if (computed_log_id && log_id_bin) {
|
|
if (memcmp(computed_log_id, log_id_bin, LOG_ID_SIZE)) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
|
|
APLOGNO(02760) "Provided log id doesn't match digest "
|
|
"of public key");
|
|
return APR_EINVAL;
|
|
}
|
|
}
|
|
|
|
newconf->log_id = log_id_bin ? log_id_bin : computed_log_id;
|
|
|
|
newconf->min_valid_time = min_time;
|
|
newconf->max_valid_time = max_time;
|
|
|
|
newconf->url = url;
|
|
if (url) {
|
|
newconf->uri = uri;
|
|
newconf->uri_str = apr_uri_unparse(p, &uri, 0);
|
|
}
|
|
newconf->public_key_pem = pubkey_fname;
|
|
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
apr_status_t read_config_db(apr_pool_t *p, server_rec *s_main,
|
|
const char *log_config_fname,
|
|
apr_array_header_t *log_config)
|
|
{
|
|
apr_status_t rv;
|
|
const apr_dbd_driver_t *driver;
|
|
apr_dbd_t *handle;
|
|
apr_dbd_results_t *res;
|
|
apr_dbd_row_t *row;
|
|
int rc;
|
|
|
|
ap_assert(log_config);
|
|
|
|
rv = apr_dbd_get_driver(p, "sqlite3", &driver);
|
|
if (rv != APR_SUCCESS) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s_main,
|
|
APLOGNO(02761) "APR SQLite3 driver can't be loaded");
|
|
return rv;
|
|
}
|
|
|
|
rv = apr_dbd_open(driver, p, log_config_fname, &handle);
|
|
if (rv != APR_SUCCESS) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s_main,
|
|
APLOGNO(02762) "Can't open SQLite3 db %s",
|
|
log_config_fname);
|
|
return rv;
|
|
}
|
|
|
|
res = NULL;
|
|
rc = apr_dbd_select(driver, p, handle, &res,
|
|
"SELECT * FROM loginfo", 0);
|
|
|
|
if (rc != 0) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s_main,
|
|
APLOGNO(02763) "SELECT of loginfo records failed");
|
|
apr_dbd_close(driver, handle);
|
|
return APR_EINVAL;
|
|
}
|
|
|
|
rc = apr_dbd_num_tuples(driver, res);
|
|
switch (rc) {
|
|
case -1:
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s_main,
|
|
APLOGNO(02764) "Unexpected asynchronous result reading %s",
|
|
log_config_fname);
|
|
apr_dbd_close(driver, handle);
|
|
return APR_EINVAL;
|
|
case 0:
|
|
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s_main,
|
|
APLOGNO(02765) "Log configuration in %s is empty",
|
|
log_config_fname);
|
|
apr_dbd_close(driver, handle);
|
|
return APR_SUCCESS;
|
|
default:
|
|
/* quiet some lints */
|
|
break;
|
|
}
|
|
|
|
for (rv = apr_dbd_get_row(driver, p, res, &row, -1);
|
|
rv == APR_SUCCESS;
|
|
rv = apr_dbd_get_row(driver, p, res, &row, -1)) {
|
|
int cur = 0;
|
|
const char *id = apr_dbd_get_entry(driver, row, cur++);
|
|
const char *log_id = apr_dbd_get_entry(driver, row, cur++);
|
|
const char *public_key = apr_dbd_get_entry(driver, row, cur++);
|
|
const char *distrusted = apr_dbd_get_entry(driver, row, cur++);
|
|
const char *min_timestamp = apr_dbd_get_entry(driver, row, cur++);
|
|
const char *max_timestamp = apr_dbd_get_entry(driver, row, cur++);
|
|
const char *url = apr_dbd_get_entry(driver, row, cur++);
|
|
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s_main, APLOGNO(03036)
|
|
"Log config: Record %s, log id %s, public key file %s,"
|
|
" distrusted %s, URL %s, time %s->%s",
|
|
id,
|
|
log_id ? log_id : "(unset)",
|
|
public_key ? public_key : "(unset)",
|
|
distrusted ? distrusted : "(unset, defaults to trusted)",
|
|
url ? url : "(unset)",
|
|
min_timestamp ? min_timestamp : "-INF",
|
|
max_timestamp ? max_timestamp : "+INF");
|
|
|
|
rv = save_log_config_entry(log_config, p, log_id,
|
|
public_key, distrusted,
|
|
min_timestamp, max_timestamp, url);
|
|
if (rv != APR_SUCCESS) {
|
|
apr_dbd_close(driver, handle);
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
apr_dbd_close(driver, handle);
|
|
|
|
return APR_SUCCESS;
|
|
}
|
|
|
|
int log_valid_for_received_sct(const ct_log_config *l, apr_time_t to_check)
|
|
{
|
|
if (l->distrusted == DISTRUSTED) {
|
|
return 0;
|
|
}
|
|
|
|
if (l->max_valid_time && l->max_valid_time < to_check) {
|
|
return 0;
|
|
}
|
|
|
|
if (l->min_valid_time && l->min_valid_time < to_check) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int log_valid_for_sent_sct(const ct_log_config *l)
|
|
{
|
|
/* The log could return us an SCT with an older timestamp which
|
|
* is within the trusted time interval for the log, but for
|
|
* simplicity let's just assume that if the log isn't still
|
|
* within a trusted interval we won't send SCTs from the log.
|
|
*/
|
|
return log_valid_for_received_sct(l, apr_time_now());
|
|
}
|
|
|
|
int log_configured_for_fetching_sct(const ct_log_config *l)
|
|
{
|
|
/* must have a url and a public key configured in order to obtain
|
|
* an SCT from the log
|
|
*/
|
|
return l->url != NULL && l->public_key != NULL;
|
|
}
|