From: Jakub Zelenka Date: Fri, 17 Jul 2026 22:09:30 +0000 (+0200) Subject: apps: decode DTLSv1.2 records in s_client/s_server -msg output X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e0bf61917aa5e9f75c0ee36d0809620823fe818f;p=thirdparty%2Fopenssl.git apps: decode DTLSv1.2 records in s_client/s_server -msg output The msg_cb message callback used by the -msg option only recognised DTLSv1.0 among the DTLS versions, so DTLSv1.2 records were logged as "Not TLS data or unknown version" instead of being decoded. Add DTLS1_2_VERSION to the recognised version check and to the ssl_versions lookup table, and wrap the now-overlong condition. Add a test that runs s_client against s_server over TLSv1.2, TLSv1.3 and DTLSv1.2, logging the protocol messages via -msg, and checks that every record is decoded (no "Not TLS data or unknown version" lines). Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Tim Hudson Reviewed-by: Paul Dale Reviewed-by: Frederik Wedel-Heinen MergeDate: Mon Jul 20 09:09:07 2026 (Merged from https://github.com/openssl/openssl/pull/31994) --- diff --git a/apps/lib/s_cb.c b/apps/lib/s_cb.c index 4f2503502b0..f83ffd7236e 100644 --- a/apps/lib/s_cb.c +++ b/apps/lib/s_cb.c @@ -576,6 +576,7 @@ static STRINT_PAIR ssl_versions[] = { { "TLS 1.2", TLS1_2_VERSION }, { "TLS 1.3", TLS1_3_VERSION }, { "DTLS 1.0", DTLS1_VERSION }, + { "DTLS 1.2", DTLS1_2_VERSION }, { "DTLS 1.0 (bad)", DTLS1_BAD_VER }, { NULL } }; @@ -653,7 +654,10 @@ void msg_cb(int write_p, int version, int content_type, const void *buf, const char *str_version, *str_content_type = "", *str_details1 = "", *str_details2 = ""; const unsigned char *bp = buf; - if (version == TLS1_VERSION || version == TLS1_1_VERSION || version == TLS1_2_VERSION || version == TLS1_3_VERSION || version == DTLS1_VERSION || version == DTLS1_BAD_VER) { + if (version == TLS1_VERSION || version == TLS1_1_VERSION + || version == TLS1_2_VERSION || version == TLS1_3_VERSION + || version == DTLS1_VERSION || version == DTLS1_2_VERSION + || version == DTLS1_BAD_VER) { str_version = lookup(version, ssl_versions, "???"); switch (content_type) { case SSL3_RT_CHANGE_CIPHER_SPEC: diff --git a/test/recipes/20-test_app_s_client_msg.t b/test/recipes/20-test_app_s_client_msg.t new file mode 100644 index 00000000000..75d388efe85 --- /dev/null +++ b/test/recipes/20-test_app_s_client_msg.t @@ -0,0 +1,110 @@ +#! /usr/bin/env perl +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; +use warnings; + +use IPC::Open3; +use OpenSSL::Test qw/:DEFAULT result_dir srctop_file bldtop_file/; +use OpenSSL::Test::Utils; + +my $test_name = "test_app_s_client_msg"; +setup($test_name); + +plan skip_all => "$test_name needs sock enabled" + if disabled("sock"); +plan skip_all => "$test_name is not available on Windows or VMS" + if $^O =~ /^(VMS|MSWin32|msys)$/; + +my $shlib_wrap = bldtop_file("util", "wrap.pl"); +my $apps_openssl = bldtop_file("apps", "openssl"); +my $server_pem = srctop_file("test", "certs", "servercert.pem"); +my $server_key = srctop_file("test", "certs", "serverkey.pem"); +my $resultdir = result_dir(); + +# Each case exercises the s_client message callback (-msg) over a different +# protocol version. Every record must be decoded; before the DTLSv1.2 fix such +# records were logged as "Not TLS data or unknown version". +my @cases = ( + { name => "TLSv1.2", flag => "-tls1_2", disabled => "tls1_2" }, + { name => "TLSv1.3", flag => "-tls1_3", disabled => "tls1_3" }, + { name => "DTLSv1.2", flag => "-dtls1_2", disabled => "dtls1_2" }, +); +@cases = grep { !disabled($_->{disabled}) } @cases; + +plan tests => scalar @cases; + +# Run one s_server/s_client handshake logging protocol messages via -msgfile. +# Returns the number of decoded and undecoded records seen in the log. +sub run_case +{ + my $case = shift; + my $msgfile = "$resultdir/s_client-msg-$case->{disabled}.txt"; + my ($records, $unknown) = (0, 0); + + eval { + local $SIG{ALRM} = sub { die "timeout\n" }; + alarm 60; + + # Start a server speaking just this protocol version + my @s_server_cmd = ("s_server", $case->{flag}, "-accept", "0", + "-naccept", "1", "-cert", $server_pem, + "-key", $server_key); + my $s_server_pid = open3(my $s_server_i, my $s_server_o, my $s_server_e, + $shlib_wrap, $apps_openssl, @s_server_cmd); + + # Figure out what port it is listening on + my $server_port = "0"; + while (<$s_server_o>) { + print($_); + chomp; + if (/^ACCEPT \S+?:(\d+)/) { + $server_port = $1; + last; + } elsif (/^Using default/) { + ; + } else { + last; + } + } + + # Connect a client that logs the protocol messages to a file. -msgfile + # sets the log destination but selects SSL_trace; the trailing -msg + # switches the callback back to msg_cb (the code under test) while + # keeping the file destination. + my @s_client_cmd = ("s_client", $case->{flag}, "-msgfile", $msgfile, + "-msg", "-connect", "localhost:$server_port"); + my $s_client_pid = open3(my $s_client_i, my $s_client_o, my $s_client_e, + $shlib_wrap, $apps_openssl, @s_client_cmd); + + # Quit the client once connected, then reap both processes + print $s_client_i "Q\n"; + waitpid($s_client_pid, 0); + kill 'HUP', $s_server_pid if kill 0, $s_server_pid; + waitpid($s_server_pid, 0); + + alarm 0; + }; + die $@ if $@ && $@ ne "timeout\n"; + print("TIMEOUT: $case->{name} timed out\n") if $@; + + if (open(my $fh, '<', $msgfile)) { + while (<$fh>) { + $records++ if /^(?:>>>|<<<)/; + $unknown++ if /Not TLS data or unknown version/; + } + close($fh); + } + return ($records, $unknown); +} + +foreach my $case (@cases) { + my ($records, $unknown) = run_case($case); + ok($records > 0 && $unknown == 0, + "s_client -msg decodes all $case->{name} records"); +}