You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2021/11/30 16:29:21 UTC

svn commit: r1895432 [5/5] - in /httpd/httpd/trunk: changes-entries/ modules/tls/

Added: httpd/httpd/trunk/modules/tls/tls_util.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/tls/tls_util.c?rev=1895432&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/tls/tls_util.c (added)
+++ httpd/httpd/trunk/modules/tls/tls_util.c Tue Nov 30 16:29:20 2021
@@ -0,0 +1,367 @@
+/* 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 <assert.h>
+#include <apr_lib.h>
+#include <apr_file_info.h>
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include <rustls.h>
+
+#include "tls_proto.h"
+#include "tls_util.h"
+
+
+extern module AP_MODULE_DECLARE_DATA tls_module;
+APLOG_USE_MODULE(tls);
+
+
+tls_data_t tls_data_from_str(const char *s)
+{
+    tls_data_t d;
+    d.data = (const unsigned char*)s;
+    d.len = s? strlen(s) : 0;
+    return d;
+}
+
+tls_data_t tls_data_assign_copy(apr_pool_t *p, const tls_data_t *d)
+{
+    tls_data_t copy;
+    copy.data = apr_pmemdup(p, d->data, d->len);
+    copy.len = d->len;
+    return copy;
+}
+
+tls_data_t *tls_data_copy(apr_pool_t *p, const tls_data_t *d)
+{
+    tls_data_t *copy;
+    copy = apr_pcalloc(p, sizeof(*copy));
+    *copy = tls_data_assign_copy(p, d);
+    return copy;
+}
+
+const char *tls_data_to_str(apr_pool_t *p, const tls_data_t *d)
+{
+    char *s = apr_pcalloc(p, d->len+1);
+    memcpy(s, d->data, d->len);
+    return s;
+}
+
+apr_status_t tls_util_rustls_error(
+    apr_pool_t *p, rustls_result rr, const char **perr_descr)
+{
+    if (perr_descr) {
+        char buffer[HUGE_STRING_LEN];
+        apr_size_t len = 0;
+
+        rustls_error(rr, buffer, sizeof(buffer), &len);
+        *perr_descr = apr_pstrndup(p, buffer, len);
+    }
+    return APR_EGENERAL;
+}
+
+int tls_util_is_file(
+    apr_pool_t *p, const char *fpath)
+{
+    apr_finfo_t finfo;
+
+    return (fpath != NULL
+        && apr_stat(&finfo, fpath, APR_FINFO_TYPE|APR_FINFO_SIZE, p) == 0
+        && finfo.filetype == APR_REG);
+}
+
+apr_status_t tls_util_file_load(
+    apr_pool_t *p, const char *fpath, apr_size_t min_len, apr_size_t max_len, tls_data_t *data)
+{
+    apr_finfo_t finfo;
+    apr_status_t rv;
+    apr_file_t *f = NULL;
+    unsigned char *buffer;
+    apr_size_t len;
+    const char *err = NULL;
+    tls_data_t *d;
+
+    rv = apr_stat(&finfo, fpath, APR_FINFO_TYPE|APR_FINFO_SIZE, p);
+    if (APR_SUCCESS != rv) {
+        err = "cannot stat"; goto cleanup;
+    }
+    if (finfo.filetype != APR_REG) {
+        err = "not a plain file";
+        rv = APR_EINVAL; goto cleanup;
+    }
+    if (finfo.size > LONG_MAX) {
+        err = "file is too large";
+        rv = APR_EINVAL; goto cleanup;
+    }
+    len = (apr_size_t)finfo.size;
+    if (len < min_len || len > max_len) {
+        err = "file size not in allowed range";
+        rv = APR_EINVAL; goto cleanup;
+    }
+    d = apr_pcalloc(p, sizeof(*d));
+    buffer = apr_pcalloc(p, len+1); /* keep it NUL terminated in any case */
+    rv = apr_file_open(&f, fpath, APR_FOPEN_READ, 0, p);
+    if (APR_SUCCESS != rv) {
+        err = "error opening"; goto cleanup;
+    }
+    rv = apr_file_read(f, buffer, &len);
+    if (APR_SUCCESS != rv) {
+        err = "error reading"; goto cleanup;
+    }
+cleanup:
+    if (f) apr_file_close(f);
+    if (APR_SUCCESS == rv) {
+        data->data = buffer;
+        data->len = len;
+    }
+    else {
+        memset(data, 0, sizeof(*data));
+        ap_log_perror(APLOG_MARK, APLOG_ERR, rv, p, APLOGNO(10361)
+                      "Failed to load file %s: %s", fpath, err? err: "-");
+    }
+    return rv;
+}
+
+int tls_util_array_uint16_contains(const apr_array_header_t* a, apr_uint16_t n)
+{
+    int i;
+    for (i = 0; i < a->nelts; ++i) {
+        if (APR_ARRAY_IDX(a, i, apr_uint16_t) == n) return 1;
+    }
+    return 0;
+}
+
+const apr_array_header_t *tls_util_array_uint16_remove(
+    apr_pool_t *pool, const apr_array_header_t* from, const apr_array_header_t* others)
+{
+    apr_array_header_t *na = NULL;
+    apr_uint16_t id;
+    int i, j;
+
+    for (i = 0; i < from->nelts; ++i) {
+        id = APR_ARRAY_IDX(from, i, apr_uint16_t);
+        if (tls_util_array_uint16_contains(others, id)) {
+            if (na == NULL) {
+                /* first removal, make a new result array, copy elements before */
+                na = apr_array_make(pool, from->nelts, sizeof(apr_uint16_t));
+                for (j = 0; j < i; ++j) {
+                    APR_ARRAY_PUSH(na, apr_uint16_t) = APR_ARRAY_IDX(from, j, apr_uint16_t);
+                }
+            }
+        }
+        else if (na) {
+            APR_ARRAY_PUSH(na, apr_uint16_t) = id;
+        }
+    }
+    return na? na : from;
+}
+
+apr_status_t tls_util_brigade_transfer(
+    apr_bucket_brigade *dest, apr_bucket_brigade *src, apr_off_t length,
+    apr_off_t *pnout)
+{
+    apr_bucket *b;
+    apr_off_t remain = length;
+    apr_status_t rv = APR_SUCCESS;
+    const char *ign;
+    apr_size_t ilen;
+
+    *pnout = 0;
+    while (!APR_BRIGADE_EMPTY(src)) {
+        b = APR_BRIGADE_FIRST(src);
+
+        if (APR_BUCKET_IS_METADATA(b)) {
+            APR_BUCKET_REMOVE(b);
+            APR_BRIGADE_INSERT_TAIL(dest, b);
+        }
+        else {
+            if (remain == (apr_off_t)b->length) {
+                /* fall through */
+            }
+            else if (remain <= 0) {
+                goto cleanup;
+            }
+            else {
+                if (b->length == ((apr_size_t)-1)) {
+                    rv= apr_bucket_read(b, &ign, &ilen, APR_BLOCK_READ);
+                    if (APR_SUCCESS != rv) goto cleanup;
+                }
+                if (remain < (apr_off_t)b->length) {
+                    apr_bucket_split(b, (apr_size_t)remain);
+                }
+            }
+            APR_BUCKET_REMOVE(b);
+            APR_BRIGADE_INSERT_TAIL(dest, b);
+            remain -= (apr_off_t)b->length;
+            *pnout += (apr_off_t)b->length;
+        }
+    }
+cleanup:
+    return rv;
+}
+
+apr_status_t tls_util_brigade_copy(
+    apr_bucket_brigade *dest, apr_bucket_brigade *src, apr_off_t length,
+    apr_off_t *pnout)
+{
+    apr_bucket *b, *next;
+    apr_off_t remain = length;
+    apr_status_t rv = APR_SUCCESS;
+    const char *ign;
+    apr_size_t ilen;
+
+    *pnout = 0;
+    for (b = APR_BRIGADE_FIRST(src);
+         b != APR_BRIGADE_SENTINEL(src);
+         b = next) {
+        next = APR_BUCKET_NEXT(b);
+
+        if (APR_BUCKET_IS_METADATA(b)) {
+            /* fall through */
+        }
+        else {
+            if (remain == (apr_off_t)b->length) {
+                /* fall through */
+            }
+            else if (remain <= 0) {
+                goto cleanup;
+            }
+            else {
+                if (b->length == ((apr_size_t)-1)) {
+                    rv = apr_bucket_read(b, &ign, &ilen, APR_BLOCK_READ);
+                    if (APR_SUCCESS != rv) goto cleanup;
+                }
+                if (remain < (apr_off_t)b->length) {
+                    apr_bucket_split(b, (apr_size_t)remain);
+                }
+            }
+        }
+        rv = apr_bucket_copy(b, &b);
+        if (APR_SUCCESS != rv) goto cleanup;
+        APR_BRIGADE_INSERT_TAIL(dest, b);
+        remain -= (apr_off_t)b->length;
+        *pnout += (apr_off_t)b->length;
+    }
+cleanup:
+    return rv;
+}
+
+apr_status_t tls_util_brigade_split_line(
+    apr_bucket_brigade *dest, apr_bucket_brigade *src,
+    apr_read_type_e block, apr_off_t length,
+    apr_off_t *pnout)
+{
+    apr_off_t nstart, nend;
+    apr_status_t rv;
+
+    apr_brigade_length(dest, 0, &nstart);
+    rv = apr_brigade_split_line(dest, src, block, length);
+    if (APR_SUCCESS != rv) goto cleanup;
+    apr_brigade_length(dest, 0, &nend);
+    /* apr_brigade_split_line() has the nasty habit of leaving a 0-length bucket
+     * at the start of the brigade when it transfered the whole content. Get rid of it.
+     */
+    if (!APR_BRIGADE_EMPTY(src)) {
+         apr_bucket *b = APR_BRIGADE_FIRST(src);
+        if (!APR_BUCKET_IS_METADATA(b) && 0 == b->length) {
+            APR_BUCKET_REMOVE(b);
+            apr_bucket_delete(b);
+        }
+    }
+cleanup:
+    *pnout = (APR_SUCCESS == rv)? (nend - nstart) : 0;
+    return rv;
+}
+
+int tls_util_name_matches_server(const char *name, server_rec *s)
+{
+    apr_array_header_t *names;
+    char **alias;
+    int i;
+
+    if (!s || !s->server_hostname) return 0;
+    if (!strcasecmp(name, s->server_hostname)) return 1;
+    /* first the fast equality match, then the pattern wild_name matches */
+    names = s->names;
+    if (!names) return 0;
+    alias = (char **)names->elts;
+    for (i = 0; i < names->nelts; ++i) {
+        if (alias[i] && !strcasecmp(name, alias[i])) return 1;
+    }
+    names = s->wild_names;
+    if (!names) return 0;
+    alias = (char **)names->elts;
+    for (i = 0; i < names->nelts; ++i) {
+        if (alias[i] && !ap_strcasecmp_match(name, alias[i])) return 1;
+    }
+    return 0;
+}
+
+apr_size_t tls_util_bucket_print(char *buffer, apr_size_t bmax,
+                                 apr_bucket *b, const char *sep)
+{
+    apr_size_t off = 0;
+    if (sep && *sep) {
+        off += (size_t)apr_snprintf(buffer+off, bmax-off, "%s", sep);
+    }
+
+    if (bmax <= off) {
+        return off;
+    }
+    else if (APR_BUCKET_IS_METADATA(b)) {
+        off += (size_t)apr_snprintf(buffer+off, bmax-off, "%s", b->type->name);
+    }
+    else if (bmax > off) {
+        off += (size_t)apr_snprintf(buffer+off, bmax-off, "%s[%ld]",
+                                    b->type->name, (long)(b->length == ((apr_size_t)-1)?
+                                   -1 : (int)b->length));
+    }
+    return off;
+}
+
+apr_size_t tls_util_bb_print(char *buffer, apr_size_t bmax,
+                             const char *tag, const char *sep,
+                             apr_bucket_brigade *bb)
+{
+    apr_size_t off = 0;
+    const char *sp = "";
+    apr_bucket *b;
+
+    if (bmax > 1) {
+        if (bb) {
+            memset(buffer, 0, bmax--);
+            off += (size_t)apr_snprintf(buffer+off, bmax-off, "%s(", tag);
+            for (b = APR_BRIGADE_FIRST(bb);
+                 (bmax > off) && (b != APR_BRIGADE_SENTINEL(bb));
+                 b = APR_BUCKET_NEXT(b)) {
+
+                off += tls_util_bucket_print(buffer+off, bmax-off, b, sp);
+                sp = " ";
+            }
+            if (bmax > off) {
+                off += (size_t)apr_snprintf(buffer+off, bmax-off, ")%s", sep);
+            }
+        }
+        else {
+            off += (size_t)apr_snprintf(buffer+off, bmax-off, "%s(null)%s", tag, sep);
+        }
+    }
+    return off;
+}
+

Added: httpd/httpd/trunk/modules/tls/tls_util.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/tls/tls_util.h?rev=1895432&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/tls/tls_util.h (added)
+++ httpd/httpd/trunk/modules/tls/tls_util.h Tue Nov 30 16:29:20 2021
@@ -0,0 +1,157 @@
+/* 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.
+ */
+#ifndef tls_util_h
+#define tls_util_h
+
+#define TLS_DIM(a)      (sizeof(a)/sizeof(a[0]))
+
+
+/**
+ * Simple struct to hold a range of bytes and its length together.
+ */
+typedef struct tls_data_t tls_data_t;
+struct tls_data_t {
+    const unsigned char* data;
+    apr_size_t len;
+};
+
+/**
+ * Return a tls_data_t for a string.
+ */
+tls_data_t tls_data_from_str(const char *s);
+
+/**
+ * Create a copy of a tls_data_t using the given pool.
+ */
+tls_data_t *tls_data_copy(apr_pool_t *p, const tls_data_t *d);
+
+/**
+ * Return a copy of a tls_data_t bytes allocated from pool.
+ */
+tls_data_t tls_data_assign_copy(apr_pool_t *p, const tls_data_t *d);
+
+/**
+ * Convert the data bytes in `d` into a NUL-terminated string.
+ * There is no check if the data bytes already contain NUL.
+ */
+const char *tls_data_to_str(apr_pool_t *p, const tls_data_t *d);
+
+/**
+ * Return != 0 if fpath is a 'real' file.
+ */
+int tls_util_is_file(apr_pool_t *p, const char *fpath);
+
+/**
+ * Inspect a 'rustls_result', retrieve the error description for it and
+ * return the apr_status_t to use as our error status.
+ */
+apr_status_t tls_util_rustls_error(apr_pool_t *p, rustls_result rr, const char **perr_descr);
+
+/**
+ *  Load up to `max_len` bytes into a buffer allocated from the pool.
+ *  @return ARP_SUCCESS on successful load.
+ *          APR_EINVAL when the file was not a regular file or is too large.
+ */
+apr_status_t tls_util_file_load(
+    apr_pool_t *p, const char *fpath, size_t min_len, size_t max_len, tls_data_t *data);
+
+/**
+ * Return != 0 iff the array of apr_uint16_t contains value n.
+ */
+int tls_util_array_uint16_contains(const apr_array_header_t* a, apr_uint16_t n);
+
+/**
+ * Remove all apr_uint16_t in `others` from array `from`.
+ * Returns the new array or, if no overlap was found, the `from` array unchanged.
+ */
+const apr_array_header_t *tls_util_array_uint16_remove(
+    apr_pool_t *pool, const apr_array_header_t* from, const apr_array_header_t* others);
+
+/**
+ * Transfer up to <length> bytes from <src> to <dest>, including all
+ * encountered meta data buckets. The transfered buckets/data are
+ * removed from <src>.
+ * Return the actual byte count transfered in <pnout>.
+ */
+apr_status_t tls_util_brigade_transfer(
+    apr_bucket_brigade *dest, apr_bucket_brigade *src, apr_off_t length,
+    apr_off_t *pnout);
+
+/**
+ * Copy up to <length> bytes from <src> to <dest>, including all
+ * encountered meta data buckets. <src> remains semantically unchaanged,
+ * meaning there might have been buckets split or changed while reading
+ * their content.
+ * Return the actual byte count copied in <pnout>.
+ */
+apr_status_t tls_util_brigade_copy(
+    apr_bucket_brigade *dest, apr_bucket_brigade *src, apr_off_t length,
+    apr_off_t *pnout);
+
+/**
+ * Get a line of max `length` bytes from `src` into `dest`.
+ * Return the number of bytes transferred in `pnout`.
+ */
+apr_status_t tls_util_brigade_split_line(
+    apr_bucket_brigade *dest, apr_bucket_brigade *src,
+    apr_read_type_e block, apr_off_t length,
+    apr_off_t *pnout);
+
+/**
+ * Return != 0 iff the given <name> matches the configured 'ServerName'
+ * or one of the 'ServerAlias' name of <s>, including wildcard patterns
+ * as understood by ap_strcasecmp_match().
+ */
+int tls_util_name_matches_server(const char *name, server_rec *s);
+
+
+/**
+ * Print a bucket's meta data (type and length) to the buffer.
+ * @return number of characters printed
+ */
+apr_size_t tls_util_bucket_print(char *buffer, apr_size_t bmax,
+                                 apr_bucket *b, const char *sep);
+
+/**
+ * Prints the brigade bucket types and lengths into the given buffer
+ * up to bmax.
+ * @return number of characters printed
+ */
+apr_size_t tls_util_bb_print(char *buffer, apr_size_t bmax,
+                             const char *tag, const char *sep,
+                             apr_bucket_brigade *bb);
+/**
+ * Logs the bucket brigade (which bucket types with what length)
+ * to the log at the given level.
+ * @param c the connection to log for
+ * @param sid the stream identifier this brigade belongs to
+ * @param level the log level (as in APLOG_*)
+ * @param tag a short message text about the context
+ * @param bb the brigade to log
+ */
+#define tls_util_bb_log(c, level, tag, bb) \
+do { \
+    char buffer[4 * 1024]; \
+    const char *line = "(null)"; \
+    apr_size_t len, bmax = sizeof(buffer)/sizeof(buffer[0]); \
+    len = tls_util_bb_print(buffer, bmax, (tag), "", (bb)); \
+    ap_log_cerror(APLOG_MARK, level, 0, (c), "bb_dump(%ld): %s", \
+        ((c)->master? (c)->master->id : (c)->id), (len? buffer : line)); \
+} while(0)
+
+
+
+#endif /* tls_util_h */
\ No newline at end of file

Added: httpd/httpd/trunk/modules/tls/tls_var.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/tls/tls_var.c?rev=1895432&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/tls/tls_var.c (added)
+++ httpd/httpd/trunk/modules/tls/tls_var.c Tue Nov 30 16:29:20 2021
@@ -0,0 +1,397 @@
+/* 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 <assert.h>
+#include <apr_lib.h>
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_connection.h>
+#include <http_core.h>
+#include <http_main.h>
+#include <http_log.h>
+#include <ap_socache.h>
+
+#include <rustls.h>
+
+#include "tls_conf.h"
+#include "tls_core.h"
+#include "tls_cert.h"
+#include "tls_util.h"
+#include "tls_var.h"
+#include "tls_version.h"
+
+
+extern module AP_MODULE_DECLARE_DATA tls_module;
+APLOG_USE_MODULE(tls);
+
+typedef struct {
+    apr_pool_t *p;
+    server_rec *s;
+    conn_rec *c;
+    request_rec *r;
+    tls_conf_conn_t *cc;
+    const char *name;
+    const char *arg_s;
+    int arg_i;
+} tls_var_lookup_ctx_t;
+
+typedef const char *var_lookup(const tls_var_lookup_ctx_t *ctx);
+
+static const char *var_get_ssl_protocol(const tls_var_lookup_ctx_t *ctx)
+{
+    return ctx->cc->tls_protocol_name;
+}
+
+static const char *var_get_ssl_cipher(const tls_var_lookup_ctx_t *ctx)
+{
+    return ctx->cc->tls_cipher_name;
+}
+
+static const char *var_get_sni_hostname(const tls_var_lookup_ctx_t *ctx)
+{
+    return ctx->cc->sni_hostname;
+}
+
+static const char *var_get_version_interface(const tls_var_lookup_ctx_t *ctx)
+{
+    tls_conf_server_t *sc = tls_conf_server_get(ctx->s);
+    return sc->global->module_version;
+}
+
+static const char *var_get_version_library(const tls_var_lookup_ctx_t *ctx)
+{
+    tls_conf_server_t *sc = tls_conf_server_get(ctx->s);
+    return sc->global->crustls_version;
+}
+
+static const char *var_get_false(const tls_var_lookup_ctx_t *ctx)
+{
+    (void)ctx;
+    return "false";
+}
+
+static const char *var_get_null(const tls_var_lookup_ctx_t *ctx)
+{
+    (void)ctx;
+    return "NULL";
+}
+
+static const char *var_get_client_s_dn_cn(const tls_var_lookup_ctx_t *ctx)
+{
+    /* There is no support in the crustls/rustls/webpki APIs to
+     * parse X.509 certificates and extract information about
+     * subject, issuer, etc. */
+    if (!ctx->cc->peer_certs || !ctx->cc->peer_certs->nelts) return NULL;
+    return "Not Implemented";
+}
+
+static const char *var_get_client_verify(const tls_var_lookup_ctx_t *ctx)
+{
+    return ctx->cc->peer_certs? "SUCCESS" : "NONE";
+}
+
+static const char *var_get_session_resumed(const tls_var_lookup_ctx_t *ctx)
+{
+    return ctx->cc->session_id_cache_hit? "Resumed" : "Initial";
+}
+
+static const char *var_get_client_cert(const tls_var_lookup_ctx_t *ctx)
+{
+    const rustls_certificate *cert;
+    const char *pem;
+    apr_status_t rv;
+    int cert_idx = 0;
+
+    if (ctx->arg_s) {
+        if (strcmp(ctx->arg_s, "chain")) return NULL;
+        /* ctx->arg_i'th chain cert, which is in out list as */
+        cert_idx = ctx->arg_i + 1;
+    }
+    if (!ctx->cc->peer_certs || cert_idx >= ctx->cc->peer_certs->nelts) return NULL;
+    cert = APR_ARRAY_IDX(ctx->cc->peer_certs, cert_idx, const rustls_certificate*);
+    if (APR_SUCCESS != (rv = tls_cert_to_pem(&pem, ctx->p, cert))) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ctx->s, APLOGNO(10315)
+                         "Failed to create client certificate PEM");
+        return NULL;
+    }
+    return pem;
+}
+
+static const char *var_get_server_cert(const tls_var_lookup_ctx_t *ctx)
+{
+    const rustls_certificate *cert;
+    const char *pem;
+    apr_status_t rv;
+
+    if (!ctx->cc->key) return NULL;
+    cert = rustls_certified_key_get_certificate(ctx->cc->key, 0);
+    if (!cert) return NULL;
+    if (APR_SUCCESS != (rv = tls_cert_to_pem(&pem, ctx->p, cert))) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ctx->s, APLOGNO(10316)
+                         "Failed to create server certificate PEM");
+        return NULL;
+    }
+    return pem;
+}
+
+typedef struct {
+    const char *name;
+    var_lookup* fn;
+    const char *arg_s;
+    int arg_i;
+} var_def_t;
+
+static const var_def_t VAR_DEFS[] = {
+    { "SSL_PROTOCOL", var_get_ssl_protocol, NULL, 0 },
+    { "SSL_CIPHER", var_get_ssl_cipher, NULL, 0 },
+    { "SSL_TLS_SNI", var_get_sni_hostname, NULL, 0 },
+    { "SSL_CLIENT_S_DN_CN", var_get_client_s_dn_cn, NULL, 0 },
+    { "SSL_VERSION_INTERFACE", var_get_version_interface, NULL, 0 },
+    { "SSL_VERSION_LIBRARY", var_get_version_library, NULL, 0 },
+    { "SSL_SECURE_RENEG", var_get_false, NULL, 0 },
+    { "SSL_COMPRESS_METHOD", var_get_null, NULL, 0 },
+    { "SSL_CIPHER_EXPORT", var_get_false, NULL, 0 },
+    { "SSL_CLIENT_VERIFY", var_get_client_verify, NULL, 0 },
+    { "SSL_SESSION_RESUMED", var_get_session_resumed, NULL, 0 },
+    { "SSL_CLIENT_CERT", var_get_client_cert, NULL, 0 },
+    { "SSL_CLIENT_CHAIN_0", var_get_client_cert, "chain", 0 },
+    { "SSL_CLIENT_CHAIN_1", var_get_client_cert, "chain", 1 },
+    { "SSL_CLIENT_CHAIN_2", var_get_client_cert, "chain", 2 },
+    { "SSL_CLIENT_CHAIN_3", var_get_client_cert, "chain", 3 },
+    { "SSL_CLIENT_CHAIN_4", var_get_client_cert, "chain", 4 },
+    { "SSL_CLIENT_CHAIN_5", var_get_client_cert, "chain", 5 },
+    { "SSL_CLIENT_CHAIN_6", var_get_client_cert, "chain", 6 },
+    { "SSL_CLIENT_CHAIN_7", var_get_client_cert, "chain", 7 },
+    { "SSL_CLIENT_CHAIN_8", var_get_client_cert, "chain", 8 },
+    { "SSL_CLIENT_CHAIN_9", var_get_client_cert, "chain", 9 },
+    { "SSL_SERVER_CERT", var_get_server_cert, NULL, 0 },
+};
+
+static const char *const TlsAlwaysVars[] = {
+    "SSL_TLS_SNI",
+    "SSL_PROTOCOL",
+    "SSL_CIPHER",
+    "SSL_CLIENT_S_DN_CN",
+};
+
+/* what mod_ssl defines, plus server cert and client cert DN and SAN entries */
+static const char *const StdEnvVars[] = {
+    "SSL_VERSION_INTERFACE", /* implemented: module version string */
+    "SSL_VERSION_LIBRARY",   /* implemented: crustls/rustls version string */
+    "SSL_SECURE_RENEG",      /* implemented: always "false" */
+    "SSL_COMPRESS_METHOD",   /* implemented: always "NULL" */
+    "SSL_CIPHER_EXPORT",     /* implemented: always "false" */
+    "SSL_CIPHER_USEKEYSIZE",
+    "SSL_CIPHER_ALGKEYSIZE",
+    "SSL_CLIENT_VERIFY",     /* implemented: always "SUCCESS" or "NONE" */
+    "SSL_CLIENT_M_VERSION",
+    "SSL_CLIENT_M_SERIAL",
+    "SSL_CLIENT_V_START",
+    "SSL_CLIENT_V_END",
+    "SSL_CLIENT_V_REMAIN",
+    "SSL_CLIENT_S_DN",
+    "SSL_CLIENT_I_DN",
+    "SSL_CLIENT_A_KEY",
+    "SSL_CLIENT_A_SIG",
+    "SSL_CLIENT_CERT_RFC4523_CEA",
+    "SSL_SERVER_M_VERSION",
+    "SSL_SERVER_M_SERIAL",
+    "SSL_SERVER_V_START",
+    "SSL_SERVER_V_END",
+    "SSL_SERVER_S_DN",
+    "SSL_SERVER_I_DN",
+    "SSL_SERVER_A_KEY",
+    "SSL_SERVER_A_SIG",
+    "SSL_SESSION_ID",        /* not implemented: highly sensitive data we do not expose */
+    "SSL_SESSION_RESUMED",   /* implemented: if our cache was hit successfully */
+};
+
+/* Cert related variables, export when TLSOption ExportCertData is set */
+static const char *const ExportCertVars[] = {
+    "SSL_CLIENT_CERT",       /* implemented: */
+    "SSL_CLIENT_CHAIN_0",    /* implemented: */
+    "SSL_CLIENT_CHAIN_1",    /* implemented: */
+    "SSL_CLIENT_CHAIN_2",    /* implemented: */
+    "SSL_CLIENT_CHAIN_3",    /* implemented: */
+    "SSL_CLIENT_CHAIN_4",    /* implemented: */
+    "SSL_CLIENT_CHAIN_5",    /* implemented: */
+    "SSL_CLIENT_CHAIN_6",    /* implemented: */
+    "SSL_CLIENT_CHAIN_7",    /* implemented: */
+    "SSL_CLIENT_CHAIN_8",    /* implemented: */
+    "SSL_CLIENT_CHAIN_9",    /* implemented: */
+    "SSL_SERVER_CERT",       /* implemented: */
+};
+
+void tls_var_init_lookup_hash(apr_pool_t *pool, apr_hash_t *map)
+{
+    const var_def_t *def;
+    apr_size_t i;
+
+    (void)pool;
+    for (i = 0; i < TLS_DIM(VAR_DEFS); ++i) {
+        def = &VAR_DEFS[i];
+        apr_hash_set(map, def->name, APR_HASH_KEY_STRING, def);
+    }
+}
+
+static const char *invoke(var_def_t* def, tls_var_lookup_ctx_t *ctx)
+{
+    if (TLS_CONN_ST_IS_ENABLED(ctx->cc)) {
+        const char *val = ctx->cc->subprocess_env?
+            apr_table_get(ctx->cc->subprocess_env, def->name) : NULL;
+        if (val && *val) return val;
+        ctx->arg_s = def->arg_s;
+        ctx->arg_i = def->arg_i;
+        return def->fn(ctx);
+    }
+    return NULL;
+}
+
+static void set_var(
+    tls_var_lookup_ctx_t *ctx, apr_hash_t *lookups, apr_table_t *table)
+{
+    var_def_t* def = apr_hash_get(lookups, ctx->name, APR_HASH_KEY_STRING);
+    if (def) {
+        const char *val = invoke(def, ctx);
+        if (val && *val) {
+            apr_table_setn(table, ctx->name, val);
+        }
+    }
+}
+
+const char *tls_var_lookup(
+    apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, const char *name)
+{
+    const char *val = NULL;
+    tls_conf_server_t *sc;
+    var_def_t* def;
+
+    ap_assert(p);
+    ap_assert(name);
+    s = s? s : (r? r->server : (c? c->base_server : NULL));
+    c = c? c : (r? r->connection : NULL);
+
+    sc = tls_conf_server_get(s? s : ap_server_conf);
+    def = apr_hash_get(sc->global->var_lookups, name, APR_HASH_KEY_STRING);
+    if (def) {
+        tls_var_lookup_ctx_t ctx;
+        ctx.p = p;
+        ctx.s = s;
+        ctx.c = c;
+        ctx.r = r;
+        ctx.cc = c? tls_conf_conn_get(c->master? c->master : c) : NULL;
+                ctx.cc = c? tls_conf_conn_get(c->master? c->master : c) : NULL;
+        ctx.name = name;
+        val = invoke(def, &ctx);
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c, "tls lookup of var '%s' -> '%s'", name, val);
+    }
+    return val;
+}
+
+static void add_vars(apr_table_t *env, conn_rec *c, server_rec *s, request_rec *r)
+{
+    tls_conf_server_t *sc;
+    tls_conf_dir_t *dc, *sdc;
+    tls_var_lookup_ctx_t ctx;
+    apr_size_t i;
+    int overlap;
+
+    sc = tls_conf_server_get(s);
+    dc = r? tls_conf_dir_get(r) : tls_conf_dir_server_get(s);
+    sdc = r? tls_conf_dir_server_get(s): dc;
+    ctx.p = r? r->pool : c->pool;
+    ctx.s = s;
+    ctx.c = c;
+    ctx.r = r;
+    ctx.cc = tls_conf_conn_get(c->master? c->master : c);
+    /* Can we re-use teh precomputed connection values? */
+    overlap = (r && ctx.cc->subprocess_env && r->server == ctx.cc->server);
+    if (overlap) {
+        apr_table_overlap(env, ctx.cc->subprocess_env, APR_OVERLAP_TABLES_SET);
+    }
+    else {
+        apr_table_setn(env, "HTTPS", "on");
+        for (i = 0; i < TLS_DIM(TlsAlwaysVars); ++i) {
+            ctx.name = TlsAlwaysVars[i];
+            set_var(&ctx, sc->global->var_lookups, env);
+        }
+    }
+    if (dc->std_env_vars == TLS_FLAG_TRUE) {
+        for (i = 0; i < TLS_DIM(StdEnvVars); ++i) {
+            ctx.name = StdEnvVars[i];
+            set_var(&ctx, sc->global->var_lookups, env);
+        }
+    }
+    else if (overlap && sdc->std_env_vars == TLS_FLAG_TRUE) {
+        /* Remove variables added on connection init that are disbled here */
+        for (i = 0; i < TLS_DIM(StdEnvVars); ++i) {
+            apr_table_unset(env, StdEnvVars[i]);
+        }
+    }
+    if (dc->export_cert_vars == TLS_FLAG_TRUE) {
+        for (i = 0; i < TLS_DIM(ExportCertVars); ++i) {
+            ctx.name = ExportCertVars[i];
+            set_var(&ctx, sc->global->var_lookups, env);
+        }
+    }
+    else if (overlap && sdc->std_env_vars == TLS_FLAG_TRUE) {
+        /* Remove variables added on connection init that are disbled here */
+        for (i = 0; i < TLS_DIM(ExportCertVars); ++i) {
+            apr_table_unset(env, ExportCertVars[i]);
+        }
+    }
+ }
+
+apr_status_t tls_var_handshake_done(conn_rec *c)
+{
+    tls_conf_conn_t *cc;
+    tls_conf_server_t *sc;
+    apr_status_t rv = APR_SUCCESS;
+
+    cc = tls_conf_conn_get(c);
+    if (!TLS_CONN_ST_IS_ENABLED(cc)) goto cleanup;
+
+    sc = tls_conf_server_get(cc->server);
+    if (cc->peer_certs && sc->var_user_name) {
+        cc->user_name = tls_var_lookup(c->pool, cc->server, c, NULL, sc->var_user_name);
+        if (!cc->user_name) {
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cc->server, APLOGNO(10317)
+                "Failed to set r->user to '%s'", sc->var_user_name);
+        }
+    }
+    cc->subprocess_env = apr_table_make(c->pool, 5);
+    add_vars(cc->subprocess_env, c, cc->server, NULL);
+
+cleanup:
+    return rv;
+}
+
+int tls_var_request_fixup(request_rec *r)
+{
+    conn_rec *c = r->connection;
+    tls_conf_conn_t *cc;
+
+    cc = tls_conf_conn_get(c->master? c->master : c);
+    if (!TLS_CONN_ST_IS_ENABLED(cc)) goto cleanup;
+    if (cc->user_name) {
+        /* why is r->user a char* and not const? */
+        r->user = apr_pstrdup(r->pool, cc->user_name);
+    }
+    add_vars(r->subprocess_env, c, r->server, r);
+
+cleanup:
+    return DECLINED;
+}

Added: httpd/httpd/trunk/modules/tls/tls_var.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/tls/tls_var.h?rev=1895432&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/tls/tls_var.h (added)
+++ httpd/httpd/trunk/modules/tls/tls_var.h Tue Nov 30 16:29:20 2021
@@ -0,0 +1,39 @@
+/* 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.
+ */
+#ifndef tls_var_h
+#define tls_var_h
+
+void tls_var_init_lookup_hash(apr_pool_t *pool, apr_hash_t *map);
+
+/**
+ * Callback for installation in Apache's 'ssl_var_lookup' hook to provide
+ * SSL related variable lookups to other modules.
+ */
+const char *tls_var_lookup(
+    apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, const char *name);
+
+/**
+ * A connection has been handshaked. Prepare commond TLS variables on this connection.
+ */
+apr_status_t tls_var_handshake_done(conn_rec *c);
+
+/**
+ * A request is ready for processing, add TLS variables r->subprocess_env if applicable.
+ * This is a hook function returning OK/DECLINED.
+ */
+int tls_var_request_fixup(request_rec *r);
+
+#endif /* tls_var_h */
\ No newline at end of file

Added: httpd/httpd/trunk/modules/tls/tls_version.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/tls/tls_version.h?rev=1895432&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/tls/tls_version.h (added)
+++ httpd/httpd/trunk/modules/tls/tls_version.h Tue Nov 30 16:29:20 2021
@@ -0,0 +1,39 @@
+/* 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.
+ */
+#ifndef mod_tls_version_h
+#define mod_tls_version_h
+
+#undef PACKAGE_VERSION
+#undef PACKAGE_TARNAME
+#undef PACKAGE_STRING
+#undef PACKAGE_NAME
+#undef PACKAGE_BUGREPORT
+
+/**
+ * @macro
+ * Version number of the md module as c string
+ */
+#define MOD_TLS_VERSION "0.8.3"
+
+/**
+ * @macro
+ * Numerical representation of the version number of the md module
+ * release. This is a 24 bit number with 8 bits for major number, 8 bits
+ * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
+ */
+#define MOD_TLS_VERSION_NUM 0x000802
+
+#endif /* mod_md_md_version_h */