1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include <assert.h>
17 #include <apr_lib.h>
18 #include <apr_strings.h>
19
20 #include <httpd.h>
21 #include <http_connection.h>
22 #include <http_core.h>
23 #include <http_main.h>
24 #include <http_log.h>
25 #include <ap_socache.h>
26
27 #include <rustls.h>
28
29 #include "tls_conf.h"
30 #include "tls_core.h"
31 #include "tls_cert.h"
32 #include "tls_util.h"
33 #include "tls_var.h"
34 #include "tls_version.h"
35
36
37 extern module AP_MODULE_DECLARE_DATA tls_module;
38 APLOG_USE_MODULE(tls);
39
40 typedef struct {
41 apr_pool_t *p;
42 server_rec *s;
43 conn_rec *c;
44 request_rec *r;
45 tls_conf_conn_t *cc;
46 const char *name;
47 const char *arg_s;
48 int arg_i;
49 } tls_var_lookup_ctx_t;
50
51 typedef const char *var_lookup(const tls_var_lookup_ctx_t *ctx);
52
var_get_ssl_protocol(const tls_var_lookup_ctx_t * ctx)53 static const char *var_get_ssl_protocol(const tls_var_lookup_ctx_t *ctx)
54 {
55 return ctx->cc->tls_protocol_name;
56 }
57
var_get_ssl_cipher(const tls_var_lookup_ctx_t * ctx)58 static const char *var_get_ssl_cipher(const tls_var_lookup_ctx_t *ctx)
59 {
60 return ctx->cc->tls_cipher_name;
61 }
62
var_get_sni_hostname(const tls_var_lookup_ctx_t * ctx)63 static const char *var_get_sni_hostname(const tls_var_lookup_ctx_t *ctx)
64 {
65 return ctx->cc->sni_hostname;
66 }
67
var_get_version_interface(const tls_var_lookup_ctx_t * ctx)68 static const char *var_get_version_interface(const tls_var_lookup_ctx_t *ctx)
69 {
70 tls_conf_server_t *sc = tls_conf_server_get(ctx->s);
71 return sc->global->module_version;
72 }
73
var_get_version_library(const tls_var_lookup_ctx_t * ctx)74 static const char *var_get_version_library(const tls_var_lookup_ctx_t *ctx)
75 {
76 tls_conf_server_t *sc = tls_conf_server_get(ctx->s);
77 return sc->global->crustls_version;
78 }
79
var_get_false(const tls_var_lookup_ctx_t * ctx)80 static const char *var_get_false(const tls_var_lookup_ctx_t *ctx)
81 {
82 (void)ctx;
83 return "false";
84 }
85
var_get_null(const tls_var_lookup_ctx_t * ctx)86 static const char *var_get_null(const tls_var_lookup_ctx_t *ctx)
87 {
88 (void)ctx;
89 return "NULL";
90 }
91
var_get_client_s_dn_cn(const tls_var_lookup_ctx_t * ctx)92 static const char *var_get_client_s_dn_cn(const tls_var_lookup_ctx_t *ctx)
93 {
94 /* There is no support in the crustls/rustls/webpki APIs to
95 * parse X.509 certificates and extract information about
96 * subject, issuer, etc. */
97 if (!ctx->cc->peer_certs || !ctx->cc->peer_certs->nelts) return NULL;
98 return "Not Implemented";
99 }
100
var_get_client_verify(const tls_var_lookup_ctx_t * ctx)101 static const char *var_get_client_verify(const tls_var_lookup_ctx_t *ctx)
102 {
103 return ctx->cc->peer_certs? "SUCCESS" : "NONE";
104 }
105
var_get_session_resumed(const tls_var_lookup_ctx_t * ctx)106 static const char *var_get_session_resumed(const tls_var_lookup_ctx_t *ctx)
107 {
108 return ctx->cc->session_id_cache_hit? "Resumed" : "Initial";
109 }
110
var_get_client_cert(const tls_var_lookup_ctx_t * ctx)111 static const char *var_get_client_cert(const tls_var_lookup_ctx_t *ctx)
112 {
113 const rustls_certificate *cert;
114 const char *pem;
115 apr_status_t rv;
116 int cert_idx = 0;
117
118 if (ctx->arg_s) {
119 if (strcmp(ctx->arg_s, "chain")) return NULL;
120 /* ctx->arg_i'th chain cert, which is in out list as */
121 cert_idx = ctx->arg_i + 1;
122 }
123 if (!ctx->cc->peer_certs || cert_idx >= ctx->cc->peer_certs->nelts) return NULL;
124 cert = APR_ARRAY_IDX(ctx->cc->peer_certs, cert_idx, const rustls_certificate*);
125 if (APR_SUCCESS != (rv = tls_cert_to_pem(&pem, ctx->p, cert))) {
126 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ctx->s, APLOGNO(10315)
127 "Failed to create client certificate PEM");
128 return NULL;
129 }
130 return pem;
131 }
132
var_get_server_cert(const tls_var_lookup_ctx_t * ctx)133 static const char *var_get_server_cert(const tls_var_lookup_ctx_t *ctx)
134 {
135 const rustls_certificate *cert;
136 const char *pem;
137 apr_status_t rv;
138
139 if (!ctx->cc->key) return NULL;
140 cert = rustls_certified_key_get_certificate(ctx->cc->key, 0);
141 if (!cert) return NULL;
142 if (APR_SUCCESS != (rv = tls_cert_to_pem(&pem, ctx->p, cert))) {
143 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ctx->s, APLOGNO(10316)
144 "Failed to create server certificate PEM");
145 return NULL;
146 }
147 return pem;
148 }
149
150 typedef struct {
151 const char *name;
152 var_lookup* fn;
153 const char *arg_s;
154 int arg_i;
155 } var_def_t;
156
157 static const var_def_t VAR_DEFS[] = {
158 { "SSL_PROTOCOL", var_get_ssl_protocol, NULL, 0 },
159 { "SSL_CIPHER", var_get_ssl_cipher, NULL, 0 },
160 { "SSL_TLS_SNI", var_get_sni_hostname, NULL, 0 },
161 { "SSL_CLIENT_S_DN_CN", var_get_client_s_dn_cn, NULL, 0 },
162 { "SSL_VERSION_INTERFACE", var_get_version_interface, NULL, 0 },
163 { "SSL_VERSION_LIBRARY", var_get_version_library, NULL, 0 },
164 { "SSL_SECURE_RENEG", var_get_false, NULL, 0 },
165 { "SSL_COMPRESS_METHOD", var_get_null, NULL, 0 },
166 { "SSL_CIPHER_EXPORT", var_get_false, NULL, 0 },
167 { "SSL_CLIENT_VERIFY", var_get_client_verify, NULL, 0 },
168 { "SSL_SESSION_RESUMED", var_get_session_resumed, NULL, 0 },
169 { "SSL_CLIENT_CERT", var_get_client_cert, NULL, 0 },
170 { "SSL_CLIENT_CHAIN_0", var_get_client_cert, "chain", 0 },
171 { "SSL_CLIENT_CHAIN_1", var_get_client_cert, "chain", 1 },
172 { "SSL_CLIENT_CHAIN_2", var_get_client_cert, "chain", 2 },
173 { "SSL_CLIENT_CHAIN_3", var_get_client_cert, "chain", 3 },
174 { "SSL_CLIENT_CHAIN_4", var_get_client_cert, "chain", 4 },
175 { "SSL_CLIENT_CHAIN_5", var_get_client_cert, "chain", 5 },
176 { "SSL_CLIENT_CHAIN_6", var_get_client_cert, "chain", 6 },
177 { "SSL_CLIENT_CHAIN_7", var_get_client_cert, "chain", 7 },
178 { "SSL_CLIENT_CHAIN_8", var_get_client_cert, "chain", 8 },
179 { "SSL_CLIENT_CHAIN_9", var_get_client_cert, "chain", 9 },
180 { "SSL_SERVER_CERT", var_get_server_cert, NULL, 0 },
181 };
182
183 static const char *const TlsAlwaysVars[] = {
184 "SSL_TLS_SNI",
185 "SSL_PROTOCOL",
186 "SSL_CIPHER",
187 "SSL_CLIENT_S_DN_CN",
188 };
189
190 /* what mod_ssl defines, plus server cert and client cert DN and SAN entries */
191 static const char *const StdEnvVars[] = {
192 "SSL_VERSION_INTERFACE", /* implemented: module version string */
193 "SSL_VERSION_LIBRARY", /* implemented: crustls/rustls version string */
194 "SSL_SECURE_RENEG", /* implemented: always "false" */
195 "SSL_COMPRESS_METHOD", /* implemented: always "NULL" */
196 "SSL_CIPHER_EXPORT", /* implemented: always "false" */
197 "SSL_CIPHER_USEKEYSIZE",
198 "SSL_CIPHER_ALGKEYSIZE",
199 "SSL_CLIENT_VERIFY", /* implemented: always "SUCCESS" or "NONE" */
200 "SSL_CLIENT_M_VERSION",
201 "SSL_CLIENT_M_SERIAL",
202 "SSL_CLIENT_V_START",
203 "SSL_CLIENT_V_END",
204 "SSL_CLIENT_V_REMAIN",
205 "SSL_CLIENT_S_DN",
206 "SSL_CLIENT_I_DN",
207 "SSL_CLIENT_A_KEY",
208 "SSL_CLIENT_A_SIG",
209 "SSL_CLIENT_CERT_RFC4523_CEA",
210 "SSL_SERVER_M_VERSION",
211 "SSL_SERVER_M_SERIAL",
212 "SSL_SERVER_V_START",
213 "SSL_SERVER_V_END",
214 "SSL_SERVER_S_DN",
215 "SSL_SERVER_I_DN",
216 "SSL_SERVER_A_KEY",
217 "SSL_SERVER_A_SIG",
218 "SSL_SESSION_ID", /* not implemented: highly sensitive data we do not expose */
219 "SSL_SESSION_RESUMED", /* implemented: if our cache was hit successfully */
220 };
221
222 /* Cert related variables, export when TLSOption ExportCertData is set */
223 static const char *const ExportCertVars[] = {
224 "SSL_CLIENT_CERT", /* implemented: */
225 "SSL_CLIENT_CHAIN_0", /* implemented: */
226 "SSL_CLIENT_CHAIN_1", /* implemented: */
227 "SSL_CLIENT_CHAIN_2", /* implemented: */
228 "SSL_CLIENT_CHAIN_3", /* implemented: */
229 "SSL_CLIENT_CHAIN_4", /* implemented: */
230 "SSL_CLIENT_CHAIN_5", /* implemented: */
231 "SSL_CLIENT_CHAIN_6", /* implemented: */
232 "SSL_CLIENT_CHAIN_7", /* implemented: */
233 "SSL_CLIENT_CHAIN_8", /* implemented: */
234 "SSL_CLIENT_CHAIN_9", /* implemented: */
235 "SSL_SERVER_CERT", /* implemented: */
236 };
237
tls_var_init_lookup_hash(apr_pool_t * pool,apr_hash_t * map)238 void tls_var_init_lookup_hash(apr_pool_t *pool, apr_hash_t *map)
239 {
240 const var_def_t *def;
241 apr_size_t i;
242
243 (void)pool;
244 for (i = 0; i < TLS_DIM(VAR_DEFS); ++i) {
245 def = &VAR_DEFS[i];
246 apr_hash_set(map, def->name, APR_HASH_KEY_STRING, def);
247 }
248 }
249
invoke(var_def_t * def,tls_var_lookup_ctx_t * ctx)250 static const char *invoke(var_def_t* def, tls_var_lookup_ctx_t *ctx)
251 {
252 if (TLS_CONN_ST_IS_ENABLED(ctx->cc)) {
253 const char *val = ctx->cc->subprocess_env?
254 apr_table_get(ctx->cc->subprocess_env, def->name) : NULL;
255 if (val && *val) return val;
256 ctx->arg_s = def->arg_s;
257 ctx->arg_i = def->arg_i;
258 return def->fn(ctx);
259 }
260 return NULL;
261 }
262
set_var(tls_var_lookup_ctx_t * ctx,apr_hash_t * lookups,apr_table_t * table)263 static void set_var(
264 tls_var_lookup_ctx_t *ctx, apr_hash_t *lookups, apr_table_t *table)
265 {
266 var_def_t* def = apr_hash_get(lookups, ctx->name, APR_HASH_KEY_STRING);
267 if (def) {
268 const char *val = invoke(def, ctx);
269 if (val && *val) {
270 apr_table_setn(table, ctx->name, val);
271 }
272 }
273 }
274
tls_var_lookup(apr_pool_t * p,server_rec * s,conn_rec * c,request_rec * r,const char * name)275 const char *tls_var_lookup(
276 apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, const char *name)
277 {
278 const char *val = NULL;
279 tls_conf_server_t *sc;
280 var_def_t* def;
281
282 ap_assert(p);
283 ap_assert(name);
284 s = s? s : (r? r->server : (c? c->base_server : NULL));
285 c = c? c : (r? r->connection : NULL);
286
287 sc = tls_conf_server_get(s? s : ap_server_conf);
288 def = apr_hash_get(sc->global->var_lookups, name, APR_HASH_KEY_STRING);
289 if (def) {
290 tls_var_lookup_ctx_t ctx;
291 ctx.p = p;
292 ctx.s = s;
293 ctx.c = c;
294 ctx.r = r;
295 ctx.cc = c? tls_conf_conn_get(c->master? c->master : c) : NULL;
296 ctx.cc = c? tls_conf_conn_get(c->master? c->master : c) : NULL;
297 ctx.name = name;
298 val = invoke(def, &ctx);
299 ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c, "tls lookup of var '%s' -> '%s'", name, val);
300 }
301 return val;
302 }
303
add_vars(apr_table_t * env,conn_rec * c,server_rec * s,request_rec * r)304 static void add_vars(apr_table_t *env, conn_rec *c, server_rec *s, request_rec *r)
305 {
306 tls_conf_server_t *sc;
307 tls_conf_dir_t *dc, *sdc;
308 tls_var_lookup_ctx_t ctx;
309 apr_size_t i;
310 int overlap;
311
312 sc = tls_conf_server_get(s);
313 dc = r? tls_conf_dir_get(r) : tls_conf_dir_server_get(s);
314 sdc = r? tls_conf_dir_server_get(s): dc;
315 ctx.p = r? r->pool : c->pool;
316 ctx.s = s;
317 ctx.c = c;
318 ctx.r = r;
319 ctx.cc = tls_conf_conn_get(c->master? c->master : c);
320 /* Can we re-use the precomputed connection values? */
321 overlap = (r && ctx.cc->subprocess_env && r->server == ctx.cc->server);
322 if (overlap) {
323 apr_table_overlap(env, ctx.cc->subprocess_env, APR_OVERLAP_TABLES_SET);
324 }
325 else {
326 apr_table_setn(env, "HTTPS", "on");
327 for (i = 0; i < TLS_DIM(TlsAlwaysVars); ++i) {
328 ctx.name = TlsAlwaysVars[i];
329 set_var(&ctx, sc->global->var_lookups, env);
330 }
331 }
332 if (dc->std_env_vars == TLS_FLAG_TRUE) {
333 for (i = 0; i < TLS_DIM(StdEnvVars); ++i) {
334 ctx.name = StdEnvVars[i];
335 set_var(&ctx, sc->global->var_lookups, env);
336 }
337 }
338 else if (overlap && sdc->std_env_vars == TLS_FLAG_TRUE) {
339 /* Remove variables added on connection init that are disabled here */
340 for (i = 0; i < TLS_DIM(StdEnvVars); ++i) {
341 apr_table_unset(env, StdEnvVars[i]);
342 }
343 }
344 if (dc->export_cert_vars == TLS_FLAG_TRUE) {
345 for (i = 0; i < TLS_DIM(ExportCertVars); ++i) {
346 ctx.name = ExportCertVars[i];
347 set_var(&ctx, sc->global->var_lookups, env);
348 }
349 }
350 else if (overlap && sdc->std_env_vars == TLS_FLAG_TRUE) {
351 /* Remove variables added on connection init that are disabled here */
352 for (i = 0; i < TLS_DIM(ExportCertVars); ++i) {
353 apr_table_unset(env, ExportCertVars[i]);
354 }
355 }
356 }
357
tls_var_handshake_done(conn_rec * c)358 apr_status_t tls_var_handshake_done(conn_rec *c)
359 {
360 tls_conf_conn_t *cc;
361 tls_conf_server_t *sc;
362 apr_status_t rv = APR_SUCCESS;
363
364 cc = tls_conf_conn_get(c);
365 if (!TLS_CONN_ST_IS_ENABLED(cc)) goto cleanup;
366
367 sc = tls_conf_server_get(cc->server);
368 if (cc->peer_certs && sc->var_user_name) {
369 cc->user_name = tls_var_lookup(c->pool, cc->server, c, NULL, sc->var_user_name);
370 if (!cc->user_name) {
371 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cc->server, APLOGNO(10317)
372 "Failed to set r->user to '%s'", sc->var_user_name);
373 }
374 }
375 cc->subprocess_env = apr_table_make(c->pool, 5);
376 add_vars(cc->subprocess_env, c, cc->server, NULL);
377
378 cleanup:
379 return rv;
380 }
381
tls_var_request_fixup(request_rec * r)382 int tls_var_request_fixup(request_rec *r)
383 {
384 conn_rec *c = r->connection;
385 tls_conf_conn_t *cc;
386
387 cc = tls_conf_conn_get(c->master? c->master : c);
388 if (!TLS_CONN_ST_IS_ENABLED(cc)) goto cleanup;
389 if (cc->user_name) {
390 /* why is r->user a char* and not const? */
391 r->user = apr_pstrdup(r->pool, cc->user_name);
392 }
393 add_vars(r->subprocess_env, c, r->server, r);
394
395 cleanup:
396 return DECLINED;
397 }
398