diff options
-rw-r--r-- | lib/vtls/openssl.c | 28 | ||||
-rw-r--r-- | tests/tests-httpd/test_10_proxy.py | 70 | ||||
-rw-r--r-- | tests/tests-httpd/testenv/env.py | 7 | ||||
-rw-r--r-- | tests/tests-httpd/testenv/httpd.py | 36 |
4 files changed, 133 insertions, 8 deletions
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 51d2de9e3..971398f4d 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2122,6 +2122,22 @@ static bool subj_alt_hostcheck(struct Curl_easy *data, return FALSE; } +static CURLcode +ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + X509 *server_cert, const char *hostname, + const char *dispname); + +CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + X509 *server_cert) +{ + const char *hostname, *dispname; + int port; + + (void)conn; + Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port); + return ossl_verifyhost(data, conn, server_cert, hostname, dispname); +} + /* Quote from RFC2818 section 3.1 "Server Identity" If a subjectAltName extension of type dNSName is present, that MUST @@ -2144,8 +2160,10 @@ static bool subj_alt_hostcheck(struct Curl_easy *data, This function is now used from ngtcp2 (QUIC) as well. */ -CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - X509 *server_cert) +static CURLcode +ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + X509 *server_cert, const char *hostname, + const char *dispname) { bool matched = FALSE; int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */ @@ -2159,12 +2177,9 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, CURLcode result = CURLE_OK; bool dNSName = FALSE; /* if a dNSName field exists in the cert */ bool iPAddress = FALSE; /* if a iPAddress field exists in the cert */ - const char *hostname, *dispname; - int port; size_t hostlen; (void)conn; - Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port); hostlen = strlen(hostname); #ifndef ENABLE_IPV6 @@ -4129,7 +4144,8 @@ static CURLcode servercert(struct Curl_cfilter *cf, BIO_free(mem); if(conn_config->verifyhost) { - result = Curl_ossl_verifyhost(data, conn, backend->server_cert); + result = ossl_verifyhost(data, conn, backend->server_cert, + connssl->hostname, connssl->dispname); if(result) { X509_free(backend->server_cert); backend->server_cert = NULL; diff --git a/tests/tests-httpd/test_10_proxy.py b/tests/tests-httpd/test_10_proxy.py new file mode 100644 index 000000000..1c444eec0 --- /dev/null +++ b/tests/tests-httpd/test_10_proxy.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 2008 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# +import logging +import os +import pytest + +from testenv import Env, CurlClient + + +log = logging.getLogger(__name__) + + +@pytest.mark.skipif(condition=Env.setup_incomplete(), + reason=f"missing: {Env.incomplete_reason()}") +class TestProxy: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env, httpd): + push_dir = os.path.join(httpd.docs_dir, 'push') + if not os.path.exists(push_dir): + os.makedirs(push_dir) + + # download via http: proxy (no tunnel) + def test_10_01_http_get(self, env: Env, httpd, repeat): + curl = CurlClient(env=env) + url = f'http://localhost:{env.http_port}/data.json' + r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True, + extra_args=[ + '--proxy', f'http://{env.proxy_domain}:{env.http_port}/', + '--resolve', f'{env.proxy_domain}:{env.http_port}:127.0.0.1', + ]) + assert r.exit_code == 0 + r.check_stats(count=1, exp_status=200) + + # download via https: proxy (no tunnel) + def test_10_02_http_get(self, env: Env, httpd, repeat): + curl = CurlClient(env=env) + url = f'http://localhost:{env.http_port}/data.json' + r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True, + extra_args=[ + '--proxy', f'https://{env.proxy_domain}:{env.https_port}/', + '--resolve', f'{env.proxy_domain}:{env.https_port}:127.0.0.1', + '--proxy-cacert', env.ca.cert_file, + ]) + assert r.exit_code == 0 + r.check_stats(count=1, exp_status=200) diff --git a/tests/tests-httpd/testenv/env.py b/tests/tests-httpd/testenv/env.py index 83d3cce4c..07eb999e9 100644 --- a/tests/tests-httpd/testenv/env.py +++ b/tests/tests-httpd/testenv/env.py @@ -113,9 +113,11 @@ class EnvConfig: self.tld = 'tests-httpd.curl.se' self.domain1 = f"one.{self.tld}" self.domain2 = f"two.{self.tld}" + self.proxy_domain = f"proxy.{self.tld}" self.cert_specs = [ CertificateSpec(domains=[self.domain1], key_type='rsa2048'), CertificateSpec(domains=[self.domain2], key_type='rsa2048'), + CertificateSpec(domains=[self.proxy_domain], key_type='rsa2048'), CertificateSpec(name="clientsX", sub_specs=[ CertificateSpec(name="user1", client=True), ]), @@ -295,6 +297,11 @@ class Env: return self.CONFIG.domain2 @property + def proxy_domain(self) -> str: + return self.CONFIG.proxy_domain + + + @property def http_port(self) -> str: return self.CONFIG.http_port diff --git a/tests/tests-httpd/testenv/httpd.py b/tests/tests-httpd/testenv/httpd.py index 066ed6db7..24399454a 100644 --- a/tests/tests-httpd/testenv/httpd.py +++ b/tests/tests-httpd/testenv/httpd.py @@ -44,9 +44,9 @@ class Httpd: MODULES = [ 'log_config', 'logio', 'unixd', 'version', 'watchdog', - 'authn_core', 'authz_user', 'authz_core', + 'authn_core', 'authz_user', 'authz_core', 'authz_host', 'env', 'filter', 'headers', 'mime', - 'rewrite', 'http2', 'ssl', + 'rewrite', 'http2', 'ssl', 'proxy', 'proxy_http', 'proxy_connect', 'mpm_event', ] COMMON_MODULES_DIRS = [ @@ -191,6 +191,8 @@ class Httpd: creds1 = self.env.get_credentials(domain1) domain2 = self.env.domain2 creds2 = self.env.get_credentials(domain2) + proxy_domain = self.env.proxy_domain + proxy_creds = self.env.get_credentials(proxy_domain) self._mkpath(self._conf_dir) self._mkpath(self._logs_dir) self._mkpath(self._tmp_dir) @@ -218,6 +220,8 @@ class Httpd: f'ErrorLog {self._error_log}', f'LogLevel {self._get_log_level()}', f'LogLevel http:trace4', + f'LogLevel proxy:trace4', + f'LogLevel proxy_http:trace4', f'H2MinWorkers 16', f'H2MaxWorkers 128', f'Listen {self.env.http_port}', @@ -227,6 +231,7 @@ class Httpd: conf.extend([ # plain http host for domain1 f'<VirtualHost *:{self.env.http_port}>', f' ServerName {domain1}', + f' ServerAlias localhost', f' DocumentRoot "{self._docs_dir}"', ]) conf.extend(self._curltest_conf()) @@ -234,6 +239,33 @@ class Httpd: f'</VirtualHost>', f'', ]) + conf.extend([ # http forward proxy + f'<VirtualHost *:{self.env.http_port}>', + f' ServerName {proxy_domain}', + f' Protocols http/1.1', + f' ProxyRequests On', + f' ProxyVia On', + f' AllowCONNECT {self.env.http_port} {self.env.https_port}', + f' <Proxy "*">', + f' Require ip 127.0.0.1', + f' </Proxy>', + f'</VirtualHost>', + ]) + conf.extend([ # https forward proxy + f'<VirtualHost *:{self.env.https_port}>', + f' ServerName {proxy_domain}', + f' Protocols http/1.1', + f' SSLEngine on', + f' SSLCertificateFile {proxy_creds.cert_file}', + f' SSLCertificateKeyFile {proxy_creds.pkey_file}', + f' ProxyRequests On', + f' ProxyVia On', + f' AllowCONNECT {self.env.http_port} {self.env.https_port}', + f' <Proxy "*">', + f' Require ip 127.0.0.1', + f' </Proxy>', + f'</VirtualHost>', + ]) conf.extend([ # https host for domain1, h1 + h2 f'<VirtualHost *:{self.env.https_port}>', f' ServerName {domain1}', |