Skip to content

Commit

Permalink
tools/sslsniff: trace extra libraries
Browse files Browse the repository at this point in the history
Add --extra-lib argument, which can be specified multiple times and
allows specifying multiple extra TLS libraries (gnutls, openssl, nss)
which could be traced in addition to system-wide libraries. It's very
useful for containerized applications.
  • Loading branch information
bacher09 authored and yonghong-song committed Feb 11, 2022
1 parent 3aba46f commit 8db99af
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 20 deletions.
9 changes: 9 additions & 0 deletions man/man8/sslsniff.8
Expand Up @@ -4,6 +4,7 @@ sslsniff \- Print data passed to OpenSSL, GnuTLS or NSS. Uses Linux eBPF/bcc.
.SH SYNOPSIS
.B sslsniff [-h] [-p PID] [-u UID] [-x] [-c COMM] [-o] [-g] [-n] [-d]
.B [--hexdump] [--max-buffer-size SIZE] [-l] [--handshake]
.B [--extra-lib EXTRA_LIB]
.SH DESCRIPTION
sslsniff prints data sent to write/send and read/recv functions of
OpenSSL, GnuTLS and NSS, allowing us to read plain text content before
Expand Down Expand Up @@ -52,6 +53,10 @@ Show function latency in ms.
.TP
\--handshake
Show handshake latency, enabled only if latency option is on.
.TP
\--extra-lib EXTRA_LIB
Consist type of the library and library path separated by colon. Supported
library types are: openssl, gnutls, nss. Can be specified multiple times.
.SH EXAMPLES
.TP
Print all calls to SSL write/send and read/recv system-wide:
Expand All @@ -65,6 +70,10 @@ Print only OpenSSL calls issued by user with UID 1000
Print SSL handshake event and latency for all traced functions:
#
.B sslsniff -l --handshake
.TP
Print only calls to OpenSSL from /some/path/libssl.so
.B sslsniff --no-openssl --no-gnutls --no-nss --extra-lib
.B openssl:/some/path/libssl.so
.SH FIELDS
.TP
FUNC
Expand Down
81 changes: 62 additions & 19 deletions tools/sslsniff.py
Expand Up @@ -19,6 +19,7 @@
import argparse
import binascii
import textwrap
import os.path

# arguments
examples = """examples:
Expand All @@ -33,7 +34,27 @@
./sslsniff -x # show process UID and TID
./sslsniff -l # show function latency
./sslsniff -l --handshake # show SSL handshake latency
./sslsniff --extra-lib openssl:/path/libssl.so.1.1 # sniff extra library
"""


def ssllib_type(input_str):
valid_types = frozenset(['openssl', 'gnutls', 'nss'])

try:
lib_type, lib_path = input_str.split(':', 1)
except ValueError:
raise argparse.ArgumentTypeError("Invalid SSL library param: %r" % input_str)

if lib_type not in valid_types:
raise argparse.ArgumentTypeError("Invalid SSL library type: %r" % lib_type)

if not os.path.isfile(lib_path):
raise argparse.ArgumentTypeError("Invalid library path: %r" % lib_path)

return lib_type, lib_path


parser = argparse.ArgumentParser(
description="Sniff SSL data",
formatter_class=argparse.RawDescriptionHelpFormatter,
Expand Down Expand Up @@ -63,6 +84,8 @@
help="show function latency")
parser.add_argument("--handshake", action="store_true",
help="show SSL handshake latency, enabled only if latency option is on.")
parser.add_argument("--extra-lib", type=ssllib_type, action='append',
help="Intercept calls from extra library (format: lib_type:lib_path)")
args = parser.parse_args()


Expand Down Expand Up @@ -253,49 +276,69 @@
# need to stash the buffer address in a map on the function entry and read it
# on its exit (Mark Drayton)
#
if args.openssl:
b.attach_uprobe(name="ssl", sym="SSL_write",
def attach_openssl(lib):
b.attach_uprobe(name=lib, sym="SSL_write",
fn_name="probe_SSL_rw_enter", pid=args.pid or -1)
b.attach_uretprobe(name="ssl", sym="SSL_write",
b.attach_uretprobe(name=lib, sym="SSL_write",
fn_name="probe_SSL_write_exit", pid=args.pid or -1)
b.attach_uprobe(name="ssl", sym="SSL_read",
b.attach_uprobe(name=lib, sym="SSL_read",
fn_name="probe_SSL_rw_enter", pid=args.pid or -1)
b.attach_uretprobe(name="ssl", sym="SSL_read",
b.attach_uretprobe(name=lib, sym="SSL_read",
fn_name="probe_SSL_read_exit", pid=args.pid or -1)
if args.latency and args.handshake:
b.attach_uprobe(name="ssl", sym="SSL_do_handshake",
fn_name="probe_SSL_do_handshake_enter", pid=args.pid or -1)
b.attach_uretprobe(name="ssl", sym="SSL_do_handshake",
fn_name="probe_SSL_do_handshake_exit", pid=args.pid or -1)

if args.gnutls:
b.attach_uprobe(name="gnutls", sym="gnutls_record_send",
def attach_gnutls(lib):
b.attach_uprobe(name=lib, sym="gnutls_record_send",
fn_name="probe_SSL_rw_enter", pid=args.pid or -1)
b.attach_uretprobe(name="gnutls", sym="gnutls_record_send",
b.attach_uretprobe(name=lib, sym="gnutls_record_send",
fn_name="probe_SSL_write_exit", pid=args.pid or -1)
b.attach_uprobe(name="gnutls", sym="gnutls_record_recv",
b.attach_uprobe(name=lib, sym="gnutls_record_recv",
fn_name="probe_SSL_rw_enter", pid=args.pid or -1)
b.attach_uretprobe(name="gnutls", sym="gnutls_record_recv",
b.attach_uretprobe(name=lib, sym="gnutls_record_recv",
fn_name="probe_SSL_read_exit", pid=args.pid or -1)

if args.nss:
b.attach_uprobe(name="nspr4", sym="PR_Write",
def attach_nss(lib):
b.attach_uprobe(name=lib, sym="PR_Write",
fn_name="probe_SSL_rw_enter", pid=args.pid or -1)
b.attach_uretprobe(name="nspr4", sym="PR_Write",
b.attach_uretprobe(name=lib, sym="PR_Write",
fn_name="probe_SSL_write_exit", pid=args.pid or -1)
b.attach_uprobe(name="nspr4", sym="PR_Send",
b.attach_uprobe(name=lib, sym="PR_Send",
fn_name="probe_SSL_rw_enter", pid=args.pid or -1)
b.attach_uretprobe(name="nspr4", sym="PR_Send",
b.attach_uretprobe(name=lib, sym="PR_Send",
fn_name="probe_SSL_write_exit", pid=args.pid or -1)
b.attach_uprobe(name="nspr4", sym="PR_Read",
b.attach_uprobe(name=lib, sym="PR_Read",
fn_name="probe_SSL_rw_enter", pid=args.pid or -1)
b.attach_uretprobe(name="nspr4", sym="PR_Read",
b.attach_uretprobe(name=lib, sym="PR_Read",
fn_name="probe_SSL_read_exit", pid=args.pid or -1)
b.attach_uprobe(name="nspr4", sym="PR_Recv",
b.attach_uprobe(name=lib, sym="PR_Recv",
fn_name="probe_SSL_rw_enter", pid=args.pid or -1)
b.attach_uretprobe(name="nspr4", sym="PR_Recv",
b.attach_uretprobe(name=lib, sym="PR_Recv",
fn_name="probe_SSL_read_exit", pid=args.pid or -1)


LIB_TRACERS = {
"openssl": attach_openssl,
"gnutls": attach_gnutls,
"nss": attach_nss,
}


if args.openssl:
attach_openssl("ssl")
if args.gnutls:
attach_gnutls("gnutls")
if args.nss:
attach_nss("nspr4")


if args.extra_lib:
for lib_type, lib_path in args.extra_lib:
LIB_TRACERS[lib_type](lib_path)

# define output data structure in Python


Expand Down
14 changes: 13 additions & 1 deletion tools/sslsniff_example.txt
Expand Up @@ -160,12 +160,18 @@ READ/RECV 0.000049464 nginx 7081 1 0.004

----- END DATA -----

Tracing few extra libraries (useful for docker containers and other isolated
apps)

# ./sslsniff.py --extra-lib openssl:/var/lib/docker/overlay2/l/S4EMHE/lib/libssl.so.1.1



USAGE message:

usage: sslsniff.py [-h] [-p PID] [-u UID] [-x] [-c COMM] [-o] [-g] [-n] [-d]
[--hexdump] [--max-buffer-size MAX_BUFFER_SIZE] [-l]
[--handshake]
[--handshake] [--extra-lib EXTRA_LIB]

Sniff SSL data

Expand All @@ -186,6 +192,11 @@ optional arguments:
-l, --latency show function latency
--handshake show SSL handshake latency, enabled only if latency
option is on.
--extra-lib EXTRA_LIB
Intercept calls from extra library
(format: lib_type:lib_path)



examples:
./sslsniff # sniff OpenSSL and GnuTLS functions
Expand All @@ -199,3 +210,4 @@ examples:
./sslsniff -x # show process UID and TID
./sslsniff -l # show function latency
./sslsniff -l --handshake # show SSL handshake latency
./sslsniff --extra-lib openssl:/path/libssl.so.1.1 # sniff extra library

0 comments on commit 8db99af

Please sign in to comment.