mirror of
https://github.com/apache/httpd.git
synced 2025-08-06 11:06:17 +00:00

had none. Handled all files in modules/. I used the coccinelle script provided by Stefan. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1725392 13f79535-47bb-0310-9956-ffa450edef68
434 lines
13 KiB
C
434 lines
13 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 = malloc(len);
|
|
unsigned char *tmp = val;
|
|
SHA256_CTX sha256ctx;
|
|
|
|
ap_assert(LOG_ID_SIZE == SHA256_DIGEST_LENGTH);
|
|
|
|
i2d_PUBKEY(pubkey, &tmp);
|
|
SHA256_Init(&sha256ctx);
|
|
SHA256_Update(&sha256ctx, (unsigned char *)val, len);
|
|
SHA256_Final(digest, &sha256ctx);
|
|
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;
|
|
}
|