Skip to content

Commit 70e4b2f

Browse files
authored
Add -msg and -servername support to openssl s_client (#3098)
### Description of changes: Add -msg flag with full implementation using SSL_set_msg_callback to print TLS protocol messages in OpenSSL-compatible format. Add -servername flag that maps to DoClient's existing -server-name for SNI support. ### Call-outs: I did not end up printing the hex dumps of each message like OpenSSL does. Should be easy enough to add if we wanted to, but the compatibility gap being addressed by this does not require it. The `-msg` argument only applies to `s_client` but it had to be implemented inside `client.cc` because it impacts the `SSL_CTX`. This may raise the eyebrows of some purists. ### Testing: How is this change tested (unit tests, fuzz tests, etc.)? Are there any testing steps to be verified by the reviewer? ``` # Example -msg output ➜ tool-openssl git:(msg-fix) ✗ ./openssl s_client -connect amazon.com:443 -msg -servername amazon.com | grep -E '>>>|<<<' >>> TLS 1.2, RecordHeader [length 0005] >>> TLS 1.2, Handshake [length 05c8], ClientHello <<< TLS 1.2, RecordHeader [length 0005] <<< TLS 1.2, Handshake [length 007a], ServerHello >>> TLS 1.2, RecordHeader [length 0005] >>> TLS 1.3, ChangeCipherSpec [length 0001] <<< TLS 1.2, RecordHeader [length 0005] <<< TLS 1.2, RecordHeader [length 0005] <<< TLS 1.3, Handshake [length 000a], EncryptedExtensions <<< TLS 1.2, RecordHeader [length 0005] <<< TLS 1.3, Handshake [length 144c], Certificate <<< TLS 1.2, RecordHeader [length 0005] <<< TLS 1.3, Handshake [length 0108], CertificateVerify <<< TLS 1.2, RecordHeader [length 0005] <<< TLS 1.3, Handshake [length 0024], Finished >>> TLS 1.3, Handshake [length 0024], Finished >>> TLS 1.2, RecordHeader [length 0005] <<< TLS 1.2, RecordHeader [length 0005] <<< TLS 1.3, Handshake [length 0039], NewSessionTicket <<< TLS 1.2, RecordHeader [length 0005] <<< TLS 1.3, Handshake [length 0039], NewSessionTicket <<< TLS 1.2, RecordHeader [length 0005] <<< TLS 1.3, Alert [length 0002] ``` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.
1 parent ad3a9c0 commit 70e4b2f

2 files changed

Lines changed: 102 additions & 0 deletions

File tree

tool-openssl/s_client.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ static const argument_t kArguments[] = {
3434
"Use TLS version 1.2 only." },
3535
{ "-tls1_3", kBooleanArgument,
3636
"Use TLS version 1.3 only." },
37+
{ "-msg", kBooleanArgument,
38+
"Show protocol messages" },
39+
{ "-servername", kOptionalArgument,
40+
"Server name for SNI extension." },
3741
{ "", kOptionalArgument, "" },
3842
};
3943

@@ -67,5 +71,11 @@ bool SClientTool(const args_list_t &args) {
6771
args_map[arg_pair.first] = arg_pair.second;
6872
}
6973

74+
// Map OpenSSL-style -servername to DoClient's -server-name
75+
if (args_map.count("-servername")) {
76+
args_map["-server-name"] = args_map["-servername"];
77+
args_map.erase("-servername");
78+
}
79+
7080
return DoClient(args_map, true);
7181
}

tool/client.cc

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,94 @@ static void PrintOpenSSLConnectionInfo(SSL *ssl, bool show_certs) {
310310
print_verify_details(ssl);
311311
}
312312

313+
static const char *MsgVersionStr(int version) {
314+
switch (version) {
315+
case TLS1_3_VERSION:
316+
return "TLS 1.3";
317+
case TLS1_2_VERSION:
318+
return "TLS 1.2";
319+
case TLS1_1_VERSION:
320+
return "TLS 1.1";
321+
case TLS1_VERSION:
322+
return "TLS 1.0";
323+
case SSL3_VERSION:
324+
return "SSL 3.0";
325+
default:
326+
return "Unknown";
327+
}
328+
}
329+
330+
static const char *MsgContentTypeStr(int content_type) {
331+
switch (content_type) {
332+
case SSL3_RT_HEADER:
333+
return "RecordHeader";
334+
case SSL3_RT_HANDSHAKE:
335+
return "Handshake";
336+
case SSL3_RT_CHANGE_CIPHER_SPEC:
337+
return "ChangeCipherSpec";
338+
case SSL3_RT_ALERT:
339+
return "Alert";
340+
case SSL3_RT_APPLICATION_DATA:
341+
return "ApplicationData";
342+
default:
343+
return "Unknown";
344+
}
345+
}
346+
347+
static const char *MsgHandshakeTypeStr(uint8_t type) {
348+
switch (type) {
349+
case SSL3_MT_CLIENT_HELLO:
350+
return "ClientHello";
351+
case SSL3_MT_SERVER_HELLO:
352+
return "ServerHello";
353+
case SSL3_MT_CERTIFICATE:
354+
return "Certificate";
355+
case SSL3_MT_CERTIFICATE_REQUEST:
356+
return "CertificateRequest";
357+
case SSL3_MT_CERTIFICATE_VERIFY:
358+
return "CertificateVerify";
359+
case SSL3_MT_FINISHED:
360+
return "Finished";
361+
case SSL3_MT_ENCRYPTED_EXTENSIONS:
362+
return "EncryptedExtensions";
363+
case SSL3_MT_NEW_SESSION_TICKET:
364+
return "NewSessionTicket";
365+
default:
366+
return nullptr;
367+
}
368+
}
369+
370+
static const char *MsgAdditionalContextStr(int content_type, const void *buf,
371+
size_t len) {
372+
if (len == 0) {
373+
return nullptr;
374+
}
375+
switch (content_type) {
376+
case SSL3_RT_HANDSHAKE:
377+
return MsgHandshakeTypeStr(reinterpret_cast<const uint8_t *>(buf)[0]);
378+
default:
379+
return nullptr;
380+
}
381+
}
382+
383+
static void MsgCallback(int is_write, int version, int content_type,
384+
const void *buf, size_t len, SSL *ssl, void *arg) {
385+
const char *extra = MsgAdditionalContextStr(content_type, buf, len);
386+
if (extra) {
387+
fprintf(stderr, "%s %s, %s [length %04x], %s\n",
388+
is_write ? ">>>" : "<<<",
389+
MsgVersionStr(version ? version : TLS1_2_VERSION),
390+
MsgContentTypeStr(content_type),
391+
(unsigned)len, extra);
392+
} else {
393+
fprintf(stderr, "%s %s, %s [length %04x]\n",
394+
is_write ? ">>>" : "<<<",
395+
MsgVersionStr(version ? version : TLS1_2_VERSION),
396+
MsgContentTypeStr(content_type),
397+
(unsigned)len);
398+
}
399+
}
400+
313401
static bool DoConnection(SSL_CTX *ctx,
314402
std::map<std::string, std::string> args_map,
315403
bool (*cb)(SSL *ssl, int sock), bool is_openssl_s_client) {
@@ -349,6 +437,10 @@ static bool DoConnection(SSL_CTX *ctx,
349437
bssl::UniquePtr<BIO> bio(BIO_new_socket(sock, BIO_CLOSE));
350438
bssl::UniquePtr<SSL> ssl(SSL_new(ctx));
351439

440+
if (is_openssl_s_client && args_map.count("-msg") != 0) {
441+
SSL_set_msg_callback(ssl.get(), MsgCallback);
442+
}
443+
352444
if (args_map.count("-server-name") != 0) {
353445
if (!SSL_set_tlsext_host_name(ssl.get(),
354446
args_map["-server-name"].c_str())) {

0 commit comments

Comments
 (0)