<commit_before>/* 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.
 */

/*
 * Issues
 *
 * + Certificates
 *   These are read to obtain fingerprints and to submit to logs.
 *   The module assumes that they are configured via SSLCertificateFile
 *   with only a leaf certificate in the file.  Certificates loaded by
 *   SSLOpenSSLConfCmd are not supported.
 *
 *   See dev@httpd e-mails discussing SSL_CTX_get_{first,next}_certificate()
 *
 *   Ah, but the log needs to see intermediate certificates too...
 *
 * + SCTs can only be stored at startup (no daemon process to refresh them yet)
 * + Are we really sending the SCT(s) correctly?  That needs to be tested in
 *   detail.  But SSL client used by mod_proxy needs some minimal verification
 *   implemented anyway.
 * + Proxy flow should queue the server cert and SCT(s) for audit in a manner
 *   that facilitates the auditing support in the c-t tools.
 * + Proxy should have a setting that aborts when the backend doesn't send
 *   an SCT.  (It must recognize when one is delivered with the certificate
 *   or via OCSP stapling.)
 *
 * + Configuration kludges
 *   . ??
 *
 * + Known low-level code kludges/problems
 *   . uses system() instead of apr_proc_create(), which would allow better
 *     control of output
 *   . no way to log CT-awareness of backend server
 *
 * + Everything else
 *   . ??
 *
 * + Stuff to remember, or note elsewhere:
 *   .
 *
 */

#include "apr_strings.h"

#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include "http_protocol.h"
#include "ap_mpm.h"

#include "ssl_hooks.h"

#ifdef WIN32
#define DOTEXE ".exe"
#else
#define DOTEXE ""
#endif

#define STATUS_VAR "SSL_CT_PEER_STATUS"

/** A certificate file larger than this is suspect */
#define MAX_CERT_FILE_SIZE 30000 /* eventually this can include intermediate certs */

/** Limit on size of stored SCTs for a certificate */
#define MAX_SCTS_SIZE 2000

typedef struct ct_server_config {
    apr_array_header_t *log_urls;
    apr_array_header_t *cert_files;
    const char *sct_storage;
    const char *ct_tools_dir;
    const char *ct_exe;
} ct_server_config;

typedef struct ct_conn_config {
    int peer_ct_aware;
} ct_conn_config;

typedef struct ct_callback_info {
    server_rec *s;
    conn_rec *c;
    ct_conn_config *conncfg;
} ct_callback_info;

module AP_MODULE_DECLARE_DATA ssl_ct_module;

#define FINGERPRINT_SIZE 60

static void get_fingerprint(X509 *x, char *fingerprint, size_t fpsize)
{
    const EVP_MD *digest;
    unsigned char md[EVP_MAX_MD_SIZE];
    int i;
    unsigned int n;
    digest = EVP_get_digestbyname("sha1");
    X509_digest(x, digest, md, &n);

    ap_assert(n == 20);
    ap_assert(fpsize >= FINGERPRINT_SIZE);

    i = 0;
    while (i < n - 1) {
        apr_snprintf(fingerprint + i * 3, 4, "%02X:", md[i]);
        i++;
    }
    apr_snprintf(fingerprint + i * 3, 3, "%02X", md[i]);
}

static int dir_exists(apr_pool_t *p, const char *dirname)
{
    apr_finfo_t finfo;
    apr_status_t rv = apr_stat(&finfo, dirname, APR_FINFO_TYPE, p);

    return rv == APR_SUCCESS && finfo.filetype == APR_DIR;
}

static int file_exists(apr_pool_t *p, const char *filename)
{
    apr_finfo_t finfo;
    apr_status_t rv = apr_stat(&finfo, filename, APR_FINFO_TYPE, p);

    return rv == APR_SUCCESS && finfo.filetype == APR_REG;
}

static apr_status_t readFile(apr_pool_t *p,
                             server_rec *s,
                             const char *fn,
                             apr_size_t limit,
                             char **contents,
                             apr_size_t *contents_size)
{
    apr_file_t *f;
    apr_finfo_t finfo;
    apr_status_t rv;
    apr_size_t nbytes;

    *contents = NULL;
    *contents_size = 0;

    rv = apr_file_open(&f, fn, APR_READ | APR_BINARY, APR_FPROT_OS_DEFAULT, p);
    if (rv != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
                     "couldn't read %s", fn);
        return rv;
    }
    
    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, f);
    if (rv != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
                     "couldn't retrieve size of %s", fn);
        apr_file_close(f);
        return rv;
    }

    if (finfo.size > limit) {
        rv = APR_ENOSPC;
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
                     "size of %s exceeds limit", fn);
        apr_file_close(f);
        return rv;
    }

    nbytes = (apr_size_t)finfo.size;
    *contents = apr_palloc(p, nbytes);
    rv = apr_file_read_full(f, *contents, nbytes, contents_size);
    if (rv == APR_SUCCESS) { /* shouldn't get APR_EOF since we know
                              * how big the file is
                              */
        rv = APR_SUCCESS;
    }
    else {<commit_msg>axe historical cruft related to apr_file_read_full and APR_EOF<commit_after>/* 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.
 */

/*
 * Issues
 *
 * + Certificates
 *   These are read to obtain fingerprints and to submit to logs.
 *   The module assumes that they are configured via SSLCertificateFile
 *   with only a leaf certificate in the file.  Certificates loaded by
 *   SSLOpenSSLConfCmd are not supported.
 *
 *   See dev@httpd e-mails discussing SSL_CTX_get_{first,next}_certificate()
 *
 *   Ah, but the log needs to see intermediate certificates too...
 *
 * + SCTs can only be stored at startup (no daemon process to refresh them yet)
 * + Are we really sending the SCT(s) correctly?  That needs to be tested in
 *   detail.  But SSL client used by mod_proxy needs some minimal verification
 *   implemented anyway.
 * + Proxy flow should queue the server cert and SCT(s) for audit in a manner
 *   that facilitates the auditing support in the c-t tools.
 * + Proxy should have a setting that aborts when the backend doesn't send
 *   an SCT.  (It must recognize when one is delivered with the certificate
 *   or via OCSP stapling.)
 *
 * + Configuration kludges
 *   . ??
 *
 * + Known low-level code kludges/problems
 *   . uses system() instead of apr_proc_create(), which would allow better
 *     control of output
 *   . no way to log CT-awareness of backend server
 *
 * + Everything else
 *   . ??
 *
 * + Stuff to remember, or note elsewhere:
 *   .
 *
 */

#include "apr_strings.h"

#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include "http_protocol.h"
#include "ap_mpm.h"

#include "ssl_hooks.h"

#ifdef WIN32
#define DOTEXE ".exe"
#else
#define DOTEXE ""
#endif

#define STATUS_VAR "SSL_CT_PEER_STATUS"

/** A certificate file larger than this is suspect */
#define MAX_CERT_FILE_SIZE 30000 /* eventually this can include intermediate certs */

/** Limit on size of stored SCTs for a certificate */
#define MAX_SCTS_SIZE 2000

typedef struct ct_server_config {
    apr_array_header_t *log_urls;
    apr_array_header_t *cert_files;
    const char *sct_storage;
    const char *ct_tools_dir;
    const char *ct_exe;
} ct_server_config;

typedef struct ct_conn_config {
    int peer_ct_aware;
} ct_conn_config;

typedef struct ct_callback_info {
    server_rec *s;
    conn_rec *c;
    ct_conn_config *conncfg;
} ct_callback_info;

module AP_MODULE_DECLARE_DATA ssl_ct_module;

#define FINGERPRINT_SIZE 60

static void get_fingerprint(X509 *x, char *fingerprint, size_t fpsize)
{
    const EVP_MD *digest;
    unsigned char md[EVP_MAX_MD_SIZE];
    int i;
    unsigned int n;
    digest = EVP_get_digestbyname("sha1");
    X509_digest(x, digest, md, &n);

    ap_assert(n == 20);
    ap_assert(fpsize >= FINGERPRINT_SIZE);

    i = 0;
    while (i < n - 1) {
        apr_snprintf(fingerprint + i * 3, 4, "%02X:", md[i]);
        i++;
    }
    apr_snprintf(fingerprint + i * 3, 3, "%02X", md[i]);
}

static int dir_exists(apr_pool_t *p, const char *dirname)
{
    apr_finfo_t finfo;
    apr_status_t rv = apr_stat(&finfo, dirname, APR_FINFO_TYPE, p);

    return rv == APR_SUCCESS && finfo.filetype == APR_DIR;
}

static int file_exists(apr_pool_t *p, const char *filename)
{
    apr_finfo_t finfo;
    apr_status_t rv = apr_stat(&finfo, filename, APR_FINFO_TYPE, p);

    return rv == APR_SUCCESS && finfo.filetype == APR_REG;
}

static apr_status_t readFile(apr_pool_t *p,
                             server_rec *s,
                             const char *fn,
                             apr_size_t limit,
                             char **contents,
                             apr_size_t *contents_size)
{
    apr_file_t *f;
    apr_finfo_t finfo;
    apr_status_t rv;
    apr_size_t nbytes;

    *contents = NULL;
    *contents_size = 0;

    rv = apr_file_open(&f, fn, APR_READ | APR_BINARY, APR_FPROT_OS_DEFAULT, p);
    if (rv != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
                     "couldn't read %s", fn);
        return rv;
    }
    
    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, f);
    if (rv != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
                     "couldn't retrieve size of %s", fn);
        apr_file_close(f);
        return rv;
    }

    if (finfo.size > limit) {
        rv = APR_ENOSPC;
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
                     "size of %s exceeds limit", fn);
        apr_file_close(f);
        return rv;
    }

    nbytes = (apr_size_t)finfo.size;
    *contents = apr_palloc(p, nbytes);
    rv = apr_file_read_full(f, *contents, nbytes, contents_size);
    if (rv != APR_SUCCESS) { /* shouldn't get APR_EOF since we know
                              * how big the file is
                              */
{'fraction_non_alphanumeric': 0.08919848018816717, 'fraction_numerical': 0.006694409263614981, 'mean_word_length': 2.8146997929606625, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3184531', 'n_tokens_mistral': 3723, 'n_tokens_neox': 3591, 'n_words': 1172}