Enable hostname verification in client example (F-5621)

The client example set CERT_REQUIRED and loaded CA roots but never set
check_hostname or passed server_hostname to wrap_socket, so wolfSSL
validated the chain to a trusted CA without binding the certificate to
the requested host. A peer presenting any CA-trusted certificate for a
different hostname would be accepted by anyone reusing this as a secure
client template. Make verification configure hostname checking by
default (via a new configure_verification helper) and add a -n flag to
opt out explicitly for IP literals or test certificates.
pull/70/head
Juliusz Sosinowicz 2026-06-23 12:46:11 +00:00
parent 99a4416771
commit 9c26572a41
2 changed files with 121 additions and 6 deletions

View File

@ -89,6 +89,12 @@ def build_arg_parser():
help="Disable client cert check"
)
parser.add_argument(
"-n", action="store_true",
help="Disable server hostname check "
"(needed for IP literals or test certificates)"
)
parser.add_argument(
"-g", action="store_true",
help="Send server HTTP GET"
@ -125,6 +131,33 @@ def get_DTLSmethod(index):
wolfssl.PROTOCOL_DTLSv1_3
)[index]
def configure_verification(context, args):
"""
Configure peer certificate and hostname verification on the context
according to the parsed arguments. Returns the server_hostname to pass
to wrap_socket() (None when no hostname check should be performed).
When certificate verification is enabled (the default), hostname
verification is enabled too so that a CA-trusted certificate issued for
a different host is rejected. Pass -n to opt out explicitly (e.g. when
connecting to an IP literal or using test certificates).
"""
if args.d:
context.verify_mode = wolfssl.CERT_NONE
context.check_hostname = False
return None
context.verify_mode = wolfssl.CERT_REQUIRED
context.load_verify_locations(args.A)
if args.n:
context.check_hostname = False
return None
context.check_hostname = True
return args.h
def main():
args = build_arg_parser().parse_args()
@ -147,18 +180,15 @@ def main():
context.load_cert_chain(args.c, args.k)
if args.d:
context.verify_mode = wolfssl.CERT_NONE
else:
context.verify_mode = wolfssl.CERT_REQUIRED
context.load_verify_locations(args.A)
server_hostname = configure_verification(context, args)
if args.l:
context.set_ciphers(args.l)
secure_socket = None
try:
secure_socket = context.wrap_socket(bind_socket)
secure_socket = context.wrap_socket(
bind_socket, server_hostname=server_hostname)
if not args.C:
secure_socket.enable_crl(1)

View File

@ -0,0 +1,85 @@
# -*- coding: utf-8 -*-
#
# test_client_example.py
#
# Copyright (C) 2006-2020 wolfSSL Inc.
#
# This file is part of wolfSSL. (formerly known as CyaSSL)
#
# wolfSSL is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# wolfSSL is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
# pylint: disable=missing-docstring, invalid-name, import-error
import os
import sys
import wolfssl
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "examples"))
import client as client_example # noqa: E402
def _args(argv):
return client_example.build_arg_parser().parse_args(argv)
def _ctx():
return wolfssl.SSLContext(wolfssl.PROTOCOL_TLSv1_2)
def test_verification_enables_hostname_check_by_default():
"""
F-5621: with cert verification on (the default), the client must also
verify the peer's hostname and pass server_hostname to wrap_socket.
"""
args = _args(["-h", "example.com"])
ctx = _ctx()
server_hostname = client_example.configure_verification(ctx, args)
assert ctx.verify_mode == wolfssl.CERT_REQUIRED
assert ctx.check_hostname is True
assert server_hostname == "example.com"
def test_disable_cert_check_skips_hostname():
args = _args(["-d"])
ctx = _ctx()
# Simulate a reused context that previously had hostname checking on:
# -d must clear it, not leave it dangling against CERT_NONE.
ctx.verify_mode = wolfssl.CERT_REQUIRED
ctx.check_hostname = True
server_hostname = client_example.configure_verification(ctx, args)
assert ctx.verify_mode == wolfssl.CERT_NONE
assert ctx.check_hostname is False
assert server_hostname is None
def test_hostname_check_can_be_opted_out():
"""An explicit opt-out is provided for IP literals / test certs."""
args = _args(["-n"])
ctx = _ctx()
# Reused context with hostname checking previously enabled: -n must
# actively turn it back off.
ctx.verify_mode = wolfssl.CERT_REQUIRED
ctx.check_hostname = True
server_hostname = client_example.configure_verification(ctx, args)
assert ctx.verify_mode == wolfssl.CERT_REQUIRED
assert ctx.check_hostname is False
assert server_hostname is None